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