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 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * Portions of this source code were derived from Berkeley 4.3 BSD 32 * under license from the Regents of the University of California. 33 */ 34 35 #pragma ident "%Z%%M% %I% %E% SMI" 36 37 /* 38 * 39 * fsck.c 40 * 41 * Cachefs fsck program. 42 */ 43 44 #include <locale.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <assert.h> 49 #include <stdarg.h> 50 #include <unistd.h> 51 #include <limits.h> 52 #include <errno.h> 53 #include <wait.h> 54 #include <ctype.h> 55 #include <fcntl.h> 56 #include <ftw.h> 57 #include <dirent.h> 58 #include <search.h> 59 #include <sys/types.h> 60 #include <sys/uio.h> 61 #include <sys/param.h> 62 #include <sys/stat.h> 63 #include <sys/fcntl.h> 64 #include <sys/mount.h> 65 #include <sys/mntent.h> 66 #include <sys/mnttab.h> 67 #include <sys/mman.h> 68 #include <sys/fs/cachefs_fs.h> 69 #include <syslog.h> 70 #include "../common/subr.h" 71 #include "res.h" 72 73 char *cfs_opts[] = { 74 #define CFSOPT_PREEN 0 75 "preen", 76 #define CFSOPT_NOCLEAN 1 77 "noclean", 78 #define CFSOPT_VERBOSE 2 79 "verbose", 80 #define CFSOPT_NONOCLEAN 3 81 "nonoclean", 82 83 NULL 84 }; 85 86 extern int dlog_ck(char *dir_path, ino64_t *maxlocalfilenop); 87 88 /* forward references */ 89 void usage(char *msgp); 90 void pr_err(char *fmt, ...); 91 int cfs_check(char *cachedirp, int noclean, int mflag, int verbose, 92 int nonoclean); 93 int cache_label_file(char *cachedirp, struct cache_label *clabelp); 94 int cache_permissions(char *cachedirp); 95 int cache_check_dir(char *cachedirp, char *namep); 96 int process_fsdir(char *cachedirp, char *namep, res *resp, int verbose); 97 int process_fsinfo(char *namep, ino64_t maxlocalfileno, 98 cachefs_fsinfo_t *fsinfop, int verbose); 99 int process_fsgroup(char *dirp, char *namep, res *resp, ino64_t base, 100 int fgsize, ino64_t fsid, int local, int verbose); 101 int tree_remove(const char *namep, const struct stat64 *statp, int type, 102 struct FTW *ftwp); 103 int cache_upgrade(char *cachedirp, int lockid); 104 int file_remove(const char *namep, const struct stat64 *statp, int verbose); 105 void cache_backmnt_cleanup(char *cachedirp, char *backmnt_namep); 106 107 #define FLAGS_FTW (FTW_PHYS | FTW_MOUNT | FTW_DEPTH) 108 109 static int S_verbose = 0; 110 static char S_lostfound[MAXPATHLEN]; 111 static int S_move_lostfound = 0; 112 113 /* 114 * 115 * main 116 * 117 * Description: 118 * Main routine for the cachefs fsck program. 119 * Arguments: 120 * argc number of command line arguments 121 * argv list of command line arguments 122 * Returns: 123 * Returns: 124 * 0 file system is okay and does not need checking 125 * 1 problem unrelated to the file system 126 * 32 file system is unmounted and needs checking (fsck 127 * -m only) 128 * 33 file system is already mounted 129 * 34 cannot stat device 130 * 36 uncorrectable errors detected - terminate normally 131 * 37 a signal was caught during processing 132 * 39 uncorrectable errors detected - terminate immediately 133 * 40 for root mounted fs, same as 0 134 * Preconditions: 135 */ 136 137 int 138 main(int argc, char **argv) 139 { 140 int xx; 141 int c; 142 char *optionp; 143 char *valuep; 144 int mflag; 145 int noclean; 146 char *cachedirp; 147 int lockid; 148 int verbose; 149 int nonoclean; 150 151 (void) setlocale(LC_ALL, ""); 152 #if !defined(TEXT_DOMAIN) 153 #define TEXT_DOMAIN "SYS_TEST" 154 #endif 155 (void) textdomain(TEXT_DOMAIN); 156 157 /* verify root running command */ 158 if (getuid() != 0) { 159 fprintf(stderr, gettext( 160 "fsck -F cachefs: must be run by root\n")); 161 return (1); 162 } 163 164 /* process command line options */ 165 optionp = NULL; 166 mflag = 0; 167 noclean = 0; 168 verbose = 0; 169 nonoclean = 0; 170 while ((c = getopt(argc, argv, "mnNo:yY")) != EOF) { 171 switch (c) { 172 case 'm': /* check but do not repair */ 173 mflag = 1; 174 break; 175 176 case 'n': /* answer no to questions */ 177 case 'N': 178 /* ignored */ 179 break; 180 181 case 'o': 182 optionp = optarg; 183 while (*optionp) { 184 xx = getsubopt(&optionp, cfs_opts, &valuep); 185 switch (xx) { 186 case CFSOPT_PREEN: 187 /* preen is the default mode */ 188 break; 189 case CFSOPT_NOCLEAN: 190 noclean = 1; 191 break; 192 case CFSOPT_VERBOSE: 193 verbose++; 194 S_verbose++; 195 break; 196 case CFSOPT_NONOCLEAN: 197 nonoclean = 1; 198 break; 199 default: 200 case -1: 201 pr_err(gettext("unknown option %s"), 202 valuep); 203 return (1); 204 } 205 } 206 break; 207 208 case 'y': /* answer yes to questions */ 209 case 'Y': 210 /* ignored, this is the default */ 211 break; 212 213 default: 214 usage("invalid option"); 215 return (1); 216 } 217 } 218 219 /* verify fsck device is specified */ 220 if (argc - optind < 1) { 221 usage(gettext("must specify cache directory")); 222 return (1); 223 } 224 225 /* save cache directory */ 226 cachedirp = argv[argc - 1]; 227 228 /* ensure cache directory exists */ 229 if (access(cachedirp, F_OK) != 0) { 230 pr_err(gettext("Cache directory %s does not exist."), 231 cachedirp); 232 return (39); 233 } 234 235 /* lock the cache directory non-shared */ 236 lockid = cachefs_dir_lock(cachedirp, 0); 237 if (lockid == -1) { 238 /* exit if could not get the lock */ 239 return (1); 240 } 241 242 /* is the cache directory in use */ 243 if (cachefs_inuse(cachedirp)) { 244 if (noclean) { 245 pr_err(gettext("Cache directory %s is in use."), 246 cachedirp); 247 xx = 33; 248 } else { 249 /* assume if in use that it is clean */ 250 xx = 0; 251 } 252 cachefs_dir_unlock(lockid); 253 return (xx); 254 } 255 256 xx = cache_upgrade(cachedirp, lockid); 257 if (xx != 0) { 258 /* check the file system */ 259 xx = cfs_check(cachedirp, noclean, mflag, verbose, nonoclean); 260 } 261 262 /* unlock the cache directory */ 263 cachefs_dir_unlock(lockid); 264 265 /* inform if files moved to lost+found */ 266 if (S_move_lostfound) { 267 pr_err(gettext("Files recovered to %s"), S_lostfound); 268 } 269 270 /* return the status of the file system checking */ 271 return (xx); 272 } 273 274 /* 275 * 276 * usage 277 * 278 * Description: 279 * Prints a short usage message. 280 * Arguments: 281 * msgp message to include with the usage message 282 * Returns: 283 * Preconditions: 284 */ 285 286 void 287 usage(char *msgp) 288 { 289 if (msgp) { 290 pr_err("%s", msgp); 291 } 292 293 (void) fprintf(stderr, 294 gettext("Usage: fsck -F cachefs [ -o specific_options ] [ -m ] " 295 "cachedir\n")); 296 } 297 298 /* 299 * 300 * pr_err 301 * 302 * Description: 303 * Prints an error message to stderr. 304 * Arguments: 305 * fmt printf style format 306 * ... arguments for fmt 307 * Returns: 308 * Preconditions: 309 * precond(fmt) 310 */ 311 312 void 313 pr_err(char *fmt, ...) 314 { 315 va_list ap; 316 317 va_start(ap, fmt); 318 (void) fprintf(stderr, gettext("fsck -F cachefs: ")); 319 (void) vfprintf(stderr, fmt, ap); 320 (void) fprintf(stderr, "\n"); 321 va_end(ap); 322 } 323 324 /* 325 * 326 * cache_upgrade 327 * 328 * Description: 329 * 330 * See if the current cache is out of date. If it is, do 331 * whatever magic is necessary to upgrade it. All such magic 332 * should be encapsulated here! 333 * 334 * Arguments: 335 * 336 * cachedirp name of the cache directory to check 337 * 338 * Returns: 339 * Returns: 340 * 0 cache was upgraded and shouldn't be checked 341 * 1 problem unrelated to the file system 342 * 36 uncorrectable errors detected - terminate normally 343 * 39 uncorrectable errors detected - terminate immediately 344 * 50 cache was already up-to-date (maybe we should fsck it) 345 * 51 cache was upgraded (but you should do fsck) 346 * Preconditions: 347 * precond(cachedirp) 348 */ 349 350 int 351 cache_upgrade(char *cachedirp, int lockid) 352 { 353 #ifdef CFSRLDEBUG 354 static int canupgrade[] = {1, 2, 3, 103, 104, 105, 106, 107, 355 4, 5, 108, 6, 7, 8, 0}; 356 #else /* CFSRLDEBUG */ 357 static int canupgrade[] = {1, 2, 3, 103, 104, 105, 106, 107, 358 4, 108, 5, 109, 110, 6, 111, 0}; 359 #endif /* CFSRLDEBUG */ 360 char labelpath[MAXPATHLEN]; 361 struct cache_label clabel; 362 int i; 363 364 if (((int)strlen(cachedirp) + (int)strlen(CACHELABEL_NAME) + 2) 365 >= MAXPATHLEN) 366 return (1); 367 368 (void) sprintf(labelpath, "%s/%s", cachedirp, CACHELABEL_NAME); 369 370 if (cachefs_label_file_get(labelpath, &clabel) != 0) 371 return (1); 372 373 /* nothing to do if we're current */ 374 if (clabel.cl_cfsversion == CFSVERSION) 375 return (50); 376 377 /* see if it's an old version that we know how to upgrade */ 378 for (i = 0; canupgrade[i] != 0; i++) 379 if (clabel.cl_cfsversion == canupgrade[i]) 380 break; 381 if (canupgrade[i] == 0) 382 return (36); 383 384 syslog(LOG_USER | LOG_INFO, 385 gettext("fsck -F cachefs: Recreating cache %s"), cachedirp); 386 387 /* currently, to `upgrade' we delete the old cache */ 388 if (cachefs_delete_all_cache(cachedirp) != 0) 389 return (36); 390 391 /* do any magic necessary to convert the old label to the new one */ 392 clabel.cl_cfsversion = CFSVERSION; 393 394 /* create the new cache! */ 395 if (cachefs_create_cache(cachedirp, NULL, &clabel) != 0) 396 return (36); 397 398 return (0); 399 } 400 401 /* 402 * 403 * cfs_check 404 * 405 * Description: 406 * This routine performs the actual checking of the cache 407 * file system. 408 * The file system must be inactive when this routine is called. 409 * Arguments: 410 * cachedirp name of the cache directory to check 411 * noclean 1 means ignore clean flag 412 * mflag 1 means no fixes, only check if mountable 413 * verbose indicate level of verbosity for diagnostics 414 * nonoclean 1 means honor clean flag; don't by default 415 * Returns: 416 * Returns: 417 * 0 file system is okay and does not need checking 418 * 1 problem unrelated to the file system 419 * 32 file system is unmounted and needs checking 420 * 33 file system is already mounted 421 * 34 cannot stat device 422 * 36 uncorrectable errors detected - terminate normally 423 * 37 a signal was caught during processing 424 * 39 uncorrectable errors detected - terminate immediately 425 * 40 for root mounted fs, same as 0, XXX 426 * Preconditions: 427 * precond(cachedirp) 428 */ 429 430 int 431 cfs_check(char *cachedirp, int noclean, int mflag, int verbose, int nonoclean) 432 { 433 DIR *dp; 434 struct dirent64 *dep; 435 char buf[MAXPATHLEN]; 436 struct stat64 statinfo; 437 int xx; 438 char *namep; 439 res *resp; 440 struct cache_label clabel; 441 442 /* if checking the clean flag is sufficient */ 443 if ((noclean == 0) && (nonoclean || mflag)) { 444 /* if the clean flag is set */ 445 if (cachefs_clean_flag_test(cachedirp)) { 446 if (verbose) { 447 pr_err(gettext("Cache %s is clean"), cachedirp); 448 } 449 return (0); 450 } 451 } 452 453 /* if mflag specified then go no farther */ 454 if (mflag) 455 return (32); 456 457 /* check the cache label file for correctness */ 458 xx = cache_label_file(cachedirp, &clabel); 459 if (xx) 460 return (xx); 461 462 /* make sure the kernel lock file exists */ 463 sprintf(buf, "%s/%s", cachedirp, CACHEFS_LOCK_FILE); 464 xx = open(buf, O_RDWR | O_CREAT, 0700); 465 if (xx == -1) { 466 pr_err(gettext("Cannot create lock file %s"), buf); 467 return (39); 468 } 469 close(xx); 470 471 /* fix permissions on the cache directory */ 472 xx = cache_permissions(cachedirp); 473 if (xx) 474 return (xx); 475 476 /* make the back file system mount directory if necessary */ 477 xx = cache_check_dir(cachedirp, BACKMNT_NAME); 478 if (xx) 479 return (xx); 480 481 /* clean out junk in the back file system mount directory */ 482 cache_backmnt_cleanup(cachedirp, BACKMNT_NAME); 483 484 /* make the lost+found directory if necessary */ 485 xx = cache_check_dir(cachedirp, CACHEFS_LOSTFOUND_NAME); 486 if (xx) 487 return (xx); 488 489 /* construct the path to the lost and found directory for file_remove */ 490 sprintf(S_lostfound, "%s/%s", cachedirp, CACHEFS_LOSTFOUND_NAME); 491 492 /* construct the path name of the resource file */ 493 namep = RESOURCE_NAME; 494 xx = strlen(cachedirp) + strlen(namep) + 3; 495 if (xx >= MAXPATHLEN) { 496 pr_err(gettext("Path name too long %s/%s"), 497 cachedirp, namep); 498 return (39); 499 } 500 sprintf(buf, "%s/%s", cachedirp, namep); 501 502 /* make a res object to operate on the resource file */ 503 resp = res_create(buf, clabel.cl_maxinodes, verbose); 504 if (resp == NULL) { 505 pr_err(gettext("Could not process resource file %s: %s"), 506 buf, strerror(errno)); 507 return (39); 508 } 509 510 /* open the cache directory */ 511 if ((dp = opendir(cachedirp)) == NULL) { 512 pr_err(gettext("Cannot open directory %s: %s"), cachedirp, 513 strerror(errno)); 514 res_destroy(resp); 515 return (39); 516 } 517 518 /* mark all directories */ 519 while ((dep = readdir64(dp)) != NULL) { 520 /* ignore . and .. */ 521 if ((strcmp(dep->d_name, ".") == 0) || 522 (strcmp(dep->d_name, "..") == 0)) 523 continue; 524 525 /* check path length */ 526 xx = strlen(cachedirp) + strlen(dep->d_name) + 3; 527 if (xx >= MAXPATHLEN) { 528 pr_err(gettext("Path name too long %s/%s"), 529 cachedirp, dep->d_name); 530 closedir(dp); 531 res_destroy(resp); 532 return (39); 533 } 534 535 /* stat the file */ 536 sprintf(buf, "%s/%s", cachedirp, dep->d_name); 537 xx = lstat64(buf, &statinfo); 538 if (xx == -1) { 539 if (errno != ENOENT) { 540 pr_err(gettext("Cannot stat %s: %s"), cachedirp, 541 strerror(errno)); 542 closedir(dp); 543 res_destroy(resp); 544 return (39); 545 } 546 continue; 547 } 548 549 /* if a directory */ 550 if (S_ISDIR(statinfo.st_mode)) { 551 xx = chmod(buf, 0700); 552 if (xx == -1) { 553 pr_err(gettext("Cannot chmod %s: %s"), buf, 554 strerror(errno)); 555 closedir(dp); 556 res_destroy(resp); 557 return (39); 558 } 559 } 560 } 561 562 /* process files in the cache directory */ 563 rewinddir(dp); 564 while ((dep = readdir64(dp)) != NULL) { 565 /* ignore . and .. */ 566 if ((strcmp(dep->d_name, ".") == 0) || 567 (strcmp(dep->d_name, "..") == 0)) 568 continue; 569 570 /* stat the file */ 571 sprintf(buf, "%s/%s", cachedirp, dep->d_name); 572 xx = lstat64(buf, &statinfo); 573 if (xx == -1) { 574 if (errno != ENOENT) { 575 pr_err(gettext("Cannot stat %s: %s"), cachedirp, 576 strerror(errno)); 577 closedir(dp); 578 res_destroy(resp); 579 return (39); 580 } 581 continue; 582 } 583 584 /* ignore directories */ 585 if (S_ISDIR(statinfo.st_mode)) 586 continue; 587 588 /* if not a link */ 589 if (!S_ISLNK(statinfo.st_mode)) { 590 /* 591 * XXX make sure a valid file 592 * Update file and block counts for this file. 593 * This file will be <2GB. 594 */ 595 res_addfile(resp, (long)statinfo.st_size); 596 continue; 597 } 598 599 /* process the file system cache directory */ 600 xx = process_fsdir(cachedirp, dep->d_name, resp, verbose); 601 if (xx) { 602 closedir(dp); 603 res_destroy(resp); 604 return (xx); 605 } 606 } 607 608 /* look for directories that do not belong */ 609 rewinddir(dp); 610 while ((dep = readdir64(dp)) != NULL) { 611 /* ignore . and .. */ 612 if ((strcmp(dep->d_name, ".") == 0) || 613 (strcmp(dep->d_name, "..") == 0)) 614 continue; 615 616 /* stat the file */ 617 sprintf(buf, "%s/%s", cachedirp, dep->d_name); 618 xx = lstat64(buf, &statinfo); 619 if (xx == -1) { 620 if (errno != ENOENT) { 621 pr_err(gettext("Cannot stat %s: %s"), cachedirp, 622 strerror(errno)); 623 closedir(dp); 624 res_destroy(resp); 625 return (39); 626 } 627 continue; 628 } 629 630 /* XXX should we unlink extraneous regular files? */ 631 632 /* ignore all but directories */ 633 if (!S_ISDIR(statinfo.st_mode)) 634 continue; 635 636 /* ignore directories we have checked */ 637 if ((statinfo.st_mode & S_IAMB) != 0700) 638 continue; 639 640 /* ignore the mount directory */ 641 if (strcmp(dep->d_name, BACKMNT_NAME) == 0) 642 continue; 643 644 /* ignore the lost+found directory */ 645 if (strcmp(dep->d_name, CACHEFS_LOSTFOUND_NAME) == 0) 646 continue; 647 648 /* remove the directory */ 649 xx = nftw64(buf, tree_remove, 3, FLAGS_FTW); 650 if (xx != 0) { 651 pr_err(gettext("Error walking tree %s."), namep); 652 closedir(dp); 653 res_destroy(resp); 654 return (39); 655 } 656 657 if (verbose) 658 pr_err(gettext("Directory removed: %s"), buf); 659 } 660 661 /* close the directory */ 662 closedir(dp); 663 664 /* add one file and one block for the cache directory itself */ 665 res_addfile(resp, 1); 666 667 /* finish off the resource file processing */ 668 xx = res_done(resp); 669 if (xx == -1) { 670 pr_err(gettext("Could not finish resource file %s: %s"), 671 buf, strerror(errno)); 672 return (39); 673 } 674 res_destroy(resp); 675 676 /* return success */ 677 return (0); 678 } 679 680 /* 681 * 682 * cache_label_file 683 * 684 * Description: 685 * This routine performs the checking and fixing up of the 686 * cache label file. 687 * Arguments: 688 * cachedirp name of the cache directory to check 689 * clabelp cache label contents put here if not NULL 690 * Returns: 691 * 0 file system is okay and does not need checking 692 * 1 problem unrelated to the file system 693 * 32 file system is unmounted and needs checking 694 * 33 file system is already mounted 695 * 34 cannot stat device 696 * 36 uncorrectable errors detected - terminate normally 697 * 37 a signal was caught during processing 698 * 39 uncorrectable errors detected - terminate immediately 699 * Preconditions: 700 * precond(cachedirp) 701 */ 702 703 int 704 cache_label_file(char *cachedirp, struct cache_label *clabelp) 705 { 706 int xx; 707 char buf1[MAXPATHLEN]; 708 char buf2[MAXPATHLEN]; 709 char *namep; 710 struct cache_label clabel1, clabel2; 711 712 namep = CACHELABEL_NAME; 713 714 /* see if path name is too long */ 715 xx = strlen(cachedirp) + strlen(namep) + 10; 716 if (xx >= MAXPATHLEN) { 717 pr_err(gettext("Cache directory name %s is too long"), 718 cachedirp); 719 return (39); 720 } 721 722 /* make a path to the cache label file and its backup copy */ 723 sprintf(buf1, "%s/%s", cachedirp, namep); 724 sprintf(buf2, "%s/%s.dup", cachedirp, namep); 725 726 /* get the contents of the cache label file */ 727 xx = cachefs_label_file_get(buf1, &clabel1); 728 if (xx == -1) { 729 /* get the backup cache label file contents */ 730 xx = cachefs_label_file_get(buf2, &clabel2); 731 if (xx == -1) { 732 pr_err(gettext("Run `cfsadmin -d all %s'\n" 733 "and then run\n" 734 "`cfsadmin -c %s'\n"), cachedirp, cachedirp); 735 return (39); 736 } 737 738 /* write the cache label file */ 739 xx = cachefs_label_file_put(buf1, &clabel2); 740 if (xx == -1) { 741 pr_err(gettext("Run `cfsadmin -d all %s'\n" 742 "and then run\n" 743 "`cfsadmin -c %s'\n"), cachedirp, cachedirp); 744 return (39); 745 } 746 pr_err(gettext("Cache label file %s repaired."), buf1); 747 748 /* copy out the contents to the caller */ 749 if (clabelp) 750 *clabelp = clabel2; 751 752 /* return success */ 753 return (0); 754 } 755 756 /* get the contents of the backup cache label file */ 757 xx = cachefs_label_file_get(buf2, &clabel2); 758 if (xx == -1) { 759 /* write the backup cache label file */ 760 xx = cachefs_label_file_put(buf2, &clabel1); 761 if (xx == -1) { 762 return (39); 763 } 764 pr_err(gettext("Cache label file %s repaired."), buf2); 765 } 766 767 /* copy out the contents to the caller */ 768 if (clabelp) 769 *clabelp = clabel1; 770 771 /* return success */ 772 return (0); 773 } 774 775 /* 776 * 777 * cache_permissions 778 * 779 * Description: 780 * Checks the permissions on the cache directory and fixes 781 * them if necessary. 782 * Arguments: 783 * cachedirp name of the cache directory to check 784 * Returns: 785 * 0 file system is okay and does not need checking 786 * 1 problem unrelated to the file system 787 * 32 file system is unmounted and needs checking 788 * 33 file system is already mounted 789 * 34 cannot stat device 790 * 36 uncorrectable errors detected - terminate normally 791 * 37 a signal was caught during processing 792 * 39 uncorrectable errors detected - terminate immediately 793 * Preconditions: 794 * precond(cachedirp) 795 */ 796 797 int 798 cache_permissions(char *cachedirp) 799 { 800 int xx; 801 struct stat64 statinfo; 802 803 /* get info about the cache directory */ 804 xx = lstat64(cachedirp, &statinfo); 805 if (xx == -1) { 806 pr_err(gettext("Could not stat %s: %s"), cachedirp, 807 strerror(errno)); 808 return (34); 809 } 810 811 /* check the mode bits */ 812 if ((statinfo.st_mode & S_IAMB) != 0) { 813 814 /* fix the mode bits */ 815 xx = chmod(cachedirp, 0); 816 if (xx == -1) { 817 pr_err(gettext("Could not set modes bits on " 818 "cache directory %s: %s"), 819 cachedirp, strerror(errno)); 820 return (36); 821 } 822 pr_err(gettext("Mode bits reset on cache directory %s"), 823 cachedirp); 824 } 825 826 /* return success */ 827 return (0); 828 } 829 830 /* 831 * 832 * cache_check_dir 833 * 834 * Description: 835 * Checks for the existance of the directory 836 * and creates it if necessary. 837 * Arguments: 838 * cachedirp name of the cache directory containing the dir 839 * namep name of dir 840 * Returns: 841 * 0 file system is okay and does not need checking 842 * 1 problem unrelated to the file system 843 * 32 file system is unmounted and needs checking 844 * 33 file system is already mounted 845 * 34 cannot stat device 846 * 36 uncorrectable errors detected - terminate normally 847 * 37 a signal was caught during processing 848 * 39 uncorrectable errors detected - terminate immediately 849 * Preconditions: 850 * precond(cachedirp) 851 * precond(dirp) 852 */ 853 854 int 855 cache_check_dir(char *cachedirp, char *namep) 856 { 857 int xx; 858 char buf[MAXPATHLEN]; 859 struct stat64 statinfo; 860 861 /* see if path name is too long */ 862 xx = strlen(cachedirp) + strlen(namep) + 3; 863 if (xx >= MAXPATHLEN) { 864 pr_err(gettext("Cache directory name %s is too long"), 865 cachedirp); 866 return (39); 867 } 868 869 /* make the pathname of the directory */ 870 sprintf(buf, "%s/%s", cachedirp, namep); 871 872 /* get info on the directory */ 873 xx = lstat64(buf, &statinfo); 874 if (xx == -1) { 875 /* if an error other than it does not exist */ 876 if (errno != ENOENT) { 877 pr_err(gettext("Error on lstat(2) of %s: %s"), 878 buf, strerror(errno)); 879 return (39); 880 } 881 882 /* make the directory */ 883 xx = mkdir(buf, 0); 884 if (xx == -1) { 885 pr_err(gettext("Could not create directory %s"), 886 buf); 887 return (39); 888 } 889 pr_err(gettext("Created directory %s"), buf); 890 } 891 892 /* else see if really a directory */ 893 else if (!S_ISDIR(statinfo.st_mode)) { 894 /* get rid of the file */ 895 xx = unlink(buf); 896 if (xx == -1) { 897 pr_err(gettext("Cannot remove %s: %s"), buf, 898 strerror(errno)); 899 return (39); 900 } 901 902 /* make the directory */ 903 xx = mkdir(buf, 0); 904 if (xx == -1) { 905 pr_err(gettext("Could not create directory %s"), 906 buf); 907 return (39); 908 } 909 pr_err(gettext("Created directory %s"), buf); 910 } 911 912 /* return success */ 913 return (0); 914 } 915 916 /* 917 * 918 * process_fsdir 919 * 920 * Description: 921 * Performs the necessary checking and repair on the 922 * specified file system cache directory. 923 * Calls res_addfile and res_addident as appropriate. 924 * Arguments: 925 * cachedirp name of cache directory 926 * namep name of link file for the file system cache 927 * resp res object for res_addfile and res_addident calls 928 * verbose indicate level of verbosity for diagnostics 929 * Returns: 930 * 0 file system is okay and does not need checking 931 * 1 problem unrelated to the file system 932 * 32 file system is unmounted and needs checking 933 * 33 file system is already mounted 934 * 34 cannot stat device 935 * 36 uncorrectable errors detected - terminate normally 936 * 37 a signal was caught during processing 937 * 39 uncorrectable errors detected - terminate immediately 938 * Preconditions: 939 * precond(cachedirp) 940 * precond(namep && is a sym link) 941 * precond(resp) 942 */ 943 944 int 945 process_fsdir(char *cachedirp, char *namep, res *resp, int verbose) 946 { 947 DIR *dp; 948 struct dirent64 *dep; 949 char linkpath[MAXPATHLEN]; 950 char dirpath[MAXPATHLEN]; 951 char attrpath[MAXPATHLEN]; 952 char buf[MAXPATHLEN]; 953 int xx; 954 struct stat64 statinfo; 955 char *atp = ATTRCACHE_NAME; 956 int fd; 957 ino64_t base; 958 int local; 959 char *strp; 960 ino64_t fsid; 961 int error = 0; 962 int hashsize = 0; 963 ENTRY hitem; 964 ino64_t maxlocalfileno; 965 cachefs_fsinfo_t fsinfo; 966 time32_t btime; 967 968 /* construct the path to the sym link */ 969 xx = strlen(cachedirp) + strlen(namep) + 3; 970 if (xx >= MAXPATHLEN) { 971 pr_err(gettext("Pathname too long %s/%s"), cachedirp, namep); 972 error = 39; 973 goto out; 974 } 975 sprintf(linkpath, "%s/%s", cachedirp, namep); 976 977 /* read the contents of the link */ 978 xx = readlink(linkpath, buf, sizeof (buf)); 979 if (xx == -1) { 980 pr_err(gettext("Unable to read link %s: %s"), linkpath, 981 strerror(errno)); 982 error = 39; 983 goto out; 984 } 985 buf[xx] = '\0'; 986 987 /* do a one time check on lengths of files */ 988 xx = strlen(cachedirp) + strlen(buf) + 20 + 20; 989 if (xx >= MAXPATHLEN) { 990 pr_err(gettext("Pathname too long %s/%s"), cachedirp, buf); 991 error = 39; 992 goto out; 993 } 994 995 /* construct the path to the directory */ 996 sprintf(dirpath, "%s/%s", cachedirp, buf); 997 998 /* stat the directory */ 999 xx = lstat64(dirpath, &statinfo); 1000 if ((xx == -1) || (strtoull(buf, NULL, 16) != statinfo.st_ino)) { 1001 if ((xx == -1) && (errno != ENOENT)) { 1002 pr_err(gettext("Could not stat %s: %s"), dirpath, 1003 strerror(errno)); 1004 error = 39; 1005 } else 1006 error = -1; 1007 goto out; 1008 } 1009 fsid = statinfo.st_ino; 1010 1011 /* 1012 * Check for a disconnect log(dlog) file and verify it. 1013 */ 1014 xx = dlog_ck(dirpath, &maxlocalfileno); 1015 if (xx) { 1016 error = -1; 1017 goto out; 1018 } 1019 1020 /* process the fsinfo file */ 1021 sprintf(buf, "%s/%s", dirpath, CACHEFS_FSINFO); 1022 xx = process_fsinfo(buf, maxlocalfileno, &fsinfo, verbose); 1023 if (xx) { 1024 error = -1; 1025 pr_err(gettext("Cannot update fsinfo file %s"), buf); 1026 goto out; 1027 } 1028 1029 /* create the unmount file in the cachedir */ 1030 sprintf(buf, "%s/%s", dirpath, CACHEFS_UNMNT_FILE); 1031 /* this file will be < 2GB */ 1032 fd = open(buf, O_CREAT | O_RDWR, 0666); 1033 if (fd == -1) { 1034 pr_err(gettext("Cannot create unmnt file %s: %s"), buf, 1035 strerror(errno)); 1036 error = -1; 1037 goto out; 1038 } 1039 btime = get_boottime(); 1040 if (write(fd, &btime, sizeof (btime)) == -1) { 1041 pr_err(gettext("Cannot write cachedir unmnt file %s: %s"), buf, 1042 strerror(errno)); 1043 error = -1; 1044 goto out; 1045 } 1046 close(fd); 1047 1048 /* create the unmount file */ 1049 sprintf(buf, "%s/%s", dirpath, CACHEFS_UNMNT_FILE); 1050 /* this file will be < 2GB */ 1051 fd = open(buf, O_CREAT | O_RDWR, 0666); 1052 if (fd == -1) { 1053 pr_err(gettext("Cannot create unmnt file %s: %s"), buf, 1054 strerror(errno)); 1055 error = -1; 1056 goto out; 1057 } 1058 close(fd); 1059 1060 /* construct the name to the attrcache directory */ 1061 sprintf(attrpath, "%s/%s", dirpath, atp); 1062 1063 /* open the attrcache directory */ 1064 if ((dp = opendir(attrpath)) == NULL) { 1065 pr_err(gettext("Cannot open directory %s: %s"), attrpath, 1066 strerror(errno)); 1067 error = -1; 1068 goto out; 1069 } 1070 1071 /* make one pass, counting how big to make the hash table */ 1072 while (readdir64(dp) != NULL) 1073 ++hashsize; 1074 if (hcreate(hashsize + 1000) == 0) { 1075 pr_err(gettext("Cannot allocate heap space.")); 1076 (void) closedir(dp); 1077 hashsize = 0; 1078 error = 39; 1079 goto out; 1080 } 1081 rewinddir(dp); 1082 1083 /* loop reading the contents of the directory */ 1084 while ((dep = readdir64(dp)) != NULL) { 1085 /* ignore . and .. */ 1086 if ((strcmp(dep->d_name, ".") == 0) || 1087 (strcmp(dep->d_name, "..") == 0)) 1088 continue; 1089 1090 /* check for a reasonable name */ 1091 xx = strlen(dep->d_name); 1092 if ((xx != 16) && (xx != 17)) { 1093 /* bad file */ 1094 pr_err(gettext("Unknown file %s/%s"), 1095 attrpath, dep->d_name); 1096 closedir(dp); 1097 error = 39; 1098 goto out; 1099 } 1100 1101 /* derive the base number from the file name */ 1102 if (*(dep->d_name) == 'L') { 1103 local = 1; 1104 base = strtoull(dep->d_name + 1, &strp, 16); 1105 } else { 1106 local = 0; 1107 base = strtoull(dep->d_name, &strp, 16); 1108 } 1109 if (*strp != '\0') { 1110 /* bad file */ 1111 pr_err(gettext("Unknown file %s/%s"), 1112 attrpath, dep->d_name); 1113 closedir(dp); 1114 error = 39; 1115 goto out; 1116 } 1117 1118 /* process the file group */ 1119 error = process_fsgroup(dirpath, dep->d_name, resp, 1120 base, fsinfo.fi_fgsize, fsid, local, verbose); 1121 if (error) { 1122 closedir(dp); 1123 goto out; 1124 } 1125 } 1126 closedir(dp); 1127 1128 /* open the fscache directory */ 1129 if ((dp = opendir(dirpath)) == NULL) { 1130 pr_err(gettext("Cannot open directory %s: %s"), dirpath, 1131 strerror(errno)); 1132 error = 39; 1133 goto out; 1134 } 1135 1136 /* loop reading the contents of the directory */ 1137 while ((dep = readdir64(dp)) != NULL) { 1138 /* ignore . and .. */ 1139 if ((strcmp(dep->d_name, ".") == 0) || 1140 (strcmp(dep->d_name, "..") == 0)) 1141 continue; 1142 1143 /* ignore cachefs special files */ 1144 xx = strncmp(dep->d_name, CACHEFS_PREFIX, CACHEFS_PREFIX_LEN); 1145 if (xx == 0) 1146 continue; 1147 1148 hitem.key = dep->d_name; 1149 hitem.data = NULL; 1150 if (hsearch(hitem, FIND) == NULL) { 1151 sprintf(buf, "%s/%s", dirpath, dep->d_name); 1152 if (verbose) { 1153 printf("Unreferenced dir %s\n", buf); 1154 } 1155 xx = nftw64(buf, tree_remove, 3, FLAGS_FTW); 1156 if (xx != 0) { 1157 pr_err(gettext("Could not remove %s"), buf); 1158 error = 39; 1159 closedir(dp); 1160 goto out; 1161 } 1162 } 1163 } 1164 closedir(dp); 1165 1166 /* add the info file to the resource */ 1167 res_addfile(resp, 1); 1168 1169 /* add the directory to the resources */ 1170 res_addfile(resp, 1); 1171 1172 /* add the sym link to the resources */ 1173 res_addfile(resp, 1); 1174 1175 /* change the mode on the directory to indicate we visited it */ 1176 xx = chmod(dirpath, 0777); 1177 if (xx == -1) { 1178 pr_err(gettext("Cannot chmod %s: %s"), dirpath, 1179 strerror(errno)); 1180 error = 39; 1181 goto out; 1182 } 1183 1184 out: 1185 /* free up the heap allocated by the hash functions */ 1186 if (hashsize != 0) 1187 hdestroy(); 1188 1189 if (error == -1) { 1190 /* remove the sym link */ 1191 xx = unlink(linkpath); 1192 if (xx == -1) { 1193 pr_err(gettext("Unable to remove %s: %s"), linkpath, 1194 strerror(errno)); 1195 error = 39; 1196 } else { 1197 error = 0; 1198 } 1199 } 1200 1201 return (error); 1202 } 1203 1204 /* 1205 * Processes and fixes up the fsinfo file. 1206 */ 1207 int 1208 process_fsinfo(char *namep,