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