diff -urN multipath-tools-0.1.4/ChangeLog multipath-tools-0.1.5/ChangeLog
--- multipath-tools-0.1.4/ChangeLog	2004-03-17 12:18:41.000000000 +0100
+++ multipath-tools-0.1.5/ChangeLog	2004-03-25 17:51:57.000000000 +0100
@@ -1,3 +1,15 @@
+2004-03-25 multipath-tools-0.1.5
+	* kpartx to manage the nested bdevs as /dev/cciss/c0d0.
+	  parts are named sysfs style : cciss!c0d0p*
+	* kpartx loop support
+	* kpartx do DM updates if part maps already present
+	* merge kpartx for partitioned multipath support
+	* add get_null_uid to getuid methods. assign it the "0" index
+	  devices with this getuid are thus ignored by multipath.
+	  warning : change /etc/multipath.conf (get_evpd_wwid == 1)
+	* mv all_scsi_ids out of the 2.6 code path, into the 2.4 one
+	* unlink runfile on malloc exit path
+	* update multipath manpage (MikeC)
 2004-03-17 multipath-tools-0.1.4
 	* multipath clean up
 		* split default hw table in hwtable.h
diff -urN multipath-tools-0.1.4/Makefile multipath-tools-0.1.5/Makefile
--- multipath-tools-0.1.4/Makefile	2004-03-01 16:27:38.000000000 +0100
+++ multipath-tools-0.1.5/Makefile	2004-03-22 16:19:49.000000000 +0100
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 2003 Christophe Varoqui, <christophe.varoqui@free.fr>
 
-SUBDIRS = libdevmapper devmap_name multipath multipathd
+SUBDIRS = libdevmapper devmap_name multipath multipathd kpartx
 
 recurse:
 	@for dir in $(SUBDIRS); do\
diff -urN multipath-tools-0.1.4/kpartx/ChangeLog multipath-tools-0.1.5/kpartx/ChangeLog
--- multipath-tools-0.1.4/kpartx/ChangeLog	1970-01-01 01:00:00.000000000 +0100
+++ multipath-tools-0.1.5/kpartx/ChangeLog	2004-03-22 16:13:52.000000000 +0100
@@ -0,0 +1,9 @@
+002:
+* convert to kpartx name everywhere
+* remove all HDGEO ioctl code
+* now work with files by mapping loops on the fly
+* merged and massage lopart.[ch] from lomount.[ch]
+  (due credit to original author here : hpa ?)
+* added a fn find_loop_by_file in lopart.[ch]
+001:
+* Initial release
diff -urN multipath-tools-0.1.4/kpartx/Makefile multipath-tools-0.1.5/kpartx/Makefile
--- multipath-tools-0.1.4/kpartx/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ multipath-tools-0.1.5/kpartx/Makefile	2004-03-23 11:30:07.000000000 +0100
@@ -0,0 +1,40 @@
+EXEC        = kpartx
+
+prefix      =
+exec_prefix = ${prefix}
+bindir      = ${exec_prefix}/sbin
+udevdir     = ../../..
+klibcdir    = $(udevdir)/klibc
+arch        = i386
+klibcarch   = $(klibcdir)/klibc/arch/$(arch)/include
+
+CC = gcc
+GCCINCDIR := ${shell $(CC) -print-search-dirs | sed -ne "s/install: \(.*\)/\1include/gp"}
+KERNEL_DIR = /lib/modules/${shell uname -r}/build
+CFLAGS = -pipe -g -O2 -Wall -Wunused -Wstrict-prototypes -nostdinc \
+	 -I$(klibcdir)/klibc/include -I$(klibcdir)/klibc/include/bits32 \
+	 -I$(GCCINCDIR) -I$(KERNEL_DIR)/include -I. -I$(klibcarch)
+
+OBJ = bsd.o dos.o kpartx.o solaris.o unixware.o gpt.o crc32.o lopart.o xstrncpy.o
+CRT0 = $(udevdir)/klibc/klibc/crt0.o
+LIB = $(udevdir)/klibc/klibc/libc.a
+LIBGCC := $(shell $(CC) -print-libgcc-file-name )
+
+DMOBJS = ../libdevmapper/libdm-common.o \
+	 ../libdevmapper/ioctl/libdevmapper.o
+
+$(EXEC): $(OBJ)
+	$(LD) -o $(EXEC) $(CRT0) $(OBJ) $(DMOBJS) $(LIB) $(LIBGCC)
+	strip $(EXEC)
+
+clean:
+	rm -f $(OBJ) *.o $(EXEC) *~
+
+install:
+	install -d $(DESTDIR)$(bindir)
+	install -m 755 $(EXEC) $(DESTDIR)$(bindir)/
+
+uninstall:
+	rm $(DESTDIR)$(bindir)/$(EXEC)
+
+$(OBJ): kpartx.h
diff -urN multipath-tools-0.1.4/kpartx/README multipath-tools-0.1.5/kpartx/README
--- multipath-tools-0.1.4/kpartx/README	1970-01-01 01:00:00.000000000 +0100
+++ multipath-tools-0.1.5/kpartx/README	2004-03-22 16:13:01.000000000 +0100
@@ -0,0 +1,9 @@
+Rationale :
+===========
+This version of partx is intented to be build 
+static against klibc. Hence you need a recent
+compiled klibc build at hand.
+
+With due respect to the original authors,
+have fun,
+cvaroqui
diff -urN multipath-tools-0.1.4/kpartx/bsd.c multipath-tools-0.1.5/kpartx/bsd.c
--- multipath-tools-0.1.4/kpartx/bsd.c	1970-01-01 01:00:00.000000000 +0100
+++ multipath-tools-0.1.5/kpartx/bsd.c	2004-01-24 23:25:02.000000000 +0100
@@ -0,0 +1,83 @@
+#include <stdio.h>
+#include "kpartx.h"
+
+#define BSD_DISKMAGIC	(0x82564557UL)	/* The disk magic number */
+#define XBSD_MAXPARTITIONS	16
+#define BSD_FS_UNUSED		0
+
+struct bsd_disklabel {
+	unsigned int	d_magic;	/* the magic number */
+	short int	d_type;		/* drive type */
+	short int	d_subtype;	/* controller/d_type specific */
+	char	d_typename[16];		/* type name, e.g. "eagle" */
+	char	d_packname[16];		/* pack identifier */ 
+	unsigned int	d_secsize;	/* # of bytes per sector */
+	unsigned int	d_nsectors;	/* # of data sectors per track */
+	unsigned int	d_ntracks;	/* # of tracks per cylinder */
+	unsigned int	d_ncylinders;	/* # of data cylinders per unit */
+	unsigned int	d_secpercyl;	/* # of data sectors per cylinder */
+	unsigned int	d_secperunit;	/* # of data sectors per unit */
+	unsigned short	d_sparespertrack;/* # of spare sectors per track */
+	unsigned short	d_sparespercyl;	/* # of spare sectors per cylinder */
+	unsigned int	d_acylinders;	/* # of alt. cylinders per unit */
+	unsigned short	d_rpm;		/* rotational speed */
+	unsigned short	d_interleave;	/* hardware sector interleave */
+	unsigned short	d_trackskew;	/* sector 0 skew, per track */
+	unsigned short	d_cylskew;	/* sector 0 skew, per cylinder */
+	unsigned int	d_headswitch;	/* head switch time, usec */
+	unsigned int	d_trkseek;	/* track-to-track seek, usec */
+	unsigned int	d_flags;	/* generic flags */
+	unsigned int	d_drivedata[5];	/* drive-type specific information */
+	unsigned int	d_spare[5];	/* reserved for future use */
+	unsigned int	d_magic2;	/* the magic number (again) */
+	unsigned short	d_checksum;	/* xor of data incl. partitions */
+
+			/* filesystem and partition information: */
+	unsigned short	d_npartitions;	/* number of partitions in following */
+	unsigned int	d_bbsize;	/* size of boot area at sn0, bytes */
+	unsigned int	d_sbsize;	/* max size of fs superblock, bytes */
+	struct	bsd_partition {		/* the partition table */
+		unsigned int	p_size;	  /* number of sectors in partition */
+		unsigned int	p_offset; /* starting sector */
+		unsigned int	p_fsize;  /* filesystem basic fragment size */
+		unsigned char	p_fstype; /* filesystem type, see below */
+		unsigned char	p_frag;	  /* filesystem fragments per block */
+		unsigned short	p_cpg;	  /* filesystem cylinders per group */
+	} d_partitions[XBSD_MAXPARTITIONS];/* actually may be more */
+};
+
+int
+read_bsd_pt(int fd, struct slice all, struct slice *sp, int ns) {
+	struct bsd_disklabel *l;
+	struct bsd_partition *p;
+	unsigned int offset = all.start;
+	int max_partitions;
+	char *bp;
+	int n = 0;
+
+	bp = getblock(fd, offset+1); 	/* 1 sector suffices */
+	if (bp == NULL)
+		return -1;
+
+	l = (struct bsd_disklabel *) bp;
+	if (l->d_magic != BSD_DISKMAGIC)
+		return -1;
+
+	max_partitions = 16;
+	if (l->d_npartitions < max_partitions)
+		max_partitions = l->d_npartitions;
+	for (p = l->d_partitions; p - l->d_partitions <  max_partitions; p++) {
+		if (p->p_fstype == BSD_FS_UNUSED)
+			/* nothing */;
+		else if (n < ns) {
+			sp[n].start = p->p_offset;
+			sp[n].size = p->p_size;
+			n++;
+		} else {
+			fprintf(stderr,
+				"bsd_partition: too many slices\n");
+			break;
+		}
+	}
+	return n;
+}
diff -urN multipath-tools-0.1.4/kpartx/crc32.c multipath-tools-0.1.5/kpartx/crc32.c
--- multipath-tools-0.1.4/kpartx/crc32.c	1970-01-01 01:00:00.000000000 +0100
+++ multipath-tools-0.1.5/kpartx/crc32.c	2002-04-10 12:11:07.000000000 +0200
@@ -0,0 +1,393 @@
+/* 
+ * crc32.c
+ * This code is in the public domain; copyright abandoned.
+ * Liability for non-performance of this code is limited to the amount
+ * you paid for it.  Since it is distributed for free, your refund will
+ * be very very small.  If it breaks, you get to keep both pieces.
+ */
+
+#include "crc32.h"
+
+#if __GNUC__ >= 3	/* 2.x has "attribute", but only 3.0 has "pure */
+#define attribute(x) __attribute__(x)
+#else
+#define attribute(x)
+#endif
+
+/*
+ * There are multiple 16-bit CRC polynomials in common use, but this is
+ * *the* standard CRC-32 polynomial, first popularized by Ethernet.
+ * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0
+ */
+#define CRCPOLY_LE 0xedb88320
+#define CRCPOLY_BE 0x04c11db7
+
+/* How many bits at a time to use.  Requires a table of 4<<CRC_xx_BITS bytes. */
+/* For less performance-sensitive, use 4 */
+#define CRC_LE_BITS 8
+#define CRC_BE_BITS 8
+
+/*
+ * Little-endian CRC computation.  Used with serial bit streams sent
+ * lsbit-first.  Be sure to use cpu_to_le32() to append the computed CRC.
+ */
+#if CRC_LE_BITS > 8 || CRC_LE_BITS < 1 || CRC_LE_BITS & CRC_LE_BITS-1
+# error CRC_LE_BITS must be a power of 2 between 1 and 8
+#endif
+
+#if CRC_LE_BITS == 1
+/*
+ * In fact, the table-based code will work in this case, but it can be
+ * simplified by inlining the table in ?: form.
+ */
+#define crc32init_le()
+#define crc32cleanup_le()
+/**
+ * crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32
+ * @crc - seed value for computation.  ~0 for Ethernet, sometimes 0 for
+ *        other uses, or the previous crc32 value if computing incrementally.
+ * @p   - pointer to buffer over which CRC is run
+ * @len - length of buffer @p
+ * 
+ */
+uint32_t attribute((pure)) crc32_le(uint32_t crc, unsigned char const *p, size_t len)
+{
+	int i;
+	while (len--) {
+		crc ^= *p++;
+		for (i = 0; i < 8; i++)
+			crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
+	}
+	return crc;
+}
+#else				/* Table-based approach */
+
+static uint32_t *crc32table_le;
+/**
+ * crc32init_le() - allocate and initialize LE table data
+ *
+ * crc is the crc of the byte i; other entries are filled in based on the
+ * fact that crctable[i^j] = crctable[i] ^ crctable[j].
+ *
+ */
+static int
+crc32init_le(void)
+{
+	unsigned i, j;
+	uint32_t crc = 1;
+
+	crc32table_le =
+		malloc((1 << CRC_LE_BITS) * sizeof(uint32_t));
+	if (!crc32table_le)
+		return 1;
+	crc32table_le[0] = 0;
+
+	for (i = 1 << (CRC_LE_BITS - 1); i; i >>= 1) {
+		crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
+		for (j = 0; j < 1 << CRC_LE_BITS; j += 2 * i)
+			crc32table_le[i + j] = crc ^ crc32table_le[j];
+	}
+	return 0;
+}
+
+/**
+ * crc32cleanup_le(): free LE table data
+ */
+static void
+crc32cleanup_le(void)
+{
+	if (crc32table_le) free(crc32table_le);
+	crc32table_le = NULL;
+}
+
+/**
+ * crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32
+ * @crc - seed value for computation.  ~0 for Ethernet, sometimes 0 for
+ *        other uses, or the previous crc32 value if computing incrementally.
+ * @p   - pointer to buffer over which CRC is run
+ * @len - length of buffer @p
+ * 
+ */
+uint32_t attribute((pure)) crc32_le(uint32_t crc, unsigned char const *p, size_t len)
+{
+	while (len--) {
+# if CRC_LE_BITS == 8
+		crc = (crc >> 8) ^ crc32table_le[(crc ^ *p++) & 255];
+# elif CRC_LE_BITS == 4
+		crc ^= *p++;
+		crc = (crc >> 4) ^ crc32table_le[crc & 15];
+		crc = (crc >> 4) ^ crc32table_le[crc & 15];
+# elif CRC_LE_BITS == 2
+		crc ^= *p++;
+		crc = (crc >> 2) ^ crc32table_le[crc & 3];
+		crc = (crc >> 2) ^ crc32table_le[crc & 3];
+		crc = (crc >> 2) ^ crc32table_le[crc & 3];
+		crc = (crc >> 2) ^ crc32table_le[crc & 3];
+# endif
+	}
+	return crc;
+}
+#endif
+
+/*
+ * Big-endian CRC computation.  Used with serial bit streams sent
+ * msbit-first.  Be sure to use cpu_to_be32() to append the computed CRC.
+ */
+#if CRC_BE_BITS > 8 || CRC_BE_BITS < 1 || CRC_BE_BITS & CRC_BE_BITS-1
+# error CRC_BE_BITS must be a power of 2 between 1 and 8
+#endif
+
+#if CRC_BE_BITS == 1
+/*
+ * In fact, the table-based code will work in this case, but it can be
+ * simplified by inlining the table in ?: form.
+ */
+#define crc32init_be()
+#define crc32cleanup_be()
+
+/**
+ * crc32_be() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32
+ * @crc - seed value for computation.  ~0 for Ethernet, sometimes 0 for
+ *        other uses, or the previous crc32 value if computing incrementally.
+ * @p   - pointer to buffer over which CRC is run
+ * @len - length of buffer @p
+ * 
+ */
+uint32_t attribute((pure)) crc32_be(uint32_t crc, unsigned char const *p, size_t len)
+{
+	int i;
+	while (len--) {
+		crc ^= *p++ << 24;
+		for (i = 0; i < 8; i++)
+			crc =
+			    (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE :
+					  0);
+	}
+	return crc;
+}
+
+#else				/* Table-based approach */
+static uint32_t *crc32table_be;
+
+/**
+ * crc32init_be() - allocate and initialize BE table data
+ */
+static int
+crc32init_be(void)
+{
+	unsigned i, j;
+	uint32_t crc = 0x80000000;
+
+	crc32table_be =
+		malloc((1 << CRC_BE_BITS) * sizeof(uint32_t));
+	if (!crc32table_be)
+		return 1;
+	crc32table_be[0] = 0;
+
+	for (i = 1; i < 1 << CRC_BE_BITS; i <<= 1) {
+		crc = (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : 0);
+		for (j = 0; j < i; j++)
+			crc32table_be[i + j] = crc ^ crc32table_be[j];
+	}
+	return 0;
+}
+
+/**
+ * crc32cleanup_be(): free BE table data
+ */
+static void
+crc32cleanup_be(void)
+{
+	if (crc32table_be) free(crc32table_be);
+	crc32table_be = NULL;
+}
+
+
+/**
+ * crc32_be() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32
+ * @crc - seed value for computation.  ~0 for Ethernet, sometimes 0 for
+ *        other uses, or the previous crc32 value if computing incrementally.
+ * @p   - pointer to buffer over which CRC is run
+ * @len - length of buffer @p
+ * 
+ */
+uint32_t attribute((pure)) crc32_be(uint32_t crc, unsigned char const *p, size_t len)
+{
+	while (len--) {
+# if CRC_BE_BITS == 8
+		crc = (crc << 8) ^ crc32table_be[(crc >> 24) ^ *p++];
+# elif CRC_BE_BITS == 4
+		crc ^= *p++ << 24;
+		crc = (crc << 4) ^ crc32table_be[crc >> 28];
+		crc = (crc << 4) ^ crc32table_be[crc >> 28];
+# elif CRC_BE_BITS == 2
+		crc ^= *p++ << 24;
+		crc = (crc << 2) ^ crc32table_be[crc >> 30];
+		crc = (crc << 2) ^ crc32table_be[crc >> 30];
+		crc = (crc << 2) ^ crc32table_be[crc >> 30];
+		crc = (crc << 2) ^ crc32table_be[crc >> 30];
+# endif
+	}
+	return crc;
+}
+#endif
+
+/*
+ * A brief CRC tutorial.
+ *
+ * A CRC is a long-division remainder.  You add the CRC to the message,
+ * and the whole thing (message+CRC) is a multiple of the given
+ * CRC polynomial.  To check the CRC, you can either check that the
+ * CRC matches the recomputed value, *or* you can check that the
+ * remainder computed on the message+CRC is 0.  This latter approach
+ * is used by a lot of hardware implementations, and is why so many
+ * protocols put the end-of-frame flag after the CRC.
+ *
+ * It's actually the same long division you learned in school, except that
+ * - We're working in binary, so the digits are only 0 and 1, and
+ * - When dividing polynomials, there are no carries.  Rather than add and
+ *   subtract, we just xor.  Thus, we tend to get a bit sloppy about
+ *   the difference between adding and subtracting.
+ *
+ * A 32-bit CRC polynomial is actually 33 bits long.  But since it's
+ * 33 bits long, bit 32 is always going to be set, so usually the CRC
+ * is written in hex with the most significant bit omitted.  (If you're
+ * familiar with the IEEE 754 floating-point format, it's the same idea.)
+ *
+ * Note that a CRC is computed over a string of *bits*, so you have
+ * to decide on the endianness of the bits within each byte.  To get
+ * the best error-detecting properties, this should correspond to the
+ * order they're actually sent.  For example, standard RS-232 serial is
+ * little-endian; the most significant bit (sometimes used for parity)
+ * is sent last.  And when appending a CRC word to a message, you should
+ * do it in the right order, matching the endianness.
+ *
+ * Just like with ordinary division, the remainder is always smaller than
+ * the divisor (the CRC polynomial) you're dividing by.  Each step of the
+ * division, you take one more digit (bit) of the dividend and append it
+ * to the current remainder.  Then you figure out the appropriate multiple
+ * of the divisor to subtract to being the remainder back into range.
+ * In binary, it's easy - it has to be either 0 or 1, and to make the
+ * XOR cancel, it's just a copy of bit 32 of the remainder.
+ *
+ * When computing a CRC, we don't care about the quotient, so we can
+ * throw the quotient bit away, but subtract the appropriate multiple of
+ * the polynomial from the remainder and we're back to where we started,
+ * ready to process the next bit.
+ *
+ * A big-endian CRC written this way would be coded like:
+ * for (i = 0; i < input_bits; i++) {
+ * 	multiple = remainder & 0x80000000 ? CRCPOLY : 0;
+ * 	remainder = (remainder << 1 | next_input_bit()) ^ multiple;
+ * }
+ * Notice how, to get at bit 32 of the shifted remainder, we look
+ * at bit 31 of the remainder *before* shifting it.
+ *
+ * But also notice how the next_input_bit() bits we're shifting into
+ * the remainder don't actually affect any decision-making until
+ * 32 bits later.  Thus, the first 32 cycles of this are pretty boring.
+ * Also, to add the CRC to a message, we need a 32-bit-long hole for it at
+ * the end, so we have to add 32 extra cycles shifting in zeros at the
+ * end of every message,
+ *
+ * So the standard trick is to rearrage merging in the next_input_bit()
+ * until the moment it's needed.  Then the first 32 cycles can be precomputed,
+ * and merging in the final 32 zero bits to make room for the CRC can be
+ * skipped entirely.
+ * This changes the code to:
+ * for (i = 0; i < input_bits; i++) {
+ *      remainder ^= next_input_bit() << 31;
+ * 	multiple = (remainder & 0x80000000) ? CRCPOLY : 0;
+ * 	remainder = (remainder << 1) ^ multiple;
+ * }
+ * With this optimization, the little-endian code is simpler:
+ * for (i = 0; i < input_bits; i++) {
+ *      remainder ^= next_input_bit();
+ * 	multiple = (remainder & 1) ? CRCPOLY : 0;
+ * 	remainder = (remainder >> 1) ^ multiple;
+ * }
+ *
+ * Note that the other details of endianness have been hidden in CRCPOLY
+ * (which must be bit-reversed) and next_input_bit().
+ *
+ * However, as long as next_input_bit is returning the bits in a sensible
+ * order, we can actually do the merging 8 or more bits at a time rather
+ * than one bit at a time:
+ * for (i = 0; i < input_bytes; i++) {
+ * 	remainder ^= next_input_byte() << 24;
+ * 	for (j = 0; j < 8; j++) {
+ * 		multiple = (remainder & 0x80000000) ? CRCPOLY : 0;
+ * 		remainder = (remainder << 1) ^ multiple;
+ * 	}
+ * }
+ * Or in little-endian:
+ * for (i = 0; i < input_bytes; i++) {
+ * 	remainder ^= next_input_byte();
+ * 	for (j = 0; j < 8; j++) {
+ * 		multiple = (remainder & 1) ? CRCPOLY : 0;
+ * 		remainder = (remainder << 1) ^ multiple;
+ * 	}
+ * }
+ * If the input is a multiple of 32 bits, you can even XOR in a 32-bit
+ * word at a time and increase the inner loop count to 32.
+ *
+ * You can also mix and match the two loop styles, for example doing the
+ * bulk of a message byte-at-a-time and adding bit-at-a-time processing
+ * for any fractional bytes at the end.
+ *
+ * The only remaining optimization is to the byte-at-a-time table method.
+ * Here, rather than just shifting one bit of the remainder to decide
+ * in the correct multiple to subtract, we can shift a byte at a time.
+ * This produces a 40-bit (rather than a 33-bit) intermediate remainder,
+ * but again the multiple of the polynomial to subtract depends only on
+ * the high bits, the high 8 bits in this case.  
+ *
+ * The multile we need in that case is the low 32 bits of a 40-bit
+ * value whose high 8 bits are given, and which is a multiple of the
+ * generator polynomial.  This is simply the CRC-32 of the given
+ * one-byte message.
+ *
+ * Two more details: normally, appending zero bits to a message which
+ * is already a multiple of a polynomial produces a larger multiple of that
+ * polynomial.  To enable a CRC to detect this condition, it's common to
+ * invert the CRC before appending it.  This makes the remainder of the
+ * message+crc come out not as zero, but some fixed non-zero value.
+ *
+ * The same problem applies to zero bits prepended to the message, and
+ * a similar solution is used.  Instead of starting with a remainder of
+ * 0, an initial remainder of all ones is used.  As long as you start
+ * the same way on decoding, it doesn't make a difference.
+ */
+
+
+/**
+ * init_crc32(): generates CRC32 tables
+ * 
+ * On successful initialization, use count is increased.
+ * This guarantees that the library functions will stay resident
+ * in memory, and prevents someone from 'rmmod crc32' while
+ * a driver that needs it is still loaded.
+ * This also greatly simplifies drivers, as there's no need
+ * to call an initialization/cleanup function from each driver.
+ * Since crc32.o is a library module, there's no requirement
+ * that the user can unload it.
+ */
+int
+init_crc32(void)
+{
+	int rc1, rc2, rc;
+	rc1 = crc32init_le();
+	rc2 = crc32init_be();
+	rc = rc1 || rc2;
+	return rc;
+}
+
+/**
+ * cleanup_crc32(): frees crc32 data when no longer needed
+ */
+void
+cleanup_crc32(void)
+{
+	crc32cleanup_le();
+	crc32cleanup_be();
+}
diff -urN multipath-tools-0.1.4/kpartx/crc32.h multipath-tools-0.1.5/kpartx/crc32.h
--- multipath-tools-0.1.4/kpartx/crc32.h	1970-01-01 01:00:00.000000000 +0100
+++ multipath-tools-0.1.5/kpartx/crc32.h	2002-04-10 12:11:07.000000000 +0200
@@ -0,0 +1,19 @@
+/*
+ * crc32.h
+ */
+#ifndef _CRC32_H
+#define _CRC32_H
+
+#include <inttypes.h>
+#include <stdlib.h>
+
+extern int init_crc32(void);
+extern void cleanup_crc32(void);
+extern uint32_t  crc32_le(uint32_t crc, unsigned char const *p, size_t len);
+extern uint32_t  crc32_be(uint32_t crc, unsigned char const *p, size_t len);
+
+#define crc32(seed, data, length)  crc32_le(seed, (unsigned char const *)data, length)
+#define ether_crc_le(length, data) crc32_le(~0, data, length)
+#define ether_crc(length, data)    crc32_be(~0, data, length)
+
+#endif /* _CRC32_H */
diff -urN multipath-tools-0.1.4/kpartx/dos.c multipath-tools-0.1.5/kpartx/dos.c
--- multipath-tools-0.1.4/kpartx/dos.c	1970-01-01 01:00:00.000000000 +0100
+++ multipath-tools-0.1.5/kpartx/dos.c	2004-01-24 23:25:24.000000000 +0100
@@ -0,0 +1,111 @@
+#include <stdio.h>
+#include "kpartx.h"
+#include "dos.h"
+
+static int
+is_extended(int type) {
+	return (type == 5 || type == 0xf || type == 0x85);
+}
+
+static int
+read_extended_partition(int fd, struct partition *ep,
+			struct slice *sp, int ns)
+{
+	struct partition *p;
+	unsigned long start, here;
+	unsigned char *bp;
+	int loopct = 0;
+	int moretodo = 1;
+	int i, n=0;
+
+	here = start = ep->start_sect;
+
+	while (moretodo) {
+		moretodo = 0;
+		if (++loopct > 100)
+			return n;
+
+		bp = getblock(fd, here);
+		if (bp == NULL)
+			return n;
+
+		if (bp[510] != 0x55 || bp[511] != 0xaa)
+			return n;
+
+		p = (struct partition *) (bp + 0x1be);
+
+		for (i=0; i<2; i++, p++) {
+			if (p->nr_sects == 0 || is_extended(p->sys_type))
+				continue;
+			if (n < ns) {
+				sp[n].start = here + p->start_sect;
+				sp[n].size = p->nr_sects;
+				n++;
+			} else {
+				fprintf(stderr,
+				    "dos_extd_partition: too many slices\n");
+				return n;
+			}
+			loopct = 0;
+		}
+
+		p -= 2;
+		for (i=0; i<2; i++, p++) {
+			if(p->nr_sects != 0 && is_extended(p->sys_type)) {
+				here = start + p->start_sect;
+				moretodo = 1;
+				break;
+			}
+		}
+	}
+	return n;
+}
+
+static int
+is_gpt(int type) {
+	return (type == 0xEE);
+}
+
+int
+read_dos_pt(int fd, struct slice all, struct slice *sp, int ns) {
+	struct partition *p;
+	unsigned long offset = all.start;
+	int i, n=0;
+	unsigned char *bp;
+
+	bp = getblock(fd, offset);
+	if (bp == NULL)
+		return -1;
+
+	if (bp[510] != 0x55 || bp[511] != 0xaa)
+		return -1;
+
+	p = (struct partition *) (bp + 0x1be);
+	for (i=0; i<4; i++) {
+		if (is_gpt(p->sys_type)) {
+			return 0;
+		}
+		p++;
+	}
+	p = (struct partition *) (bp + 0x1be);
+	for (i=0; i<4; i++) {
+		/* always add, even if zero length */
+		if (n < ns) {
+			sp[n].start = p->start_sect;
+			sp[n].size = p->nr_sects;
+			n++;
+		} else {
+			fprintf(stderr,
+				"dos_partition: too many slices\n");
+			break;
+		}
+		p++;
+	}
+	p = (struct partition *) (bp + 0x1be);
+	for (i=0; i<4; i++) {
+		if (is_extended(p->sys_type))
+			n += read_extended_partition(fd, p, sp+n, ns-n);
+		p++;
+	}
+	return n;
+}
diff -urN multipath-tools-0.1.4/kpartx/dos.h multipath-tools-0.1.5/kpartx/dos.h
--- multipath-tools-0.1.4/kpartx/dos.h	1970-01-01 01:00:00.000000000 +0100
+++ multipath-tools-0.1.5/kpartx/dos.h	2002-04-10 12:11:07.000000000 +0200
@@ -0,0 +1,13 @@
+#ifndef DOS_H_INCLUDED
+#define DOS_H_INCLUDED
+
+struct partition {
+	unsigned char boot_ind;	/* 0x80 - active */
+	unsigned char bh, bs, bc;
+	unsigned char sys_type;
+	unsigned char eh, es, ec;
+	unsigned int start_sect;
+	unsigned int nr_sects;
+};
+
+#endif				/* DOS_H_INCLUDED */
diff -urN multipath-tools-0.1.4/kpartx/efi.h multipath-tools-0.1.5/kpartx/efi.h
--- multipath-tools-0.1.4/kpartx/efi.h	1970-01-01 01:00:00.000000000 +0100
+++ multipath-tools-0.1.5/kpartx/efi.h	2002-04-10 12:11:07.000000000 +0200
@@ -0,0 +1,57 @@
+/*
+  efi.[ch] - Manipulates EFI variables as exported in /proc/efi/vars
+ 
+  Copyright (C) 2001 Dell Computer Corporation <Matt_Domsch@dell.com>
+ 
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef EFI_H
+#define EFI_H
+
+/*
+ * Extensible Firmware Interface
+ * Based on 'Extensible Firmware Interface Specification'
+ *      version 1.02, 12 December, 2000
+ */
+#include <stdint.h>
+
+typedef struct {
+	uint8_t  b[16];
+} efi_guid_t;
+
+#define EFI_GUID(a,b,c,d0,d1,d2,d3,d4,d5,d6,d7) \
+((efi_guid_t) \
+{{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \
+  (b) & 0xff, ((b) >> 8) & 0xff, \
+  (c) & 0xff, ((c) >> 8) & 0xff, \
+  (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }})
+
+
+/******************************************************
+ * GUIDs
+ ******************************************************/
+#define NULL_GUID \
+EFI_GUID( 0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
+
+static inline int
+efi_guidcmp(efi_guid_t left, efi_guid_t right)
+{
+	return memcmp(&left, &right, sizeof (efi_guid_t));
+}
+
+typedef uint16_t efi_char16_t;		/* UNICODE character */
+
+#endif /* EFI_H */
diff -urN multipath-tools-0.1.4/kpartx/gpt.c multipath-tools-0.1.5/kpartx/gpt.c
--- multipath-tools-0.1.4/kpartx/gpt.c	1970-01-01 01:00:00.000000000 +0100
+++ multipath-tools-0.1.5/kpartx/gpt.c	2004-01-24 23:27:09.000000000 +0100
@@ -0,0 +1,610 @@
+/*
+    gpt.[ch]
+
+    Copyright (C) 2000-2001 Dell Computer Corporation <Matt_Domsch@dell.com> 
+
+    EFI GUID Partition Table handling
+    Per Intel EFI Specification v1.02
+    http://developer.intel.com/technology/efi/efi.htm
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#define _FILE_OFFSET_BITS 64
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <asm/byteorder.h>
+#include "crc32.h"
+#include "gpt.h"
+
+#define BLKGETLASTSECT  _IO(0x12,108)   /* get last sector of block device */
+#define BLKGETSIZE _IO(0x12,96)	        /* return device size */
+#define BLKSSZGET  _IO(0x12,104)	/* get block device sector size */
+#define BLKGETSIZE64 _IOR(0x12,114,sizeof(uint64_t))	/* return device size in bytes (u64 *arg) */
+
+struct blkdev_ioctl_param {
+        unsigned int block;
+        size_t content_length;
+        char * block_contents;
+};
+
+/**
+ * efi_crc32() - EFI version of crc32 function
+ * @buf: buffer to calculate crc32 of
+ * @len - length of buf
+ *
+ * Description: Returns EFI-style CRC32 value for @buf
+ * 
+ * This function uses the little endian Ethernet polynomial
+ * but seeds the function with ~0, and xor's with ~0 at the end.
+ * Note, the EFI Specification, v1.02, has a reference to
+ * Dr. Dobbs Journal, May 1994 (actually it's in May 1992).
+ */
+static inline uint32_t
+efi_crc32(const void *buf, unsigned long len)
+{
+	return (crc32(~0L, buf, len) ^ ~0L);
+}
+
+/**
+ * is_pmbr_valid(): test Protective MBR for validity
+ * @mbr: pointer to a legacy mbr structure
+ *
+ * Description: Returns 1 if PMBR is valid, 0 otherwise.
+ * Validity depends on two things:
+ *  1) MSDOS signature is in the last two bytes of the MBR
+ *  2) One partition of type 0xEE is found
+ */
+static int
+is_pmbr_valid(legacy_mbr *mbr)
+{
+	int i, found = 0, signature = 0;
+	if (!mbr)
+		return 0;
+	signature = (__le16_to_cpu(mbr->signature) == MSDOS_MBR_SIGNATURE);
+	for (i = 0; signature && i < 4; i++) {
+		if (mbr->partition[i].sys_type ==
+                    EFI_PMBR_OSTYPE_EFI_GPT) {
+			found = 1;
+			break;
+		}
+	}
+	return (signature && found);
+}
+
+
+/************************************************************
+ * get_sector_size
+ * Requires:
+ *  - filedes is an open file descriptor, suitable for reading
+ * Modifies: nothing
+ * Returns:
+ *  sector size, or 512.
+ ************************************************************/
+static int
+get_sector_size(int filedes)
+{
+	int rc, sector_size = 512;
+
+	rc = ioctl(filedes, BLKSSZGET, &sector_size);
+	if (rc)
+		sector_size = 512;
+	return sector_size;
+}
+
+/************************************************************
+ * _get_num_sectors
+ * Requires:
+ *  - filedes is an open file descriptor, suitable for reading
+ * Modifies: nothing
+ * Returns:
+ *  Last LBA value on success 
+ *  0 on error
+ *
+ * Try getting BLKGETSIZE64 and BLKSSZGET first,
+ * then BLKGETSIZE if necessary.
+ *  Kernels 2.4.15-2.4.18 and 2.5.0-2.5.3 have a broken BLKGETSIZE64
+ *  which returns the number of 512-byte sectors, not the size of
+ *  the disk in bytes. Fixed in kernels 2.4.18-pre8 and 2.5.4-pre3.
+ ************************************************************/
+static uint64_t
+_get_num_sectors(int filedes)
+{
+	unsigned long sectors=0;
+	int rc;
+#if 0
+        uint64_t bytes=0;
+
+ 	rc = ioctl(filedes, BLKGETSIZE64, &bytes);
+	if (!rc)
+		return bytes / get_sector_size(filedes);
+#endif
+        rc = ioctl(filedes, BLKGETSIZE, &sectors);
+        if (rc)
+                return 0;
+        
+	return sectors;
+}
+
+/************************************************************
+ * last_lba(): return number of last logical block of device
+ * 
+ * @fd
+ * 
+ * Description: returns Last LBA value on success, 0 on error.
+ * Notes: The value st_blocks gives the size of the file
+ *        in 512-byte blocks, which is OK if
+ *        EFI_BLOCK_SIZE_SHIFT == 9.
+ ************************************************************/
+
+static uint64_t
+last_lba(int filedes)
+{
+	int rc;
+	uint64_t sectors = 0;
+	struct stat s;
+	memset(&s, 0, sizeof (s));
+	rc = fstat(filedes, &s);
+	if (rc == -1) {
+		fprintf(stderr, "last_lba() could not stat: %s\n",
+			strerror(errno));
+		return 0;
+	}
+
+	if (S_ISBLK(s.st_mode)) {
+		sectors = _get_num_sectors(filedes);
+	} else {
+		fprintf(stderr,
+			"last_lba(): I don't know how to handle files with mode %x\n",
+			s.st_mode);
+		sectors = 1;
+	}
+
+	return sectors - 1;
+}
+
+
+static ssize_t
+read_lastoddsector(int fd, uint64_t lba, void *buffer, size_t count)
+{
+        int rc;
+        struct blkdev_ioctl_param ioctl_param;
+
+        if (!buffer) return 0; 
+
+        ioctl_param.block = 0; /* read the last sector */
+        ioctl_param.content_length = count;
+        ioctl_param.block_contents = buffer;
+
+        rc = ioctl(fd, BLKGETLASTSECT, &ioctl_param);
+        if (rc == -1) perror("read failed");
+
+        return !rc;
+}
+
+static ssize_t
+read_lba(int fd, uint64_t lba, void *buffer, size_t bytes)
+{
+	int sector_size = get_sector_size(fd);
+	off_t offset = lba * sector_size;
+        ssize_t bytesread;
+
+	lseek(fd, offset, SEEK_SET);
+	bytesread = read(fd, buffer, bytes);
+
+        /* Kludge.  This is necessary to read/write the last
+           block of an odd-sized disk, until Linux 2.5.x kernel fixes.
+           This is only used by gpt.c, and only to read
+           one sector, so we don't have to be fancy.
+        */
+        if (!bytesread && !(last_lba(fd) & 1) && lba == last_lba(fd)) {
+                bytesread = read_lastoddsector(fd, lba, buffer, bytes);
+        }
+        return bytesread;
+}
+
+/**
+ * alloc_read_gpt_entries(): reads partition entries from disk
+ * @fd  is an open file descriptor to the whole disk
+ * @gpt is a buffer into which the GPT will be put  
+ * Description: Returns ptes on success,  NULL on error.
+ * Allocates space for PTEs based on information found in @gpt.
+ * Notes: remember to free pte when you're done!
+ */
+static gpt_entry *
+alloc_read_gpt_entries(int fd, gpt_header * gpt)
+{
+	gpt_entry *pte;
+        size_t count = __le32_to_cpu(gpt->num_partition_entries) *
+                __le32_to_cpu(gpt->sizeof_partition_entry);
+
+        if (!count) return NULL;
+
+	pte = (gpt_entry *)malloc(count);
+	if (!pte)
+		return NULL;
+	memset(pte, 0, count);
+
+	if (!read_lba(fd, __le64_to_cpu(gpt->partition_entry_lba), pte,
+                      count)) {
+		free(pte);
+		return NULL;
+	}
+	return pte;
+}
+
+/**
+ * alloc_read_gpt_header(): Allocates GPT header, reads into it from disk
+ * @fd  is an open file descriptor to the whole disk
+ * @lba is the Logical Block Address of the partition table
+ * 
+ * Description: returns GPT header on success, NULL on error.   Allocates
+ * and fills a GPT header starting at @ from @bdev.
+ * Note: remember to free gpt when finished with it.
+ */
+static gpt_header *
+alloc_read_gpt_header(int fd, uint64_t lba)
+{
+	gpt_header *gpt;
+	gpt = (gpt_header *)
+	    malloc(sizeof (gpt_header));
+	if (!gpt)
+		return NULL;
+	memset(gpt, 0, sizeof (*gpt));
+	if (!read_lba(fd, lba, gpt, sizeof (gpt_header))) {
+		free(gpt);
+		return NULL;
+	}
+
+	return gpt;
+}
+
+/**
+ * is_gpt_valid() - tests one GPT header and PTEs for validity
+ * @fd  is an open file descriptor to the whole disk
+ * @lba is the logical block address of the GPT header to test
+ * @gpt is a GPT header ptr, filled on return.
+ * @ptes is a PTEs ptr, filled on return.
+ *
+ * Description: returns 1 if valid,  0 on error.
+ * If valid, returns pointers to newly allocated GPT header and PTEs.
+ */
+static int
+is_gpt_valid(int fd, uint64_t lba,
+             gpt_header ** gpt, gpt_entry ** ptes)
+{
+	int rc = 0;		/* default to not valid */
+	uint32_t crc, origcrc;
+
+	if (!gpt || !ptes)
+                return 0;
+	if (!(*gpt = alloc_read_gpt_header(fd, lba)))
+		return 0;
+
+	/* Check the GUID Partition Table signature */
+	if (__le64_to_cpu((*gpt)->signature) != GPT_HEADER_SIGNATURE) {
+		/* 
+		   printf("GUID Partition Table Header signature is wrong: %" PRIx64" != %" PRIx64 "\n",
+		   __le64_to_cpu((*gpt)->signature), GUID_PT_HEADER_SIGNATURE);
+		 */
+		free(*gpt);
+		*gpt = NULL;
+		return rc;
+	}
+
+	/* Check the GUID Partition Table Header CRC */
+	origcrc = __le32_to_cpu((*gpt)->header_crc32);
+	(*gpt)->header_crc32 = 0;
+	crc = efi_crc32(*gpt, __le32_to_cpu((*gpt)->header_size));
+	if (crc != origcrc) {
+		// printf( "GPTH CRC check failed, %x != %x.\n", origcrc, crc);
+		(*gpt)->header_crc32 = __cpu_to_le32(origcrc);
+		free(*gpt);
+		*gpt = NULL;
+		return 0;
+	}
+	(*gpt)->header_crc32 = __cpu_to_le32(origcrc);
+
+	/* Check that the my_lba entry points to the LBA
+	 * that contains the GPT we read */
+	if (__le64_to_cpu((*gpt)->my_lba) != lba) {
+		// printf( "my_lba % PRIx64 "x != lba %"PRIx64 "x.\n", __le64_to_cpu((*gpt)->my_lba), lba);
+		free(*gpt);
+		*gpt = NULL;
+		return 0;
+	}
+
+	if (!(*ptes = alloc_read_gpt_entries(fd, *gpt))) {
+		free(*gpt);
+		*gpt = NULL;
+		return 0;
+	}
+
+	/* Check the GUID Partition Entry Array CRC */
+	crc = efi_crc32(*ptes,
+                        __le32_to_cpu((*gpt)->num_partition_entries) *
+			__le32_to_cpu((*gpt)->sizeof_partition_entry));
+	if (crc != __le32_to_cpu((*gpt)->partition_entry_array_crc32)) {
+		// printf("GUID Partitition Entry Array CRC check failed.\n");
+		free(*gpt);
+		*gpt = NULL;
+		free(*ptes);
+		*ptes = NULL;
+		return 0;
+	}
+
+	/* We're done, all's well */
+	return 1;
+}
+/**
+ * compare_gpts() - Search disk for valid GPT headers and PTEs
+ * @pgpt is the primary GPT header
+ * @agpt is the alternate GPT header
+ * @lastlba is the last LBA number
+ * Description: Returns nothing.  Sanity checks pgpt and agpt fields
+ * and prints warnings on discrepancies.
+ * 
+ */
+static void
+compare_gpts(gpt_header *pgpt, gpt_header *agpt, uint64_t lastlba)
+{
+	int error_found = 0;
+	if (!pgpt || !agpt)
+		return;
+	if (__le64_to_cpu(pgpt->my_lba) != __le64_to_cpu(agpt->alternate_lba)) {
+		fprintf(stderr, 
+		       "GPT:Primary header LBA != Alt. header alternate_lba\n");
+		fprintf(stderr,  "GPT:%" PRIx64 "x != %" PRIx64 "x\n",
+		       __le64_to_cpu(pgpt->my_lba),
+                       __le64_to_cpu(agpt->alternate_lba));
+		error_found++;
+	}
+	if (__le64_to_cpu(pgpt->alternate_lba) != __le64_to_cpu(agpt->my_lba)) {
+		fprintf(stderr, 
+		       "GPT:Primary header alternate_lba != Alt. header my_lba\n");
+		fprintf(stderr,  "GPT:%" PRIx64 " != %" PRIx64 "\n",
+		       __le64_to_cpu(pgpt->alternate_lba),
+                       __le64_to_cpu(agpt->my_lba));
+		error_found++;
+	}
+	if (__le64_to_cpu(pgpt->first_usable_lba) !=
+            __le64_to_cpu(agpt->first_usable_lba)) {
+		fprintf(stderr,  "GPT:first_usable_lbas don't match.\n");
+		fprintf(stderr,  "GPT:%" PRIx64 " != %" PRIx64 "\n",
+		       __le64_to_cpu(pgpt->first_usable_lba),
+                       __le64_to_cpu(agpt->first_usable_lba));
+		error_found++;
+	}
+	if (__le64_to_cpu(pgpt->last_usable_lba) !=
+            __le64_to_cpu(agpt->last_usable_lba)) {
+		fprintf(stderr,  "GPT:last_usable_lbas don't match.\n");
+		fprintf(stderr,  "GPT:%" PRIx64 " != %" PRIx64 "\n",
+		       __le64_to_cpu(pgpt->last_usable_lba),
+                       __le64_to_cpu(agpt->last_usable_lba));
+		error_found++;
+	}
+	if (efi_guidcmp(pgpt->disk_guid, agpt->disk_guid)) {
+		fprintf(stderr,  "GPT:disk_guids don't match.\n");
+		error_found++;
+	}
+	if (__le32_to_cpu(pgpt->num_partition_entries) !=
+            __le32_to_cpu(agpt->num_partition_entries)) {
+		fprintf(stderr,  "GPT:num_partition_entries don't match: "
+		       "0x%x != 0x%x\n",
+		       __le32_to_cpu(pgpt->num_partition_entries),
+		       __le32_to_cpu(agpt->num_partition_entries));
+		error_found++;
+	}
+	if (__le32_to_cpu(pgpt->sizeof_partition_entry) !=
+            __le32_to_cpu(agpt->sizeof_partition_entry)) {
+		fprintf(stderr, 
+		       "GPT:sizeof_partition_entry values don't match: "
+		       "0x%x != 0x%x\n",
+                       __le32_to_cpu(pgpt->sizeof_partition_entry),
+		       __le32_to_cpu(agpt->sizeof_partition_entry));
+		error_found++;
+	}
+	if (__le32_to_cpu(pgpt->partition_entry_array_crc32) !=
+            __le32_to_cpu(agpt->partition_entry_array_crc32)) {
+		fprintf(stderr, 
+		       "GPT:partition_entry_array_crc32 values don't match: "
+		       "0x%x != 0x%x\n",
+                       __le32_to_cpu(pgpt->partition_entry_array_crc32),
+		       __le32_to_cpu(agpt->partition_entry_array_crc32));
+		error_found++;
+	}
+	if (__le64_to_cpu(pgpt->alternate_lba) != lastlba) {
+		fprintf(stderr, 
+		       "GPT:Primary header thinks Alt. header is not at the end of the disk.\n");
+		fprintf(stderr,  "GPT:%" PRIx64 " != %" PRIx64 "\n",
+		       __le64_to_cpu(pgpt->alternate_lba), lastlba);
+		error_found++;
+	}
+
+	if (__le64_to_cpu(agpt->my_lba) != lastlba) {
+		fprintf(stderr, 
+		       "GPT:Alternate GPT header not at the end of the disk.\n");
+		fprintf(stderr,  "GPT:%" PRIx64 " != %" PRIx64 "\n",
+		       __le64_to_cpu(agpt->my_lba), lastlba);
+		error_found++;
+	}
+
+	if (error_found)
+		fprintf(stderr, 
+		       "GPT: Use GNU Parted to correct GPT errors.\n");
+	return;
+}
+
+/**
+ * find_valid_gpt() - Search disk for valid GPT headers and PTEs
+ * @fd  is an open file descriptor to the whole disk
+ * @gpt is a GPT header ptr, filled on return.
+ * @ptes is a PTEs ptr, filled on return.
+ * Description: Returns 1 if valid, 0 on error.
+ * If valid, returns pointers to newly allocated GPT header and PTEs.
+ * Validity depends on finding either the Primary GPT header and PTEs valid,
+ * or the Alternate GPT header and PTEs valid, and the PMBR valid.
+ */
+static int
+find_valid_gpt(int fd, gpt_header ** gpt, gpt_entry ** ptes)
+{
+        extern int force_gpt;
+	int good_pgpt = 0, good_agpt = 0, good_pmbr = 0;
+	gpt_header *pgpt = NULL, *agpt = NULL;
+	gpt_entry *pptes = NULL, *aptes = NULL;
+	legacy_mbr *legacymbr = NULL;
+	uint64_t lastlba;
+	if (!gpt || !ptes)
+		return 0;
+
+	lastlba = last_lba(fd);
+	good_pgpt = is_gpt_valid(fd, GPT_PRIMARY_PARTITION_TABLE_LBA,
+				 &pgpt, &pptes);
+        if (good_pgpt) {
+		good_agpt = is_gpt_valid(fd,
+                                         __le64_to_cpu(pgpt->alternate_lba),
+					 &agpt, &aptes);
+                if (!good_agpt) {
+                        good_agpt = is_gpt_valid(fd, lastlba,
+                                                 &agpt, &aptes);
+                }
+        }
+        else {
+                good_agpt = is_gpt_valid(fd, lastlba,
+                                         &agpt, &aptes);
+        }
+
+        /* The obviously unsuccessful case */
+        if (!good_pgpt && !good_agpt) {
+                goto fail;
+        }
+
+	/* This will be added to the EFI Spec. per Intel after v1.02. */
+        legacymbr = malloc(sizeof (*legacymbr));
+        if (legacymbr) {
+                memset(legacymbr, 0, sizeof (*legacymbr));
+                read_lba(fd, 0, (uint8_t *) legacymbr,
+                         sizeof (*legacymbr));
+                good_pmbr = is_pmbr_valid(legacymbr);
+                free(legacymbr);
+                legacymbr=NULL;
+        }
+
+        /* Failure due to bad PMBR */
+        if ((good_pgpt || good_agpt) && !good_pmbr && !force_gpt) {
+                fprintf(stderr,
+                       "  Warning: Disk has a valid GPT signature "
+                       "but invalid PMBR.\n"
+                       "  Assuming this disk is *not* a GPT disk anymore.\n"
+                       "  Use gpt kernel option to override.  "
+                       "Use GNU Parted to correct disk.\n");
+                goto fail;
+        }
+
+        /* Would fail due to bad PMBR, but force GPT anyhow */
+        if ((good_pgpt || good_agpt) && !good_pmbr && force_gpt) {
+                fprintf(stderr, 
+                       "  Warning: Disk has a valid GPT signature but "
+                       "invalid PMBR.\n"
+                       "  Use GNU Parted to correct disk.\n"
+                       "  gpt option taken, disk treated as GPT.\n");
+        }
+
+        compare_gpts(pgpt, agpt, lastlba);
+
+        /* The good cases */
+        if (good_pgpt && (good_pmbr || force_gpt)) {
+                *gpt  = pgpt;
+                *ptes = pptes;
+                if (agpt)  { free(agpt);   agpt = NULL; }
+                if (aptes) { free(aptes); aptes = NULL; }
+                if (!good_agpt) {
+                        fprintf(stderr, 
+			       "Alternate GPT is invalid, "
+                               "using primary GPT.\n");
+                }
+                return 1;
+        }
+        else if (good_agpt && (good_pmbr || force_gpt)) {
+                *gpt  = agpt;
+                *ptes = aptes;
+                if (pgpt)  { free(pgpt);   pgpt = NULL; }
+                if (pptes) { free(pptes); pptes = NULL; }
+                fprintf(stderr, 
+                       "Primary GPT is invalid, using alternate GPT.\n");
+                return 1;
+        }
+
+ fail:
+        if (pgpt)  { free(pgpt);   pgpt=NULL; }
+        if (agpt)  { free(agpt);   agpt=NULL; }
+        if (pptes) { free(pptes); pptes=NULL; }
+        if (aptes) { free(aptes); aptes=NULL; }
+        *gpt = NULL;
+        *ptes = NULL;
+        return 0;
+}
+
+/**
+ * read_gpt_pt() 
+ * @fd
+ * @all - slice with start/size of whole disk
+ *
+ *  0 if this isn't our partition table
+ *  number of partitions if successful
+ *
+ */
+int
+read_gpt_pt (int fd, struct slice all, struct slice *sp, int ns)
+{
+	gpt_header *gpt = NULL;
+	gpt_entry *ptes = NULL;
+	uint32_t i;
+	int n = 0;
+        int last_used_index=-1;
+
+	if (!find_valid_gpt (fd, &gpt, &ptes) || !gpt || !ptes) {
+		if (gpt)
+			free (gpt);
+		if (ptes)
+			free (ptes);
+		return 0;
+	}
+
+	for (i = 0; i < __le32_to_cpu(gpt->num_partition_entries) && i < ns; i++) {
+		if (!efi_guidcmp (NULL_GUID, ptes[i].partition_type_guid)) {
+			sp[n].start = 0;
+			sp[n].size = 0;
+			n++;
+		} else {
+			sp[n].start = __le64_to_cpu(ptes[i].starting_lba);
+			sp[n].size  = __le64_to_cpu(ptes[i].ending_lba) -
+				__le64_to_cpu(ptes[i].starting_lba) + 1;
+                        last_used_index=n;
+			n++;
+		}
+	}
+	free (ptes);
+	free (gpt);
+	return last_used_index+1;
+}
diff -urN multipath-tools-0.1.4/kpartx/gpt.h multipath-tools-0.1.5/kpartx/gpt.h
--- multipath-tools-0.1.4/kpartx/gpt.h	1970-01-01 01:00:00.000000000 +0100
+++ multipath-tools-0.1.5/kpartx/gpt.h	2004-01-24 23:26:45.000000000 +0100
@@ -0,0 +1,131 @@
+/*
+    gpt.[ch]
+
+    Copyright (C) 2000-2001 Dell Computer Corporation <Matt_Domsch@dell.com> 
+
+    EFI GUID Partition Table handling
+    Per Intel EFI Specification v1.02
+    http://developer.intel.com/technology/efi/efi.htm
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#ifndef _GPT_H
+#define _GPT_H
+
+
+#include <inttypes.h>
+#include "kpartx.h"
+#include "dos.h"
+#include "efi.h"
+
+#define EFI_PMBR_OSTYPE_EFI 0xEF
+#define EFI_PMBR_OSTYPE_EFI_GPT 0xEE
+#define MSDOS_MBR_SIGNATURE 0xaa55
+#define GPT_BLOCK_SIZE 512
+
+#define GPT_HEADER_SIGNATURE 0x5452415020494645
+#define GPT_HEADER_REVISION_V1_02 0x00010200
+#define GPT_HEADER_REVISION_V1_00 0x00010000
+#define GPT_HEADER_REVISION_V0_99 0x00009900
+#define GPT_PRIMARY_PARTITION_TABLE_LBA 1
+
+typedef struct _gpt_header {
+	uint64_t signature;
+	uint32_t revision;
+	uint32_t header_size;
+	uint32_t header_crc32;
+	uint32_t reserved1;
+	uint64_t my_lba;
+	uint64_t alternate_lba;
+	uint64_t first_usable_lba;
+	uint64_t last_usable_lba;
+	efi_guid_t disk_guid;
+	uint64_t partition_entry_lba;
+	uint32_t num_partition_entries;
+	uint32_t sizeof_partition_entry;
+	uint32_t partition_entry_array_crc32;
+	uint8_t reserved2[GPT_BLOCK_SIZE - 92];
+} __attribute__ ((packed)) gpt_header;
+
+typedef struct _gpt_entry_attributes {
+	uint64_t required_to_function:1;
+	uint64_t reserved:47;
+        uint64_t type_guid_specific:16;
+} __attribute__ ((packed)) gpt_entry_attributes;
+
+typedef struct _gpt_entry {
+	efi_guid_t partition_type_guid;
+	efi_guid_t unique_partition_guid;
+	uint64_t starting_lba;
+	uint64_t ending_lba;
+	gpt_entry_attributes attributes;
+	efi_char16_t partition_name[72 / sizeof(efi_char16_t)];
+} __attribute__ ((packed)) gpt_entry;
+
+
+/* 
+   These values are only defaults.  The actual on-disk structures
+   may define different sizes, so use those unless creating a new GPT disk!
+*/
+
+#define GPT_DEFAULT_RESERVED_PARTITION_ENTRY_ARRAY_SIZE 16384
+/* 
+   Number of actual partition entries should be calculated
+   as: 
+*/
+#define GPT_DEFAULT_RESERVED_PARTITION_ENTRIES \
+        (GPT_DEFAULT_RESERVED_PARTITION_ENTRY_ARRAY_SIZE / \
+         sizeof(gpt_entry))
+
+
+/* Protected Master Boot Record  & Legacy MBR share same structure */
+/* Needs to be packed because the u16s force misalignment. */
+
+typedef struct _legacy_mbr {
+	uint8_t bootcode[440];
+	uint32_t unique_mbr_signature;
+	uint16_t unknown;
+	struct partition partition[4];
+	uint16_t signature;
+} __attribute__ ((packed)) legacy_mbr;
+
+
+#define EFI_GPT_PRIMARY_PARTITION_TABLE_LBA 1
+
+/* Functions */
+int read_gpt_pt (int fd, struct slice all, struct slice *sp, int ns);
+
+
+#endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4 
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff -urN multipath-tools-0.1.4/kpartx/kpartx.c multipath-tools-0.1.5/kpartx/kpartx.c
--- multipath-tools-0.1.4/kpartx/kpartx.c	1970-01-01 01:00:00.000000000 +0100
+++ multipath-tools-0.1.5/kpartx/kpartx.c	2004-03-25 17:41:08.000000000 +0100
@@ -0,0 +1,505 @@
+/*
+ * Given a block device and a partition table type,
+ * try to parse the partition table, and list the
+ * contents. Optionally add or remove partitions.
+ *
+ * Read wholedisk and add all partitions:
+ *	kpartx [-a|-d|-l] [-v] wholedisk
+ *
+ * aeb, 2000-03-21
+ * cva, 2002-10-26
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <ctype.h>
+
+#include "../libdevmapper/libdevmapper.h"
+#include "kpartx.h"
+#include "crc32.h"
+
+/* loop devices */
+#include "lopart.h"
+
+#define SIZE(a) (sizeof(a)/sizeof((a)[0]))
+
+#define MAXTYPES	64
+#define MAXSLICES	256
+#define DM_TARGET	"linear"
+#define LO_NAME_SIZE    64
+
+struct slice slices[MAXSLICES];
+
+enum action { LIST, ADD, DELETE };
+
+struct pt {
+	char *type;
+	ptreader *fn;
+} pts[MAXTYPES];
+
+int ptct;
+
+static void
+addpts(char *t, ptreader f)
+{
+	if (ptct >= MAXTYPES) {
+		fprintf(stderr, "addpts: too many types\n");
+		exit(1);
+	}
+	pts[ptct].type = t;
+	pts[ptct].fn = f;
+	ptct++;
+}
+
+static void
+initpts(void)
+{
+	addpts("gpt", read_gpt_pt);
+	addpts("dos", read_dos_pt);
+	addpts("bsd", read_bsd_pt);
+	addpts("solaris", read_solaris_pt);
+	addpts("unixware", read_unixware_pt);
+}
+
+/* Used in gpt.c */
+int force_gpt=0;
+
+static int
+usage(void) {
+	printf("usage : kpartx [-a|-d|-l] [-v] wholedisk\n");
+	printf("\t-a add partition devmappings\n");
+	printf("\t-d del partition devmappings\n");
+	printf("\t-l list partitions devmappings that would be added by -a\n");
+	printf("\t-v verbose\n");
+	return 1;
+}
+
+static int
+dm_simplecmd (int task, const char *name) {
+	int r = 0;
+	struct dm_task *dmt;
+
+	if (!(dmt = dm_task_create (task)))
+		return 0;
+
+	if (!dm_task_set_name (dmt, name))
+		goto out;
+
+	r = dm_task_run (dmt);
+
+	out:
+	dm_task_destroy (dmt);
+	return r;
+}
+
+static int
+dm_addmap (int task, const char *name, const char *params, long size) {
+        struct dm_task *dmt;
+
+        if (!(dmt = dm_task_create (task)))
+                return 0;
+
+        if (!dm_task_set_name (dmt, name))
+                goto addout;
+
+        if (!dm_task_add_target (dmt, 0, size, DM_TARGET, params))
+                goto addout;
+
+        if (!dm_task_run (dmt))
+                goto addout;
+
+        addout:
+        dm_task_destroy (dmt);
+        return 1;
+}
+
+static int
+map_present (char * str)
+{
+	int r = 0;
+	struct dm_task *dmt;
+	struct dm_names *names;
+	unsigned next = 0;
+
+	if (!(dmt = dm_task_create (DM_DEVICE_LIST)))
+		return 0;
+
+        if (!dm_task_run (dmt))
+		goto out;
+
+	if (!(names = dm_task_get_names (dmt)))
+		goto out;
+
+	if (!names->dev)
+		goto out;
+
+	do {
+		if (0 == strcmp (names->name, str))
+			r = 1;
+
+		next = names->next;
+		names = (void *) names + next;
+	} while (next);
+
+	out:
+	dm_task_destroy (dmt);
+	return r;
+}
+
+static void
+set_delimiter (char * device, char * delimiter)
+{
+	char * p = device;
+
+	while (*(p++) != 0x0)
+		continue;
+
+	if (isdigit (*(p - 2)))
+		*delimiter = 'p';
+}
+
+static void
+strip_slash (char * device)
+{
+	char * p = device;
+
+	while (*(p++) != 0x0) {
+		
+		if (*p == '/')
+			*p = '!';
+	}
+}
+
+int
+main(int argc, char **argv){
+        int fd, i, j, k, n, op;
+	struct slice all;
+	struct pt *ptp;
+	enum action what = LIST;
+	char *p, *type, *diskdevice, *device;
+	int lower, upper;
+	int verbose = 0;
+	struct dm_task *dmt;
+	char partname[20], params[30];
+	char * loopdev = NULL;
+	char delim[8] = "";
+	int loopro = 0;
+	struct stat buf;
+
+	initpts();
+	init_crc32();
+
+	lower = upper = 0;
+	type = device = diskdevice = NULL;
+	
+	if (argc < 2) {
+		usage();
+		exit(1);
+	}
+
+	for (i = 1; i < argc; ++i) {
+		if (0 == strcmp("-g", argv[i])) {
+			force_gpt=1;
+			continue;
+		}
+		if (0 == strcmp("-t", argv[i])) {
+			strcpy(type, argv[++i]);
+			continue;
+		}
+		if (0 == strcmp("-v", argv[i])) {
+			verbose = 1;
+			continue;
+		}
+		if (0 == strcmp("-n", argv[i])) {
+			p = argv[++i];
+			lower = atoi(p);
+			if ((p[1] == '-') && p[2])
+				upper = atoi(p+2);
+			else
+				upper = lower;
+			continue;
+		}
+
+		if (0 == strcmp("-l", argv[i])) {
+			what = LIST;
+			continue;
+		}
+		else if (0 == strcmp("-a", argv[i])) {
+			what = ADD;
+			continue;
+		}
+		else if (0 == strcmp("-d", argv[i])) {
+			what = DELETE;
+			continue;
+		}
+		
+		if ((i == argc-2) && (argv[i][0] != '-')) {
+			device = argv[i];
+			diskdevice = argv[++i];
+			break;
+		}
+
+		if ((i == argc-1) && (argv[i][0] != '-')) {
+			diskdevice = device = argv[i];
+			break;
+		}
+
+		usage ();
+		exit (1);
+	}
+
+	if (stat (device, &buf)) {
+		printf("failed to stat() device\n");
+		exit (1);
+	}
+
+	if (S_ISREG (buf.st_mode)) {
+		loopdev = malloc(LO_NAME_SIZE * sizeof (char));
+		
+		if (!loopdev)
+			exit (1);
+
+		/* already looped file ? */
+		loopdev = find_loop_by_file(device);
+
+		if (!loopdev && what == DELETE)
+			exit (0);
+				
+		if (!loopdev) {
+			loopdev = find_unused_loop_device();
+
+			if (set_loop (loopdev, device, 0, &loopro)) {
+				fprintf (stderr,
+					 "can't set up loop\n");
+				exit (1);
+			}
+		}
+
+		device = loopdev;
+	}
+
+	set_delimiter (device, &delim[0]);
+			
+	fd = open (device, O_RDONLY);
+
+	if (fd == -1) {
+		perror (device);
+		exit (1);
+	}
+
+	if (!lower)
+		lower = 1;
+
+	/* add/remove partitions to the kernel devmapper tables */
+
+	for (i = 0; i < ptct; i++) {
+
+		ptp = &pts[i];
+
+		if (type && strcmp (type, ptp->type) > 0)
+			continue;
+		
+		/* here we get partitions */
+		n = ptp->fn (fd, all, slices, SIZE(slices));
+
+		if (n >= 0 && verbose)
+			printf ("%s: %d slices\n", ptp->type, n);
+
+		if (n > 0)
+			close (fd);
+
+		if (n > 0 && what == LIST) {
+
+			for (j = 0; j < n; j++) {
+
+				if (slices[j].size == 0)
+					continue;
+
+				printf ("%s%d : 0 %d %s %d\n",
+				        device+5, j+1, slices[j].size, device,
+				        slices[j].start);
+			}
+		}
+
+		if (n > 0 && what == DELETE) {
+
+			for (j = 0; j < n; j++) {
+
+				sprintf (partname, 
+					 "%s%s%d", device+5 , delim, j+1);
+
+				strip_slash (partname);
+			
+				if (slices[j].size == 0)
+					continue;
+
+				if (!map_present (partname))
+					continue;
+
+				if (!(dmt = dm_task_create (DM_DEVICE_REMOVE)))
+					return 0;
+
+				if (!dm_task_set_name (dmt, partname))
+					goto delout;
+			
+				if (!dm_task_run (dmt))
+					goto delout;
+
+				if (verbose)
+					printf ("Deleted device map : %s\n",
+						 partname);
+
+				delout:
+					dm_task_destroy (dmt);
+			}
+
+			if (S_ISREG (buf.st_mode)) {
+
+				if (del_loop (device) && verbose) {
+				    printf ("can't delete loop : %s\n", device);
+				    exit (1);
+				}
+
+				printf ("loop deleted : %s\n", device);
+			}
+		}
+
+		if (n > 0 && what == ADD) {
+
+			/* test for overlap, as in the case of an
+			   extended partition, and reduce size */
+
+			for (j=0; j<n; j++) {
+
+				for (k=j+1; k<n; k++) {
+
+					if (slices[k].start > slices[j].start &&
+					    slices[k].start < slices[j].start +
+					    slices[j].size) {
+						slices[j].size = slices[k].start -
+								 slices[j].start;
+
+						if (verbose)
+							printf("reduced size of "
+							       "partition #%d to %d\n",
+							       lower+j, slices[j].size);
+					}
+				}
+			}
+
+			for (j=0; j<n; j++) {
+
+				if (slices[j].size == 0)
+					continue;
+
+				sprintf(partname, "%s%s%d", 
+					device+5 , delim, j+1);
+
+				strip_slash (partname);
+
+				sprintf(params, "%s %d", device, (unsigned) slices[j].start);
+
+				op = (map_present (partname) ? DM_DEVICE_RELOAD : DM_DEVICE_CREATE);
+
+				dm_addmap (op, partname, params, (unsigned) slices[j].size);
+
+				if (op == DM_DEVICE_RELOAD)
+					dm_simplecmd (DM_DEVICE_RESUME, partname);
+
+				if (verbose)
+					printf("Added %s : 0 %d %s %s\n",
+						partname, slices[j].size,
+						DM_TARGET, params);
+			}
+		}
+
+		if (n > 0)
+			break;
+	}
+	return 0;
+}
+
+void *
+xmalloc (size_t size) {
+	void *t;
+
+	if (size == 0)
+		return NULL;
+
+	t = malloc (size);
+
+	if (t == NULL) {
+		fprintf(stderr, "Out of memory\n");
+		exit(1);
+	}
+
+	return t;
+}
+
+/*
+ * sseek: seek to specified sector
+ */
+#if !defined (__alpha__) && !defined (__ia64__)
+#include <linux/unistd.h>       /* _syscall */
+static
+_syscall5(int,  _llseek,  uint,  fd, ulong, hi, ulong, lo,
+	  long long *, res, uint, wh);
+#endif
+
+static int
+sseek(int fd, unsigned int secnr) {
+	long long in, out;
+	in = ((long long) secnr << 9);
+	out = 1;
+
+#if !defined (__alpha__) && !defined (__ia64__)
+	if (_llseek (fd, in>>32, in & 0xffffffff, &out, SEEK_SET) != 0
+	    || out != in)
+#else
+	if ((out = lseek(fd, in, SEEK_SET)) != in)
+#endif
+	{
+		fprintf(stderr, "llseek error\n");
+		return -1;
+	}
+	return 0;
+}
+
+static
+struct block {
+	unsigned int secnr;
+	char *block;
+	struct block *next;
+} *blockhead;
+
+char *
+getblock (int fd, unsigned int secnr) {
+	struct block *bp;
+
+	for (bp = blockhead; bp; bp = bp->next)
+
+		if (bp->secnr == secnr)
+			return bp->block;
+
+	if (sseek(fd, secnr))
+		return NULL;
+
+	bp = xmalloc(sizeof(struct block));
+	bp->secnr = secnr;
+	bp->next = blockhead;
+	blockhead = bp;
+	bp->block = (char *) xmalloc(1024);
+	
+	if (read(fd, bp->block, 1024) != 1024) {
+		fprintf(stderr, "read error, sector %d\n", secnr);
+		bp->block = NULL;
+	}
+
+	return bp->block;
+}
diff -urN multipath-tools-0.1.4/kpartx/kpartx.h multipath-tools-0.1.5/kpartx/kpartx.h
--- multipath-tools-0.1.4/kpartx/kpartx.h	1970-01-01 01:00:00.000000000 +0100
+++ multipath-tools-0.1.5/kpartx/kpartx.h	2002-04-10 12:11:07.000000000 +0200
@@ -0,0 +1,31 @@
+#ifndef PARTX_H_INCLUDED
+#define PARTX_H_INCLUDED
+
+/*
+ * For each partition type there is a routine that takes
+ * a block device and a range, and returns the list of
+ * slices found there in the supplied array SP that can
+ * hold NS entries. The return value is the number of
+ * entries stored, or -1 if the appropriate type is not
+ * present.
+ */
+
+
+/* units: 512 byte sectors */
+struct slice {
+	unsigned int start;
+	unsigned int size;
+};
+
+typedef int (ptreader)(int fd, struct slice all, struct slice *sp, int ns);
+
+extern ptreader read_dos_pt, read_bsd_pt, read_solaris_pt, read_unixware_pt, read_gpt_pt;
+
+char *getblock(int fd, unsigned int secnr);
+
+static inline int
+four2int(unsigned char *p) {
+	return p[0] + (p[1]<<8) + (p[2]<<16) + (p[3]<<24);
+}
+
+#endif /* PARTX_H_INCLUDED */
diff -urN multipath-tools-0.1.4/kpartx/loop.h multipath-tools-0.1.5/kpartx/loop.h
--- multipath-tools-0.1.4/kpartx/loop.h	1970-01-01 01:00:00.000000000 +0100
+++ multipath-tools-0.1.5/kpartx/loop.h	2004-03-24 23:52:53.000000000 +0100
@@ -0,0 +1,157 @@
+#ifndef _LINUX_LOOP_H
+#define _LINUX_LOOP_H
+
+/*
+ * include/linux/loop.h
+ *
+ * Written by Theodore Ts'o, 3/29/93.
+ *
+ * Copyright 1993 by Theodore Ts'o.  Redistribution of this file is
+ * permitted under the GNU General Public License.
+ */
+
+#define LO_NAME_SIZE	64
+#define LO_KEY_SIZE	32
+
+#ifdef __KERNEL__
+#include <linux/bio.h>
+#include <linux/blkdev.h>
+#include <linux/spinlock.h>
+
+/* Possible states of device */
+enum {
+	Lo_unbound,
+	Lo_bound,
+	Lo_rundown,
+};
+
+struct loop_func_table;
+
+struct loop_device {
+	int		lo_number;
+	int		lo_refcnt;
+	loff_t		lo_offset;
+	loff_t		lo_sizelimit;
+	int		lo_flags;
+	int		(*transfer)(struct loop_device *, int cmd,
+				    struct page *raw_page, unsigned raw_off,
+				    struct page *loop_page, unsigned loop_off,
+				    int size, sector_t real_block);
+	char		lo_file_name[LO_NAME_SIZE];
+	char		lo_crypt_name[LO_NAME_SIZE];
+	char		lo_encrypt_key[LO_KEY_SIZE];
+	int		lo_encrypt_key_size;
+	struct loop_func_table *lo_encryption;
+	__u32           lo_init[2];
+	uid_t		lo_key_owner;	/* Who set the key */
+	int		(*ioctl)(struct loop_device *, int cmd, 
+				 unsigned long arg); 
+
+	struct file *	lo_backing_file;
+	struct block_device *lo_device;
+	unsigned	lo_blocksize;
+	void		*key_data; 
+
+	int		old_gfp_mask;
+
+	spinlock_t		lo_lock;
+	struct bio 		*lo_bio;
+	struct bio		*lo_biotail;
+	int			lo_state;
+	struct semaphore	lo_sem;
+	struct semaphore	lo_ctl_mutex;
+	struct semaphore	lo_bh_mutex;
+	atomic_t		lo_pending;
+
+	request_queue_t		*lo_queue;
+};
+
+#endif /* __KERNEL__ */
+
+/*
+ * Loop flags
+ */
+#define LO_FLAGS_READ_ONLY	1
+
+#include <asm/posix_types.h>	/* for __kernel_old_dev_t */
+#include <asm/types.h>		/* for __u64 */
+
+/* Backwards compatibility version */
+struct loop_info {
+	int		   lo_number;		/* ioctl r/o */
+	__kernel_old_dev_t lo_device; 		/* ioctl r/o */
+	unsigned long	   lo_inode; 		/* ioctl r/o */
+	__kernel_old_dev_t lo_rdevice; 		/* ioctl r/o */
+	int		   lo_offset;
+	int		   lo_encrypt_type;
+	int		   lo_encrypt_key_size; 	/* ioctl w/o */
+	int		   lo_flags;			/* ioctl r/o */
+	char		   lo_name[LO_NAME_SIZE];
+	unsigned char	   lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */
+	unsigned long	   lo_init[2];
+	char		   reserved[4];
+};
+
+struct loop_info64 {
+	__u64		   lo_device;			/* ioctl r/o */
+	__u64		   lo_inode;			/* ioctl r/o */
+	__u64		   lo_rdevice;			/* ioctl r/o */
+	__u64		   lo_offset;
+	__u64		   lo_sizelimit;/* bytes, 0 == max available */
+	__u32		   lo_number;			/* ioctl r/o */
+	__u32		   lo_encrypt_type;
+	__u32		   lo_encrypt_key_size;		/* ioctl w/o */
+	__u32		   lo_flags;			/* ioctl r/o */
+	__u8		   lo_file_name[LO_NAME_SIZE];
+	__u8		   lo_crypt_name[LO_NAME_SIZE];
+	__u8		   lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */
+	__u64		   lo_init[2];
+};
+
+/*
+ * Loop filter types
+ */
+
+#define LO_CRYPT_NONE		0
+#define LO_CRYPT_XOR		1
+#define LO_CRYPT_DES		2
+#define LO_CRYPT_FISH2		3    /* Twofish encryption */
+#define LO_CRYPT_BLOW		4
+#define LO_CRYPT_CAST128	5
+#define LO_CRYPT_IDEA		6
+#define LO_CRYPT_DUMMY		9
+#define LO_CRYPT_SKIPJACK	10
+#define LO_CRYPT_CRYPTOAPI	18
+#define MAX_LO_CRYPT		20
+
+#ifdef __KERNEL__
+/* Support for loadable transfer modules */
+struct loop_func_table {
+	int number;	/* filter type */ 
+	int (*transfer)(struct loop_device *lo, int cmd,
+			struct page *raw_page, unsigned raw_off,
+			struct page *loop_page, unsigned loop_off,
+			int size, sector_t real_block);
+	int (*init)(struct loop_device *, const struct loop_info64 *); 
+	/* release is called from loop_unregister_transfer or clr_fd */
+	int (*release)(struct loop_device *); 
+	int (*ioctl)(struct loop_device *, int cmd, unsigned long arg);
+	struct module *owner;
+}; 
+
+int loop_register_transfer(struct loop_func_table *funcs);
+int loop_unregister_transfer(int number); 
+
+#endif
+/*
+ * IOCTL commands --- we will commandeer 0x4C ('L')
+ */
+
+#define LOOP_SET_FD		0x4C00
+#define LOOP_CLR_FD		0x4C01
+#define LOOP_SET_STATUS		0x4C02
+#define LOOP_GET_STATUS		0x4C03
+#define LOOP_SET_STATUS64	0x4C04
+#define LOOP_GET_STATUS64	0x4C05
+
+#endif
diff -urN multipath-tools-0.1.4/kpartx/lopart.c multipath-tools-0.1.5/kpartx/lopart.c
--- multipath-tools-0.1.4/kpartx/lopart.c	1970-01-01 01:00:00.000000000 +0100
+++ multipath-tools-0.1.5/kpartx/lopart.c	2004-03-25 17:34:35.000000000 +0100
@@ -0,0 +1,285 @@
+/* Taken from Ted's losetup.c - Mitch <m.dsouza@mrc-apu.cam.ac.uk> */
+/* Added vfs mount options - aeb - 960223 */
+/* Removed lomount - aeb - 960224 */
+
+/* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ * Sun Mar 21 1999 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ * - fixed strerr(errno) in gettext calls
+ */
+
+#define PROC_DEVICES	"/proc/devices"
+
+/*
+ * losetup.c - setup and control loop devices
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sysmacros.h>
+
+#include "loop.h"
+#include "lopart.h"
+#include "xstrncpy.h"
+
+static char *
+xstrdup (const char *s)
+{
+	char *t;
+
+	if (s == NULL)
+		return NULL;
+
+	t = strdup (s);
+
+	if (t == NULL) {
+		fprintf(stderr, "not enough memory");
+		exit(1);
+	}
+
+	return t;
+}
+
+extern int
+is_loop_device (const char *device)
+{
+	struct stat statbuf;
+	int loopmajor;
+#if 1
+	loopmajor = 7;
+#else
+	FILE *procdev;
+	char line[100], *cp;
+
+	loopmajor = 0;
+
+	if ((procdev = fopen(PROC_DEVICES, "r")) != NULL) {
+		
+		while (fgets (line, sizeof(line), procdev)) {
+			
+			if ((cp = strstr (line, " loop\n")) != NULL) {
+				*cp='\0';
+				loopmajor=atoi(line);
+				break;
+			}
+		}
+
+		fclose(procdev);
+	}
+#endif
+	return (loopmajor && stat(device, &statbuf) == 0 &&
+		S_ISBLK(statbuf.st_mode) &&
+		major(statbuf.st_rdev) == loopmajor);
+}
+
+#define SIZE(a) (sizeof(a)/sizeof(a[0]))
+
+extern char *
+find_loop_by_file (const char * filename)
+{
+	char dev[20];
+	char *loop_formats[] = { "/dev/loop%d", "/dev/loop/%d" };
+	int i, j, fd;
+	struct stat statbuf;
+	struct loop_info loopinfo;
+
+	for (j = 0; j < SIZE(loop_formats); j++) {
+
+		for (i = 0; i < 256; i++) {
+			sprintf (dev, loop_formats[j], i);
+
+			if (stat (dev, &statbuf) != 0 ||
+			    !S_ISBLK(statbuf.st_mode))
+				continue;
+
+			fd = open (dev, O_RDONLY);
+
+			if (fd < 0)
+				break;
+
+			if (ioctl (fd, LOOP_GET_STATUS, &loopinfo) != 0) {
+				close (fd);
+				continue;
+			}
+
+			if (0 == strcmp(filename, loopinfo.lo_name)) {
+				close (fd);
+				return xstrdup(dev); /*found */
+			}
+
+			close (fd);
+			continue;
+		}
+	}
+	return NULL;
+}
+
+extern char *
+find_unused_loop_device (void)
+{
+	/* Just creating a device, say in /tmp, is probably a bad idea -
+	   people might have problems with backup or so.
+	   So, we just try /dev/loop[0-7]. */
+
+	char dev[20];
+	char *loop_formats[] = { "/dev/loop%d", "/dev/loop/%d" };
+	int i, j, fd, somedev = 0, someloop = 0, loop_known = 0;
+	struct stat statbuf;
+	struct loop_info loopinfo;
+	FILE *procdev;
+
+	for (j = 0; j < SIZE(loop_formats); j++) {
+
+	    for(i = 0; i < 256; i++) {
+		sprintf(dev, loop_formats[j], i);
+
+		if (stat (dev, &statbuf) == 0 && S_ISBLK(statbuf.st_mode)) {
+			somedev++;
+			fd = open (dev, O_RDONLY);
+
+			if (fd >= 0) {
+
+				if(ioctl (fd, LOOP_GET_STATUS, &loopinfo) == 0)
+					someloop++;		/* in use */
+
+				else if (errno == ENXIO) {
+					close (fd);
+					return xstrdup(dev);/* probably free */
+				}
+
+				close (fd);
+			}
+			
+			/* continue trying as long as devices exist */
+			continue;
+		}
+		break;
+	    }
+	}
+
+	/* Nothing found. Why not? */
+	if ((procdev = fopen(PROC_DEVICES, "r")) != NULL) {
+		char line[100];
+
+		while (fgets (line, sizeof(line), procdev))
+
+			if (strstr (line, " loop\n")) {
+				loop_known = 1;
+				break;
+			}
+
+		fclose(procdev);
+
+		if (!loop_known)
+			loop_known = -1;
+	}
+
+	if (!somedev)
+		fprintf(stderr, "mount: could not find any device /dev/loop#");
+
+	else if (!someloop) {
+
+	    if (loop_known == 1)
+		fprintf(stderr,
+		    "mount: Could not find any loop device.\n"
+		    "       Maybe /dev/loop# has a wrong major number?");
+	    
+	    else if (loop_known == -1)
+		fprintf(stderr,
+		    "mount: Could not find any loop device, and, according to %s,\n"
+		    "       this kernel does not know about the loop device.\n"
+		    "       (If so, then recompile or `insmod loop.o'.)",
+		      PROC_DEVICES);
+
+	    else
+		fprintf(stderr,
+		    "mount: Could not find any loop device. Maybe this kernel does not know\n"
+		    "       about the loop device (then recompile or `insmod loop.o'), or\n"
+		    "       maybe /dev/loop# has the wrong major number?");
+
+	} else
+		fprintf(stderr, "mount: could not find any free loop device");
+	
+	return 0;
+}
+
+extern int
+set_loop (const char *device, const char *file, int offset, int *loopro)
+{
+	struct loop_info loopinfo;
+	int fd, ffd, mode;
+
+	mode = (*loopro ? O_RDONLY : O_RDWR);
+
+	if ((ffd = open (file, mode)) < 0) {
+
+		if (!*loopro && errno == EROFS)
+			ffd = open (file, mode = O_RDONLY);
+
+		if (ffd < 0) {
+			perror (file);
+			return 1;
+		}
+	}
+
+	if ((fd = open (device, mode)) < 0) {
+		perror (device);
+		return 1;
+	}
+
+	*loopro = (mode == O_RDONLY);
+	memset (&loopinfo, 0, sizeof (loopinfo));
+
+	xstrncpy (loopinfo.lo_name, file, LO_NAME_SIZE);
+	loopinfo.lo_offset = offset;
+	loopinfo.lo_encrypt_type = LO_CRYPT_NONE;
+	loopinfo.lo_encrypt_key_size = 0;
+
+	if (ioctl (fd, LOOP_SET_FD, ffd) < 0) {
+		perror ("ioctl: LOOP_SET_FD");
+		close (fd);
+		close (ffd);
+		return 1;
+	}
+
+	if (ioctl (fd, LOOP_SET_STATUS, &loopinfo) < 0) {
+		(void) ioctl (fd, LOOP_CLR_FD, 0);
+		perror ("ioctl: LOOP_SET_STATUS");
+		close (fd);
+		close (ffd);
+		return 1;
+	}
+
+	close (fd);
+	close (ffd);
+	return 0;
+}
+
+extern int 
+del_loop (const char *device)
+{
+	int fd;
+
+	if ((fd = open (device, O_RDONLY)) < 0) {
+		int errsv = errno;
+		fprintf(stderr, "loop: can't delete device %s: %s\n",
+			device, strerror (errsv));
+		return 1;
+	}
+
+	if (ioctl (fd, LOOP_CLR_FD, 0) < 0) {
+		perror ("ioctl: LOOP_CLR_FD");
+		return 1;
+	}
+
+	close (fd);
+	return 0;
+}
diff -urN multipath-tools-0.1.4/kpartx/lopart.h multipath-tools-0.1.5/kpartx/lopart.h
--- multipath-tools-0.1.4/kpartx/lopart.h	1970-01-01 01:00:00.000000000 +0100
+++ multipath-tools-0.1.5/kpartx/lopart.h	2004-03-24 23:16:37.000000000 +0100
@@ -0,0 +1,6 @@
+extern int verbose;
+extern int set_loop (const char *, const char *, int, int *);
+extern int del_loop (const char *);
+extern int is_loop_device (const char *);
+extern char * find_unused_loop_device (void);
+extern char * find_loop_by_file (const char *);
diff -urN multipath-tools-0.1.4/kpartx/solaris.c multipath-tools-0.1.5/kpartx/solaris.c
--- multipath-tools-0.1.4/kpartx/solaris.c	1970-01-01 01:00:00.000000000 +0100
+++ multipath-tools-0.1.5/kpartx/solaris.c	2004-01-24 23:25:34.000000000 +0100
@@ -0,0 +1,70 @@
+#include <stdio.h>
+#include <time.h>		/* time_t */
+#include "kpartx.h"
+
+#define SOLARIS_X86_NUMSLICE	8
+#define SOLARIS_X86_VTOC_SANE	(0x600DDEEEUL)
+
+//typedef int daddr_t;		/* or long - check */
+
+struct solaris_x86_slice {
+	unsigned short	s_tag;		/* ID tag of partition */
+	unsigned short	s_flag;		/* permision flags */
+	daddr_t 	s_start;	/* start sector no of partition */
+	long		s_size;		/* # of blocks in partition */
+};
+
+struct solaris_x86_vtoc {
+	unsigned long v_bootinfo[3];	/* info for mboot */
+	unsigned long v_sanity;		/* to verify vtoc sanity */
+	unsigned long v_version;	/* layout version */
+	char	v_volume[8];		/* volume name */
+	unsigned short	v_sectorsz;	/* sector size in bytes */
+	unsigned short	v_nparts;	/* number of partitions */
+	unsigned long v_reserved[10];	/* free space */
+	struct solaris_x86_slice
+		v_slice[SOLARIS_X86_NUMSLICE];   /* slice headers */
+	time_t	timestamp[SOLARIS_X86_NUMSLICE]; /* timestamp */
+	char	v_asciilabel[128];	/* for compatibility */
+};
+
+int
+read_solaris_pt(int fd, struct slice all, struct slice *sp, int ns) {
+	struct solaris_x86_vtoc *v;
+	struct solaris_x86_slice *s;
+	unsigned int offset = all.start;
+	int i, n;
+	char *bp;
+
+	bp = getblock(fd, offset+1); 	/* 1 sector suffices */
+	if (bp == NULL)
+		return -1;
+
+	v = (struct solaris_x86_vtoc *) bp;
+	if(v->v_sanity != SOLARIS_X86_VTOC_SANE)
+		return -1;
+
+	if(v->v_version != 1) {
+		fprintf(stderr, "Cannot handle solaris version %ld vtoc\n",
+		       v->v_version);
+		return 0;
+	}
+
+	for(i=0, n=0; i<SOLARIS_X86_NUMSLICE; i++) {
+		s = &v->v_slice[i];
+
+		if (s->s_size == 0)
+			continue;
+		if (n < ns) {
+			sp[n].start = offset + s->s_start;
+			sp[n].size = s->s_size;
+			n++;
+		} else {
+			fprintf(stderr,
+				"solaris_x86_partition: too many slices\n");
+			break;
+		}
+	}
+	return n;
+}
+
diff -urN multipath-tools-0.1.4/kpartx/sysmacros.h multipath-tools-0.1.5/kpartx/sysmacros.h
--- multipath-tools-0.1.4/kpartx/sysmacros.h	1970-01-01 01:00:00.000000000 +0100
+++ multipath-tools-0.1.5/kpartx/sysmacros.h	2004-01-25 23:35:57.000000000 +0100
@@ -0,0 +1,9 @@
+/* versions to be used with > 16-bit dev_t - leave unused for now */
+
+#ifndef major
+#define major(dev)	((dev) >> 8)
+#endif
+
+#ifndef minor
+#define minor(dev)	((dev) & 0xff)
+#endif
diff -urN multipath-tools-0.1.4/kpartx/unixware.c multipath-tools-0.1.5/kpartx/unixware.c
--- multipath-tools-0.1.4/kpartx/unixware.c	1970-01-01 01:00:00.000000000 +0100
+++ multipath-tools-0.1.5/kpartx/unixware.c	2004-01-24 23:25:43.000000000 +0100
@@ -0,0 +1,83 @@
+#include <stdio.h>
+#include "kpartx.h"
+
+#define UNIXWARE_FS_UNUSED     0
+#define UNIXWARE_NUMSLICE      16
+#define UNIXWARE_DISKMAGIC     (0xCA5E600D)
+#define UNIXWARE_DISKMAGIC2    (0x600DDEEE)
+
+struct unixware_slice {
+	unsigned short s_label;		/* label */
+	unsigned short s_flags;		/* permission flags */
+	unsigned int   start_sect;	/* starting sector */
+	unsigned int   nr_sects;	/* number of sectors in slice */
+};
+
+struct unixware_disklabel {
+	unsigned int   d_type;		/* drive type */
+	unsigned char  d_magic[4];	/* the magic number */
+	unsigned int   d_version;	/* version number */
+	char    d_serial[12];	   	/* serial number of the device */
+	unsigned int   d_ncylinders;	/* # of data cylinders per device */
+	unsigned int   d_ntracks;	/* # of tracks per cylinder */
+	unsigned int   d_nsectors;	/* # of data sectors per track */
+	unsigned int   d_secsize;	/* # of bytes per sector */
+	unsigned int   d_part_start;	/* # of first sector of this partition */
+	unsigned int   d_unknown1[12];	/* ? */
+	unsigned int   d_alt_tbl;	/* byte offset of alternate table */
+	unsigned int   d_alt_len;	/* byte length of alternate table */
+	unsigned int   d_phys_cyl;	/* # of physical cylinders per device */
+	unsigned int   d_phys_trk;	/* # of physical tracks per cylinder */
+	unsigned int   d_phys_sec;	/* # of physical sectors per track */
+	unsigned int   d_phys_bytes;	/* # of physical bytes per sector */
+	unsigned int   d_unknown2;	/* ? */
+	unsigned int   d_unknown3;	/* ? */
+	unsigned int   d_pad[8];	/* pad */
+
+	struct unixware_vtoc {
+		unsigned char   v_magic[4];	/* the magic number */
+		unsigned int    v_version;	/* version number */
+		char    v_name[8];	      	/* volume name */
+		unsigned short  v_nslices;	/* # of slices */
+		unsigned short  v_unknown1;	/* ? */
+		unsigned int    v_reserved[10];	/* reserved */
+		struct unixware_slice
+		    v_slice[UNIXWARE_NUMSLICE]; /* slice headers */
+	} vtoc;
+
+};  /* 408 */
+
+int
+read_unixware_pt(int fd, struct slice all, struct slice *sp, int ns) {
+	struct unixware_disklabel *l;
+	struct unixware_slice *p;
+	unsigned int offset = all.start;
+	char *bp;
+	int n = 0;
+
+	bp = getblock(fd, offset+29); 	/* 1 sector suffices */
+	if (bp == NULL)
+		return -1;
+
+	l = (struct unixware_disklabel *) bp;
+	if (four2int(l->d_magic) != UNIXWARE_DISKMAGIC ||
+	    four2int(l->vtoc.v_magic) != UNIXWARE_DISKMAGIC2)
+		return -1;
+
+	p = &l->vtoc.v_slice[1];	/* slice 0 is the whole disk. */
+	while (p - &l->vtoc.v_slice[0] < UNIXWARE_NUMSLICE) {
+		if (p->s_label == UNIXWARE_FS_UNUSED)
+			/* nothing */;
+		else if (n < ns) {
+			sp[n].start = p->start_sect;
+			sp[n].size = p->nr_sects;
+			n++;
+		} else {
+			fprintf(stderr,
+				"unixware_partition: too many slices\n");
+			break;
+		}
+		p++;
+	}
+	return n;
+}
diff -urN multipath-tools-0.1.4/kpartx/xstrncpy.c multipath-tools-0.1.5/kpartx/xstrncpy.c
--- multipath-tools-0.1.4/kpartx/xstrncpy.c	1970-01-01 01:00:00.000000000 +0100
+++ multipath-tools-0.1.5/kpartx/xstrncpy.c	2004-01-25 23:37:34.000000000 +0100
@@ -0,0 +1,10 @@
+/* NUL-terminated version of strncpy() */
+#include <string.h>
+#include "xstrncpy.h"
+
+/* caller guarantees n > 0 */
+void
+xstrncpy(char *dest, const char *src, size_t n) {
+	strncpy(dest, src, n-1);
+	dest[n-1] = 0;
+}
diff -urN multipath-tools-0.1.4/kpartx/xstrncpy.h multipath-tools-0.1.5/kpartx/xstrncpy.h
--- multipath-tools-0.1.4/kpartx/xstrncpy.h	1970-01-01 01:00:00.000000000 +0100
+++ multipath-tools-0.1.5/kpartx/xstrncpy.h	2004-01-25 23:37:10.000000000 +0100
@@ -0,0 +1 @@
+extern void xstrncpy(char *dest, const char *src, size_t n);
diff -urN multipath-tools-0.1.4/multipath/devinfo.c multipath-tools-0.1.5/multipath/devinfo.c
--- multipath-tools-0.1.4/multipath/devinfo.c	2004-03-02 17:39:44.000000000 +0100
+++ multipath-tools-0.1.5/multipath/devinfo.c	2004-03-22 13:17:50.000000000 +0100
@@ -154,27 +154,6 @@
         }
         buff[WWID_SIZE - 1] = '\0';
 }
-                                                                                                                 
-/* get EVPD page 0x83 off 8 */
-/* tested ok with StorageWorks */
-int
-get_evpd_wwid(char * devname, char * wwid)
-{
-        int fd;
-        char buff[MX_ALLOC_LEN + 1];
-                                                                                                                 
-        if ((fd = open(devname, O_RDONLY)) < 0)
-                        return 0;
-
-        if (0 == do_inq(fd, 0, 1, 0x83, buff, MX_ALLOC_LEN, 1)) {
-                sprint_wwid(wwid, &buff[8]);
-                close(fd);
-                return 1; /* success */
-        }
-
-        close(fd);
-        return 0; /* not good */
-}
 
 long
 get_disk_size (char * devname) {
@@ -238,3 +217,39 @@
 
 	return 1;
 }
+
+/* getuid functions */
+
+/*
+ * returns a 128 bits uid filled with zeroes
+ * used to ignore devices
+ */
+int
+get_null_uid (char * devname, char * wwid)
+{
+	memset (wwid, 0x0, 32);
+	return 0;
+}
+
+/*
+ * get EVPD page 0x83 off 8
+ * tested ok with StorageWorks
+ */
+int
+get_evpd_wwid (char * devname, char * wwid)
+{
+        int fd;
+        char buff[MX_ALLOC_LEN + 1];
+
+        if ((fd = open(devname, O_RDONLY)) < 0)
+                        return 1;
+
+        if (0 == do_inq(fd, 0, 1, 0x83, buff, MX_ALLOC_LEN, 1)) {
+                sprint_wwid(wwid, &buff[8]);
+                close(fd);
+                return 0;
+        }
+
+        close(fd);
+        return 1;
+}
diff -urN multipath-tools-0.1.4/multipath/devinfo.h multipath-tools-0.1.5/multipath/devinfo.h
--- multipath-tools-0.1.4/multipath/devinfo.h	2004-03-02 17:38:55.000000000 +0100
+++ multipath-tools-0.1.5/multipath/devinfo.h	2004-03-22 12:04:42.000000000 +0100
@@ -16,6 +16,9 @@
 void basename (char *, char *);
 int get_serial (char *, char *);
 int get_lun_strings (char *, char *, char *, char *);
-int get_evpd_wwid(char *, char *);
 long get_disk_size (char *);
 int do_tur (char *);
+
+/* getuid methods */
+int get_evpd_wwid (char *, char *);
+int get_null_uid (char *, char *);
diff -urN multipath-tools-0.1.4/multipath/main.c multipath-tools-0.1.5/multipath/main.c
--- multipath-tools-0.1.4/multipath/main.c	2004-03-17 12:09:50.000000000 +0100
+++ multipath-tools-0.1.5/multipath/main.c	2004-03-22 13:19:29.000000000 +0100
@@ -40,7 +40,11 @@
 #define argis(x) if (0 == strcmp (x, argv[i]))
 
 /* not nice */
-void *getuid_list[] = {&get_evpd_wwid, NULL};
+void *getuid_list[] = {
+	&get_null_uid,
+	&get_evpd_wwid,
+	NULL,
+};
         
 static int
 sysfsdevice2devname (char *devname, char *device)
@@ -149,7 +153,10 @@
 			
 			curpath->iopolicy = hwtable[i].iopolicy;
 
-			if (!hwtable[i].getuid (curpath->sg_dev, curpath->wwid))
+			if (getuid == NULL)
+				return 1;
+
+			if (hwtable[i].getuid (curpath->sg_dev, curpath->wwid))
 				return 1;
 		}
 	}
@@ -267,49 +274,6 @@
 }
 
 static int
-get_all_paths_nosysfs (struct env * conf, struct path * all_paths,
-		      struct scsi_dev * all_scsi_ids)
-{
-	int k, i, fd;
-	char buff[FILE_NAME_SIZE];
-	char file_name[FILE_NAME_SIZE];
-
-	for (k = 0; k < conf->max_devs; k++) {
-		strcpy (file_name, "/dev/sg");
-		sprintf (buff, "%d", k);
-		strncat (file_name, buff, FILE_NAME_SIZE);
-		strcpy (all_paths[k].sg_dev, file_name);
-
-		devinfo (&all_paths[k], conf->hwtable);
-
-		if ((fd = open (all_paths[k].sg_dev, O_RDONLY)) < 0)
-			return 0;
-
-		if (0 > ioctl (fd, SG_GET_SCSI_ID, &(all_paths[k].sg_id)))
-			printf ("device %s failed on sg ioctl, skip\n",
-			       file_name);
-
-		close (fd);
-
-		for (i = 0; i < conf->max_devs; i++) {
-			if ((all_paths[k].sg_id.host_no ==
-			     all_scsi_ids[i].host_no)
-			    && (all_paths[k].sg_id.scsi_id ==
-				(all_scsi_ids[i].scsi_id.dev_id & 0xff))
-			    && (all_paths[k].sg_id.lun ==
-				((all_scsi_ids[i].scsi_id.dev_id >> 8) & 0xff))
-			    && (all_paths[k].sg_id.channel ==
-				((all_scsi_ids[i].scsi_id.
-				  dev_id >> 16) & 0xff))) {
-				strcpy (all_paths[k].dev, all_scsi_ids[i].dev);
-				break;
-			}
-		}
-	}
-	return 0;
-}
-
-static int
 get_all_scsi_ids (struct env * conf, struct scsi_dev * all_scsi_ids)
 {
 	int k, big, little, res, host_no, fd;
@@ -366,6 +330,62 @@
 	return 0;
 }
 
+static int
+get_all_paths_nosysfs (struct env * conf, struct path * all_paths)
+{
+	int k, i, fd;
+	char buff[FILE_NAME_SIZE];
+	char file_name[FILE_NAME_SIZE];
+	struct scsi_dev * all_scsi_ids;
+
+	/* get all scsi ids for pivoting sd / sg */
+	all_scsi_ids = malloc (conf->max_devs * sizeof (struct scsi_dev));
+
+	if (all_scsi_ids == NULL) {
+		fprintf (stderr, "can not allocate memory\n");
+		unlink (RUN);
+		exit (1);
+	}
+
+	get_all_scsi_ids (conf, all_scsi_ids);
+
+	for (k = 0; k < conf->max_devs; k++) {
+		strcpy (file_name, "/dev/sg");
+		sprintf (buff, "%d", k);
+		strncat (file_name, buff, FILE_NAME_SIZE);
+		strcpy (all_paths[k].sg_dev, file_name);
+
+		devinfo (&all_paths[k], conf->hwtable);
+
+		if ((fd = open (all_paths[k].sg_dev, O_RDONLY)) < 0)
+			return 0;
+
+		if (0 > ioctl (fd, SG_GET_SCSI_ID, &(all_paths[k].sg_id)))
+			printf ("device %s failed on sg ioctl, skip\n",
+			       file_name);
+
+		close (fd);
+
+		for (i = 0; i < conf->max_devs; i++) {
+			if ((all_paths[k].sg_id.host_no ==
+			     all_scsi_ids[i].host_no)
+			    && (all_paths[k].sg_id.scsi_id ==
+				(all_scsi_ids[i].scsi_id.dev_id & 0xff))
+			    && (all_paths[k].sg_id.lun ==
+				((all_scsi_ids[i].scsi_id.dev_id >> 8) & 0xff))
+			    && (all_paths[k].sg_id.channel ==
+				((all_scsi_ids[i].scsi_id.
+				  dev_id >> 16) & 0xff))) {
+				strcpy (all_paths[k].dev, all_scsi_ids[i].dev);
+				break;
+			}
+		}
+	}
+
+	free (all_scsi_ids);
+	return 0;
+}
+
 /* print_path style */
 #define ALL	0
 #define NOWWID	1
@@ -672,7 +692,6 @@
 {
 	struct multipath * mp;
 	struct path * all_paths;
-	struct scsi_dev * all_scsi_ids;
 	struct env conf;
 	int i, k, nmp;
 	int try = 0;
@@ -772,14 +791,15 @@
 	/* dynamic allocations */
 	mp = malloc (conf.max_devs * sizeof (struct multipath));
 	all_paths = malloc (conf.max_devs * sizeof (struct path));
-	all_scsi_ids = malloc (conf.max_devs * sizeof (struct scsi_dev));
 
-	if (mp == NULL || all_paths == NULL || all_scsi_ids == NULL)
+	if (mp == NULL || all_paths == NULL) {
+		fprintf (stderr, "can not allocate memory\n");
+		unlink (RUN);
 		exit (1);
+	}
 
 	if (sysfs_get_mnt_path (conf.sysfs_path, FILE_NAME_SIZE)) {
-		get_all_scsi_ids (&conf, all_scsi_ids);
-		get_all_paths_nosysfs (&conf, all_paths, all_scsi_ids);
+		get_all_paths_nosysfs (&conf, all_paths);
 	} else {
 		get_all_paths_sysfs (&conf, all_paths);
 	}
@@ -814,7 +834,6 @@
 	/* free allocs */
 	free (mp);
 	free (all_paths);
-	free (all_scsi_ids);
 
 	/* release runfile */
 	unlink (RUN);
diff -urN multipath-tools-0.1.4/multipath/multipath.8 multipath-tools-0.1.5/multipath/multipath.8
--- multipath-tools-0.1.4/multipath/multipath.8	2004-03-05 13:41:47.000000000 +0100
+++ multipath-tools-0.1.5/multipath/multipath.8	2004-03-18 16:05:55.000000000 +0100
@@ -5,12 +5,15 @@
 .B multipath
 .RB [\| \-v | \-q \|]
 .RB [\| \-d \|]
+.RB [\| \-D\ \c
+.IR major \c
+.IR \ minor \|]
 .RB [\| \-i\ \c
 .IR int \|]
 .RB [\| \-m\ \c
 .IR max_devs \|]
 .RB [\| \-p\ \c
-.BR failover | multibus | group_by_serial \|]
+.BR failover | multibus | group_by_serial | group_by_tur \|]
 .RB [\| device \|]
 .SH DESCRIPTION
 .B multipath
@@ -51,6 +54,9 @@
 .TP
 .B group_by_serial
 1 priority group per serial
+.TP
+.B group_by_tur
+1 priority group per TUR state
 .RE
 .TP
 .BI device
diff -urN multipath-tools-0.1.4/multipath/multipath.conf multipath-tools-0.1.5/multipath/multipath.conf
--- multipath-tools-0.1.4/multipath/multipath.conf	2004-03-13 15:39:47.000000000 +0100
+++ multipath-tools-0.1.5/multipath/multipath.conf	2004-03-22 13:21:31.000000000 +0100
@@ -7,23 +7,24 @@
 # 3 GROUP_BY_TUR
 #
 # getuid :
-# 0 EVPD_WWID
+# 0 ZERO UID = ignore these logical units
+# 1 EVPD_WWID
 #
-"COMPAQ  ", "HSV110 (C)COMPAQ", "3", "0"
-"COMPAQ  ", "MSA1000         ", "3", "0"
-"COMPAQ  ", "MSA1000 VOLUME  ", "3", "0"
-"DEC     ", "HSG80           ", "3", "0"
-"HP      ", "HSV100          ", "3", "0"
-"HP      ", "A6189A          ", "3", "0"
-"HP      ", "OPEN-           ", "3", "0"
-"DDN     ", "SAN DataDirector", "3", "0"
-"FSC     ", "CentricStor     ", "3", "0"
-"HITACHI ", "DF400           ", "3", "0"
-"HITACHI ", "DF500           ", "3", "0"
-"HITACHI ", "DF600           ", "3", "0"
-"IBM     ", "ProFibre 4000R  ", "3", "0"
-"SGI     ", "TP9100          ", "3", "0"
-"SGI     ", "TP9300          ", "3", "0"
-"SGI     ", "TP9400          ", "3", "0"
-"SGI     ", "TP9500          ", "3", "0"
+"COMPAQ  ", "HSV110 (C)COMPAQ", "3", "1"
+"COMPAQ  ", "MSA1000         ", "3", "1"
+"COMPAQ  ", "MSA1000 VOLUME  ", "3", "1"
+"DEC     ", "HSG80           ", "3", "1"
+"HP      ", "HSV100          ", "3", "1"
+"HP      ", "A6189A          ", "3", "1"
+"HP      ", "OPEN-           ", "3", "1"
+"DDN     ", "SAN DataDirector", "3", "1"
+"FSC     ", "CentricStor     ", "3", "1"
+"HITACHI ", "DF400           ", "3", "1"
+"HITACHI ", "DF500           ", "3", "1"
+"HITACHI ", "DF600           ", "3", "1"
+"IBM     ", "ProFibre 4000R  ", "3", "1"
+"SGI     ", "TP9100          ", "3", "1"
+"SGI     ", "TP9300          ", "3", "1"
+"SGI     ", "TP9400          ", "3", "1"
+"SGI     ", "TP9500          ", "3", "1"
 
