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