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