1/* vi: set sw=4 ts=4: */ 2/* 3 * Support functions for mounting devices by label/uuid 4 * 5 * Copyright (C) 2006 by Jason Schoon <floydpink@gmail.com> 6 * Some portions cribbed from e2fsprogs, util-linux, dosfstools 7 * 8 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. 9 */ 10#include <sys/mount.h> /* BLKGETSIZE64 */ 11#if !defined(BLKGETSIZE64) 12# define BLKGETSIZE64 _IOR(0x12,114,size_t) 13#endif 14#include "volume_id_internal.h" 15 16static struct uuidCache_s { 17 struct uuidCache_s *next; 18 dev_t devno; 19 char *device; 20 char *label; 21 char *uc_uuid; /* prefix makes it easier to grep for */ 22} *uuidCache; 23 24/* Returns !0 on error. 25 * Otherwise, returns malloc'ed strings for label and uuid 26 * (and they can't be NULL, although they can be ""). 27 * NB: closes fd. */ 28static int 29get_label_uuid(int fd, char **label, char **uuid) 30{ 31 int rv = 1; 32 uint64_t size; 33 struct volume_id *vid; 34 35 /* fd is owned by vid now */ 36 vid = volume_id_open_node(fd); 37 38 if (ioctl(/*vid->*/fd, BLKGETSIZE64, &size) != 0) 39 size = 0; 40 41 if (volume_id_probe_all(vid, /*0,*/ size) != 0) 42 goto ret; 43 44 if (vid->label[0] != '\0' || vid->uuid[0] != '\0') { 45 *label = xstrndup(vid->label, sizeof(vid->label)); 46 *uuid = xstrndup(vid->uuid, sizeof(vid->uuid)); 47 dbg("found label '%s', uuid '%s'", *label, *uuid); 48 rv = 0; 49 } 50 ret: 51 free_volume_id(vid); /* also closes fd */ 52 return rv; 53} 54 55/* NB: we take ownership of (malloc'ed) label and uuid */ 56static void 57uuidcache_addentry(char *device, dev_t devno, char *label, char *uuid) 58{ 59 struct uuidCache_s *last; 60 61 if (!uuidCache) { 62 last = uuidCache = xzalloc(sizeof(*uuidCache)); 63 } else { 64 for (last = uuidCache; last->next; last = last->next) 65 continue; 66 last->next = xzalloc(sizeof(*uuidCache)); 67 last = last->next; 68 } 69 /*last->next = NULL; - xzalloc did it*/ 70 last->devno = devno; 71 last->device = device; 72 last->label = label; 73 last->uc_uuid = uuid; 74} 75 76/* If get_label_uuid() on device_name returns success, 77 * add a cache entry for this device. 78 * If device node does not exist, it will be temporarily created. */ 79static int FAST_FUNC 80uuidcache_check_device(const char *device, 81 struct stat *statbuf, 82 void *userData UNUSED_PARAM, 83 int depth UNUSED_PARAM) 84{ 85 char *uuid = uuid; /* for compiler */ 86 char *label = label; 87 int fd; 88 89 /* note: this check rejects links to devices, among other nodes */ 90 if (!S_ISBLK(statbuf->st_mode)) 91 return TRUE; 92 93 /* Users report that mucking with floppies (especially non-present 94 * ones) is significant PITA. This is a horribly dirty hack, 95 * but it is very useful in real world. 96 * If this will ever need to be enabled, consider using O_NONBLOCK. 97 */ 98 if (major(statbuf->st_rdev) == 2) 99 return TRUE; 100 101 fd = open(device, O_RDONLY); 102 if (fd < 0) 103 return TRUE; 104 105 /* get_label_uuid() closes fd in all cases (success & failure) */ 106 if (get_label_uuid(fd, &label, &uuid) == 0) { 107 /* uuidcache_addentry() takes ownership of all three params */ 108 uuidcache_addentry(xstrdup(device), statbuf->st_rdev, label, uuid); 109 } 110 return TRUE; 111} 112 113static void 114uuidcache_init(void) 115{ 116 if (uuidCache) 117 return; 118 119 /* We were scanning /proc/partitions 120 * and /proc/sys/dev/cdrom/info here. 121 * Missed volume managers. I see that "standard" blkid uses these: 122 * /dev/mapper/control 123 * /proc/devices 124 * /proc/evms/volumes 125 * /proc/lvm/VGs 126 * This is unacceptably complex. Let's just scan /dev. 127 * (Maybe add scanning of /sys/block/XXX/dev for devices 128 * somehow not having their /dev/XXX entries created?) */ 129 130 recursive_action("/dev", ACTION_RECURSE, 131 uuidcache_check_device, /* file_action */ 132 NULL, /* dir_action */ 133 NULL, /* userData */ 134 0 /* depth */); 135} 136 137#define UUID 1 138#define VOL 2 139#define DEVNO 3 140 141static char * 142get_spec_by_x(int n, const char *t, dev_t *devnoPtr) 143{ 144 struct uuidCache_s *uc; 145 146 uuidcache_init(); 147 uc = uuidCache; 148 149 while (uc) { 150 switch (n) { 151 case UUID: 152 /* case of hex numbers doesn't matter */ 153 if (strcasecmp(t, uc->uc_uuid) == 0) 154 goto found; 155 break; 156 case VOL: 157 if (uc->label[0] && strcmp(t, uc->label) == 0) 158 goto found; 159 break; 160 case DEVNO: 161 if (uc->devno == (*devnoPtr)) 162 goto found; 163 break; 164 } 165 uc = uc->next; 166 } 167 return NULL; 168 169found: 170 if (devnoPtr) 171 *devnoPtr = uc->devno; 172 return xstrdup(uc->device); 173} 174 175/* Used by blkid */ 176void display_uuid_cache(void) 177{ 178 struct uuidCache_s *u; 179 180 uuidcache_init(); 181 u = uuidCache; 182 while (u) { 183 printf("%s:", u->device); 184 if (u->label[0]) 185 printf(" LABEL=\"%s\"", u->label); 186 if (u->uc_uuid[0]) 187 printf(" UUID=\"%s\"", u->uc_uuid); 188 bb_putchar('\n'); 189 u = u->next; 190 } 191} 192 193/* Used by mount and findfs & old_e2fsprogs */ 194 195char *get_devname_from_label(const char *spec) 196{ 197 return get_spec_by_x(VOL, spec, NULL); 198} 199 200char *get_devname_from_uuid(const char *spec) 201{ 202 return get_spec_by_x(UUID, spec, NULL); 203} 204 205char *get_devname_from_device(dev_t dev) 206{ 207 return get_spec_by_x(DEVNO, NULL, &dev); 208} 209 210int resolve_mount_spec(char **fsname) 211{ 212 char *tmp = *fsname; 213 214 if (strncmp(*fsname, "UUID=", 5) == 0) 215 tmp = get_devname_from_uuid(*fsname + 5); 216 else if (strncmp(*fsname, "LABEL=", 6) == 0) 217 tmp = get_devname_from_label(*fsname + 6); 218 else 219 return 0; /* no UUID= or LABEL= prefix found */ 220 221 if (!tmp) 222 return -2; /* device defined by UUID= or LABEL= wasn't found */ 223 224 *fsname = tmp; 225 return 1; 226} 227