[libmultipath] add mpath[0..n] feature forgoten files
authorChristophe Varoqui <root@xa-s05.(none)>
Tue, 25 Oct 2005 14:23:33 +0000 (16:23 +0200)
committerChristophe Varoqui <root@xa-s05.(none)>
Tue, 25 Oct 2005 14:23:33 +0000 (16:23 +0200)
/libmultipath/alias.[ch] were forgoten when the patch was commited.

kpartx/gpt.c.orig [deleted file]
libmultipath/alias.c [new file with mode: 0644]
libmultipath/alias.h [new file with mode: 0644]

diff --git a/kpartx/gpt.c.orig b/kpartx/gpt.c.orig
deleted file mode 100644 (file)
index d5e2dd5..0000000
+++ /dev/null
@@ -1,625 +0,0 @@
-/*
-    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 "gpt.h"
-#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"
-
-#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)) {
-               error_found++;
-               fprintf(stderr, 
-                      "GPT:Primary header LBA != Alt. header alternate_lba\n");
-#ifdef DEBUG
-               fprintf(stderr,  "GPT:%" PRIx64 " != %" PRIx64 "\n",
-                      __le64_to_cpu(pgpt->my_lba),
-                       __le64_to_cpu(agpt->alternate_lba));
-#endif
-       }
-       if (__le64_to_cpu(pgpt->alternate_lba) != __le64_to_cpu(agpt->my_lba)) {
-               error_found++;
-               fprintf(stderr, 
-                      "GPT:Primary header alternate_lba != Alt. header my_lba\n");
-#ifdef DEBUG
-               fprintf(stderr,  "GPT:%" PRIx64 " != %" PRIx64 "\n",
-                      __le64_to_cpu(pgpt->alternate_lba),
-                       __le64_to_cpu(agpt->my_lba));
-#endif
-       }
-       if (__le64_to_cpu(pgpt->first_usable_lba) !=
-            __le64_to_cpu(agpt->first_usable_lba)) {
-               error_found++;
-               fprintf(stderr,  "GPT:first_usable_lbas don't match.\n");
-#ifdef DEBUG
-               fprintf(stderr,  "GPT:%" PRIx64 " != %" PRIx64 "\n",
-                      __le64_to_cpu(pgpt->first_usable_lba),
-                       __le64_to_cpu(agpt->first_usable_lba));
-#endif
-       }
-       if (__le64_to_cpu(pgpt->last_usable_lba) !=
-            __le64_to_cpu(agpt->last_usable_lba)) {
-               error_found++;
-               fprintf(stderr,  "GPT:last_usable_lbas don't match.\n");
-#ifdef DEBUG
-               fprintf(stderr,  "GPT:%" PRIx64 " != %" PRIx64 "\n",
-                      __le64_to_cpu(pgpt->last_usable_lba),
-                       __le64_to_cpu(agpt->last_usable_lba));
-#endif
-       }
-       if (efi_guidcmp(pgpt->disk_guid, agpt->disk_guid)) {
-               error_found++;
-               fprintf(stderr,  "GPT:disk_guids don't match.\n");
-       }
-       if (__le32_to_cpu(pgpt->num_partition_entries) !=
-            __le32_to_cpu(agpt->num_partition_entries)) {
-               error_found++;
-               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));
-       }
-       if (__le32_to_cpu(pgpt->sizeof_partition_entry) !=
-            __le32_to_cpu(agpt->sizeof_partition_entry)) {
-               error_found++;
-               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));
-       }
-       if (__le32_to_cpu(pgpt->partition_entry_array_crc32) !=
-            __le32_to_cpu(agpt->partition_entry_array_crc32)) {
-               error_found++;
-               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));
-       }
-       if (__le64_to_cpu(pgpt->alternate_lba) != lastlba) {
-               error_found++;
-               fprintf(stderr, 
-                      "GPT:Primary header thinks Alt. header is not at the end of the disk.\n");
-#ifdef DEBUG
-               fprintf(stderr,  "GPT:%" PRIx64 " != %" PRIx64 "\n",
-                      __le64_to_cpu(pgpt->alternate_lba), lastlba);
-#endif
-       }
-
-       if (__le64_to_cpu(agpt->my_lba) != lastlba) {
-               error_found++;
-               fprintf(stderr, 
-                      "GPT:Alternate GPT header not at the end of the disk.\n");
-#ifdef DEBUG
-               fprintf(stderr,  "GPT:%" PRIx64 " != %" PRIx64 "\n",
-                      __le64_to_cpu(agpt->my_lba), lastlba);
-#endif
-       }
-
-       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 --git a/libmultipath/alias.c b/libmultipath/alias.c
new file mode 100644 (file)
index 0000000..bb45db0
--- /dev/null
@@ -0,0 +1,271 @@
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+#include <stdio.h>
+#include "debug.h"
+#include "uxsock.h"
+#include "alias.h"
+
+
+/*
+ * significant parts of this file were taken from iscsi-bindings.c of the
+ * linux-iscsi project.
+ * Copyright (C) 2002 Cisco Systems, Inc.
+ *
+ * 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.
+ *
+ * See the file COPYING included with this distribution for more details.
+ */
+
+static int
+ensure_directories_exist(char *str, mode_t dir_mode)
+{
+       char *pathname;
+       char *end;
+       int err;
+
+       pathname = strdup(str);
+       if (!pathname){
+               condlog(0, "Cannot copy bindings file pathname : %s",
+                       strerror(errno));
+               return -1;
+       }
+       end = pathname;
+       /* skip leading slashes */
+       while (end && *end && (*end == '/'))
+               end++;
+
+       while ((end = strchr(end, '/'))) {
+               /* if there is another slash, make the dir. */
+               *end = '\0';
+               err = mkdir(pathname, dir_mode);
+               if (err && errno != EEXIST) {
+                       condlog(0, "Cannot make directory [%s] : %s",
+                               pathname, strerror(errno));
+                       free(pathname);
+                       return -1;
+               }
+               if (!err)
+                       condlog(3, "Created dir [%s]", pathname);
+               *end = '/';
+               end++;
+       }
+       free(pathname);
+       return 0;
+}
+
+static int
+lock_bindings_file(int fd)
+{
+       struct flock lock;
+       int retrys = BINDINGS_FILE_RETRYS;
+       
+       memset(&lock, 0, sizeof(lock));
+       lock.l_type = F_WRLCK;
+       lock.l_whence = SEEK_SET;
+
+       while (fcntl(fd, F_SETLK, &lock) < 0) {
+               if (errno != EACCES && errno != EAGAIN) {
+                       condlog(0, "Cannot lock bindings file : %s",
+                               strerror(errno));
+                       return -1;
+               } else {
+                       condlog(0, "Bindings file is currently locked");
+                       if ((retrys--) == 0)
+                               return -1;
+               }
+               /* because I'm paranoid */
+               memset(&lock, 0, sizeof(lock));
+               lock.l_type = F_WRLCK;
+               lock.l_whence = SEEK_SET;
+               
+               condlog(0, "retrying");
+               sleep(1);
+       }
+       return 0;
+}
+
+
+static int
+open_bindings_file(char *file)
+{
+       int fd;
+       struct stat s;
+
+       if (ensure_directories_exist(file, 0700))
+               return -1;
+       fd = open(file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+       if (fd < 0) {
+               condlog(0, "Cannot open bindings file [%s] : %s", file,
+                       strerror(errno));
+               return -1;
+       }
+
+       if (lock_bindings_file(fd) < 0)
+               goto fail;
+       
+       memset(&s, 0, sizeof(s));
+       if (fstat(fd, &s) < 0){
+               condlog(0, "Cannot stat bindings file : %s", strerror(errno));
+               goto fail;
+       }
+       if (s.st_size == 0) {
+               /* If bindings file is empty, write the header */
+               size_t len = strlen(BINDINGS_FILE_HEADER);
+               if (write_all(fd, BINDINGS_FILE_HEADER, len) != len) {
+                       condlog(0,
+                               "Cannot write header to bindings file : %s",
+                               strerror(errno));
+                       /* cleanup partially written header */
+                       ftruncate(fd, 0);
+                       goto fail;
+               }
+               fsync(fd);
+               condlog(3, "Initialized new bindings file [%s]", file);
+       }
+       
+       return fd;
+
+fail:
+       close(fd);
+       return -1;
+}
+
+
+static int
+lookup_binding(int fd, char *map_wwid, char **map_alias)
+{
+       char buf[LINE_MAX];
+       FILE *f;
+       unsigned int line_nr = 0;
+       int scan_fd;
+       int id = 0;
+
+       *map_alias = NULL;
+       scan_fd = dup(fd);
+       if (scan_fd < 0) {
+               condlog(0, "Cannot dup bindings file descriptor : %s",
+                       strerror(errno));
+               return -1;
+       }
+       f = fdopen(scan_fd, "r");
+       if (!f) {
+               condlog(0, "cannot fdopen on bindings file descriptor : %s",
+                       strerror(errno));
+               close(scan_fd);
+               return -1;
+       }
+       while (fgets(buf, LINE_MAX, f)) {
+               char *c, *alias, *wwid;
+               int curr_id;
+
+               line_nr++;
+               c = strpbrk(buf, "#\n\r");
+               if (c)
+                       *c = '\0';
+               alias = strtok(buf, " \t");
+               if (!alias) /* blank line */
+                       continue;
+               if (sscanf(alias, "mpath%d", &curr_id) == 1 && curr_id >= id)
+                       id = curr_id + 1;
+               wwid = strtok(NULL, " \t");
+               if (!wwid){
+                       condlog(3,
+                               "Ignoring malformed line %u in bindings file",
+                               line_nr);
+                       continue;
+               }
+               if (strcmp(wwid, map_wwid) == 0){
+                       condlog(3, "Found matching wwid [%s] in bindings file."
+                               "\nSetting alias to %s", wwid, alias);
+                       *map_alias = strdup(alias);
+                       if (*map_alias == NULL)
+                               condlog(0, "Cannot copy alias from bindings "
+                                       "file : %s", strerror(errno));
+                       fclose(f);
+                       return id;
+               }
+       }
+       condlog(3, "No matching wwid [%s] in bindings file.", map_wwid);
+       fclose(f);
+       return id;
+}      
+
+static char *
+allocate_binding(int fd, char *wwid, int id)
+{
+       char buf[LINE_MAX];
+       off_t offset;
+       char *alias, *c;
+       
+       if (id < 0) {
+               condlog(0, "Bindings file full. Cannot allocate new binding");
+               return NULL;
+       }
+       
+       snprintf(buf, LINE_MAX, "mpath%d %s\n", id, wwid);
+       buf[LINE_MAX - 1] = '\0';
+
+       offset = lseek(fd, 0, SEEK_END);
+       if (offset < 0){
+               condlog(0, "Cannot seek to end of bindings file : %s",
+                       strerror(errno));
+               return NULL;
+       }
+       if (write_all(fd, buf, strlen(buf)) != strlen(buf)){
+               condlog(0, "Cannot write binding to bindings file : %s",
+                       strerror(errno));
+               /* clear partial write */
+               ftruncate(fd, offset);
+               return NULL;
+       }
+       c = strchr(buf, ' ');
+       *c = '\0';
+       alias = strdup(buf);
+       if (alias == NULL)
+               condlog(0, "cannot copy new alias from bindings file : %s",
+                       strerror(errno));
+       else
+               condlog(3, "Created new binding [%s] for WWID [%s]", alias,
+                       wwid);
+       return alias;
+}              
+
+char *
+get_user_friendly_alias(char *wwid)
+{
+       char *alias;
+       int fd, id;
+
+       if (!wwid || *wwid == '\0') {
+               condlog(3, "Cannot find binding for empty WWID");
+               return NULL;
+       }
+
+       fd = open_bindings_file(BINDINGS_FILE_NAME);
+       if (fd < 0)
+               return NULL;
+       id = lookup_binding(fd, wwid, &alias);
+       if (id < 0) {
+               close(fd);
+               return NULL;
+       }
+       if (!alias)
+               alias = allocate_binding(fd, wwid, id);
+
+       close(fd);
+       return alias;
+}
diff --git a/libmultipath/alias.h b/libmultipath/alias.h
new file mode 100644 (file)
index 0000000..055d540
--- /dev/null
@@ -0,0 +1,13 @@
+#define BINDINGS_FILE_NAME "/var/lib/multipath/bindings"
+#define BINDINGS_FILE_RETRYS 3
+#define BINDINGS_FILE_HEADER \
+"# Multipath bindings, Version : 1.0\n" \
+"# NOTE: this file is automatically maintained by the multipath program.\n" \
+"# You should not need to edit this file in normal circumstances.\n" \
+"#\n" \
+"# Format:\n" \
+"# alias wwid\n" \
+"#\n"
+
+
+char *get_user_friendly_alias(char *wwid);