1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * This module adds support to the RCM framework for mounted filesystems. 31 * 32 * The module provides this functionality: 33 * 1) reports device usage for mounted filesystems 34 * 2) prevents offline operations for mounted resources 35 * 3) prevents suspend operations (unless forced) of those filesystems 36 * deemed critical for the continued operation of the OS 37 * 4) propagates RCM operations from mounted resources to the consumers 38 * of files within the mounted filesystems 39 */ 40 41 #include <stdio.h> 42 #include <assert.h> 43 #include <string.h> 44 #include <synch.h> 45 #include <libintl.h> 46 #include <errno.h> 47 #include <sys/mnttab.h> 48 #include <sys/param.h> 49 #include <sys/stat.h> 50 #include <sys/utssys.h> 51 52 #include "rcm_module.h" 53 54 /* Definitions */ 55 56 #define HASH_DEFAULT 4 57 #define HASH_THRESHOLD 256 58 59 #define OPT_IGNORE "ignore" 60 61 #define MSG_HDR_STD gettext("mounted filesystem") 62 #define MSG_HDR_STD_MULTI gettext("mounted filesystems") 63 #define MSG_HDR_CRIT gettext("cannot suspend filesystem") 64 #define MSG_HDR_CRIT_MULTI gettext("cannot suspend filesystems") 65 #define MSG_SEPARATOR gettext(", ") 66 #define MSG_FAIL_USAGE gettext("failed to construct usage string.") 67 #define MSG_FAIL_DEPENDENTS gettext("failed while calling dependents.") 68 #define MSG_FAIL_REMOVE gettext("filesystems cannot be removed.") 69 #define MSG_FAIL_INTERNAL gettext("internal processing failure.") 70 71 typedef struct hashentry { 72 int n_mounts; 73 char *special; 74 char **mountps; 75 struct hashentry *next; 76 } hashentry_t; 77 78 typedef struct { 79 time_t timestamp; 80 uint32_t hash_size; 81 hashentry_t **mounts; 82 } cache_t; 83 84 /* Forward Declarations */ 85 86 /* module interface routines */ 87 static int mnt_register(rcm_handle_t *); 88 static int mnt_unregister(rcm_handle_t *); 89 static int mnt_getinfo(rcm_handle_t *, char *, id_t, uint_t, char **, char **, 90 nvlist_t *, rcm_info_t **); 91 static int mnt_suspend(rcm_handle_t *, char *, id_t, timespec_t *, 92 uint_t, char **, rcm_info_t **); 93 static int mnt_resume(rcm_handle_t *, char *, id_t, uint_t, char **, 94 rcm_info_t **); 95 static int mnt_offline(rcm_handle_t *, char *, id_t, uint_t, char **, 96 rcm_info_t **); 97 static int mnt_online(rcm_handle_t *, char *, id_t, uint_t, char **, 98 rcm_info_t **); 99 static int mnt_remove(rcm_handle_t *, char *, id_t, uint_t, char **, 100 rcm_info_t **); 101 102 /* cache functions */ 103 static cache_t *cache_create(); 104 static int cache_insert(cache_t *, struct mnttab *); 105 static int cache_sync(rcm_handle_t *, cache_t **); 106 static hashentry_t *cache_lookup(cache_t *, char *); 107 static void free_cache(cache_t **); 108 static void free_entry(hashentry_t **); 109 static void free_list(char **); 110 111 /* miscellaneous functions */ 112 static uint32_t hash(uint32_t, char *); 113 static void register_rsrc(rcm_handle_t *, char *); 114 static void unregister_rsrc(rcm_handle_t *, char *); 115 static char *create_message(char *, char *, char **); 116 static int detect_critical_failure(char **, uint_t, char **); 117 static int is_critical(char *); 118 static int use_cache(char *, char **, char ***); 119 static void prune_dependents(char **, char *); 120 static char **create_dependents(hashentry_t *); 121 122 /* Module-Private data */ 123 124 static struct rcm_mod_ops mnt_ops = 125 { 126 RCM_MOD_OPS_VERSION, 127 mnt_register, 128 mnt_unregister, 129 mnt_getinfo, 130 mnt_suspend, 131 mnt_resume, 132 mnt_offline, 133 mnt_online, 134 mnt_remove 135 }; 136 137 static cache_t *mnt_cache; 138 static mutex_t cache_lock; 139 140 /* Module Interface Routines */ 141 142 /* 143 * rcm_mod_init() 144 * 145 * Called when module is loaded. Returns the ops vector. 146 */ 147 struct rcm_mod_ops * 148 rcm_mod_init() 149 { 150 return (&mnt_ops); 151 } 152 153 /* 154 * rcm_mod_info() 155 * 156 * Returns a string identifying this module. 157 */ 158 const char * 159 rcm_mod_info() 160 { 161 return ("File system module %I%"); 162 } 163 164 /* 165 * rcm_mod_fini() 166 * 167 * Called when module is unloaded. Frees up all used memory. 168 * 169 * Locking: the cache is locked for the duration of this function. 170 */ 171 int 172 rcm_mod_fini() 173 { 174 (void) mutex_lock(&cache_lock); 175 free_cache(&mnt_cache); 176 (void) mutex_unlock(&cache_lock); 177 178 return (RCM_SUCCESS); 179 } 180 181 /* 182 * mnt_register() 183 * 184 * Called to synchronize the module's registrations. Results in the 185 * construction of a new cache, destruction of any old cache data, 186 * and a full synchronization of the module's registrations. 187 * 188 * Locking: the cache is locked for the duration of this function. 189 */ 190 int 191 mnt_register(rcm_handle_t *hd) 192 { 193 assert(hd != NULL); 194 195 rcm_log_message(RCM_TRACE1, "FILESYS: register()\n"); 196 197 (void) mutex_lock(&cache_lock); 198 199 /* cache_sync() does all of the necessary work */ 200 if (cache_sync(hd, &mnt_cache) < 0) { 201 rcm_log_message(RCM_ERROR, 202 "FILESYS: failed to synchronize cache (%s).\n", 203 strerror(errno)); 204 (void) mutex_unlock(&cache_lock); 205 return (RCM_FAILURE); 206 } 207 208 (void) mutex_unlock(&cache_lock); 209 210 return (RCM_SUCCESS); 211 } 212 213 /* 214 * mnt_unregister() 215 * 216 * Manually walk through the cache, unregistering all the special 217 * files and mount points. 218 * 219 * Locking: the cache is locked throughout the execution of this 220 * routine because it reads and modifies cache links continuously. 221 */ 222 int 223 mnt_unregister(rcm_handle_t *hd) 224 { 225 uint32_t index; 226 hashentry_t *entry; 227 228 assert(hd != NULL); 229 230 rcm_log_message(RCM_TRACE1, "FILESYS: unregister()\n"); 231 232 (void) mutex_lock(&cache_lock); 233 234 /* Unregister everything in the cache */ 235 if (mnt_cache) { 236 for (index = 0; index < mnt_cache->hash_size; index++) { 237 for (entry = mnt_cache->mounts[index]; entry != NULL; 238 entry = entry->next) { 239 unregister_rsrc(hd, entry->special); 240 } 241 } 242 } 243 244 /* Destroy the cache */ 245 free_cache(&mnt_cache); 246 247 (void) mutex_unlock(&cache_lock); 248 249 return (RCM_SUCCESS); 250 } 251 252 /* 253 * mnt_offline() 254 * 255 * Filesystem resources cannot be offlined. Always returns failure. 256 * Since no real action is taken, QUERY or not doesn't matter. 257 */ 258 int 259 mnt_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, 260 char **errorp, rcm_info_t **dependent_info) 261 { 262 char **dependents; 263 264 assert(hd != NULL); 265 assert(rsrc != NULL); 266 assert(id == (id_t)0); 267 assert(errorp != NULL); 268 269 rcm_log_message(RCM_TRACE1, "FILESYS: offline(%s)\n", rsrc); 270 271 /* Retrieve necessary info from the cache */ 272 if (use_cache(rsrc, errorp, &dependents) < 0) 273 return (RCM_FAILURE); 274 275 /* Convert the gathered dependents into an error message */ 276 *errorp = create_message(MSG_HDR_STD, MSG_HDR_STD_MULTI, dependents); 277 if (*errorp == NULL) { 278 rcm_log_message(RCM_ERROR, 279 "FILESYS: failed to construct offline message (%s).\n", 280 strerror(errno)); 281 } 282 free_list(dependents); 283 284 return (RCM_FAILURE); 285 } 286 287 /* 288 * mnt_online() 289 * 290 * Filesystem resources aren't offlined, so there's really nothing to do 291 * here. 292 */ 293 int 294 mnt_online(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, char **errorp, 295 rcm_info_t **dependent_reason) 296 { 297 assert(hd != NULL); 298 assert(rsrc != NULL); 299 assert(id == (id_t)0); 300 assert(errorp != NULL); 301 302 rcm_log_message(RCM_TRACE1, "FILESYS: online(%s)\n", rsrc); 303 304 return (RCM_SUCCESS); 305 } 306 307 /* 308 * mnt_getinfo() 309 * 310 * Report how a given resource is in use by this module. And also 311 * possibly include dependent consumers of the mounted filesystems. 312 */ 313 int 314 mnt_getinfo(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, char **usagep, 315 char **errorp, nvlist_t *props, rcm_info_t **depend_info) 316 { 317 int rv = RCM_SUCCESS; 318 char **dependents; 319 320 assert(hd != NULL); 321 assert(rsrc != NULL); 322 assert(id == (id_t)0); 323 assert(usagep != NULL); 324 assert(errorp != NULL); 325 assert(props != NULL); 326 327 rcm_log_message(RCM_TRACE1, "FILESYS: getinfo(%s)\n", rsrc); 328 329 /* Retrieve necessary info from the cache */ 330 if (use_cache(rsrc, errorp, &dependents) < 0) 331 return (RCM_FAILURE); 332 333 /* Convert the gathered dependents into a usage message */ 334 *usagep = create_message(MSG_HDR_STD, MSG_HDR_STD_MULTI, dependents); 335 if (*usagep == NULL) { 336 rcm_log_message(RCM_ERROR, 337 "FILESYS: failed to construct usage message (%s).\n", 338 strerror(errno)); 339 *errorp = strdup(MSG_FAIL_USAGE); 340 free_list(dependents); 341 return (RCM_FAILURE); 342 } 343 344 /* Recurse on dependents if necessary */ 345 if ((flag & RCM_INCLUDE_DEPENDENT) && (dependents != NULL)) { 346 prune_dependents(dependents, rsrc); 347 if (dependents[0] != NULL) { 348 if ((rv = rcm_get_info_list(hd, dependents, flag, 349 depend_info)) != RCM_SUCCESS) { 350 *errorp = strdup(MSG_FAIL_DEPENDENTS); 351 } 352 } 353 } 354 355 /* Free up info retrieved from the cache */ 356 free_list(dependents); 357 358 return (rv); 359 } 360 361 /* 362 * mnt_suspend() 363 * 364 * Notify all dependents that the resource is being suspended. 365 * Since no real action is taken, QUERY or not doesn't matter. 366 */ 367 int 368 mnt_suspend(rcm_handle_t *hd, char *rsrc, id_t id, timespec_t *interval, 369 uint_t flag, char **errorp, rcm_info_t **depend_info) 370 { 371 int rv = RCM_SUCCESS; 372 char **dependents; 373 374 assert(hd != NULL); 375 assert(rsrc != NULL); 376 assert(id == (id_t)0); 377 assert(interval != NULL); 378 assert(errorp != NULL); 379 380 rcm_log_message(RCM_TRACE1, "FILESYS: suspend(%s)\n", rsrc); 381 382 /* Retrieve necessary info from the cache */ 383 if (use_cache(rsrc, errorp, &dependents) < 0) 384 return (RCM_FAILURE); 385 386 /* Unforced suspensions fail if any of the dependents are critical */ 387 if (detect_critical_failure(errorp, flag, dependents)) { 388 free_list(dependents); 389 return (RCM_FAILURE); 390 } 391 392 /* Recurse on dependents if necessary */ 393 if ((flag & RCM_INCLUDE_DEPENDENT) && (dependents != NULL)) { 394 prune_dependents(dependents, rsrc); 395 if (dependents[0] != NULL) 396 if ((rv = rcm_request_suspend_list(hd, dependents, flag, 397 interval, depend_info)) != RCM_SUCCESS) { 398 *errorp = strdup(MSG_FAIL_DEPENDENTS); 399 } 400 } 401 free_list(dependents); 402 403 return (rv); 404 } 405 406 /* 407 * mnt_resume() 408 * 409 * Resume all the dependents of a suspended filesystem. 410 */ 411 int 412 mnt_resume(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, char **errorp, 413 rcm_info_t **depend_info) 414 { 415 int rv = RCM_SUCCESS; 416 char **dependents; 417 418 assert(hd != NULL); 419 assert(rsrc != NULL); 420 assert(id == (id_t)0); 421 assert(errorp != NULL); 422 423 rcm_log_message(RCM_TRACE1, "FILESYS: resume(%s)\n", rsrc); 424 425 /* Retrieve necessary info from the cache */ 426 if (use_cache(rsrc, errorp, &dependents) < 0) 427 return (RCM_FAILURE); 428 429 /* Recurse on dependents if necessary */ 430 if ((flag & RCM_INCLUDE_DEPENDENT) && (dependents != NULL)) { 431 prune_dependents(dependents, rsrc); 432 if (dependents[0] != NULL) { 433 if ((rv = rcm_notify_resume_list(hd, dependents, flag, 434 depend_info)) != RCM_SUCCESS) { 435 *errorp = strdup(MSG_FAIL_DEPENDENTS); 436 } 437 } 438 } 439 free_list(dependents); 440 441 return (rv); 442 } 443 444 /* 445 * mnt_remove() 446 * 447 * Remove should never be called since offline always fails. 448 * 449 * Return failure and log the mistake if a remove is ever received for a 450 * mounted filesystem resource. 451 */ 452 int 453 mnt_remove(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, char **errorp, 454 rcm_info_t **depend_info) 455 { 456 assert(hd != NULL); 457 assert(rsrc != NULL); 458 assert(id == (id_t)0); 459 assert(errorp != NULL); 460 461 rcm_log_message(RCM_TRACE1, "FILESYS: remove(%s)\n", rsrc); 462 463 /* Log the mistake */ 464 rcm_log_message(RCM_ERROR, "FILESYS: invalid remove of \"%s\"\n", rsrc); 465 *errorp = strdup(MSG_FAIL_REMOVE); 466 467 return (RCM_FAILURE); 468 } 469 470 /* 471 * Cache management routines 472 */ 473 474 /* 475 * cache_create() 476 * 477 * This routine constructs a new cache of the current mnttab file. 478 * 479 * Locking: the cache must be locked prior to calling this function. 480 * 481 * Return Values: NULL with errno set on failure, new cache point on 482 * success. 483 */ 484 static cache_t * 485 cache_create() 486 { 487 FILE *fp; 488 cache_t *cache; 489 int i; 490 uint32_t size; 491 struct stat st; 492 struct mnttab mt; 493 494 /* 495 * To keep the hash table relatively sparse, default values are 496 * used for smaller mnttab files and these values are scaled up 497 * as a fraction of the total mnttab file size for larger ones. 498 */ 499 if (stat(MNTTAB, &st) < 0) { 500 rcm_log_message(RCM_ERROR, 501 "FILESYS: failed to stat \"%s\" (%s).\n", MNTTAB, 502 strerror(errno)); 503 errno = EBADF; 504 return (NULL); 505 } 506 if (st.st_size > HASH_THRESHOLD) { 507 size = st.st_size / HASH_THRESHOLD; 508 for (i = 0; size > 1; i++, size >>= 1); 509 for (; i > -1; i--, size <<= 1); 510 } else { 511 size = HASH_DEFAULT; 512 } 513 514 /* Allocate a new empty cache */ 515 if ((cache = (cache_t *)calloc(1, sizeof (cache_t))) == NULL) { 516 rcm_log_message(RCM_ERROR, 517 "FILESYS: failed to allocate cache (%s).\n", 518 strerror(errno)); 519 errno = ENOMEM; 520 return (NULL); 521 } 522 cache->hash_size = size; 523 cache->timestamp = st.st_mtime; 524 525 /* Allocate an empty hash table for the registered special devices */ 526 cache->mounts = (hashentry_t **)calloc(size, sizeof (hashentry_t *)); 527 if (cache->mounts == NULL) { 528 rcm_log_message(RCM_ERROR, 529 "FILESYS: failed to allocate mount table (%s).\n", 530 strerror(errno)); 531 free_cache(&cache); 532 errno = ENOMEM; 533 return (NULL); 534 } 535 536 /* Open the mnttab file */ 537 if ((fp = fopen(MNTTAB, "r")) == NULL) { 538 rcm_log_message(RCM_ERROR, 539 "FILESYS: failed to open \"%s\" (%s).\n", MNTTAB, 540 strerror(errno)); 541 free_cache(&cache); 542 errno = EIO; 543 return (NULL); 544 } 545 546 /* Insert each mnttab entry into the cache */ 547 while (getmntent(fp, &mt) == 0) { 548 549 /* Well, not each entry... some are meant to be ignored */ 550 if ((mt.mnt_mntopts != NULL) && 551 (hasmntopt(&mt, OPT_IGNORE) != NULL)) 552 continue; 553 554 if (cache_insert(cache, &mt) < 0) { 555 rcm_log_message(RCM_ERROR, 556 "FILESYS: cache insertion failure (%s).\n", 557 strerror(errno)); 558 free_cache(&cache); 559 (void) fclose(fp); 560 errno = EFAULT; 561 return (NULL); 562 } 563 } 564 565 /* Close the mnttab file */ 566 (void) fclose(fp); 567 568 return (cache); 569 } 570 571 /* 572 * free_cache() 573 * 574 * Free up all the memory associated with a cache. 575 * 576 * Locking: the cache must be locked before calling this function. 577 */ 578 static void 579 free_cache(cache_t **cachep) 580 { 581 uint32_t index; 582 hashentry_t *entry; 583 hashentry_t *entry_tmp; 584 585 /* Do nothing with empty caches */ 586 if ((cachep == NULL) || (*cachep == NULL)) 587 return; 588 589 if ((*cachep)->mounts) { 590 /* Walk through the hashtable, emptying it */ 591 for (index = 0; index < (*cachep)->hash_size; index++) { 592 entry = (*cachep)->mounts[index]; 593 while (entry) { 594 entry_tmp = entry->next; 595 free_entry(&entry); 596 entry = entry_tmp; 597 } 598 } 599 free((*cachep)->mounts); 600 } 601 602 free(*cachep); 603 *cachep = NULL; 604 } 605 606 /* 607 * free_entry() 608 * 609 * Free up memory associated with a hashtable entry. 610 * 611 * Locking: the cache must be locked before calling this function. 612 */ 613 static void 614 free_entry(hashentry_t **entryp) 615 { 616 if (entryp) { 617 if (*entryp) { 618 if ((*entryp)->special) 619 free((*entryp)->special); 620 free_list((*entryp)->mountps); 621 free(*entryp); 622 } 623 *entryp = NULL; 624 } 625 } 626 627 /* 628 * free_list() 629 * 630 * Free up memory associated with a null terminated list of names. 631 */ 632 static void 633 free_list(char **list) 634 { 635 int i; 636 637 if (list) { 638 for (i = 0; list[i] != NULL; i++) 639 free(list[i]); 640 free(list); 641 } 642 } 643 644 /* 645 * cache_sync() 646 * 647 * Resynchronize the mnttab cache with the mnttab file. 648 * 649 * Locking: the cache must be locked before calling this function. 650 * 651 * Return Values: -1 with errno set on failure, 0 on success. 652 */ 653 static int 654 cache_sync(rcm_handle_t *hd, cache_t **cachep) 655 { 656 uint32_t index; 657 cache_t *new_cache; 658 cache_t *old_cache; 659 hashentry_t *entry; 660 struct stat st; 661 662 /* Only accept valid arguments */ 663 if ((hd == NULL) || (cachep == NULL)) { 664 rcm_log_message(RCM_ERROR, 665 "FILESYS: invalid arguments to cache_sync().\n"); 666 errno = EINVAL; 667 return (-1); 668 } 669 670 /* Do nothing if there's already an up-to-date cache */ 671 old_cache = *cachep; 672 if (old_cache) { 673 if (stat(MNTTAB, &st) == 0) { 674 if (old_cache->timestamp >= st.st_mtime) { 675 return (0); 676 } 677 } else { 678 rcm_log_message(RCM_WARNING, 679 "FILESYS: failed to stat \"%s\", cache is stale " 680 "(%s).\n", MNTTAB, strerror(errno)); 681 errno = EIO; 682 return (-1); 683 } 684 } 685 686 /* Create a new cache based on the new mnttab file. */ 687 if ((new_cache = cache_create()) == NULL) { 688 rcm_log_message(RCM_WARNING, 689 "FILESYS: failed creating cache, cache is stale (%s).\n", 690 strerror(errno)); 691 errno = EIO; 692 return (-1); 693 } 694 695 /* Register any specials found in the new cache but not the old one */ 696 for (index = 0; index < new_cache->hash_size; index++) { 697 for (entry = new_cache->mounts[index]; entry != NULL; 698 entry = entry->next) { 699 if (cache_lookup(old_cache, entry->special) == NULL) { 700 register_rsrc(hd, entry->special); 701 } 702 } 703 } 704 705 /* Pass the new cache pointer to the calling function */ 706 *cachep = new_cache; 707 708 /* If there wasn't an old cache, return successfully now */ 709 if (old_cache == NULL) 710 return (0); 711 712 /* 713 * If there was an old cache, then unregister whatever specials it 714 * contains that aren't in the new cache. And then destroy the old 715 * cache. 716 */ 717 for (index = 0; index < old_cache->hash_size; index++) { 718 for (entry = old_cache->mounts[index]; entry != NULL; 719 entry = entry->next) { 720 if (cache_lookup(new_cache, entry->special) == NULL) { 721 unregister_rsrc(hd, entry->special); 722 } 723 } 724 } 725 free_cache(&old_cache); 726 727 return (0); 728 } 729 730 /* 731 * cache_insert() 732 * 733 * Given a cache and a mnttab entry, this routine inserts that entry in 734 * the cache. The mnttab entry's special device is added to the 'mounts' 735 * hashtable of the cache, and the entry's mountp value is added to the 736 * list of associated mountpoints for the corresponding hashtable entry. 737 * 738 * Locking: the cache must be locked before calling this function. 739 * 740 * Return Values: -1 with errno set on failure, 0 on success. 741 */ 742 static int 743 cache_insert(cache_t *cache, struct mnttab *mt) 744 { 745 uint32_t index; 746 hashentry_t *entry; 747 char **mountps; 748 749 /* Only accept valid arguments */ 750 if ((cache == NULL) || 751 (cache->mounts == NULL) || 752 (mt == NULL) || 753 (mt->mnt_special == NULL) || 754 (mt->mnt_mountp == NULL)) { 755 errno = EINVAL; 756 return (-1); 757 } 758 759 /* 760 * Disregard any non-loopback mounts whose special device names 761 * don't begin with "/dev". 762 */ 763 if ((strncmp(mt->mnt_special, "/dev", strlen("/dev")) != 0) && 764 (strcmp(mt->mnt_fstype, "lofs") != 0)) 765 return (0); 766 767 /* 768 * Find the special device's entry in the mounts hashtable, allocating 769 * a new entry if necessary. 770 */ 771 index = hash(cache->hash_size, mt->mnt_special); 772 for (entry = cache->mounts[index]; entry != NULL; entry = entry->next) { 773 if (strcmp(entry->special, mt->mnt_special) == 0) 774 break; 775 } 776 if (entry == NULL) { 777 entry = (hashentry_t *)calloc(1, sizeof (hashentry_t)); 778 if ((entry == NULL) || 779 ((entry->special = strdup(mt->mnt_special)) == NULL)) { 780 rcm_log_message(RCM_ERROR, 781 "FILESYS: failed to allocate special device name " 782 "(%s).\n", strerror(errno)); 783 free_entry(&entry); 784 errno = ENOMEM; 785 return (-1); 786 } 787 entry->next = cache->mounts[index]; 788 cache->mounts[index] = entry; 789 } 790 791 /* 792 * Keep entries in the list of mounts unique, so exit early if the 793 * mount is already in the list. 794 */ 795 for (index = 0; index < entry->n_mounts; index++) { 796 if (strcmp(entry->mountps[index], mt->mnt_mountp) == 0) 797 return (0); 798 } 799 800 /* 801 * Add this mountpoint to the list of mounts associated with the 802 * special device. 803 */ 804 mountps = (char **)realloc(entry->mountps, 805 (entry->n_mounts + 2) * sizeof (char *)); 806 if ((mountps == NULL) || 807 ((mountps[entry->n_mounts] = strdup(mt->mnt_mountp)) == NULL)) { 808 rcm_log_message(RCM_ERROR, 809 "FILESYS: failed to allocate mountpoint name (%s).\n", 810 strerror(errno)); 811 if (entry->n_mounts == 0) { 812 cache->mounts[index] = entry->next; 813 free_entry(&entry); 814 } 815 errno = ENOMEM; 816 return (-1); 817 } 818 mountps[entry->n_mounts + 1] = NULL; 819 entry->n_mounts++; 820 entry->mountps = mountps; 821 822 return (0); 823 } 824 825 /* 826 * cache_lookup() 827 * 828 * Searches the cached table of mounts for a special device entry. 829 * 830 * Locking: the cache must be locked before calling this function. 831 * 832 * Return Value: NULL with errno set if failure, pointer to existing 833 * cache entry when successful. 834 */ 835 static hashentry_t * 836 cache_lookup(cache_t *cache, char *rsrc) 837 { 838 uint32_t index; 839 hashentry_t *entry; 840 841 /* Only accept valid arguments */ 842 if ((cache == NULL) || (cache->mounts == NULL) || (rsrc == NULL)) { 843 errno = EINVAL; 844 return (NULL); 845 } 846 847 /* Search the cached mounts table for the resource's entry */ 848 index = hash(cache->hash_size, rsrc); 849 if (cache->mounts[index]) { 850 for (entry = cache->mounts[index]; entry != NULL; 851 entry = entry->next) { 852 if (strcmp(entry->special, rsrc) == 0) 853 return (entry); 854 } 855 } 856 857 errno = ENOENT; 858 return (NULL); 859 } 860 861 /* 862 * Miscellaneous Functions 863 */ 864 865 /* 866 * hash() 867 * 868 * A naive hashing function that converts a string 's' to an index in a 869 * hash table of size 'h'. It seems to spread entries around well enough. 870 */ 871 static uint32_t 872 hash(uint32_t h, char *s) 873 { 874 uint32_t sum = 0; 875 unsigned char *byte; 876 877 if ((byte = (unsigned char *)s) != NULL) { 878 while (*byte) { 879 sum += 0x3F & (uint32_t)*byte; 880 byte++; 881 } 882 } 883 884 return (sum % h); 885 } 886 887 /* 888 * register_rsrc() 889 * 890 * Registers for any given resource, unless it's "/". 891 */ 892 static void 893 register_rsrc(rcm_handle_t *hd, char *rsrc) 894 { 895 /* Only accept valid arguments */ 896 if ((hd == NULL) || (rsrc == NULL)) 897 return; 898 899 /* 900 * Register any resource other than "/" or "/devices" 901 */ 902 if ((strcmp(rsrc, "/") != 0) && (strcmp(rsrc, "/devices") != 0)) { 903 rcm_log_message(RCM_DEBUG, "FILESYS: registering %s\n", rsrc); 904 if (rcm_register_interest(hd, rsrc, 0, NULL) != RCM_SUCCESS) { 905 rcm_log_message(RCM_WARNING, 906 "FILESYS: failed to register %s\n", rsrc); 907 } 908 } 909 910 } 911 912 /* 913 * unregister_rsrc() 914 * 915 * Unregister a resource. This does a little filtering since we know 916 * "/" can't be registered, so we never bother unregistering for it. 917 */ 918 static void 919 unregister_rsrc(rcm_handle_t *hd, char *rsrc) 920 { 921 assert(hd != NULL); 922 assert(rsrc != NULL); 923 924 /* Unregister any resource other than "/" */ 925 if (strcmp(rsrc, "/") != 0) { 926 rcm_log_message(RCM_DEBUG, "FILESYS: unregistering %s\n", rsrc); 927 (void) rcm_unregister_interest(hd, rsrc, 0); 928 } 929 } 930 931 /* 932 * create_message() 933 * 934 * Given some header strings and a list of dependent names, this 935 * constructs a single string. If there's only one dependent, the 936 * string consists of the first header and the only dependent appended 937 * to the end of the string enclosed in quotemarks. If there are 938 * multiple dependents, then the string uses the second header and the 939 * full list of dependents is appended at the end as a comma separated 940 * list of names enclosed in quotemarks. 941 */ 942 static char * 943 create_message(char *header, char *header_multi, char **dependents) 944 { 945 int i; 946 size_t len; 947 int ndependents; 948 char *msg_buf; 949 char *msg_header; 950 char *separator = MSG_SEPARATOR; 951 952 assert(header != NULL); 953 assert(header_multi != NULL); 954 assert(dependents != NULL); 955 956 /* Count the number of dependents */ 957 for (ndependents = 0; dependents[ndependents] != NULL; ndependents++); 958 959 /* If there are no dependents, fail */ 960 if (ndependents == 0) { 961 errno = ENOENT; 962 return (NULL); 963 } 964 965 /* Pick the appropriate header to use based on amount of dependents */ 966 if (ndependents == 1) { 967 msg_header = header; 968 } else { 969 msg_header = header_multi; 970 } 971 972 /* Compute the size required for the message buffer */ 973 len = strlen(msg_header) + 2; /* +2 for the space and a NULL */ 974 for (i = 0; dependents[i] != NULL; i++) 975 len += strlen(dependents[i]) + 2; /* +2 for quotemarks */ 976 len += strlen(separator) * (ndependents - 1); 977 978 /* Allocate the message buffer */ 979 if ((msg_buf = (char *)calloc(len, sizeof (char))) == NULL) { 980 rcm_log_message(RCM_ERROR, 981 "FILESYS: failed to allocate message buffer (%s).\n", 982 strerror(errno)); 983 errno = ENOMEM; 984 return (NULL); 985 } 986 987 /* Fill in the message buffer */ 988 (void) snprintf(msg_buf, len, "%s ", msg_header); 989 for (i = 0; dependents[i] != NULL; i++) { 990 (void) strlcat(msg_buf, "\"", len); 991 (void) strlcat(msg_buf, dependents[i], len); 992 (void) strlcat(msg_buf, "\"", len); 993 if ((i + 1) < ndependents) 994 (void) strlcat(msg_buf, separator, len); 995 } 996 997 return (msg_buf); 998 } 999 1000 /* 1001 * create_dependents() 1002 * 1003 * Creates a copy of the list of dependent mounts associated with a 1004 * given hashtable entry from the cache. 1005 * 1006 * Return Values: NULL with errno set on failure, the resulting list of 1007 * dependent resources when successful. 1008 */ 1009 static char ** 1010 create_dependents(hashentry_t *entry) 1011 { 1012 int i; 1013 char **dependents; 1014 1015 if (entry == NULL) { 1016 errno = EINVAL; 1017 return (NULL); 1018 } 1019 1020 if (entry->n_mounts == 0) { 1021 errno = ENOENT; 1022 return (NULL); 1023 } 1024 1025 /* Allocate space for the full dependency list */ 1026 dependents = (char **)calloc(entry->n_mounts + 1, sizeof (char *)); 1027 if (dependents == NULL) { 1028 rcm_log_message(RCM_ERROR, 1029 "FILESYS: failed to allocate dependents (%s).\n", 1030 strerror(errno)); 1031 errno = ENOMEM; 1032 return (NULL); 1033 } 1034 1035 /* Copy all the dependent names into the new list of dependents */ 1036 for (i = 0; i < entry->n_mounts; i++) { 1037 if ((dependents[i] = strdup(entry->mountps[i])) == NULL) { 1038 rcm_log_message(RCM_ERROR, 1039 "FILESYS: failed to allocate dependent \"%s\" " 1040 "(%s).\n", entry->mountps[i], strerror(errno)); 1041 free_list(dependents); 1042 errno = ENOMEM; 1043 return (NULL); 1044 } 1045 } 1046 1047 return (dependents); 1048 } 1049 1050 /* 1051 * detect_critical_failure() 1052 * 1053 * Given a list of dependents, a place to store an error message, and 1054 * the flags associated with an operation, this function detects whether 1055 * or not the operation should fail due to the presence of any critical 1056 * filesystem resources. When a failure is detected, an appropriate 1057 * error message is constructed and passed back to the caller. This is 1058 * called during a suspend request operation. 1059 * 1060 * Return Values: 0 when a critical resource failure shouldn't prevent 1061 * the operation, and 1 when such a failure condition does exist. 1062 */ 1063 static int 1064 detect_critical_failure(char **errorp, uint_t flags, char **dependents) 1065 { 1066 int i; 1067 int n_critical; 1068 char *tmp; 1069 1070 /* Do nothing if the operation is forced or there are no dependents */ 1071 if ((errorp == NULL) || (flags & RCM_FORCE) || (dependents == NULL)) 1072 return (0); 1073 1074 /* 1075 * Count how many of the dependents are critical, and shift the 1076 * critical resources to the head of the list. 1077 */ 1078 if (dependents) { 1079 for (i = 0, n_critical = 0; dependents[i] != NULL; i++) { 1080 if (is_critical(dependents[i])) { 1081 if (n_critical != i) { 1082 tmp = dependents[n_critical]; 1083 dependents[n_critical] = dependents[i]; 1084 dependents[i] = tmp; 1085 } 1086 n_critical++; 1087 } 1088 } 1089 } 1090 1091 /* If no criticals were found, do nothing and return */ 1092 if (n_critical == 0) 1093 return (0); 1094 1095 /* 1096 * Criticals were found. Prune the list appropriately and construct 1097 * an error message. 1098 */ 1099 1100 /* Prune non-criticals out of the list */ 1101 for (i = n_critical; dependents[i] != NULL; i++) { 1102 free(dependents[i]); 1103 dependents[i] = NULL; 1104 } 1105 1106 /* Construct the critical resource error message */ 1107 *errorp = create_message(MSG_HDR_CRIT, MSG_HDR_CRIT_MULTI, dependents); 1108 1109 return (1); 1110 } 1111 1112 /* 1113 * is_critical() 1114 * 1115 * Test a resource to determine if it's critical to the system and thus 1116 * cannot be suspended. 1117 * 1118 * Return Values: 1 if the named resource is critical, 0 if not. 1119 */ 1120 static int 1121 is_critical(char *rsrc) 1122 { 1123 assert(rsrc != NULL); 1124 1125 if ((strcmp(rsrc, "/") == 0) || 1126 (strcmp(rsrc, "/usr") == 0) || 1127 (strcmp(rsrc, "/usr/lib") == 0) || 1128 (strcmp(rsrc, "/usr/bin") == 0) || 1129 (strcmp(rsrc, "/tmp") == 0) || 1130 (strcmp(rsrc, "/var") == 0) || 1131 (strcmp(rsrc, "/var/run") == 0) || 1132 (strcmp(rsrc, "/etc") == 0) || 1133 (strcmp(rsrc, "/etc/mnttab") == 0) || 1134 (strcmp(rsrc, "/sbin") == 0)) 1135 return (1); 1136 1137 return (0); 1138 } 1139 1140 /* 1141 * use_cache() 1142 * 1143 * This routine handles all the tasks necessary to lookup a resource 1144 * in the cache and extract a separate list of dependents for that 1145 * entry. If an error occurs while doing this, an appropriate error 1146 * message is passed back to the caller. 1147 * 1148 * Locking: the cache is locked for the whole duration of this function. 1149 */ 1150 static int 1151 use_cache(char *rsrc, char **errorp, char ***dependentsp) 1152 { 1153 hashentry_t *entry; 1154 1155 (void) mutex_lock(&cache_lock); 1156 if ((entry = cache_lookup(mnt_cache, rsrc)) == NULL) { 1157 rcm_log_message(RCM_ERROR, 1158 "FILESYS: failed looking up \"%s\" in cache (%s).\n", 1159 rsrc, strerror(errno)); 1160 *errorp = strdup(MSG_FAIL_INTERNAL); 1161 (void) mutex_unlock(&cache_lock); 1162 return (-1); 1163 } 1164 *dependentsp = create_dependents(entry); 1165 (void) mutex_unlock(&cache_lock); 1166 1167 return (0); 1168 } 1169 1170 /* 1171 * prune_dependents() 1172 * 1173 * Before calling back into RCM with a list of dependents, the list 1174 * must be cleaned up a little. To avoid infinite recursion, "/" and 1175 * the named resource must be pruned out of the list. 1176 */ 1177 static void 1178 prune_dependents(char **dependents, char *rsrc) 1179 { 1180 int i; 1181 int n; 1182 1183 if (dependents) { 1184 1185 /* Set 'n' to the total length of the list */ 1186 for (n = 0; dependents[n] != NULL; n++); 1187 1188 /* 1189 * Move offending dependents to the tail of the list and 1190 * then truncate the list. 1191 */ 1192 for (i = 0; dependents[i] != NULL; i++) { 1193 if ((strcmp(dependents[i], rsrc) == 0) || 1194 (strcmp(dependents[i], "/") == 0)) { 1195 free(dependents[i]); 1196 dependents[i] = dependents[n - 1]; 1197 dependents[n] = NULL; 1198 i--; 1199 n--; 1200 } 1201 } 1202 } 1203 } 1204