get_devname.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  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 "volume_id_internal.h"
  11. //#define BLKGETSIZE64 _IOR(0x12,114,size_t)
  12. static struct uuidCache_s {
  13. struct uuidCache_s *next;
  14. // int major, minor;
  15. char *device;
  16. char *label;
  17. char *uc_uuid; /* prefix makes it easier to grep for */
  18. } *uuidCache;
  19. /* Returns !0 on error.
  20. * Otherwise, returns malloc'ed strings for label and uuid
  21. * (and they can't be NULL, although they can be "") */
  22. #if !ENABLE_FEATURE_VOLUMEID_ISO9660
  23. #define get_label_uuid(device, label, uuid, iso_only) \
  24. get_label_uuid(device, label, uuid)
  25. #endif
  26. static int
  27. get_label_uuid(const char *device, char **label, char **uuid, int iso_only)
  28. {
  29. int rv = 1;
  30. uint64_t size;
  31. struct volume_id *vid;
  32. vid = volume_id_open_node(device);
  33. if (!vid)
  34. return rv;
  35. if (ioctl(vid->fd, BLKGETSIZE64, &size) != 0)
  36. size = 0;
  37. #if ENABLE_FEATURE_VOLUMEID_ISO9660
  38. if ((iso_only ?
  39. volume_id_probe_iso9660(vid, 0) :
  40. volume_id_probe_all(vid, 0, size)
  41. ) != 0
  42. ) {
  43. goto ret;
  44. }
  45. #else
  46. if (volume_id_probe_all(vid, 0, size) != 0) {
  47. goto ret;
  48. }
  49. #endif
  50. if (vid->label[0] != '\0' || vid->uuid[0] != '\0') {
  51. *label = xstrndup(vid->label, sizeof(vid->label));
  52. *uuid = xstrndup(vid->uuid, sizeof(vid->uuid));
  53. dbg("found label '%s', uuid '%s' on %s", *label, *uuid, device);
  54. rv = 0;
  55. }
  56. ret:
  57. free_volume_id(vid);
  58. return rv;
  59. }
  60. /* NB: we take ownership of (malloc'ed) label and uuid */
  61. static void
  62. uuidcache_addentry(char *device, /*int major, int minor,*/ char *label, char *uuid)
  63. {
  64. struct uuidCache_s *last;
  65. if (!uuidCache) {
  66. last = uuidCache = xzalloc(sizeof(*uuidCache));
  67. } else {
  68. for (last = uuidCache; last->next; last = last->next)
  69. continue;
  70. last->next = xzalloc(sizeof(*uuidCache));
  71. last = last->next;
  72. }
  73. /*last->next = NULL; - xzalloc did it*/
  74. // last->major = major;
  75. // last->minor = minor;
  76. last->device = device;
  77. last->label = label;
  78. last->uc_uuid = uuid;
  79. }
  80. /* If get_label_uuid() on device_name returns success,
  81. * add a cache entry for this device.
  82. * If device node does not exist, it will be temporarily created. */
  83. #if !ENABLE_FEATURE_VOLUMEID_ISO9660
  84. #define uuidcache_check_device(device_name, ma, mi, iso_only) \
  85. uuidcache_check_device(device_name, ma, mi)
  86. #endif
  87. static void
  88. uuidcache_check_device(const char *device_name, int ma, int mi, int iso_only)
  89. {
  90. char *device, *last_slash;
  91. char *uuid, *label;
  92. char *ptr;
  93. int must_remove = 0;
  94. int added = 0;
  95. last_slash = NULL;
  96. device = xasprintf("/dev/%s", device_name);
  97. if (access(device, F_OK) != 0) {
  98. /* device does not exist, temporarily create */
  99. int slash_cnt = 0;
  100. if ((ma | mi) < 0)
  101. goto ret; /* we don't know major:minor! */
  102. ptr = device;
  103. while (*ptr)
  104. if (*ptr++ == '/')
  105. slash_cnt++;
  106. if (slash_cnt > 2) {
  107. // BUG: handles only slash_cnt == 3 case
  108. last_slash = strrchr(device, '/');
  109. *last_slash = '\0';
  110. if (mkdir(device, 0644)) {
  111. bb_perror_msg("can't create directory %s", device);
  112. *last_slash = '/';
  113. last_slash = NULL; /* prevents rmdir */
  114. } else {
  115. *last_slash = '/';
  116. }
  117. }
  118. mknod(device, S_IFBLK | 0600, makedev(ma, mi));
  119. must_remove = 1;
  120. }
  121. uuid = NULL;
  122. label = NULL;
  123. if (get_label_uuid(device, &label, &uuid, iso_only) == 0) {
  124. uuidcache_addentry(device, /*ma, mi,*/ label, uuid);
  125. /* "device" is owned by cache now, don't free */
  126. added = 1;
  127. }
  128. if (must_remove)
  129. unlink(device);
  130. if (last_slash) {
  131. *last_slash = '\0';
  132. rmdir(device);
  133. }
  134. ret:
  135. if (!added)
  136. free(device);
  137. }
  138. /* Run uuidcache_check_device() for every device mentioned
  139. * in /proc/partitions */
  140. static void
  141. uuidcache_init_partitions(void)
  142. {
  143. char line[100];
  144. int ma, mi;
  145. unsigned long long sz;
  146. FILE *procpt;
  147. int firstPass;
  148. int handleOnFirst;
  149. char *chptr;
  150. procpt = xfopen("/proc/partitions", "r");
  151. /*
  152. # cat /proc/partitions
  153. major minor #blocks name
  154. 8 0 293036184 sda
  155. 8 1 6835626 sda1
  156. 8 2 1 sda2
  157. 8 5 979933 sda5
  158. 8 6 15623181 sda6
  159. 8 7 97659103 sda7
  160. 8 8 171935631 sda8
  161. */
  162. for (firstPass = 1; firstPass >= 0; firstPass--) {
  163. fseek(procpt, 0, SEEK_SET);
  164. while (fgets(line, sizeof(line), procpt)) {
  165. /* The original version of this code used sscanf, but
  166. diet's sscanf is quite limited */
  167. chptr = line;
  168. if (*chptr != ' ') continue;
  169. chptr = skip_whitespace(chptr);
  170. ma = bb_strtou(chptr, &chptr, 0);
  171. if (ma < 0) continue;
  172. chptr = skip_whitespace(chptr);
  173. mi = bb_strtou(chptr, &chptr, 0);
  174. if (mi < 0) continue;
  175. chptr = skip_whitespace(chptr);
  176. sz = bb_strtoull(chptr, &chptr, 0);
  177. if ((long long)sz == -1LL) continue;
  178. chptr = skip_whitespace(chptr);
  179. /* skip extended partitions (heuristic: size 1) */
  180. if (sz == 1)
  181. continue;
  182. *strchrnul(chptr, '\n') = '\0';
  183. /* now chptr => device name */
  184. dbg("/proc/partitions: maj:%d min:%d sz:%llu name:'%s'",
  185. ma, mi, sz, chptr);
  186. if (!chptr[0])
  187. continue;
  188. /* look only at md devices on first pass */
  189. handleOnFirst = (chptr[0] == 'm' && chptr[1] == 'd');
  190. if (firstPass != handleOnFirst)
  191. continue;
  192. /* heuristic: partition name ends in a digit */
  193. if (isdigit(chptr[strlen(chptr) - 1])) {
  194. uuidcache_check_device(chptr, ma, mi, 0);
  195. }
  196. }
  197. }
  198. fclose(procpt);
  199. }
  200. static void
  201. dev_get_major_minor(char *device_name, int *major, int *minor)
  202. {
  203. char dev[16];
  204. char *dev_path;
  205. char *colon;
  206. int sz;
  207. dev_path = xasprintf("/sys/block/%s/dev", device_name);
  208. sz = open_read_close(dev_path, dev, sizeof(dev) - 1);
  209. if (sz < 0)
  210. goto ret;
  211. dev[sz] = '\0';
  212. colon = strchr(dev, ':');
  213. if (!colon)
  214. goto ret;
  215. *major = bb_strtou(dev, NULL, 10);
  216. *minor = bb_strtou(colon + 1, NULL, 10);
  217. ret:
  218. free(dev_path);
  219. return;
  220. }
  221. static void
  222. uuidcache_init_cdroms(void)
  223. {
  224. #define PROC_CDROMS "/proc/sys/dev/cdrom/info"
  225. char line[100];
  226. int ma, mi;
  227. FILE *proccd;
  228. proccd = fopen(PROC_CDROMS, "r");
  229. if (!proccd) {
  230. // static smallint warn = 0;
  231. // if (!warn) {
  232. // warn = 1;
  233. // bb_error_msg("can't open %s, UUID and LABEL "
  234. // "conversion cannot be done for CD-Roms",
  235. // PROC_CDROMS);
  236. // }
  237. return;
  238. }
  239. while (fgets(line, sizeof(line), proccd)) {
  240. static const char drive_name_string[] ALIGN1 = "drive name:";
  241. if (strncmp(line, drive_name_string, sizeof(drive_name_string) - 1) == 0) {
  242. char *device_name;
  243. device_name = strtok(skip_whitespace(line + sizeof(drive_name_string) - 1), " \t\n");
  244. while (device_name && device_name[0]) {
  245. ma = mi = -1;
  246. dev_get_major_minor(device_name, &ma, &mi);
  247. uuidcache_check_device(device_name, ma, mi, 1);
  248. device_name = strtok(NULL, " \t\n");
  249. }
  250. break;
  251. }
  252. }
  253. fclose(proccd);
  254. }
  255. static void
  256. uuidcache_init(void)
  257. {
  258. if (uuidCache)
  259. return;
  260. uuidcache_init_partitions();
  261. uuidcache_init_cdroms();
  262. }
  263. #define UUID 1
  264. #define VOL 2
  265. #ifdef UNUSED
  266. static char *
  267. get_spec_by_x(int n, const char *t, int *majorPtr, int *minorPtr)
  268. {
  269. struct uuidCache_s *uc;
  270. uuidcache_init();
  271. uc = uuidCache;
  272. while (uc) {
  273. switch (n) {
  274. case UUID:
  275. if (strcmp(t, uc->uc_uuid) == 0) {
  276. *majorPtr = uc->major;
  277. *minorPtr = uc->minor;
  278. return uc->device;
  279. }
  280. break;
  281. case VOL:
  282. if (strcmp(t, uc->label) == 0) {
  283. *majorPtr = uc->major;
  284. *minorPtr = uc->minor;
  285. return uc->device;
  286. }
  287. break;
  288. }
  289. uc = uc->next;
  290. }
  291. return NULL;
  292. }
  293. static unsigned char
  294. fromhex(char c)
  295. {
  296. if (isdigit(c))
  297. return (c - '0');
  298. return ((c|0x20) - 'a' + 10);
  299. }
  300. static char *
  301. get_spec_by_uuid(const char *s, int *major, int *minor)
  302. {
  303. unsigned char uuid[16];
  304. int i;
  305. if (strlen(s) != 36 || s[8] != '-' || s[13] != '-'
  306. || s[18] != '-' || s[23] != '-'
  307. ) {
  308. goto bad_uuid;
  309. }
  310. for (i = 0; i < 16; i++) {
  311. if (*s == '-')
  312. s++;
  313. if (!isxdigit(s[0]) || !isxdigit(s[1]))
  314. goto bad_uuid;
  315. uuid[i] = ((fromhex(s[0]) << 4) | fromhex(s[1]));
  316. s += 2;
  317. }
  318. return get_spec_by_x(UUID, (char *)uuid, major, minor);
  319. bad_uuid:
  320. fprintf(stderr, _("mount: bad UUID"));
  321. return 0;
  322. }
  323. static char *
  324. get_spec_by_volume_label(const char *s, int *major, int *minor)
  325. {
  326. return get_spec_by_x(VOL, s, major, minor);
  327. }
  328. static int display_uuid_cache(void)
  329. {
  330. struct uuidCache_s *u;
  331. size_t i;
  332. uuidcache_init();
  333. u = uuidCache;
  334. while (u) {
  335. printf("%s %s %s\n", u->device, u->label, u->uc_uuid);
  336. u = u->next;
  337. }
  338. return 0;
  339. }
  340. #endif // UNUSED
  341. /* Used by mount and findfs */
  342. char *get_devname_from_label(const char *spec)
  343. {
  344. struct uuidCache_s *uc;
  345. int spec_len = strlen(spec);
  346. uuidcache_init();
  347. uc = uuidCache;
  348. while (uc) {
  349. // FIXME: empty label ("LABEL=") matches anything??!
  350. if (uc->label[0] && strncmp(spec, uc->label, spec_len) == 0) {
  351. return xstrdup(uc->device);
  352. }
  353. uc = uc->next;
  354. }
  355. return NULL;
  356. }
  357. char *get_devname_from_uuid(const char *spec)
  358. {
  359. struct uuidCache_s *uc;
  360. uuidcache_init();
  361. uc = uuidCache;
  362. while (uc) {
  363. /* case of hex numbers doesn't matter */
  364. if (strcasecmp(spec, uc->uc_uuid) == 0) {
  365. return xstrdup(uc->device);
  366. }
  367. uc = uc->next;
  368. }
  369. return NULL;
  370. }