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 * 31 * cfsadmin.c 32 * 33 * Cache FS admin utility. 34 */ 35 36 #include <assert.h> 37 #include <locale.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <unistd.h> 41 #include <string.h> 42 #include <errno.h> 43 #include <limits.h> 44 #include <dirent.h> 45 #include <ftw.h> 46 #include <fcntl.h> 47 #include <ctype.h> 48 #include <stdarg.h> 49 #include <sys/param.h> 50 #include <sys/types.h> 51 #include <sys/stat.h> 52 #include <sys/statvfs.h> 53 #include <sys/mman.h> 54 #include <sys/mnttab.h> 55 #include <sys/fs/cachefs_fs.h> 56 #include <sys/fs/cachefs_dir.h> 57 #include <sys/utsname.h> 58 #include <rpc/rpc.h> 59 #include <priv.h> 60 #include "../common/subr.h" 61 #include "../common/cachefsd.h" 62 63 char *cfsadmin_opts[] = { 64 #define COPT_MAXBLOCKS 0 65 "maxblocks", 66 #define COPT_MINBLOCKS 1 67 "minblocks", 68 #define COPT_THRESHBLOCKS 2 69 "threshblocks", 70 71 #define COPT_MAXFILES 3 72 "maxfiles", 73 #define COPT_MINFILES 4 74 "minfiles", 75 #define COPT_THRESHFILES 5 76 "threshfiles", 77 78 #define COPT_MAXFILESIZE 6 79 "maxfilesize", 80 81 #define COPT_HIBLOCKS 7 82 "hiblocks", 83 #define COPT_LOWBLOCKS 8 84 "lowblocks", 85 #define COPT_HIFILES 9 86 "hifiles", 87 #define COPT_LOWFILES 10 88 "lowfiles", 89 NULL 90 }; 91 92 #define bad(val) ((val) == NULL || !isdigit(*(val))) 93 94 /* numbers must be valid percentages ranging from 0 to 100 */ 95 #define badpercent(val) \ 96 ((val) == NULL || !isdigit(*(val)) || \ 97 atoi((val)) < 0 || atoi((val)) > 100) 98 99 /* forward references */ 100 void usage(char *msg); 101 void pr_err(char *fmt, ...); 102 int cfs_get_opts(char *oarg, struct cachefs_user_values *uvp); 103 int update_cachelabel(char *dirp, char *optionp); 104 void user_values_defaults(struct cachefs_user_values *uvp); 105 int check_user_values_for_sanity(const struct cachefs_user_values *uvp); 106 int cache_stats(char *dirp); 107 int resource_file_grow(char *dirp, int oldcnt, int newcnt); 108 int resource_file_dirty(char *dirp); 109 void simulate_disconnection(char *namep, int disconnect); 110 111 /* 112 * 113 * main 114 * 115 * Description: 116 * Main routine for the cfsadmin program. 117 * Arguments: 118 * argc number of command line arguments 119 * argv command line arguments 120 * Returns: 121 * Returns 0 for failure, > 0 for an error. 122 * Preconditions: 123 */ 124 125 int 126 main(int argc, char **argv) 127 { 128 int c; 129 int xx; 130 int lockid; 131 132 char *cacheid; 133 char *cachedir; 134 135 int cflag; 136 int uflag; 137 int dflag; 138 int sflag; 139 int allflag; 140 int lflag; 141 char *optionp; 142 int Cflag; 143 int Dflag; 144 145 priv_set_t *priv_needed, *priv_effective; 146 147 (void) setlocale(LC_ALL, ""); 148 149 #if !defined(TEXT_DOMAIN) 150 #define TEXT_DOMAIN "SYS_TEST" 151 #endif 152 (void) textdomain(TEXT_DOMAIN); 153 154 /* set defaults for command line options */ 155 cflag = 0; 156 uflag = 0; 157 dflag = 0; 158 sflag = 0; 159 allflag = 0; 160 lflag = 0; 161 optionp = NULL; 162 Cflag = 0; 163 Dflag = 0; 164 165 /* parse the command line arguments */ 166 while ((c = getopt(argc, argv, "cCDuo:d:sl")) != EOF) { 167 switch (c) { 168 169 case 'c': /* create */ 170 cflag = 1; 171 break; 172 173 /* 174 * -C and -D are undocumented calls used 175 * to simulate disconnection on a file system. 176 */ 177 case 'C': /* connect file system */ 178 Cflag = 1; 179 break; 180 case 'D': /* disconnect file system */ 181 Dflag = 1; 182 break; 183 184 case 'u': /* update */ 185 uflag = 1; 186 break; 187 188 case 'd': /* delete */ 189 dflag = 1; 190 if (strcmp(optarg, "all") == 0) 191 allflag = 1; 192 else 193 cacheid = optarg; 194 break; 195 196 case 's': /* consistency on demand */ 197 sflag = 1; 198 break; 199 200 case 'l': /* list cache ids */ 201 lflag = 1; 202 break; 203 204 case 'o': /* options for update and create */ 205 optionp = optarg; 206 break; 207 208 default: 209 usage(gettext("illegal option")); 210 return (1); 211 } 212 } 213 214 if ((cflag + dflag + lflag + sflag + uflag + Cflag + Dflag) == 0) { 215 usage(gettext("no options specified")); 216 return (1); 217 } 218 219 if (cflag || uflag || dflag || Cflag || Dflag) 220 priv_needed = priv_str_to_set("all", ",", NULL); 221 if ((cflag || uflag) && getuid() != 0) { 222 /* These options create files. We want them to be root owned */ 223 pr_err(gettext("must be run by root")); 224 return (1); 225 } 226 227 else if (lflag) 228 priv_needed = priv_str_to_set("file_dac_search,file_dac_read", 229 ",", NULL); 230 231 else if (sflag) 232 priv_needed = priv_str_to_set("sys_config", ",", NULL); 233 234 priv_effective = priv_allocset(); 235 (void) getppriv(PRIV_EFFECTIVE, priv_effective); 236 if (priv_issubset(priv_needed, priv_effective) == 0) { 237 pr_err(gettext("Not privileged.")); 238 return (1); 239 } 240 priv_freeset(priv_effective); 241 priv_freeset(priv_needed); 242 243 if ((sflag + Cflag + Dflag) == 0) { 244 /* make sure cachedir is specified */ 245 if (argc - 1 != optind) { 246 usage(gettext("cache directory not specified")); 247 return (1); 248 } 249 cachedir = argv[argc-1]; 250 } else { 251 /* make sure at least one mount point is specified */ 252 if (argc - 1 < optind) { 253 usage(gettext("mount points not specified")); 254 return (1); 255 } 256 } 257 258 /* make sure a reasonable set of flags were specified */ 259 if ((cflag + uflag + dflag + sflag + lflag + Cflag + Dflag) != 1) { 260 /* flags are mutually exclusive, at least one must be set */ 261 usage(gettext( 262 "exactly one of -c, -u, -d, -s, -l must be specified")); 263 return (1); 264 } 265 266 /* make sure -o specified with -c or -u */ 267 if (optionp && !(cflag|uflag)) { 268 usage(gettext("-o can only be used with -c or -u")); 269 return (1); 270 } 271 272 /* if creating a cache */ 273 if (cflag) { 274 struct cachefs_user_values uv; 275 struct cache_label clabel; 276 277 /* get default cache paramaters */ 278 user_values_defaults(&uv); 279 280 /* parse the options if specified */ 281 if (optionp) { 282 xx = cfs_get_opts(optionp, &uv); 283 if (xx) 284 return (1); 285 } 286 287 /* verify options are reasonable */ 288 xx = check_user_values_for_sanity(&uv); 289 if (xx) 290 return (1); 291 292 /* lock the cache directory non-shared */ 293 lockid = cachefs_dir_lock(cachedir, 0); 294 if (lockid == -1) { 295 /* quit if could not get the lock */ 296 return (1); 297 } 298 299 /* create the cache */ 300 xx = cachefs_create_cache(cachedir, &uv, &clabel); 301 if (xx != 0) { 302 if (xx == -2) { 303 /* remove a partially created cache dir */ 304 (void) cachefs_delete_all_cache(cachedir); 305 } 306 cachefs_dir_unlock(lockid); 307 return (1); 308 } 309 cachefs_dir_unlock(lockid); 310 } 311 312 /* else if updating resource parameters */ 313 else if (uflag) { 314 /* lock the cache directory non-shared */ 315 lockid = cachefs_dir_lock(cachedir, 0); 316 if (lockid == -1) { 317 /* quit if could not get the lock */ 318 return (1); 319 } 320 321 xx = update_cachelabel(cachedir, optionp); 322 cachefs_dir_unlock(lockid); 323 if (xx != 0) { 324 return (1); 325 } 326 } 327 328 /* else if deleting a specific cacheID (or all caches) */ 329 else if (dflag) { 330 /* lock the cache directory non-shared */ 331 lockid = cachefs_dir_lock(cachedir, 0); 332 if (lockid == -1) { 333 /* quit if could not get the lock */ 334 return (1); 335 } 336 337 /* if the cache is in use */ 338 if (cachefs_inuse(cachedir)) { 339 pr_err(gettext("Cache %s is in use and " 340 "cannot be modified."), cachedir); 341 cachefs_dir_unlock(lockid); 342 return (1); 343 } 344 345 if (allflag) 346 xx = cachefs_delete_all_cache(cachedir); 347 else { 348 /* mark resource file as dirty */ 349 xx = resource_file_dirty(cachedir); 350 if (xx == 0) 351 xx = cachefs_delete_cache(cachedir, cacheid); 352 } 353 cachefs_dir_unlock(lockid); 354 if (xx != 0) { 355 return (1); 356 } 357 } 358 359 /* else if listing cache statistics */ 360 else if (lflag) { 361 xx = cache_stats(cachedir); 362 if (xx != 0) 363 return (1); 364 } 365 366 /* else if issuing a check event to cached file systems */ 367 else if (sflag) { 368 for (xx = optind; xx < argc; xx++) { 369 issue_cod(argv[xx]); 370 } 371 } 372 373 /* else if simulating a disconnection */ 374 else if (Dflag) { 375 for (xx = optind; xx < argc; xx++) { 376 simulate_disconnection(argv[xx], 1); 377 } 378 } 379 380 /* else if connection after a simulated disconnection */ 381 else if (Cflag) { 382 for (xx = optind; xx < argc; xx++) { 383 simulate_disconnection(argv[xx], 0); 384 } 385 } 386 387 /* return success */ 388 return (0); 389 } 390 391 392 /* 393 * 394 * usage 395 * 396 * Description: 397 * Prints a usage message for this utility. 398 * Arguments: 399 * msgp message to include with the usage message 400 * Returns: 401 * Preconditions: 402 * precond(msgp) 403 */ 404 405 void 406 usage(char *msgp) 407 { 408 fprintf(stderr, gettext("cfsadmin: %s\n"), msgp); 409 fprintf(stderr, gettext( 410 "usage: cfsadmin -[cu] [-o parameter-list] cachedir\n")); 411 fprintf(stderr, gettext(" cfsadmin -d [CacheID|all] cachedir\n")); 412 fprintf(stderr, gettext(" cfsadmin -l cachedir\n")); 413 fprintf(stderr, gettext(" cfsadmin -s [mntpnt1 ... | all]\n")); 414 } 415 416 /* 417 * 418 * pr_err 419 * 420 * Description: 421 * Prints an error message to stderr. 422 * Arguments: 423 * fmt printf style format 424 * ... arguments for fmt 425 * Returns: 426 * Preconditions: 427 * precond(fmt) 428 */ 429 430 void 431 pr_err(char *fmt, ...) 432 { 433 va_list ap; 434 435 va_start(ap, fmt); 436 (void) fprintf(stderr, gettext("cfsadmin: ")); 437 (void) vfprintf(stderr, fmt, ap); 438 (void) fprintf(stderr, "\n"); 439 va_end(ap); 440 } 441 442 /* 443 * 444 * cfs_get_opts 445 * 446 * Description: 447 * Decodes cfs options specified with -o. 448 * Only the fields referenced by the options are modified. 449 * Arguments: 450 * oarg options from -o option 451 * uvp place to put options 452 * Returns: 453 * Returns 0 for success, -1 for an error. 454 * Preconditions: 455 * precond(oarg) 456 * precond(uvp) 457 */ 458 459 int 460 cfs_get_opts(char *oarg, struct cachefs_user_values *uvp) 461 { 462 char *optstr, *opts, *val; 463 char *saveopts; 464 int badopt; 465 466 /* make a copy of the options because getsubopt modifies it */ 467 optstr = opts = strdup(oarg); 468 if (opts == NULL) { 469 pr_err(gettext("no memory")); 470 return (-1); 471 } 472 473 /* process the options */ 474 badopt = 0; 475 while (*opts && !badopt) { 476 saveopts = opts; 477 switch (getsubopt(&opts, cfsadmin_opts, &val)) { 478 case COPT_MAXBLOCKS: 479 if (badpercent(val)) 480 badopt = 1; 481 else 482 uvp->uv_maxblocks = atoi(val); 483 break; 484 case COPT_MINBLOCKS: 485 if (badpercent(val)) 486 badopt = 1; 487 else 488 uvp->uv_minblocks = atoi(val); 489 break; 490 case COPT_THRESHBLOCKS: 491 if (badpercent(val)) 492 badopt = 1; 493 else 494 uvp->uv_threshblocks = atoi(val); 495 break; 496 497 case COPT_MAXFILES: 498 if (badpercent(val)) 499 badopt = 1; 500 else 501 uvp->uv_maxfiles = atoi(val); 502 break; 503 case COPT_MINFILES: 504 if (badpercent(val)) 505 badopt = 1; 506 else 507 uvp->uv_minfiles = atoi(val); 508 break; 509 case COPT_THRESHFILES: 510 if (badpercent(val)) 511 badopt = 1; 512 else 513 uvp->uv_threshfiles = atoi(val); 514 break; 515 516 case COPT_MAXFILESIZE: 517 if (bad(val)) 518 badopt = 1; 519 else 520 uvp->uv_maxfilesize = atoi(val); 521 break; 522 523 case COPT_HIBLOCKS: 524 if (badpercent(val)) 525 badopt = 1; 526 else 527 uvp->uv_hiblocks = atoi(val); 528 break; 529 case COPT_LOWBLOCKS: 530 if (badpercent(val)) 531 badopt = 1; 532 else 533 uvp->uv_lowblocks = atoi(val); 534 break; 535 case COPT_HIFILES: 536 if (badpercent(val)) 537 badopt = 1; 538 else 539 uvp->uv_hifiles = atoi(val); 540 break; 541 case COPT_LOWFILES: 542 if (badpercent(val)) 543 badopt = 1; 544 else 545 uvp->uv_lowfiles = atoi(val); 546 break; 547 default: 548 /* if a bad option argument */ 549 pr_err(gettext("Invalid option %s"), saveopts); 550 return (-1); 551 } 552 } 553 554 /* if a bad value for an option, display an error message */ 555 if (badopt) { 556 pr_err(gettext("invalid argument to option: \"%s\""), 557 saveopts); 558 } 559 560 /* free the duplicated option string */ 561 free(optstr); 562 563 /* return the result */ 564 return (badopt ? -1 : 0); 565 } 566 567 /* 568 * 569 * update_cachelabel 570 * 571 * Description: 572 * Changes the parameters of the cache_label. 573 * If optionp is NULL then the cache_label is set to 574 * default values. 575 * Arguments: 576 * dirp the name of the cache directory 577 * optionp comma delimited options 578 * Returns: 579 * Returns 0 for success and -1 for an error. 580 * Preconditions: 581 * precond(dirp) 582 */ 583 584 int 585 update_cachelabel(char *dirp, char *optionp) 586 { 587 char path[CACHEFS_XMAXPATH]; 588 struct cache_label clabel_new; 589 struct cache_label clabel_orig; 590 struct cachefs_user_values uv_orig, uv_new; 591 int xx; 592 593 /* if the cache is in use */ 594 if (cachefs_inuse(dirp)) { 595 pr_err(gettext("Cache %s is in use and cannot be modified."), 596 dirp); 597 return (-1); 598 } 599 600 /* make sure we don't overwrite path */ 601 if (strlen(dirp) > (size_t)PATH_MAX) { 602 pr_err(gettext("name of label file %s is too long."), 603 dirp); 604 return (-1); 605 } 606 607 /* construct the pathname to the cach_label file */ 608 sprintf(path, "%s/%s", dirp, CACHELABEL_NAME); 609 610 /* read the current set of parameters */ 611 xx = cachefs_label_file_get(path, &clabel_orig); 612 if (xx == -1) { 613 pr_err(gettext("reading %s failed"), path); 614 return (-1); 615 } 616 xx = cachefs_label_file_vcheck(path, &clabel_orig); 617 if (xx != 0) { 618 pr_err(gettext("version mismatch on %s"), path); 619 return (-1); 620 } 621 622 /* convert the cache_label to user values */ 623 xx = cachefs_convert_cl2uv(&clabel_orig, &uv_orig, dirp); 624 if (xx) { 625 return (-1); 626 } 627 628 /* if options were specified */ 629 if (optionp) { 630 /* start with the original values */ 631 uv_new = uv_orig; 632 633 /* parse the options */ 634 xx = cfs_get_opts(optionp, &uv_new); 635 if (xx) { 636 return (-1); 637 } 638 639 /* verify options are reasonable */ 640 xx = check_user_values_for_sanity(&uv_new); 641 if (xx) { 642 return (-1); 643 } 644 } 645 646 /* else if options where not specified, get defaults */ 647 else { 648 user_values_defaults(&uv_new); 649 } 650 651 /* convert user values to a cache_label */ 652 xx = cachefs_convert_uv2cl(&uv_new, &clabel_new, dirp); 653 if (xx) { 654 return (-1); 655 } 656 657 /* do not allow the cache size to shrink */ 658 if (uv_orig.uv_maxblocks > uv_new.uv_maxblocks) { 659 pr_err(gettext("Cache size cannot be reduced," 660 " maxblocks current %d%%, requested %d%%"), 661 uv_orig.uv_maxblocks, uv_new.uv_maxblocks); 662 return (-1); 663 } 664 if (clabel_orig.cl_maxinodes > clabel_new.cl_maxinodes) { 665 pr_err(gettext("Cache size cannot be reduced," 666 " maxfiles current %d%% requested %d%%"), 667 uv_orig.uv_maxfiles, uv_new.uv_maxfiles); 668 return (-1); 669 } 670 671 /* write back the new values */ 672 xx = cachefs_label_file_put(path, &clabel_new); 673 if (xx == -1) { 674 pr_err(gettext("writing %s failed"), path); 675 return (-1); 676 } 677 678 /* put the new values in the duplicate cache label file also */ 679 sprintf(path, "%s/%s.dup", dirp, CACHELABEL_NAME); 680 xx = cachefs_label_file_put(path, &clabel_new); 681 if (xx == -1) { 682 pr_err(gettext("writing %s failed"), path); 683 return (-1); 684 } 685 686 /* grow resouces file if necessary */ 687 xx = 0; 688 if (clabel_orig.cl_maxinodes != clabel_new.cl_maxinodes) { 689 xx = resource_file_grow(dirp, clabel_orig.cl_maxinodes, 690 clabel_new.cl_maxinodes); 691 } 692 693 /* return status */ 694 return (xx); 695 } 696 697 /* 698 * 699 * user_values_defaults 700 * 701 * Description: 702 * Sets default values in the cachefs_user_values object. 703 * Arguments: 704 * uvp cachefs_user_values object to set values for 705 * Returns: 706 * Preconditions: 707 * precond(uvp) 708 */ 709 710 void 711 user_values_defaults(struct cachefs_user_values *uvp) 712 { 713 uvp->uv_maxblocks = 90; 714 uvp->uv_minblocks = 0; 715 uvp->uv_threshblocks = 85; 716 uvp->uv_maxfiles = 90; 717 uvp->uv_minfiles = 0; 718 uvp->uv_threshfiles = 85; 719 uvp->uv_maxfilesize = 3; 720 uvp->uv_hiblocks = 85; 721 uvp->uv_lowblocks = 75; 722 uvp->uv_hifiles = 85; 723 uvp->uv_lowfiles = 75; 724 } 725 726 /* 727 * 728 * check_user_values_for_sanity 729 * 730 * Description: 731 * Check the cachefs_user_values for sanity. 732 * Arguments: 733 * uvp cachefs_user_values object to check 734 * Returns: 735 * Returns 0 if okay, -1 if not. 736 * Preconditions: 737 * precond(uvp) 738 */ 739 740 int 741 check_user_values_for_sanity(const struct cachefs_user_values *uvp) 742 { 743 int ret; 744 745 ret = 0; 746 747 if (uvp->uv_lowblocks >= uvp->uv_hiblocks) { 748 pr_err(gettext("lowblocks can't be >= hiblocks.")); 749 ret = -1; 750 } 751 if (uvp->uv_lowfiles >= uvp->uv_hifiles) { 752 pr_err(gettext("lowfiles can't be >= hifiles.")); 753 ret = -1; 754 } 755 756 /* XXX more conditions to check here? */ 757 758 /* XXX make sure thresh values are between min and max values */ 759 760 /* return status */ 761 return (ret); 762 } 763 764 /* 765 * 766 * cache_stats 767 * 768 * Description: 769 * Show each cache in the directory, cache resource statistics, 770 * and, for each fs in the cache, the name of the fs, and the 771 * cache resource parameters. 772 * Arguments: 773 * dirp name of the cache directory 774 * Returns: 775 * Returns 0 for success, -1 for an error. 776 * Errors: 777 * Preconditions: 778 */ 779 780 int 781 cache_stats(char *dirp) 782 { 783 DIR *dp; 784 struct dirent64 *dep; 785 char path[CACHEFS_XMAXPATH]; 786 struct stat64 statinfo; 787 int ret; 788 int xx; 789 struct cache_label clabel; 790 struct cachefs_user_values uv; 791 792 /* make sure cache dir name is not too long */ 793 if (strlen(dirp) > (size_t)PATH_MAX) { 794 pr_err(gettext("path name %s is too long."), dirp); 795 return (-1); 796 } 797 798 /* read the cache label file */ 799 sprintf(path, "%s/%s", dirp, CACHELABEL_NAME); 800 xx = cachefs_label_file_get(path, &clabel); 801 if (xx == -1) { 802 pr_err(gettext("Reading %s failed."), path); 803 return (-1); 804 } 805 xx = cachefs_label_file_vcheck(path, &clabel); 806 if (xx != 0) { 807 pr_err(gettext("Version mismatch on %s."), path); 808 return (-1); 809 } 810 811 /* convert the cache_label to user values */ 812 xx = cachefs_convert_cl2uv(&clabel, &uv, dirp); 813 if (xx) 814 return (-1); 815 816 /* display the parameters */ 817 printf(gettext("cfsadmin: list cache FS information\n")); 818 #if 0 819 printf(gettext(" Version %3d\n"), clabel.cl_cfsversion); 820 #endif 821 printf(gettext(" maxblocks %3d%%\n"), uv.uv_maxblocks); 822 printf(gettext(" minblocks %3d%%\n"), uv.uv_minblocks); 823 printf(gettext(" threshblocks %3d%%\n"), uv.uv_threshblocks); 824 printf(gettext(" maxfiles %3d%%\n"), uv.uv_maxfiles); 825 printf(gettext(" minfiles %3d%%\n"), uv.uv_minfiles); 826 printf(gettext(" threshfiles %3d%%\n"), uv.uv_threshfiles); 827 printf(gettext(" maxfilesize %3dMB\n"), uv.uv_maxfilesize); 828 829 /* open the directory */ 830 if ((dp = opendir(dirp)) == NULL) { 831 pr_err(gettext("opendir %s failed: %s"), dirp, 832 strerror(errno)); 833 return (-1); 834 } 835 836 /* loop reading the contents of the directory */ 837 ret = 0; 838 while ((dep = readdir64(dp)) != NULL) { 839 /* ignore . and .. */ 840 if ((strcmp(dep->d_name, ".") == 0) || 841 (strcmp(dep->d_name, "..") == 0)) 842 continue; 843 844 /* stat the file */ 845 sprintf(path, "%s/%s", dirp, dep->d_name); 846 xx = lstat64(path, &statinfo); 847 if (xx == -1) { 848 pr_err(gettext("lstat %s failed: %s"), 849 path, strerror(errno)); 850 closedir(dp); 851 return (-1); 852 } 853 854 /* ignore anything that is not a link */ 855 if (!S_ISLNK(statinfo.st_mode)) 856 continue; 857 858 /* print the file system cache directory name */ 859 printf(gettext(" %s\n"), dep->d_name); 860 861 /* XXX anything else */ 862 } 863 864 /* XXX what about stats */ 865 866 /* return status */ 867 return (ret); 868 } 869 870 /* 871 * 872 * resource_file_grow 873 * 874 * Description: 875 * Grows the resource file in the specified directory 876 * to its new size. 877 * Arguments: 878 * dirp cache directory resource file is in 879 * oldcnt previous number of files in resource file 880 * newcnt new number of files in resource file 881 * Returns: 882 * Returns 0 for success, -1 for an error. 883 * Preconditions: 884 * precond(dirp) 885 * precond(oldcnt <= newcnt) 886 * precond(cache is locked exclusively) 887 * precond(cache is not in use) 888 */ 889 890 int 891 resource_file_grow(char *dirp, int oldcnt, int newcnt) 892 { 893 int fd; 894 char path[CACHEFS_XMAXPATH]; 895 int xx; 896 struct stat64 st; 897 static struct cachefs_rinfo rold, rnew; 898 struct cache_usage cusage, *cusagep; 899 char buf[MAXBSIZE]; 900 int cnt; 901 caddr_t addrp; 902 int dirty; 903 904 /* get info about the resouce file for the various sizes */ 905 cachefs_resource_size(oldcnt, &rold); 906 cachefs_resource_size(newcnt, &rnew); 907 908 /* open the resource file for writing */ 909 /* this file is < 2GB */ 910 sprintf(path, "%s/%s", dirp, RESOURCE_NAME); 911 fd = open(path, O_RDWR); 912 if (fd == -1) { 913 pr_err(gettext("Could not open %s: %s, run fsck"), path, 914 strerror(errno)); 915 return (-1); 916 } 917 918 /* get info on the file */ 919 xx = fstat64(fd, &st); 920 if (xx == -1) { 921 pr_err(gettext("Could not stat %s: %s"), path, 922 strerror(errno)); 923 close(fd); 924 return (-1); 925 } 926 927 /* make sure the size is the correct */ 928 if ((off_t)st.st_size != rold.r_fsize) { 929 pr_err(gettext("Resource file has wrong size %d %d, run fsck"), 930 (off_t)st.st_size, rold.r_fsize); 931 close(fd); 932 return (-1); 933 } 934 935 /* read the cache usage structure */ 936 xx = read(fd, &cusage, sizeof (cusage)); 937 if (xx != sizeof (cusage)) { 938 pr_err(gettext("Could not read cache_usage, %d, run fsck"), 939 xx); 940 close(fd); 941 return (-1); 942 } 943 944 /* rewind */ 945 xx = lseek(fd, 0, SEEK_SET); 946 if (xx == -1) { 947 pr_err(gettext("Could not lseek %s: %s"), path, 948 strerror(errno)); 949 close(fd); 950 return (-1); 951 } 952 953 /* indicate cache is dirty if necessary */ 954 dirty = 1; 955 if ((cusage.cu_flags & CUSAGE_ACTIVE) == 0) { 956 dirty = 0; 957 cusage.cu_flags |= CUSAGE_ACTIVE; 958 xx = write(fd, &cusage, sizeof (cusage)); 959 if (xx != sizeof (cusage)) { 960 pr_err(gettext( 961 "Could not write cache_usage, %d, run fsck"), 962 xx); 963 close(fd); 964 return (-1); 965 } 966 } 967 968 /* go to the end of the file */ 969 xx = lseek(fd, 0, SEEK_END); 970 if (xx == -1) { 971 pr_err(gettext("Could not lseek %s: %s"), path, 972 strerror(errno)); 973 close(fd); 974 return (-1); 975 } 976 977 /* grow the file to the new size */ 978 memset(buf, 0, sizeof (buf)); 979 cnt = rnew.r_fsize - rold.r_fsize; 980 assert((cnt % MAXBSIZE) == 0); 981 cnt /= MAXBSIZE; 982 while (cnt-- > 0) { 983 xx = write(fd, buf, sizeof (buf)); 984 if (xx != sizeof (buf)) { 985 pr_err(gettext("Could not write file, %d, run fsck"), 986 xx); 987 close(fd); 988 return (-1); 989 } 990 } 991 992 /* mmap the file into our address space */ 993 addrp = mmap(NULL, rnew.r_fsize, PROT_READ | PROT_WRITE, MAP_SHARED, 994 fd, 0); 995 if (addrp == (void *)-1) { 996 pr_err(gettext("Could not mmap file %s: %s"), path, 997 strerror(errno)); 998 close(fd); 999 return (-1); 1000 } 1001 1002 /* close the file descriptor, we do not need it anymore */ 1003 close(fd); 1004 1005 /* move the idents region to its new location */ 1006 memmove(addrp + rnew.r_identoffset, addrp + rold.r_identoffset, 1007 rold.r_identsize); 1008 1009 /* zero out the old idents region that is now in the pointers region */ 1010 memset(addrp + rold.r_identoffset, 0, 1011 rnew.r_identoffset - rold.r_identoffset); 1012 1013 /* sync the data to the file */ 1014 xx = msync(addrp, rnew.r_fsize, MS_SYNC); 1015 if (xx == -1) { 1016 pr_err(gettext("Could not sync file %s: %s"), path, 1017 strerror(errno)); 1018 munmap(addrp, rnew.r_fsize); 1019 return (-1); 1020 } 1021 1022 /* mark the file as clean if it was not dirty originally */ 1023 if (!dirty) { 1024 cusagep = (struct cache_usage *)addrp; 1025 cusagep->cu_flags &= ~CUSAGE_ACTIVE; 1026 1027 /* sync the data to the file */ 1028 xx = msync(addrp, rnew.r_fsize, MS_SYNC); 1029 if (xx == -1) { 1030 pr_err(gettext("Could not sync file %s: %s"), path, 1031 strerror(errno)); 1032 munmap(addrp, rnew.r_fsize); 1033 return (-1); 1034 } 1035 } 1036 1037 /* unmap the file */ 1038 munmap(addrp, rnew.r_fsize); 1039 1040 /* return success */ 1041 return (0); 1042 } 1043 1044 /* 1045 * 1046 * resource_file_dirty 1047 * 1048 * Description: 1049 * Marks the resource file as dirty. 1050 * This will cause fsck to fix it up the next time it 1051 * is run. 1052 * Arguments: 1053 * dirp cache directory resource file is in 1054 * Returns: 1055 * Returns 0 for success, -1 for an error. 1056 * Preconditions: 1057 * precond(dirp) 1058 * precond(cache is locked exclusively) 1059 * precond(cache is not in use) 1060 */ 1061 1062 int 1063 resource_file_dirty(char *dirp) 1064 { 1065 int fd; 1066 char path[CACHEFS_XMAXPATH]; 1067 int xx; 1068 struct cache_usage cusage; 1069 1070 /* open the resource file for writing */ 1071 /* this file is < 2GB */ 1072 sprintf(path, "%s/%s", dirp, RESOURCE_NAME); 1073 fd = open(path, O_RDWR); 1074 if (fd == -1) { 1075 pr_err(gettext("Could not open %s: %s, run fsck"), path, 1076 strerror(errno)); 1077 return (-1); 1078 } 1079 1080 /* read the cache usage structure */ 1081 xx = read(fd, &cusage, sizeof (cusage)); 1082 if (xx != sizeof (cusage)) { 1083 pr_err(gettext("Could not read cache_usage, %d, run fsck"), 1084 xx); 1085 close(fd); 1086 return (-1); 1087 } 1088 1089 /* rewind */ 1090 xx = lseek(fd, 0, SEEK_SET); 1091 if (xx == -1) { 1092 pr_err(gettext("Could not lseek %s: %s"), path, 1093 strerror(errno)); 1094 close(fd); 1095 return (-1); 1096 } 1097 1098 /* indicate cache is dirty if necessary */ 1099 if ((cusage.cu_flags & CUSAGE_ACTIVE) == 0) { 1100 cusage.cu_flags |= CUSAGE_ACTIVE; 1101 xx = write(fd, &cusage, sizeof (cusage)); 1102 if (xx != sizeof (cusage)) { 1103 pr_err(gettext( 1104 "Could not write cache_usage, %d, run fsck"), 1105 xx); 1106 close(fd); 1107 return (-1); 1108 } 1109 } 1110 1111 xx = close(fd); 1112 if (xx == -1) { 1113 pr_err(gettext("Could not successfully close %s: %s"), path, 1114 strerror(errno)); 1115 } 1116 return (xx); 1117 } 1118 1119 /* 1120 * 1121 * issue_cod 1122 * 1123 * Description: 1124 * Executes the _FIOCOD ioctl on the specified file. 1125 * Arguments: 1126 * name filename to issue ioctl on (or "all") 1127 * Returns: 1128 * Returns 0 for success, -1 for an error. 1129 * Preconditions: 1130 * precond(dirp) 1131 */ 1132 1133 int 1134 issue_cod(char *name) 1135 { 1136 int fd; 1137 int xx; 1138 int arg; 1139 char *dirp; 1140 FILE *mfp; 1141 struct mnttab mt, mtpref; 1142 1143 #ifndef MNTTYPE_CACHEFS 1144 #define MNTTYPE_CACHEFS "cachefs" 1145 #endif 1146 1147 arg = 0; 1148 if (strcmp(name, "all") == 0) { 1149 /* 1150 * if "all" was specified rather than a mount point, 1151 * we locate a cachefs mount in /etc/mnttab (any cachefs 1152 * mount will do). We issue the ioctl on this mount point, 1153 * and specify a non-zero argument to the ioctl. The non-zero 1154 * arg tells the kernel to do demandconst on all relevant 1155 * cachefs mounts 1156 */ 1157 if ((mfp = fopen(MNTTAB, "r")) == NULL) { 1158 pr_err(gettext("Could not open %s."), MNTTAB); 1159 return (-1); 1160 } 1161 mtpref.mnt_special = NULL; 1162 mtpref.mnt_mountp = NULL; 1163 mtpref.mnt_mntopts = NULL; 1164 mtpref.mnt_time = NULL; 1165 mtpref.mnt_fstype = MNTTYPE_CACHEFS; 1166 if (getmntany(mfp, &mt, &mtpref) != 0) { 1167 (void) fclose(mfp); 1168 return (-1); 1169 } 1170 (void) fclose(mfp); 1171 dirp = mt.mnt_mountp; 1172 arg = 1; 1173 } else { 1174 dirp = name; 1175 } 1176 1177 /* open the file */ 1178 fd = open(dirp, O_RDONLY); 1179 if (fd == -1) { 1180 pr_err(gettext("Could not open %s, %s."), 1181 dirp, strerror(errno)); 1182 return (-1); 1183 } 1184 1185 /* issue the ioctl */ 1186 xx = ioctl(fd, _FIOCOD, arg); 1187 if (xx) { 1188 if (errno == ENOTTY) { 1189 pr_err(gettext("%s is not a CacheFS file system"), 1190 dirp); 1191 } else if (errno == EBUSY) { 1192 if (arg == 0) 1193 /* we're quiet if "all" was specified */ 1194 pr_err(gettext("CacheFS file system %s is not" 1195 " mounted demandconst."), dirp); 1196 } else { 1197 pr_err(gettext("Could not issue consistency request" 1198 " on %s\n %s."), dirp, strerror(errno)); 1199 } 1200 } 1201 close(fd); 1202 return (xx); 1203 } 1204 1205 /* 1206 * 1207 * simulate_disconnection 1208 * 1209 * Description: 1210 * Sends the rpc message to the cachefsd to turn simulated 1211 * disconnection on or off 1212 * Arguments: 1213 * namep name of file system or "all" 1214 * disconnect 1 means disconnect, 0 means connect 1215 * Returns: 1216 * Preconditions: 1217 * precond(name) 1218 */ 1219 1220 void 1221 simulate_disconnection(char *namep, int disconnect) 1222 { 1223 CLIENT *clnt; 1224 enum clnt_stat retval; 1225 int ret; 1226 int xx; 1227 int result; 1228 char *hostp; 1229 struct utsname info; 1230 struct cachefsd_disconnection_args args; 1231 char *msgp; 1232 struct timeval tval; 1233 1234 /* get the host name */ 1235 xx = uname(&info); 1236 if (xx == -1) { 1237 pr_err(gettext("cannot get host name, errno %d"), errno); 1238 return; 1239 } 1240 hostp = info.nodename; 1241 1242 /* creat the connection to the daemon */ 1243 clnt = clnt_create(hostp, CACHEFSDPROG, CACHEFSDVERS, "local"); 1244 if (clnt == NULL) { 1245 pr_err(gettext("cachefsd is not running")); 1246 return; 1247 } 1248 1249 /* give it a chance to complete */ 1250 tval.tv_sec = 60 * 60 * 24; 1251 tval.tv_usec = 0; 1252 clnt_control(clnt, CLSET_TIMEOUT, (char *)&tval); 1253 1254 /* perform the operation */ 1255 args.cda_mntpt = namep; 1256 args.cda_disconnect = disconnect; 1257 retval = cachefsd_disconnection_1(&args, &ret, clnt); 1258 if (retval != RPC_SUCCESS) { 1259 clnt_perror(clnt, gettext("cachefsd is not responding")); 1260 clnt_destroy(clnt); 1261 return; 1262 } 1263 1264 /* check for error from daemon */ 1265 if (ret != 0) { 1266 if (disconnect) { 1267 switch (ret) { 1268 default: 1269 msgp = "unknown error"; 1270 break; 1271 case 1: 1272 msgp = "not mounted disconnectable"; 1273 break; 1274 case 2: 1275 msgp = "already disconnected"; 1276 break; 1277 case 3: 1278 msgp = "not a cached file system"; 1279 break; 1280 } 1281 pr_err(gettext("Could not disconnect %s: %s"), 1282 namep, msgp); 1283 } else { 1284 switch (ret) { 1285 default: 1286 msgp = "unknown error"; 1287 break; 1288 case 1: 1289 msgp = "already connected"; 1290 break; 1291 case 2: 1292 msgp = "not simulated disconnection"; 1293 break; 1294 case 3: 1295 msgp = "not a cached file system"; 1296 break; 1297 } 1298 pr_err(gettext("Could not reconnect %s: %s"), 1299 namep, msgp); 1300 } 1301 } 1302 1303 ret = 0; 1304 1305 clnt_destroy(clnt); 1306 } 1307