1 /* $NetBSD: dev-cache.c,v 1.3 2009/10/16 21:00:41 joerg Exp $ */ 2 3 /* 4 * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. 5 * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. 6 * 7 * This file is part of LVM2. 8 * 9 * This copyrighted material is made available to anyone wishing to use, 10 * modify, copy, or redistribute it subject to the terms and conditions 11 * of the GNU Lesser General Public License v.2.1. 12 * 13 * You should have received a copy of the GNU Lesser General Public License 14 * along with this program; if not, write to the Free Software Foundation, 15 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 */ 17 18 #include "lib.h" 19 #include "dev-cache.h" 20 #include "lvm-types.h" 21 #include "btree.h" 22 #include "filter.h" 23 #include "filter-persistent.h" 24 #include "toolcontext.h" 25 26 #include <unistd.h> 27 #include <sys/param.h> 28 #include <dirent.h> 29 30 #ifdef __NetBSD__ 31 #include "netbsd.h" 32 #endif 33 34 struct dev_iter { 35 struct btree_iter *current; 36 struct dev_filter *filter; 37 }; 38 39 struct dir_list { 40 struct dm_list list; 41 char dir[0]; 42 }; 43 44 static struct { 45 struct dm_pool *mem; 46 struct dm_hash_table *names; 47 struct btree *devices; 48 struct dm_regex *preferred_names_matcher; 49 50 int has_scanned; 51 struct dm_list dirs; 52 struct dm_list files; 53 54 } _cache; 55 56 #define _alloc(x) dm_pool_zalloc(_cache.mem, (x)) 57 #define _free(x) dm_pool_free(_cache.mem, (x)) 58 #define _strdup(x) dm_pool_strdup(_cache.mem, (x)) 59 60 static int _insert(const char *path, int rec); 61 62 struct device *dev_create_file(const char *filename, struct device *dev, 63 struct str_list *alias, int use_malloc) 64 { 65 int allocate = !dev; 66 67 if (allocate) { 68 if (use_malloc) { 69 if (!(dev = dm_malloc(sizeof(*dev)))) { 70 log_error("struct device allocation failed"); 71 return NULL; 72 } 73 if (!(alias = dm_malloc(sizeof(*alias)))) { 74 log_error("struct str_list allocation failed"); 75 dm_free(dev); 76 return NULL; 77 } 78 if (!(alias->str = dm_strdup(filename))) { 79 log_error("filename strdup failed"); 80 dm_free(dev); 81 dm_free(alias); 82 return NULL; 83 } 84 dev->flags = DEV_ALLOCED; 85 } else { 86 if (!(dev = _alloc(sizeof(*dev)))) { 87 log_error("struct device allocation failed"); 88 return NULL; 89 } 90 if (!(alias = _alloc(sizeof(*alias)))) { 91 log_error("struct str_list allocation failed"); 92 _free(dev); 93 return NULL; 94 } 95 if (!(alias->str = _strdup(filename))) { 96 log_error("filename strdup failed"); 97 return NULL; 98 } 99 } 100 } else if (!(alias->str = dm_strdup(filename))) { 101 log_error("filename strdup failed"); 102 return NULL; 103 } 104 105 dev->flags |= DEV_REGULAR; 106 dm_list_init(&dev->aliases); 107 dm_list_add(&dev->aliases, &alias->list); 108 dev->end = UINT64_C(0); 109 dev->dev = 0; 110 dev->fd = -1; 111 dev->open_count = 0; 112 dev->block_size = -1; 113 memset(dev->pvid, 0, sizeof(dev->pvid)); 114 dm_list_init(&dev->open_list); 115 116 return dev; 117 } 118 119 static struct device *_dev_create(dev_t d) 120 { 121 struct device *dev; 122 123 if (!(dev = _alloc(sizeof(*dev)))) { 124 log_error("struct device allocation failed"); 125 return NULL; 126 } 127 dev->flags = 0; 128 dm_list_init(&dev->aliases); 129 dev->dev = d; 130 dev->fd = -1; 131 dev->open_count = 0; 132 dev->block_size = -1; 133 dev->end = UINT64_C(0); 134 memset(dev->pvid, 0, sizeof(dev->pvid)); 135 dm_list_init(&dev->open_list); 136 137 return dev; 138 } 139 140 void dev_set_preferred_name(struct str_list *sl, struct device *dev) 141 { 142 /* 143 * Don't interfere with ordering specified in config file. 144 */ 145 if (_cache.preferred_names_matcher) 146 return; 147 148 log_debug("%s: New preferred name", sl->str); 149 dm_list_del(&sl->list); 150 dm_list_add_h(&dev->aliases, &sl->list); 151 } 152 153 /* Return 1 if we prefer path1 else return 0 */ 154 static int _compare_paths(const char *path0, const char *path1) 155 { 156 int slash0 = 0, slash1 = 0; 157 int m0, m1; 158 const char *p; 159 char p0[PATH_MAX], p1[PATH_MAX]; 160 char *s0, *s1; 161 struct stat stat0, stat1; 162 163 /* 164 * FIXME Better to compare patterns one-at-a-time against all names. 165 */ 166 if (_cache.preferred_names_matcher) { 167 m0 = dm_regex_match(_cache.preferred_names_matcher, path0); 168 m1 = dm_regex_match(_cache.preferred_names_matcher, path1); 169 170 if (m0 != m1) { 171 if (m0 < 0) 172 return 1; 173 if (m1 < 0) 174 return 0; 175 if (m0 < m1) 176 return 1; 177 if (m1 < m0) 178 return 0; 179 } 180 } 181 182 /* 183 * Built-in rules. 184 */ 185 186 /* Return the path with fewer slashes */ 187 for (p = path0; p++; p = (const char *) strchr(p, '/')) 188 slash0++; 189 190 for (p = path1; p++; p = (const char *) strchr(p, '/')) 191 slash1++; 192 193 if (slash0 < slash1) 194 return 0; 195 if (slash1 < slash0) 196 return 1; 197 198 strncpy(p0, path0, PATH_MAX); 199 strncpy(p1, path1, PATH_MAX); 200 s0 = &p0[0] + 1; 201 s1 = &p1[0] + 1; 202 203 /* We prefer symlinks - they exist for a reason! 204 * So we prefer a shorter path before the first symlink in the name. 205 * FIXME Configuration option to invert this? */ 206 while (s0) { 207 s0 = strchr(s0, '/'); 208 s1 = strchr(s1, '/'); 209 if (s0) { 210 *s0 = '\0'; 211 *s1 = '\0'; 212 } 213 if (lstat(p0, &stat0)) { 214 log_sys_very_verbose("lstat", p0); 215 return 1; 216 } 217 if (lstat(p1, &stat1)) { 218 log_sys_very_verbose("lstat", p1); 219 return 0; 220 } 221 if (S_ISLNK(stat0.st_mode) && !S_ISLNK(stat1.st_mode)) 222 return 0; 223 if (!S_ISLNK(stat0.st_mode) && S_ISLNK(stat1.st_mode)) 224 return 1; 225 if (s0) { 226 *s0++ = '/'; 227 *s1++ = '/'; 228 } 229 } 230 231 /* ASCII comparison */ 232 if (strcmp(path0, path1) < 0) 233 return 0; 234 else 235 return 1; 236 } 237 238 static int _add_alias(struct device *dev, const char *path) 239 { 240 struct str_list *sl = _alloc(sizeof(*sl)); 241 struct str_list *strl; 242 const char *oldpath; 243 int prefer_old = 1; 244 245 if (!sl) 246 return_0; 247 248 /* Is name already there? */ 249 dm_list_iterate_items(strl, &dev->aliases) { 250 if (!strcmp(strl->str, path)) { 251 log_debug("%s: Already in device cache", path); 252 return 1; 253 } 254 } 255 256 if (!(sl->str = dm_pool_strdup(_cache.mem, path))) 257 return_0; 258 259 if (!dm_list_empty(&dev->aliases)) { 260 oldpath = dm_list_item(dev->aliases.n, struct str_list)->str; 261 prefer_old = _compare_paths(path, oldpath); 262 log_debug("%s: Aliased to %s in device cache%s", 263 path, oldpath, prefer_old ? "" : " (preferred name)"); 264 265 } else 266 log_debug("%s: Added to device cache", path); 267 268 if (prefer_old) 269 dm_list_add(&dev->aliases, &sl->list); 270 else 271 dm_list_add_h(&dev->aliases, &sl->list); 272 273 return 1; 274 } 275 276 /* 277 * Either creates a new dev, or adds an alias to 278 * an existing dev. 279 */ 280 static int _insert_dev(const char *path, dev_t d) 281 { 282 struct device *dev; 283 static dev_t loopfile_count = 0; 284 int loopfile = 0; 285 286 /* Generate pretend device numbers for loopfiles */ 287 if (!d) { 288 if (dm_hash_lookup(_cache.names, path)) 289 return 1; 290 d = ++loopfile_count; 291 loopfile = 1; 292 } 293 294 /* is this device already registered ? */ 295 if (!(dev = (struct device *) btree_lookup(_cache.devices, 296 (uint32_t) d))) { 297 /* create new device */ 298 if (loopfile) { 299 if (!(dev = dev_create_file(path, NULL, NULL, 0))) 300 return_0; 301 } else if (!(dev = _dev_create(d))) 302 return_0; 303 304 if (!(btree_insert(_cache.devices, (uint32_t) d, dev))) { 305 log_err("Couldn't insert device into binary tree."); 306 _free(dev); 307 return 0; 308 } 309 } 310 311 if (!loopfile && !_add_alias(dev, path)) { 312 log_err("Couldn't add alias to dev cache."); 313 return 0; 314 } 315 316 if (!dm_hash_insert(_cache.names, path, dev)) { 317 log_err("Couldn't add name to hash in dev cache."); 318 return 0; 319 } 320 321 return 1; 322 } 323 324 static char *_join(const char *dir, const char *name) 325 { 326 size_t len = strlen(dir) + strlen(name) + 2; 327 char *r = dm_malloc(len); 328 if (r) 329 snprintf(r, len, "%s/%s", dir, name); 330 331 return r; 332 } 333 334 /* 335 * Get rid of extra slashes in the path string. 336 */ 337 static void _collapse_slashes(char *str) 338 { 339 char *ptr; 340 int was_slash = 0; 341 342 for (ptr = str; *ptr; ptr++) { 343 if (*ptr == '/') { 344 if (was_slash) 345 continue; 346 347 was_slash = 1; 348 } else 349 was_slash = 0; 350 *str++ = *ptr; 351 } 352 353 *str = *ptr; 354 } 355 356 static int _insert_dir(const char *dir) 357 { 358 int n, dirent_count, r = 1; 359 struct dirent **dirent; 360 char *path; 361 362 dirent_count = scandir(dir, &dirent, NULL, alphasort); 363 if (dirent_count > 0) { 364 for (n = 0; n < dirent_count; n++) { 365 if (dirent[n]->d_name[0] == '.') { 366 free(dirent[n]); 367 continue; 368 } 369 370 if (!(path = _join(dir, dirent[n]->d_name))) 371 return_0; 372 373 _collapse_slashes(path); 374 r &= _insert(path, 1); 375 dm_free(path); 376 377 free(dirent[n]); 378 } 379 free(dirent); 380 } 381 382 return r; 383 } 384 385 static int _insert_file(const char *path) 386 { 387 struct stat info; 388 389 if (stat(path, &info) < 0) { 390 log_sys_very_verbose("stat", path); 391 return 0; 392 } 393 394 if (!S_ISREG(info.st_mode)) { 395 log_debug("%s: Not a regular file", path); 396 return 0; 397 } 398 399 if (!_insert_dev(path, 0)) 400 return_0; 401 402 return 1; 403 } 404 405 static int _insert(const char *path, int rec) 406 { 407 struct stat info; 408 int r = 0; 409 410 if (stat(path, &info) < 0) { 411 log_sys_very_verbose("stat", path); 412 return 0; 413 } 414 415 if (S_ISDIR(info.st_mode)) { /* add a directory */ 416 /* check it's not a symbolic link */ 417 if (lstat(path, &info) < 0) { 418 log_sys_very_verbose("lstat", path); 419 return 0; 420 } 421 422 if (S_ISLNK(info.st_mode)) { 423 log_debug("%s: Symbolic link to directory", path); 424 return 0; 425 } 426 427 if (rec) 428 r = _insert_dir(path); 429 430 } else { 431 /* add a device */ 432 #ifdef __NetBSD__ 433 /* 434 * In NetBSD we have two different types of devices 435 * raw and block. I can insert only existing 436 * raw and block device. 437 */ 438 if (S_ISBLK(info.st_mode)) { 439 log_debug("%s: Not a raw device", path); 440 return_0; 441 } 442 if (nbsd_check_dev(MAJOR(info.st_rdev),path) < 0) { 443 log_debug("%s: Not a known raw device", path); 444 return_0; 445 } 446 #else 447 if (!S_ISBLK(info.st_mode)) 448 log_debug("%s: Not a block device", path); 449 #endif 450 if (!_insert_dev(path, info.st_rdev)) { 451 return_0; 452 } 453 454 r = 1; 455 } 456 457 return r; 458 } 459 460 static void _full_scan(int dev_scan) 461 { 462 struct dir_list *dl; 463 464 if (_cache.has_scanned && !dev_scan) 465 return; 466 467 dm_list_iterate_items(dl, &_cache.dirs) 468 _insert_dir(dl->dir); 469 470 dm_list_iterate_items(dl, &_cache.files) 471 _insert_file(dl->dir); 472 473 _cache.has_scanned = 1; 474 init_full_scan_done(1); 475 } 476 477 int dev_cache_has_scanned(void) 478 { 479 return _cache.has_scanned; 480 } 481 482 void dev_cache_scan(int do_scan) 483 { 484 if (!do_scan) 485 _cache.has_scanned = 1; 486 else 487 _full_scan(1); 488 } 489 490 static int _init_preferred_names(struct cmd_context *cmd) 491 { 492 const struct config_node *cn; 493 struct config_value *v; 494 struct dm_pool *scratch = NULL; 495 char **regex; 496 unsigned count = 0; 497 int i, r = 0; 498 499 _cache.preferred_names_matcher = NULL; 500 501 if (!(cn = find_config_tree_node(cmd, "devices/preferred_names")) || 502 cn->v->type == CFG_EMPTY_ARRAY) { 503 log_very_verbose("devices/preferred_names not found in config file: " 504 "using built-in preferences"); 505 return 1; 506 } 507 508 for (v = cn->v; v; v = v->next) { 509 if (v->type != CFG_STRING) { 510 log_error("preferred_names patterns must be enclosed in quotes"); 511 return 0; 512 } 513 514 count++; 515 } 516 517 if (!(scratch = dm_pool_create("preferred device name matcher", 1024))) 518 return_0; 519 520 if (!(regex = dm_pool_alloc(scratch, sizeof(*regex) * count))) { 521 log_error("Failed to allocate preferred device name " 522 "pattern list."); 523 goto out; 524 } 525 526 for (v = cn->v, i = count - 1; v; v = v->next, i--) { 527 if (!(regex[i] = dm_pool_strdup(scratch, v->v.str))) { 528 log_error("Failed to allocate a preferred device name " 529 "pattern."); 530 goto out; 531 } 532 } 533 534 if (!(_cache.preferred_names_matcher = 535 dm_regex_create(_cache.mem,(const char **) regex, count))) { 536 log_error("Preferred device name pattern matcher creation failed."); 537 goto out; 538 } 539 540 r = 1; 541 542 out: 543 dm_pool_destroy(scratch); 544 545 return r; 546 } 547 548 int dev_cache_init(struct cmd_context *cmd) 549 { 550 _cache.names = NULL; 551 _cache.has_scanned = 0; 552 553 if (!(_cache.mem = dm_pool_create("dev_cache", 10 * 1024))) 554 return_0; 555 556 if (!(_cache.names = dm_hash_create(128))) { 557 dm_pool_destroy(_cache.mem); 558 _cache.mem = 0; 559 return_0; 560 } 561 562 if (!(_cache.devices = btree_create(_cache.mem))) { 563 log_err("Couldn't create binary tree for dev-cache."); 564 goto bad; 565 } 566 567 dm_list_init(&_cache.dirs); 568 dm_list_init(&_cache.files); 569 570 if (!_init_preferred_names(cmd)) 571 goto_bad; 572 573 return 1; 574 575 bad: 576 dev_cache_exit(); 577 return 0; 578 } 579 580 static void _check_closed(struct device *dev) 581 { 582 if (dev->fd >= 0) 583 log_err("Device '%s' has been left open.", dev_name(dev)); 584 } 585 586 static void _check_for_open_devices(void) 587 { 588 dm_hash_iter(_cache.names, (dm_hash_iterate_fn) _check_closed); 589 } 590 591 void dev_cache_exit(void) 592 { 593 if (_cache.names) 594 _check_for_open_devices(); 595 596 if (_cache.preferred_names_matcher) 597 _cache.preferred_names_matcher = NULL; 598 599 if (_cache.mem) { 600 dm_pool_destroy(_cache.mem); 601 _cache.mem = NULL; 602 } 603 604 if (_cache.names) { 605 dm_hash_destroy(_cache.names); 606 _cache.names = NULL; 607 } 608 609 _cache.devices = NULL; 610 _cache.has_scanned = 0; 611 dm_list_init(&_cache.dirs); 612 dm_list_init(&_cache.files); 613 } 614 615 int dev_cache_add_dir(const char *path) 616 { 617 struct dir_list *dl; 618 struct stat st; 619 620 if (stat(path, &st)) { 621 log_error("Ignoring %s: %s", path, strerror(errno)); 622 /* But don't fail */ 623 return 1; 624 } 625 626 if (!S_ISDIR(st.st_mode)) { 627 log_error("Ignoring %s: Not a directory", path); 628 return 1; 629 } 630 631 if (!(dl = _alloc(sizeof(*dl) + strlen(path) + 1))) { 632 log_error("dir_list allocation failed"); 633 return 0; 634 } 635 636 strcpy(dl->dir, path); 637 dm_list_add(&_cache.dirs, &dl->list); 638 return 1; 639 } 640 641 int dev_cache_add_loopfile(const char *path) 642 { 643 struct dir_list *dl; 644 struct stat st; 645 646 if (stat(path, &st)) { 647 log_error("Ignoring %s: %s", path, strerror(errno)); 648 /* But don't fail */ 649 return 1; 650 } 651 652 if (!S_ISREG(st.st_mode)) { 653 log_error("Ignoring %s: Not a regular file", path); 654 return 1; 655 } 656 657 if (!(dl = _alloc(sizeof(*dl) + strlen(path) + 1))) { 658 log_error("dir_list allocation failed for file"); 659 return 0; 660 } 661 662 strcpy(dl->dir, path); 663 dm_list_add(&_cache.files, &dl->list); 664 return 1; 665 } 666 667 /* Check cached device name is still valid before returning it */ 668 /* This should be a rare occurrence */ 669 /* set quiet if the cache is expected to be out-of-date */ 670 /* FIXME Make rest of code pass/cache struct device instead of dev_name */ 671 const char *dev_name_confirmed(struct device *dev, int quiet) 672 { 673 struct stat buf; 674 const char *name; 675 int r; 676 677 if ((dev->flags & DEV_REGULAR)) 678 return dev_name(dev); 679 680 while ((r = stat(name = dm_list_item(dev->aliases.n, 681 struct str_list)->str, &buf)) || 682 (buf.st_rdev != dev->dev)) { 683 if (r < 0) { 684 if (quiet) 685 log_sys_debug("stat", name); 686 else 687 log_sys_error("stat", name); 688 } 689 if (quiet) 690 log_debug("Path %s no longer valid for device(%d,%d)", 691 name, (int) MAJOR(dev->dev), 692 (int) MINOR(dev->dev)); 693 else 694 log_error("Path %s no longer valid for device(%d,%d)", 695 name, (int) MAJOR(dev->dev), 696 (int) MINOR(dev->dev)); 697 698 /* Remove the incorrect hash entry */ 699 dm_hash_remove(_cache.names, name); 700 701 /* Leave list alone if there isn't an alternative name */ 702 /* so dev_name will always find something to return. */ 703 /* Otherwise add the name to the correct device. */ 704 if (dm_list_size(&dev->aliases) > 1) { 705 dm_list_del(dev->aliases.n); 706 if (!r) 707 _insert(name, 0); 708 continue; 709 } 710 711 /* Scanning issues this inappropriately sometimes. */ 712 log_debug("Aborting - please provide new pathname for what " 713 "used to be %s", name); 714 return NULL; 715 } 716 717 return dev_name(dev); 718 } 719 720 struct device *dev_cache_get(const char *name, struct dev_filter *f) 721 { 722 struct stat buf; 723 struct device *d = (struct device *) dm_hash_lookup(_cache.names, name); 724 725 if (d && (d->flags & DEV_REGULAR)) 726 return d; 727 728 /* If the entry's wrong, remove it */ 729 if (d && (stat(name, &buf) || (buf.st_rdev != d->dev))) { 730 dm_hash_remove(_cache.names, name); 731 d = NULL; 732 } 733 734 if (!d) { 735 _insert(name, 0); 736 d = (struct device *) dm_hash_lookup(_cache.names, name); 737 if (!d) { 738 _full_scan(0); 739 d = (struct device *) dm_hash_lookup(_cache.names, name); 740 } 741 } 742 743 return (d && (!f || (d->flags & DEV_REGULAR) || 744 f->passes_filter(f, d))) ? d : NULL; 745 } 746 747 struct dev_iter *dev_iter_create(struct dev_filter *f, int dev_scan) 748 { 749 struct dev_iter *di = dm_malloc(sizeof(*di)); 750 751 if (!di) { 752 log_error("dev_iter allocation failed"); 753 return NULL; 754 } 755 756 if (dev_scan && !trust_cache()) { 757 /* Flag gets reset between each command */ 758 if (!full_scan_done()) 759 persistent_filter_wipe(f); /* Calls _full_scan(1) */ 760 } else 761 _full_scan(0); 762 763 di->current = btree_first(_cache.devices); 764 di->filter = f; 765 766 return di; 767 } 768 769 void dev_iter_destroy(struct dev_iter *iter) 770 { 771 dm_free(iter); 772 } 773 774 static struct device *_iter_next(struct dev_iter *iter) 775 { 776 struct device *d = btree_get_data(iter->current); 777 iter->current = btree_next(iter->current); 778 return d; 779 } 780 781 struct device *dev_iter_get(struct dev_iter *iter) 782 { 783 while (iter->current) { 784 struct device *d = _iter_next(iter); 785 if (!iter->filter || (d->flags & DEV_REGULAR) || 786 iter->filter->passes_filter(iter->filter, d)) 787 return d; 788 } 789 790 return NULL; 791 } 792 793 int dev_fd(struct device *dev) 794 { 795 return dev->fd; 796 } 797 798 const char *dev_name(const struct device *dev) 799 { 800 return (dev) ? dm_list_item(dev->aliases.n, struct str_list)->str : 801 "unknown device"; 802 } 803