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