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