| /* |
| * mount_by_label.c - aeb |
| * |
| * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL> |
| * - added Native Language Support |
| * 2000-01-20 James Antill <james@and.org> |
| * - Added error message if /proc/partitions cannot be opened |
| * 2000-05-09 Erik Troan <ewt@redhat.com> |
| * - Added cache for UUID and disk labels |
| * 2000-11-07 Nathan Scott <nathans@sgi.com> |
| * - Added XFS support |
| * 2001-11-22 Kirby Bohling <kbohling@birddog.com> |
| * - Added support of labels on LVM |
| * 2002-03-21 Christoph Hellwig <hch@infradead.org> |
| * - Added JFS support |
| * 2002-07-11 Christoph Hellwig <hch@infradead.org> |
| * - Added JFS v2 format support |
| * 2002-07-26 Luciano Chavez <lnx1138@us.ibm.com> |
| * - Added EVMS support |
| */ |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <ctype.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <sys/types.h> /* needed for opendir */ |
| #include <dirent.h> |
| #include "sundries.h" /* for xstrdup */ |
| #include "linux_fs.h" |
| #include "get_label_uuid.h" |
| #include "mount_by_label.h" |
| #include "nls.h" |
| |
| #define PROC_PARTITIONS "/proc/partitions" |
| #define DEVLABELDIR "/dev" |
| #define VG_DIR "/proc/lvm/VGs" |
| #define EVMS_VOLUME_NAME_SIZE 127 |
| #define PROC_EVMS_VOLUMES "/proc/evms/volumes" |
| |
| static struct uuidCache_s { |
| struct uuidCache_s *next; |
| char uuid[16]; |
| char *label; |
| char *device; |
| } *uuidCache = NULL; |
| |
| static void |
| uuidcache_addentry(char *device, char *label, char *uuid) { |
| struct uuidCache_s *last; |
| |
| if (!uuidCache) { |
| last = uuidCache = malloc(sizeof(*uuidCache)); |
| } else { |
| for (last = uuidCache; last->next; last = last->next); |
| last->next = malloc(sizeof(*uuidCache)); |
| last = last->next; |
| } |
| last->next = NULL; |
| last->device = device; |
| last->label = label; |
| memcpy(last->uuid, uuid, sizeof(last->uuid)); |
| } |
| |
| /* LVM support - Kirby Bohling */ |
| static void |
| uuidcache_init_lvm(void) { |
| char buffer[PATH_MAX]; |
| char lvm_device[PATH_MAX]; |
| DIR *vg_dir, *lv_list; |
| struct dirent *vg_iter, *lv_iter; |
| char uuid[16], *label; |
| |
| vg_dir = opendir(VG_DIR); |
| if (vg_dir == NULL) /* to be expected */ |
| return; |
| |
| seekdir(vg_dir, 2); |
| while ((vg_iter = readdir(vg_dir)) != 0) { |
| sprintf(buffer, "%s/%s/LVs", VG_DIR, vg_iter->d_name); |
| lv_list = opendir(buffer); |
| if (lv_list == NULL) { |
| perror("mount (init_lvm)"); |
| continue; |
| } |
| seekdir(lv_list, 2); |
| while ((lv_iter = readdir(lv_list)) != 0) { |
| /* Now we have the file.. could open it and read out |
| * where the device is, read the first line, second |
| * field... Instead we guess. |
| */ |
| sprintf(lvm_device, "%s/%s/%s", DEVLABELDIR, |
| vg_iter->d_name, lv_iter->d_name); |
| if (!get_label_uuid(lvm_device, &label, uuid)) |
| uuidcache_addentry(strdup(lvm_device), |
| label, uuid); |
| } |
| closedir(lv_list); |
| } |
| closedir(vg_dir); |
| } |
| |
| static int |
| uuidcache_init_evms(void) { |
| FILE *procvol; |
| char *label; |
| char uuid[16]; |
| char volname[EVMS_VOLUME_NAME_SIZE+1]; |
| char line[EVMS_VOLUME_NAME_SIZE+80]; |
| |
| procvol = fopen(PROC_EVMS_VOLUMES, "r"); |
| if (!procvol) |
| return 0; |
| |
| while (fgets(line, sizeof(line), procvol)) { |
| if (sscanf(line, "%*d %*d %*d %*s %*s %[^\n]", volname) == 1) { |
| if (!get_label_uuid(volname, &label, uuid)) |
| uuidcache_addentry(strdup(volname), label, uuid); |
| } |
| } |
| |
| fclose(procvol); |
| |
| return 1; |
| } |
| |
| /* |
| * xvm is a proprietary sgi volume manager, it goes into /proc/partitions |
| * like this: |
| * |
| * 4 0 2210817 xvm/local/vol/myvolume/data/block |
| * 4 1 2210817 xvm/local/vol/myvolume/rt/block |
| * 4 2 2210817 xvm/local/vol/myvolume/log/block |
| * 4 3 2210818 xvm/local/vol/discs3/data/block |
| * |
| * The heuristics here are that the device should start with "xvm," |
| * but should not end in "log/block" or "rt/block" - those are |
| * special devices for the xfs filesystem external log & realtime device. |
| */ |
| |
| /* |
| * XVM support - Eric Sandeen |
| * Return 1 if this looks like an xvm device that should be scanned |
| */ |
| static int |
| is_xvm(char *ptname) |
| { |
| /* |
| * Scan anything with "xvm" and "data" in its name. |
| * That might pick up non-data xvm subvols if the |
| * volumename contains the string 'data' but |
| * that should be harmless. |
| */ |
| |
| if (strstr(ptname, "xvm") && strstr(ptname, "data")) |
| return 1; |
| |
| return 0; |
| } |
| |
| static void |
| uuidcache_init(void) { |
| char line[100]; |
| char *s; |
| int ma, mi, sz; |
| static char ptname[100]; |
| FILE *procpt; |
| char uuid[16], *label; |
| char device[110]; |
| int firstPass; |
| int handleOnFirst; |
| #if 0 |
| char iobuf[32*1024]; /* For setvbuf */ |
| #endif |
| |
| if (uuidCache) |
| return; |
| |
| if (uuidcache_init_evms()) |
| return; |
| |
| procpt = fopen(PROC_PARTITIONS, "r"); |
| if (!procpt) { |
| static int warn = 0; |
| if (!warn++) |
| error (_("mount: could not open %s, so UUID and LABEL " |
| "conversion cannot be done.\n"), |
| PROC_PARTITIONS); |
| return; |
| } |
| #if 0 |
| /* Ugly kludge - the contents of /proc/partitions change in time, |
| and this causes failures when the file is not read in one go. |
| In particular, one cannot use stdio on /proc/partitions. |
| Doing this ourselves is not easy either, since stat returns 0 |
| so the size is unknown. We might try increasing buffer sizes |
| until a single read gets all. For now only pick a largish buffer size. */ |
| /* All these troubles are mainly caused by people who patch the kernel |
| to keep statistics in /proc/partitions. Of course, statistics belong |
| in some /proc/diskstats, not in some /proc file that happened to |
| exist already. */ |
| |
| setvbuf(procpt, iobuf, _IOFBF, sizeof(iobuf)); |
| #endif |
| |
| for (firstPass = 1; firstPass >= 0; firstPass--) { |
| fseek(procpt, 0, SEEK_SET); |
| |
| while (fgets(line, sizeof(line), procpt)) { |
| if (sscanf (line, " %d %d %d %[^\n ]", |
| &ma, &mi, &sz, ptname) != 4) |
| continue; |
| |
| /* skip extended partitions (heuristic: size 1) */ |
| if (sz == 1) |
| continue; |
| |
| /* look only at md devices on first pass */ |
| handleOnFirst = !strncmp(ptname, "md", 2); |
| if (firstPass != handleOnFirst) |
| continue; |
| |
| /* skip entire disk (minor 0, 64, ... on ide; |
| 0, 16, ... on sd) */ |
| /* heuristic: partition name ends in a digit */ |
| /* devfs has .../disc and .../part1 etc. */ |
| |
| for(s = ptname; *s; s++); |
| if (isdigit(s[-1]) || is_xvm(ptname)) { |
| |
| /* |
| * Note: this is a heuristic only - there is no reason |
| * why these devices should live in /dev. |
| * Perhaps this directory should be specifiable by option. |
| * One might for example have /devlabel with links to /dev |
| * for the devices that may be accessed in this way. |
| * (This is useful, if the cdrom on /dev/hdc must not |
| * be accessed.) |
| */ |
| sprintf(device, "%s/%s", DEVLABELDIR, ptname); |
| if (!get_label_uuid(device, &label, uuid)) |
| uuidcache_addentry(strdup(device), label, uuid); |
| } |
| } |
| } |
| |
| fclose(procpt); |
| |
| uuidcache_init_lvm(); |
| } |
| |
| #define UUID 1 |
| #define VOL 2 |
| |
| static char * |
| get_spec_by_x(int n, const char *t) { |
| struct uuidCache_s *uc; |
| |
| uuidcache_init(); |
| uc = uuidCache; |
| |
| while(uc) { |
| switch (n) { |
| case UUID: |
| if (!memcmp(t, uc->uuid, sizeof(uc->uuid))) |
| return xstrdup(uc->device); |
| break; |
| case VOL: |
| if (!strcmp(t, uc->label)) |
| return xstrdup(uc->device); |
| break; |
| } |
| uc = uc->next; |
| } |
| return NULL; |
| } |
| |
| static u_char |
| fromhex(char c) { |
| if (isdigit(c)) |
| return (c - '0'); |
| else if (islower(c)) |
| return (c - 'a' + 10); |
| else |
| return (c - 'A' + 10); |
| } |
| |
| char * |
| get_spec_by_uuid(const char *s) { |
| u_char uuid[16]; |
| int i; |
| |
| if (strlen(s) != 36 || |
| s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-') |
| goto bad_uuid; |
| for (i=0; i<16; i++) { |
| if (*s == '-') s++; |
| if (!isxdigit(s[0]) || !isxdigit(s[1])) |
| goto bad_uuid; |
| uuid[i] = ((fromhex(s[0])<<4) | fromhex(s[1])); |
| s += 2; |
| } |
| return get_spec_by_x(UUID, uuid); |
| |
| bad_uuid: |
| die(EX_USAGE, _("mount: bad UUID")); |
| return NULL; /* just for gcc */ |
| } |
| |
| char * |
| get_spec_by_volume_label(const char *s) { |
| return get_spec_by_x(VOL, s); |
| } |
| |
| const char * |
| get_volume_label_by_spec(const char *spec) { |
| struct uuidCache_s *uc; |
| |
| uuidcache_init(); |
| uc = uuidCache; |
| |
| while(uc) { |
| if (!strcmp(spec, uc->device)) |
| return uc->label; |
| uc = uc->next; |
| } |
| return NULL; |
| } |
| |
| /* |
| * second_occurrence_of_vol_label() |
| * As labels are user defined they are not necessarily |
| * system-wide unique. Make sure that they are. |
| */ |
| const char * |
| second_occurrence_of_vol_label (const char *label) { |
| struct uuidCache_s *last; |
| int occurrences = 0; |
| |
| uuidcache_init(); |
| |
| for (last = uuidCache; last->next; last = last->next) { |
| if (!strcmp(last->label, label)) { |
| occurrences++; |
| if (occurrences == 2) |
| return last->device; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| |