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, -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 +#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< 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 +#include + +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 +#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 + + 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 + +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 + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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, §or_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, §ors); + 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 + + 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 +#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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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 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 /* _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 +#include +#include + +/* 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 /* for __kernel_old_dev_t */ +#include /* 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 */ +/* Added vfs mount options - aeb - 960223 */ +/* Removed lomount - aeb - 960224 */ + +/* 1999-02-22 Arkadiusz Mi¶kiewicz + * - added Native Language Support + * Sun Mar 21 1999 - Arnaldo Carvalho de Melo + * - fixed strerr(errno) in gettext calls + */ + +#define PROC_DEVICES "/proc/devices" + +/* + * losetup.c - setup and control loop devices + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include /* 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; iv_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 +#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 +#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"