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 #if defined(__DragonFly__) 408 int dragonfly_check_dev(int major, const char *path); 409 #endif 410 411 static int _insert(const char *path, int rec) 412 { 413 struct stat info; 414 int r = 0; 415 416 if (stat(path, &info) < 0) { 417 log_sys_very_verbose("stat", path); 418 return 0; 419 } 420 421 if (S_ISDIR(info.st_mode)) { /* add a directory */ 422 /* check it's not a symbolic link */ 423 if (lstat(path, &info) < 0) { 424 log_sys_very_verbose("lstat", path); 425 return 0; 426 } 427 428 if (S_ISLNK(info.st_mode)) { 429 log_debug("%s: Symbolic link to directory", path); 430 return 0; 431 } 432 433 if (rec) 434 r = _insert_dir(path); 435 436 } else { 437 /* add a device */ 438 #ifdef __NetBSD__ 439 /* 440 * In NetBSD we have two different types of devices 441 * raw and block. I can insert only existing 442 * raw and block device. 443 */ 444 if (S_ISBLK(info.st_mode)) { 445 log_debug("%s: Not a raw device", path); 446 return_0; 447 } 448 if (nbsd_check_dev(MAJOR(info.st_rdev),path) < 0) { 449 log_debug("%s: Not a known raw device", path); 450 return_0; 451 } 452 #elif defined(__DragonFly__) 453 if (dragonfly_check_dev(MAJOR(info.st_rdev),path) < 0) { 454 log_debug("%s: Device not added to cache", path); 455 return_0; 456 } 457 458 #else 459 if (!S_ISBLK(info.st_mode)) 460 log_debug("%s: Not a block device", path); 461 #endif 462 if (!_insert_dev(path, info.st_rdev)) { 463 return_0; 464 } 465 466 r = 1; 467 } 468 469 return r; 470 } 471 472 static void _full_scan(int dev_scan) 473 { 474 struct dir_list *dl; 475 476 if (_cache.has_scanned && !dev_scan) 477 return; 478 479 dm_list_iterate_items(dl, &_cache.dirs) 480 _insert_dir(dl->dir); 481 482 dm_list_iterate_items(dl, &_cache.files) 483 _insert_file(dl->dir); 484 485 _cache.has_scanned = 1; 486 init_full_scan_done(1); 487 } 488 489 int dev_cache_has_scanned(void) 490 { 491 return _cache.has_scanned; 492 } 493 494 void dev_cache_scan(int do_scan) 495 { 496 if (!do_scan) 497 _cache.has_scanned = 1; 498 else 499 _full_scan(1); 500 } 501 502 static int _init_preferred_names(struct cmd_context *cmd) 503 { 504 const struct config_node *cn; 505 struct config_value *v; 506 struct dm_pool *scratch = NULL; 507 char **regex; 508 unsigned count = 0; 509 int i, r = 0; 510 511 _cache.preferred_names_matcher = NULL; 512 513 if (!(cn = find_config_tree_node(cmd, "devices/preferred_names")) || 514 cn->v->type == CFG_EMPTY_ARRAY) { 515 log_very_verbose("devices/preferred_names not found in config file: " 516 "using built-in preferences"); 517 return 1; 518 } 519 520 for (v = cn->v; v; v = v->next) { 521 if (v->type != CFG_STRING) { 522 log_error("preferred_names patterns must be enclosed in quotes"); 523 return 0; 524 } 525 526 count++; 527 } 528 529 if (!(scratch = dm_pool_create("preferred device name matcher", 1024))) 530 return_0; 531 532 if (!(regex = dm_pool_alloc(scratch, sizeof(*regex) * count))) { 533 log_error("Failed to allocate preferred device name " 534 "pattern list."); 535 goto out; 536 } 537 538 for (v = cn->v, i = count - 1; v; v = v->next, i--) { 539 if (!(regex[i] = dm_pool_strdup(scratch, v->v.str))) { 540 log_error("Failed to allocate a preferred device name " 541 "pattern."); 542 goto out; 543 } 544 } 545 546 if (!(_cache.preferred_names_matcher = 547 dm_regex_create(_cache.mem,(const char **) regex, count))) { 548 log_error("Preferred device name pattern matcher creation failed."); 549 goto out; 550 } 551 552 r = 1; 553 554 out: 555 dm_pool_destroy(scratch); 556 557 return r; 558 } 559 560 int dev_cache_init(struct cmd_context *cmd) 561 { 562 _cache.names = NULL; 563 _cache.has_scanned = 0; 564 565 if (!(_cache.mem = dm_pool_create("dev_cache", 10 * 1024))) 566 return_0; 567 568 if (!(_cache.names = dm_hash_create(128))) { 569 dm_pool_destroy(_cache.mem); 570 _cache.mem = 0; 571 return_0; 572 } 573 574 if (!(_cache.devices = btree_create(_cache.mem))) { 575 log_error("Couldn't create binary tree for dev-cache."); 576 goto bad; 577 } 578 579 dm_list_init(&_cache.dirs); 580 dm_list_init(&_cache.files); 581 582 if (!_init_preferred_names(cmd)) 583 goto_bad; 584 585 return 1; 586 587 bad: 588 dev_cache_exit(); 589 return 0; 590 } 591 592 static void _check_closed(struct device *dev) 593 { 594 if (dev->fd >= 0) 595 log_error("Device '%s' has been left open.", dev_name(dev)); 596 } 597 598 static void _check_for_open_devices(void) 599 { 600 dm_hash_iter(_cache.names, (dm_hash_iterate_fn) _check_closed); 601 } 602 603 void dev_cache_exit(void) 604 { 605 if (_cache.names) 606 _check_for_open_devices(); 607 608 if (_cache.preferred_names_matcher) 609 _cache.preferred_names_matcher = NULL; 610 611 if (_cache.mem) { 612 dm_pool_destroy(_cache.mem); 613 _cache.mem = NULL; 614 } 615 616 if (_cache.names) { 617 dm_hash_destroy(_cache.names); 618 _cache.names = NULL; 619 } 620 621 _cache.devices = NULL; 622 _cache.has_scanned = 0; 623 dm_list_init(&_cache.dirs); 624 dm_list_init(&_cache.files); 625 } 626 627 int dev_cache_add_dir(const char *path) 628 { 629 struct dir_list *dl; 630 struct stat st; 631 632 if (stat(path, &st)) { 633 log_error("Ignoring %s: %s", path, strerror(errno)); 634 /* But don't fail */ 635 return 1; 636 } 637 638 if (!S_ISDIR(st.st_mode)) { 639 log_error("Ignoring %s: Not a directory", path); 640 return 1; 641 } 642 643 if (!(dl = _alloc(sizeof(*dl) + strlen(path) + 1))) { 644 log_error("dir_list allocation failed"); 645 return 0; 646 } 647 648 strcpy(dl->dir, path); 649 dm_list_add(&_cache.dirs, &dl->list); 650 return 1; 651 } 652 653 int dev_cache_add_loopfile(const char *path) 654 { 655 struct dir_list *dl; 656 struct stat st; 657 658 if (stat(path, &st)) { 659 log_error("Ignoring %s: %s", path, strerror(errno)); 660 /* But don't fail */ 661 return 1; 662 } 663 664 if (!S_ISREG(st.st_mode)) { 665 log_error("Ignoring %s: Not a regular file", path); 666 return 1; 667 } 668 669 if (!(dl = _alloc(sizeof(*dl) + strlen(path) + 1))) { 670 log_error("dir_list allocation failed for file"); 671 return 0; 672 } 673 674 strcpy(dl->dir, path); 675 dm_list_add(&_cache.files, &dl->list); 676 return 1; 677 } 678 679 /* Check cached device name is still valid before returning it */ 680 /* This should be a rare occurrence */ 681 /* set quiet if the cache is expected to be out-of-date */ 682 /* FIXME Make rest of code pass/cache struct device instead of dev_name */ 683 const char *dev_name_confirmed(struct device *dev, int quiet) 684 { 685 struct stat buf; 686 const char *name; 687 int r; 688 689 if ((dev->flags & DEV_REGULAR)) 690 return dev_name(dev); 691 692 while ((r = stat(name = dm_list_item(dev->aliases.n, 693 struct str_list)->str, &buf)) || 694 (buf.st_rdev != dev->dev)) { 695 if (r < 0) { 696 if (quiet) 697 log_sys_debug("stat", name); 698 else 699 log_sys_error("stat", name); 700 } 701 if (quiet) 702 log_debug("Path %s no longer valid for device(%d,%d)", 703 name, (int) MAJOR(dev->dev), 704 (int) MINOR(dev->dev)); 705 else 706 log_error("Path %s no longer valid for device(%d,%d)", 707 name, (int) MAJOR(dev->dev), 708 (int) MINOR(dev->dev)); 709 710 /* Remove the incorrect hash entry */ 711 dm_hash_remove(_cache.names, name); 712 713 /* Leave list alone if there isn't an alternative name */ 714 /* so dev_name will always find something to return. */ 715 /* Otherwise add the name to the correct device. */ 716 if (dm_list_size(&dev->aliases) > 1) { 717 dm_list_del(dev->aliases.n); 718 if (!r) 719 _insert(name, 0); 720 continue; 721 } 722 723 /* Scanning issues this inappropriately sometimes. */ 724 log_debug("Aborting - please provide new pathname for what " 725 "used to be %s", name); 726 return NULL; 727 } 728 729 return dev_name(dev); 730 } 731 732 struct device *dev_cache_get(const char *name, struct dev_filter *f) 733 { 734 struct stat buf; 735 struct device *d = (struct device *) dm_hash_lookup(_cache.names, name); 736 737 if (d && (d->flags & DEV_REGULAR)) 738 return d; 739 740 /* If the entry's wrong, remove it */ 741 if (d && (stat(name, &buf) || (buf.st_rdev != d->dev))) { 742 dm_hash_remove(_cache.names, name); 743 d = NULL; 744 } 745 746 if (!d) { 747 _insert(name, 0); 748 d = (struct device *) dm_hash_lookup(_cache.names, name); 749 if (!d) { 750 _full_scan(0); 751 d = (struct device *) dm_hash_lookup(_cache.names, name); 752 } 753 } 754 755 return (d && (!f || (d->flags & DEV_REGULAR) || 756 f->passes_filter(f, d))) ? d : NULL; 757 } 758 759 struct dev_iter *dev_iter_create(struct dev_filter *f, int dev_scan) 760 { 761 struct dev_iter *di = dm_malloc(sizeof(*di)); 762 763 if (!di) { 764 log_error("dev_iter allocation failed"); 765 return NULL; 766 } 767 768 if (dev_scan && !trust_cache()) { 769 /* Flag gets reset between each command */ 770 if (!full_scan_done()) 771 persistent_filter_wipe(f); /* Calls _full_scan(1) */ 772 } else 773 _full_scan(0); 774 775 di->current = btree_first(_cache.devices); 776 di->filter = f; 777 778 return di; 779 } 780 781 void dev_iter_destroy(struct dev_iter *iter) 782 { 783 dm_free(iter); 784 } 785 786 static struct device *_iter_next(struct dev_iter *iter) 787 { 788 struct device *d = btree_get_data(iter->current); 789 iter->current = btree_next(iter->current); 790 return d; 791 } 792 793 struct device *dev_iter_get(struct dev_iter *iter) 794 { 795 while (iter->current) { 796 struct device *d = _iter_next(iter); 797 if (!iter->filter || (d->flags & DEV_REGULAR) || 798 iter->filter->passes_filter(iter->filter, d)) 799 return d; 800 } 801 802 return NULL; 803 } 804 805 int dev_fd(struct device *dev) 806 { 807 return dev->fd; 808 } 809 810 const char *dev_name(const struct device *dev) 811 { 812 return (dev) ? dm_list_item(dev->aliases.n, struct str_list)->str : 813 "unknown device"; 814 } 815