devname.c revision f4e89bcdf2870820ff262e3aed04cbb5374d7fdd
1/* 2 * devname.c - get a dev by its device inode name 3 * 4 * Copyright (C) Andries Brouwer 5 * Copyright (C) 1999, 2000, 2001, 2002, 2003 Theodore Ts'o 6 * Copyright (C) 2001 Andreas Dilger 7 * 8 * %Begin-Header% 9 * This file may be redistributed under the terms of the 10 * GNU Lesser General Public License. 11 * %End-Header% 12 */ 13 14#define _GNU_SOURCE 1 15 16#include <stdio.h> 17#include <string.h> 18#include <limits.h> 19#if HAVE_UNISTD_H 20#include <unistd.h> 21#endif 22#include <stdlib.h> 23#include <string.h> 24#include <ctype.h> 25#if HAVE_SYS_TYPES_H 26#include <sys/types.h> 27#endif 28#if HAVE_SYS_STAT_H 29#include <sys/stat.h> 30#endif 31#if HAVE_ERRNO_H 32#include <errno.h> 33#endif 34#if HAVE_SYS_MKDEV_H 35#include <sys/mkdev.h> 36#endif 37#include <time.h> 38 39#include "blkidP.h" 40 41#undef HAVE_DEVMAPPER 42 43#ifdef HAVE_DEVMAPPER 44#include <libdevmapper.h> 45#endif 46 47/* 48 * Find a dev struct in the cache by device name, if available. 49 * 50 * If there is no entry with the specified device name, and the create 51 * flag is set, then create an empty device entry. 52 */ 53blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags) 54{ 55 blkid_dev dev = NULL, tmp; 56 struct list_head *p, *pnext; 57 58 if (!cache || !devname) 59 return NULL; 60 61 list_for_each(p, &cache->bic_devs) { 62 tmp = list_entry(p, struct blkid_struct_dev, bid_devs); 63 if (strcmp(tmp->bid_name, devname)) 64 continue; 65 66 DBG(DEBUG_DEVNAME, 67 printf("found devname %s in cache\n", tmp->bid_name)); 68 dev = tmp; 69 break; 70 } 71 72 if (!dev && (flags & BLKID_DEV_CREATE)) { 73 dev = blkid_new_dev(); 74 if (!dev) 75 return NULL; 76 dev->bid_time = INT_MIN; 77 dev->bid_name = blkid_strdup(devname); 78 dev->bid_cache = cache; 79 list_add_tail(&dev->bid_devs, &cache->bic_devs); 80 cache->bic_flags |= BLKID_BIC_FL_CHANGED; 81 } 82 83 if (flags & BLKID_DEV_VERIFY) { 84 dev = blkid_verify(cache, dev); 85 if (!dev || !(dev->bid_flags & BLKID_BID_FL_VERIFIED)) 86 return dev; 87 /* 88 * If the device is verified, then search the blkid 89 * cache for any entries that match on the type, uuid, 90 * and label, and verify them; if a cache entry can 91 * not be verified, then it's stale and so we remove 92 * it. 93 */ 94 list_for_each_safe(p, pnext, &cache->bic_devs) { 95 blkid_dev dev2; 96 if (!p) 97 break; 98 dev2 = list_entry(p, struct blkid_struct_dev, bid_devs); 99 if (dev2->bid_flags & BLKID_BID_FL_VERIFIED) 100 continue; 101 if (!dev->bid_type || !dev2->bid_type || 102 strcmp(dev->bid_type, dev2->bid_type)) 103 continue; 104 if (dev->bid_label && dev2->bid_label && 105 strcmp(dev->bid_label, dev2->bid_label)) 106 continue; 107 if (dev->bid_uuid && dev2->bid_uuid && 108 strcmp(dev->bid_uuid, dev2->bid_uuid)) 109 continue; 110 if ((dev->bid_label && !dev2->bid_label) || 111 (!dev->bid_label && dev2->bid_label) || 112 (dev->bid_uuid && !dev2->bid_uuid) || 113 (!dev->bid_uuid && dev2->bid_uuid)) 114 continue; 115 dev2 = blkid_verify(cache, dev2); 116 if (dev2 && !(dev2->bid_flags & BLKID_BID_FL_VERIFIED)) 117 blkid_free_dev(dev2); 118 } 119 } 120 return dev; 121} 122 123#ifdef HAVE_DEVMAPPER 124static int dm_device_is_leaf(const dev_t dev); 125#endif 126 127/* Directories where we will try to search for device names */ 128static const char *dirlist[] = { "/dev", "/devfs", "/devices", NULL }; 129 130/* 131 * Probe a single block device to add to the device cache. 132 */ 133static void probe_one(blkid_cache cache, const char *ptname, 134 dev_t devno, int pri, int only_if_new) 135{ 136 blkid_dev dev = NULL; 137 struct list_head *p, *pnext; 138 const char **dir; 139 char *devname = NULL; 140 141 /* See if we already have this device number in the cache. */ 142 list_for_each_safe(p, pnext, &cache->bic_devs) { 143 blkid_dev tmp = list_entry(p, struct blkid_struct_dev, 144 bid_devs); 145#ifdef HAVE_DEVMAPPER 146 if (!dm_device_is_leaf(devno)) 147 continue; 148#endif 149 if (tmp->bid_devno == devno) { 150 if (only_if_new && !access(tmp->bid_name, F_OK)) 151 return; 152 dev = blkid_verify(cache, tmp); 153 if (dev && (dev->bid_flags & BLKID_BID_FL_VERIFIED)) 154 break; 155 dev = 0; 156 } 157 } 158 if (dev && dev->bid_devno == devno) 159 goto set_pri; 160 161 /* 162 * Take a quick look at /dev/ptname for the device number. We check 163 * all of the likely device directories. If we don't find it, or if 164 * the stat information doesn't check out, use blkid_devno_to_devname() 165 * to find it via an exhaustive search for the device major/minor. 166 */ 167 for (dir = dirlist; *dir; dir++) { 168 struct stat st; 169 char device[256]; 170 171 sprintf(device, "%s/%s", *dir, ptname); 172 if ((dev = blkid_get_dev(cache, device, BLKID_DEV_FIND)) && 173 dev->bid_devno == devno) 174 goto set_pri; 175 176 if (stat(device, &st) == 0 && S_ISBLK(st.st_mode) && 177 st.st_rdev == devno) { 178 devname = blkid_strdup(device); 179 break; 180 } 181 } 182 /* Do a short-cut scan of /dev/mapper first */ 183 if (!devname) 184 blkid__scan_dir("/dev/mapper", devno, 0, &devname); 185 if (!devname) { 186 devname = blkid_devno_to_devname(devno); 187 if (!devname) 188 return; 189 } 190 dev = blkid_get_dev(cache, devname, BLKID_DEV_NORMAL); 191 free(devname); 192 193set_pri: 194 if (dev) { 195 if (pri) 196 dev->bid_pri = pri; 197 else if (!strncmp(dev->bid_name, "/dev/mapper/", 11)) 198 dev->bid_pri = BLKID_PRI_DM; 199 else if (!strncmp(ptname, "md", 2)) 200 dev->bid_pri = BLKID_PRI_MD; 201 } 202 return; 203} 204 205#ifdef HAVE_DEVMAPPER 206static void dm_quiet_log(int level __BLKID_ATTR((unused)), 207 const char *file __BLKID_ATTR((unused)), 208 int line __BLKID_ATTR((unused)), 209 const char *f __BLKID_ATTR((unused)), ...) 210{ 211 return; 212} 213 214/* 215 * device-mapper support 216 */ 217static int dm_device_has_dep(const dev_t dev, const char *name) 218{ 219 struct dm_task *task; 220 struct dm_deps *deps; 221 struct dm_info info; 222 unsigned int i; 223 int ret = 0; 224 225 task = dm_task_create(DM_DEVICE_DEPS); 226 if (!task) 227 goto out; 228 229 if (!dm_task_set_name(task, name)) 230 goto out; 231 232 if (!dm_task_run(task)) 233 goto out; 234 235 if (!dm_task_get_info(task, &info)) 236 goto out; 237 238 if (!info.exists) 239 goto out; 240 241 deps = dm_task_get_deps(task); 242 if (!deps || deps->count == 0) 243 goto out; 244 245 for (i = 0; i < deps->count; i++) { 246 dev_t dep_dev = deps->device[i]; 247 248 if (dev == dep_dev) { 249 ret = 1; 250 goto out; 251 } 252 } 253 254out: 255 if (task) 256 dm_task_destroy(task); 257 258 return ret; 259} 260 261static int dm_device_is_leaf(const dev_t dev) 262{ 263 struct dm_task *task; 264 struct dm_names *names; 265 unsigned int next = 0; 266 int n, ret = 1; 267 268 dm_log_init(dm_quiet_log); 269 task = dm_task_create(DM_DEVICE_LIST); 270 if (!task) 271 goto out; 272 273 dm_log_init(0); 274 275 if (!dm_task_run(task)) 276 goto out; 277 278 names = dm_task_get_names(task); 279 if (!names || !names->dev) 280 goto out; 281 282 n = 0; 283 do { 284 names = (struct dm_names *) ((char *)names + next); 285 286 if (dm_device_has_dep(dev, names->name)) 287 ret = 0; 288 289 next = names->next; 290 } while (next); 291 292out: 293 if (task) 294 dm_task_destroy(task); 295 296 return ret; 297} 298 299static dev_t dm_get_devno(const char *name) 300{ 301 struct dm_task *task; 302 struct dm_info info; 303 dev_t ret = 0; 304 305 task = dm_task_create(DM_DEVICE_INFO); 306 if (!task) 307 goto out; 308 309 if (!dm_task_set_name(task, name)) 310 goto out; 311 312 if (!dm_task_run(task)) 313 goto out; 314 315 if (!dm_task_get_info(task, &info)) 316 goto out; 317 318 if (!info.exists) 319 goto out; 320 321 ret = makedev(info.major, info.minor); 322 323out: 324 if (task) 325 dm_task_destroy(task); 326 327 return ret; 328} 329 330static void dm_probe_all(blkid_cache cache, int only_if_new) 331{ 332 struct dm_task *task; 333 struct dm_names *names; 334 unsigned int next = 0; 335 int n; 336 337 dm_log_init(dm_quiet_log); 338 task = dm_task_create(DM_DEVICE_LIST); 339 if (!task) 340 goto out; 341 dm_log_init(0); 342 343 if (!dm_task_run(task)) 344 goto out; 345 346 names = dm_task_get_names(task); 347 if (!names || !names->dev) 348 goto out; 349 350 n = 0; 351 do { 352 int rc; 353 char *device = NULL; 354 dev_t dev = 0; 355 356 names = (struct dm_names *) ((char *)names + next); 357 358 rc = asprintf(&device, "mapper/%s", names->name); 359 if (rc < 0) 360 goto try_next; 361 362 dev = dm_get_devno(names->name); 363 if (dev == 0) 364 goto try_next; 365 366 if (!dm_device_is_leaf(dev)) 367 goto try_next; 368 369 probe_one(cache, device, dev, BLKID_PRI_DM, only_if_new); 370 371try_next: 372 free(device); 373 next = names->next; 374 } while (next); 375 376out: 377 if (task) 378 dm_task_destroy(task); 379} 380#endif /* HAVE_DEVMAPPER */ 381 382#define PROC_PARTITIONS "/proc/partitions" 383#define VG_DIR "/proc/lvm/VGs" 384 385/* 386 * This function initializes the UUID cache with devices from the LVM 387 * proc hierarchy. We currently depend on the names of the LVM 388 * hierarchy giving us the device structure in /dev. (XXX is this a 389 * safe thing to do?) 390 */ 391#ifdef VG_DIR 392#include <dirent.h> 393static dev_t lvm_get_devno(const char *lvm_device) 394{ 395 FILE *lvf; 396 char buf[1024]; 397 int ma, mi; 398 dev_t ret = 0; 399 400 DBG(DEBUG_DEVNAME, printf("opening %s\n", lvm_device)); 401 if ((lvf = fopen(lvm_device, "r")) == NULL) { 402 DBG(DEBUG_DEVNAME, printf("%s: (%d) %s\n", lvm_device, errno, 403 strerror(errno))); 404 return 0; 405 } 406 407 while (fgets(buf, sizeof(buf), lvf)) { 408 if (sscanf(buf, "device: %d:%d", &ma, &mi) == 2) { 409 ret = makedev(ma, mi); 410 break; 411 } 412 } 413 fclose(lvf); 414 415 return ret; 416} 417 418static void lvm_probe_all(blkid_cache cache, int only_if_new) 419{ 420 DIR *vg_list; 421 struct dirent *vg_iter; 422 int vg_len = strlen(VG_DIR); 423 dev_t dev; 424 425 if ((vg_list = opendir(VG_DIR)) == NULL) 426 return; 427 428 DBG(DEBUG_DEVNAME, printf("probing LVM devices under %s\n", VG_DIR)); 429 430 while ((vg_iter = readdir(vg_list)) != NULL) { 431 DIR *lv_list; 432 char *vdirname; 433 char *vg_name; 434 struct dirent *lv_iter; 435 436 vg_name = vg_iter->d_name; 437 if (!strcmp(vg_name, ".") || !strcmp(vg_name, "..")) 438 continue; 439 vdirname = malloc(vg_len + strlen(vg_name) + 8); 440 if (!vdirname) 441 goto exit; 442 sprintf(vdirname, "%s/%s/LVs", VG_DIR, vg_name); 443 444 lv_list = opendir(vdirname); 445 free(vdirname); 446 if (lv_list == NULL) 447 continue; 448 449 while ((lv_iter = readdir(lv_list)) != NULL) { 450 char *lv_name, *lvm_device; 451 452 lv_name = lv_iter->d_name; 453 if (!strcmp(lv_name, ".") || !strcmp(lv_name, "..")) 454 continue; 455 456 lvm_device = malloc(vg_len + strlen(vg_name) + 457 strlen(lv_name) + 8); 458 if (!lvm_device) { 459 closedir(lv_list); 460 goto exit; 461 } 462 sprintf(lvm_device, "%s/%s/LVs/%s", VG_DIR, vg_name, 463 lv_name); 464 dev = lvm_get_devno(lvm_device); 465 sprintf(lvm_device, "%s/%s", vg_name, lv_name); 466 DBG(DEBUG_DEVNAME, printf("LVM dev %s: devno 0x%04X\n", 467 lvm_device, 468 (unsigned int) dev)); 469 probe_one(cache, lvm_device, dev, BLKID_PRI_LVM, 470 only_if_new); 471 free(lvm_device); 472 } 473 closedir(lv_list); 474 } 475exit: 476 closedir(vg_list); 477} 478#endif 479 480#define PROC_EVMS_VOLUMES "/proc/evms/volumes" 481 482static int 483evms_probe_all(blkid_cache cache, int only_if_new) 484{ 485 char line[100]; 486 int ma, mi, sz, num = 0; 487 FILE *procpt; 488 char device[110]; 489 490 procpt = fopen(PROC_EVMS_VOLUMES, "r"); 491 if (!procpt) 492 return 0; 493 while (fgets(line, sizeof(line), procpt)) { 494 if (sscanf (line, " %d %d %d %*s %*s %[^\n ]", 495 &ma, &mi, &sz, device) != 4) 496 continue; 497 498 DBG(DEBUG_DEVNAME, printf("Checking partition %s (%d, %d)\n", 499 device, ma, mi)); 500 501 probe_one(cache, device, makedev(ma, mi), BLKID_PRI_EVMS, 502 only_if_new); 503 num++; 504 } 505 fclose(procpt); 506 return num; 507} 508 509/* 510 * Read the device data for all available block devices in the system. 511 */ 512static int probe_all(blkid_cache cache, int only_if_new) 513{ 514 FILE *proc; 515 char line[1024]; 516 char ptname0[128], ptname1[128], *ptname = 0; 517 char *ptnames[2]; 518 dev_t devs[2]; 519 int ma, mi; 520 unsigned long long sz; 521 int lens[2] = { 0, 0 }; 522 int which = 0, last = 0; 523 524 ptnames[0] = ptname0; 525 ptnames[1] = ptname1; 526 527 if (!cache) 528 return -BLKID_ERR_PARAM; 529 530 if (cache->bic_flags & BLKID_BIC_FL_PROBED && 531 time(0) - cache->bic_time < BLKID_PROBE_INTERVAL) 532 return 0; 533 534 blkid_read_cache(cache); 535#ifdef HAVE_DEVMAPPER 536 dm_probe_all(cache, only_if_new); 537#endif 538 evms_probe_all(cache, only_if_new); 539#ifdef VG_DIR 540 lvm_probe_all(cache, only_if_new); 541#endif 542 543 proc = fopen(PROC_PARTITIONS, "r"); 544 if (!proc) 545 return -BLKID_ERR_PROC; 546 547 while (fgets(line, sizeof(line), proc)) { 548 last = which; 549 which ^= 1; 550 ptname = ptnames[which]; 551 552 if (sscanf(line, " %d %d %llu %128[^\n ]", 553 &ma, &mi, &sz, ptname) != 4) 554 continue; 555 devs[which] = makedev(ma, mi); 556 557 DBG(DEBUG_DEVNAME, printf("read partition name %s\n", ptname)); 558 559 /* Skip whole disk devs unless they have no partitions. 560 * If base name of device has changed, also 561 * check previous dev to see if it didn't have a partn. 562 * heuristic: partition name ends in a digit, & partition 563 * names contain whole device name as substring. 564 * 565 * Skip extended partitions. 566 * heuristic: size is 1 567 * 568 * FIXME: skip /dev/{ida,cciss,rd} whole-disk devs 569 */ 570 571 lens[which] = strlen(ptname); 572 573 /* ends in a digit, clearly a partition, so check */ 574 if (isdigit(ptname[lens[which] - 1])) { 575 DBG(DEBUG_DEVNAME, 576 printf("partition dev %s, devno 0x%04X\n", 577 ptname, (unsigned int) devs[which])); 578 579 if (sz > 1) 580 probe_one(cache, ptname, devs[which], 0, 581 only_if_new); 582 lens[which] = 0; /* mark as checked */ 583 } 584 585 /* 586 * If last was not checked because it looked like a whole-disk 587 * dev, and the device's base name has changed, 588 * check last as well. 589 */ 590 if (lens[last] && strncmp(ptnames[last], ptname, lens[last])) { 591 DBG(DEBUG_DEVNAME, 592 printf("whole dev %s, devno 0x%04X\n", 593 ptnames[last], (unsigned int) devs[last])); 594 probe_one(cache, ptnames[last], devs[last], 0, 595 only_if_new); 596 lens[last] = 0; 597 } 598 } 599 600 /* Handle the last device if it wasn't partitioned */ 601 if (lens[which]) 602 probe_one(cache, ptname, devs[which], 0, only_if_new); 603 604 fclose(proc); 605 blkid_flush_cache(cache); 606 return 0; 607} 608 609int blkid_probe_all(blkid_cache cache) 610{ 611 int ret; 612 613 DBG(DEBUG_PROBE, printf("Begin blkid_probe_all()\n")); 614 ret = probe_all(cache, 0); 615 cache->bic_time = time(0); 616 cache->bic_flags |= BLKID_BIC_FL_PROBED; 617 DBG(DEBUG_PROBE, printf("End blkid_probe_all()\n")); 618 return ret; 619} 620 621int blkid_probe_all_new(blkid_cache cache) 622{ 623 int ret; 624 625 DBG(DEBUG_PROBE, printf("Begin blkid_probe_all_new()\n")); 626 ret = probe_all(cache, 1); 627 DBG(DEBUG_PROBE, printf("End blkid_probe_all_new()\n")); 628 return ret; 629} 630 631 632#ifdef TEST_PROGRAM 633int main(int argc, char **argv) 634{ 635 blkid_cache cache = NULL; 636 int ret; 637 638 blkid_debug_mask = DEBUG_ALL; 639 if (argc != 1) { 640 fprintf(stderr, "Usage: %s\n" 641 "Probe all devices and exit\n", argv[0]); 642 exit(1); 643 } 644 if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) { 645 fprintf(stderr, "%s: error creating cache (%d)\n", 646 argv[0], ret); 647 exit(1); 648 } 649 if (blkid_probe_all(cache) < 0) 650 printf("%s: error probing devices\n", argv[0]); 651 652 blkid_put_cache(cache); 653 return (0); 654} 655#endif 656