/* * get_device_by_label.h * * Copyright 1999 by Andries Brouwer * Copyright 1999, 2000 by Theodore Ts'o * * This file may be redistributed under the terms of the GNU Public * License. * * Taken from aeb's mount, 990619 * Updated from aeb's mount, 20000725 * Added call to ext2fs_find_block_device, so that we can find devices * even if devfs (ugh) is compiled in, but not mounted, since * this messes up /proc/partitions, by TYT. */ #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_MKDEV_H #include #endif #ifdef HAVE_SYS_SYSMACROS_H #include #endif #include #include "nls-enable.h" #include "fsck.h" #include "get_device_by_label.h" /* function prototype from libext2 */ extern char *ext2fs_find_block_device(dev_t device); #define PROC_PARTITIONS "/proc/partitions" #define PROC_EVMS_VOLUMES "/proc/evms/volumes" #define DEVLABELDIR "/dev" #define VG_DIR "/proc/lvm/VGs" #define EXT2_SUPER_MAGIC 0xEF53 struct ext2_super_block { unsigned char s_dummy1[56]; unsigned char s_magic[2]; unsigned char s_dummy2[46]; unsigned char s_uuid[16]; unsigned char s_volume_name[16]; }; #define ext2magic(s) ((unsigned int) s.s_magic[0] + (((unsigned int) s.s_magic[1]) << 8)) #define XFS_SUPER_MAGIC "XFSB" struct xfs_super_block { unsigned char s_magic[4]; unsigned char s_dummy[28]; unsigned char s_uuid[16]; unsigned char s_dummy2[60]; unsigned char s_fname[12]; }; static struct uuidCache_s { struct uuidCache_s *next; char uuid[16]; char *label; char *device; } *uuidCache = NULL; char *string_copy(const char *s) { char *ret; ret = malloc(strlen(s)+1); if (ret) strcpy(ret, s); return ret; } /* for now, only ext2 and xfs are supported */ static int get_label_uuid(const char *device, char **label, char *uuid) { /* start with ext2 and xfs tests, taken from mount_guess_fstype */ /* should merge these later */ int fd; size_t label_size; unsigned char *sb_uuid = 0, *sb_label = 0; struct ext2_super_block e2sb; struct xfs_super_block xfsb; fd = open(device, O_RDONLY); if (fd < 0) return 1; if (lseek(fd, 1024, SEEK_SET) == 1024 && read(fd, (char *) &e2sb, sizeof(e2sb)) == sizeof(e2sb) && (ext2magic(e2sb) == EXT2_SUPER_MAGIC)) { sb_uuid = e2sb.s_uuid; sb_label = e2sb.s_volume_name; label_size = sizeof(e2sb.s_volume_name); } else if (lseek(fd, 0, SEEK_SET) == 0 && read(fd, (char *) &xfsb, sizeof(xfsb)) == sizeof(xfsb) && strncmp((char *) &xfsb.s_magic, XFS_SUPER_MAGIC, 4) == 0) { sb_uuid = xfsb.s_uuid; sb_label = xfsb.s_fname; label_size = sizeof(xfsb.s_fname); } else { close(fd); return 1; } close(fd); if (sb_uuid) memcpy(uuid, sb_uuid, sizeof(e2sb.s_uuid)); if (sb_label) { if ((*label = calloc(label_size + 1, 1)) != NULL) memcpy(*label, sb_label, label_size); } return 0; } 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)); } /* * This function initializes the UUID cache with devices from the LVM * proc hierarchy. We currently depend on the names of the LVM * hierarchy giving us the device structure in /dev. (XXX is this a * safe thing to do?) */ #ifdef VG_DIR static void init_lvm(void) { DIR *vg_dir, *lv_list; char *vdirname, *lvm_device; char uuid[16], *label, *vname, *lname; struct dirent *vg_iter, *lv_iter; if ((vg_dir = opendir(VG_DIR)) == NULL) return; while ((vg_iter = readdir(vg_dir)) != 0) { vname = vg_iter->d_name; if (!strcmp(vname, ".") || !strcmp(vname, "..")) continue; vdirname = malloc(strlen(VG_DIR)+strlen(vname)+8); if (!vdirname) { closedir(vg_dir); return; } sprintf(vdirname, "%s/%s/LVs", VG_DIR, vname); lv_list = opendir(vdirname); free(vdirname); if (lv_list != NULL) return; while ((lv_iter = readdir(lv_list)) != 0) { lname = lv_iter->d_name; if (!strcmp(lname, ".") || !strcmp(lname, "..")) continue; lvm_device = malloc(strlen(DEVLABELDIR) + strlen(vname)+ strlen(lname)+8); if (!lvm_device) { closedir(lv_list); closedir(vg_dir); return; } sprintf(lvm_device, "%s/%s/%s", DEVLABELDIR, vname, lname); if (!get_label_uuid(lvm_device, &label, uuid)) { uuidcache_addentry(string_copy(lvm_device), label, uuid); } else free(lvm_device); } closedir(lv_list); } closedir( vg_dir ); } #endif static void read_partitions(void) { char line[100]; char *s; int ma, mi, sz; static char ptname[100]; FILE *procpt; char uuid[16], *label, *devname; char device[110]; dev_t dev; struct stat statbuf; int firstPass; int handleOnFirst; procpt = fopen(PROC_PARTITIONS, "r"); if (!procpt) return; 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 */ for(s = ptname; *s; s++); if (isdigit(s[-1])) { /* * We first look in /dev for the device, but * if we don't find it, or if the stat * information doesn't check out, we use * ext2fs_find_block_device to find it. */ sprintf(device, "%s/%s", DEVLABELDIR, ptname); dev = makedev(ma, mi); if ((stat(device, &statbuf) < 0) || (statbuf.st_rdev != dev)) { devname = ext2fs_find_block_device(dev); } else devname = string_copy(device); if (!devname) continue; #ifdef DEBUG printf("Checking partition %s (%d, %d)\n", devname, ma, mi); #endif if (!get_label_uuid(devname, &label, uuid)) uuidcache_addentry(devname, label, uuid); else free(devname); } } } fclose(procpt); } static void read_evms(void) { char line[100]; char *s; int ma, mi, sz; FILE *procpt; char uuid[16], *label, *devname; char device[110]; dev_t dev; struct stat statbuf; procpt = fopen(PROC_EVMS_VOLUMES, "r"); if (!procpt) return; while (fgets(line, sizeof(line), procpt)) { if (sscanf (line, " %d %d %d %*s %*s %[^\n ]", &ma, &mi, &sz, device) != 4) continue; /* * We first look for the device in the named location, * but if we don't find it, or if the stat information * doesn't check out, we use ext2fs_find_block_device * to find it. */ dev = makedev(ma, mi); if ((stat(device, &statbuf) < 0) || (statbuf.st_rdev != dev)) { devname = ext2fs_find_block_device(dev); } else devname = string_copy(device); if (!devname) continue; #ifdef DEBUG printf("Checking partition %s (%d, %d)\n", devname, ma, mi); #endif if (!get_label_uuid(devname, &label, uuid)) uuidcache_addentry(devname, label, uuid); else free(devname); } fclose(procpt); } static void uuidcache_init(void) { if (uuidCache) return; #ifdef VG_DIR init_lvm(); #endif read_evms(); read_partitions(); } #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; if (t == NULL) return NULL; while(uc) { switch (n) { case UUID: if (!memcmp(t, uc->uuid, sizeof(uc->uuid))) return string_copy(uc->device); break; case VOL: if (!strcmp(t, uc->label)) return string_copy(uc->device); break; } uc = uc->next; } return NULL; } static 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) { 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: fprintf(stderr, _("WARNING: %s: bad UUID\n"), s); return NULL; } 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; } /* * Interpret the device name if necessary. * Frees the pointer passed to it if we return a different device string. */ char *interpret_spec(char *spec) { char *dev = NULL; if (!spec) return NULL; if (!strncmp(spec, "UUID=", 5)) dev = get_spec_by_uuid(spec+5); else if (!strncmp(spec, "LABEL=", 6)) dev = get_spec_by_volume_label(spec+6); else dev = string_copy(spec); return dev; } #ifdef DEBUG main(int argc, char **argv) { uuidcache_init(); } #endif