1 /* 2 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 7 /* All Rights Reserved */ 8 9 10 /* 11 * Copyright (c) 1980, 1986, 1990 The Regents of the University of California. 12 * All rights reserved. 13 * 14 * Redistribution and use in source and binary forms are permitted 15 * provided that: (1) source distributions retain this entire copyright 16 * notice and comment, and (2) distributions including binaries display 17 * the following acknowledgement: ``This product includes software 18 * developed by the University of California, Berkeley and its contributors'' 19 * in the documentation or other materials provided with the distribution 20 * and in all advertising materials mentioning features or use of this 21 * software. Neither the name of the University nor the names of its 22 * contributors may be used to endorse or promote products derived 23 * from this software without specific prior written permission. 24 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 25 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 26 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 27 */ 28 29 #pragma ident "%Z%%M% %I% %E% SMI" 30 31 #include <stdio.h> 32 #include <sys/param.h> 33 #include <sys/types.h> 34 #include <sys/int_types.h> 35 #include <sys/sysmacros.h> 36 #include <sys/mntent.h> 37 38 #define bcopy(f, t, n) memcpy(t, f, n) 39 #define bzero(s, n) memset(s, 0, n) 40 #define bcmp(s, d, n) memcmp(s, d, n) 41 42 #define index(s, r) strchr(s, r) 43 #define rindex(s, r) strrchr(s, r) 44 45 #include <sys/fs/ufs_fs.h> 46 #include <sys/vnode.h> 47 #include <sys/fs/ufs_inode.h> 48 #include <sys/stat.h> 49 #include <sys/wait.h> 50 #include <sys/mnttab.h> 51 #include <sys/signal.h> 52 #include <string.h> 53 #include <ctype.h> /* use isdigit macro rather than 4.1 libc routine */ 54 #include "fsck.h" 55 #include <sys/vfstab.h> 56 #include <sys/ustat.h> 57 #include <sys/statvfs.h> 58 #include <errno.h> 59 60 int mflag = 0; /* sanity check only */ 61 char hotroot; 62 63 uint_t largefile_count = 0; /* global largefile counter */ 64 65 extern int optind; 66 extern char *optarg; 67 68 char *mntopt(); 69 char *malloc(); 70 void catch(), catchquit(), voidquit(); 71 int returntosingle; 72 void checkfilesys(); 73 void update_lf(); 74 void main(); 75 void check_sanity(); 76 void usage(); 77 struct dinode *getnextinode(); 78 79 char *subopts [] = { 80 #define PREEN 0 81 "p", 82 #define BLOCK 1 83 "b", 84 #define DEBUG 2 85 "d", 86 #define READ_ONLY 3 87 "r", 88 #define ONLY_WRITES 4 89 "w", 90 #define CONVERT 5 /* setup.c convert between fffs and ffs */ 91 "c", 92 #define FORCE 6 /* force checking, even if clean */ 93 "f", 94 NULL 95 }; 96 97 char **sargv; 98 void 99 main(argc, argv) 100 int argc; 101 char *argv[]; 102 { 103 int c; 104 char *suboptions, *value; 105 int suboption; 106 107 /* 108 * Save argv pointer to be used if a hole in a directory's block list 109 * is found. 110 */ 111 sargv = argv; 112 while ((c = getopt(argc, argv, "mnNo:VyYz")) != EOF) { 113 switch (c) { 114 115 case 'm': 116 mflag++; 117 break; 118 119 case 'n': /* default no answer flag */ 120 case 'N': 121 nflag++; 122 yflag = 0; 123 break; 124 125 case 'o': 126 /* 127 * ufs specific options. 128 */ 129 suboptions = optarg; 130 while (*suboptions != '\0') { 131 switch ((suboption = getsubopt(&suboptions, 132 subopts, &value))) { 133 134 case PREEN: 135 preen++; 136 break; 137 138 case BLOCK: 139 if (value == NULL) { 140 usage(); 141 } else { 142 bflag = atoi(value); 143 } 144 printf("Alternate super block location: %d.\n", 145 bflag); 146 break; 147 148 case CONVERT: 149 cvtflag++; 150 break; 151 152 case DEBUG: 153 debug++; 154 break; 155 156 case READ_ONLY: 157 break; 158 159 case ONLY_WRITES: 160 /* check only writable filesystems */ 161 wflag++; 162 break; 163 164 case FORCE: 165 fflag++; 166 break; 167 168 default: 169 usage(); 170 } 171 } 172 break; 173 174 case 'V': 175 { 176 int opt_count; 177 char *opt_text; 178 179 (void) fprintf(stdout, "fsck -F ufs "); 180 for (opt_count = 1; opt_count < argc; 181 opt_count++) { 182 opt_text = argv[opt_count]; 183 if (opt_text) 184 (void) fprintf(stdout, " %s ", 185 opt_text); 186 } 187 (void) fprintf(stdout, "\n"); 188 } 189 break; 190 191 case 'y': /* default yes answer flag */ 192 case 'Y': 193 yflag++; 194 nflag = 0; 195 break; 196 197 case '?': 198 usage(); 199 } 200 } 201 argc -= optind; 202 argv = &argv[optind]; 203 rflag++; /* check raw devices */ 204 if (signal(SIGINT, SIG_IGN) != (int)SIG_IGN) 205 (void) signal(SIGINT, catch); 206 if (preen) 207 (void) signal(SIGQUIT, catchquit); 208 209 if (argc) { 210 while (argc-- > 0) { 211 if (wflag && !writable(*argv)) { 212 (void) fprintf(stderr, "not writeable '%s'\n", 213 *argv); 214 argv++; 215 } else 216 checkfilesys(*argv++); 217 } 218 exit(exitstat); 219 } 220 } 221 222 223 void 224 checkfilesys(filesys) 225 char *filesys; 226 { 227 daddr32_t n_ffree, n_bfree; 228 struct dups *dp; 229 struct zlncnt *zlnp; 230 char *devstr; 231 232 mountfd = -1; 233 hotroot = 0; 234 mountedfs = 0; 235 iscorrupt = 1; 236 isconvert = 0; 237 ismdd = 0; 238 islog = 0; 239 islogok = 0; 240 dirholes = 0; 241 needs_reclaim = 0; 242 errorlocked = is_errorlocked(filesys); 243 244 if ((devstr = setup(filesys)) == 0) { 245 if (iscorrupt == 0) 246 return; 247 if (preen) 248 pfatal("CAN'T CHECK FILE SYSTEM."); 249 if ((exitstat == 0) && (mflag)) 250 exitstat = 32; 251 exit(exitstat); 252 } 253 else 254 devname = devstr; 255 if (mflag) 256 check_sanity(filesys); /* this never returns */ 257 if (debug) 258 printclean(); 259 iscorrupt = 0; 260 /* 261 * 1: scan inodes tallying blocks used 262 */ 263 if (preen == 0) { 264 if (mountedfs) 265 printf("** Currently Mounted on %s\n", sblock.fs_fsmnt); 266 else 267 printf("** Last Mounted on %s\n", sblock.fs_fsmnt); 268 if (mflag) { 269 printf("** Phase 1 - Sanity Check only\n"); 270 return; 271 } else { 272 printf("** Phase 1 - Check Blocks and Sizes\n"); 273 } 274 } 275 pass1(); 276 277 /* 278 * 1b: locate first references to duplicates, if any 279 */ 280 if (duplist) { 281 if (preen) 282 pfatal("INTERNAL ERROR: dups with -p"); 283 printf("** Phase 1b - Rescan For More DUPS\n"); 284 pass1b(); 285 } 286 287 /* 288 * 2: traverse directories from root to mark all connected directories 289 */ 290 if (preen == 0) { 291 printf("** Phase 2 - Check Pathnames\n"); 292 } 293 pass2(); 294 295 /* 296 * 3: scan inodes looking for disconnected directories 297 */ 298 if (preen == 0) { 299 printf("** Phase 3 - Check Connectivity\n"); 300 } 301 pass3(); 302 303 /* 304 * 3b: check acls 305 */ 306 pass3b(); 307 308 /* 309 * 4: scan inodes looking for disconnected files; check reference counts 310 */ 311 if (preen == 0) { 312 printf("** Phase 4 - Check Reference Counts\n"); 313 } 314 pass4(); 315 316 /* 317 * 5: check and repair resource counts in cylinder groups 318 */ 319 if (preen == 0) { 320 printf("** Phase 5 - Check Cyl groups\n"); 321 } 322 pass5(); 323 324 updateclean(); 325 if (debug) 326 printclean(); 327 328 /* 329 * print out summary statistics 330 */ 331 n_ffree = sblock.fs_cstotal.cs_nffree; 332 n_bfree = sblock.fs_cstotal.cs_nbfree; 333 pwarn("%d files, %d used, %d free ", 334 n_files, n_blks, n_ffree + sblock.fs_frag * n_bfree); 335 if (preen) 336 printf("\n"); 337 pwarn("(%d frags, %d blocks, %.1f%% fragmentation)\n", 338 n_ffree, n_bfree, (float)(n_ffree * 100) / sblock.fs_dsize); 339 if (debug && 340 (n_files -= maxino - UFSROOTINO - sblock.fs_cstotal.cs_nifree)) 341 printf("%d files missing\n", n_files); 342 if (debug) { 343 n_blks += sblock.fs_ncg * 344 (cgdmin(&sblock, 0) - cgsblock(&sblock, 0)); 345 n_blks += cgsblock(&sblock, 0) - cgbase(&sblock, 0); 346 n_blks += howmany(sblock.fs_cssize, sblock.fs_fsize); 347 if (n_blks -= maxfsblock - (n_ffree + sblock.fs_frag * n_bfree)) 348 printf("%d blocks missing\n", n_blks); 349 if (duplist != NULL) { 350 printf("The following duplicate blocks remain:"); 351 for (dp = duplist; dp; dp = dp->next) 352 printf(" %d,", dp->dup); 353 printf("\n"); 354 } 355 if (zlnhead != NULL) { 356 printf("The following zero link count inodes remain:"); 357 for (zlnp = zlnhead; zlnp; zlnp = zlnp->next) 358 printf(" %d,", zlnp->zlncnt); 359 printf("\n"); 360 } 361 } 362 zlnhead = (struct zlncnt *)0; 363 duplist = muldup = (struct dups *)0; 364 inocleanup(); 365 ckfini(); 366 free(blockmap); 367 free(statemap); 368 free((char *)lncntp); 369 lncntp = NULL; 370 blockmap = statemap = NULL; 371 if (iscorrupt) 372 exitstat = 36; 373 if (!fsmodified) 374 return; 375 if (!preen) 376 printf("\n***** FILE SYSTEM WAS MODIFIED *****\n"); 377 378 if (dirholes) { 379 380 if (preen) { 381 printf("\nFixed directory holes, Re-checking %s\n", 382 devname); 383 execv("/usr/sbin/fsck", sargv); 384 printf("Exec failed %s\n", strerror(errno)); 385 } 386 387 printf("\nFixed directories with holes, Run fsck once again\n"); 388 exitstat = 36; 389 } else { 390 if ((mountedfs && !errorlocked) || hotroot) { 391 exitstat = 40; 392 } 393 } 394 } 395 396 397 /* 398 * exit 0 - file system is unmounted and okay 399 * exit 32 - file system is unmounted and needs checking 400 * exit 33 - file system is mounted 401 * for root file system 402 * exit 34 - cannot stat device 403 */ 404 405 void 406 check_sanity(filename) 407 char *filename; 408 { 409 struct stat64 stbd, stbr; 410 struct ustat usb; 411 char *devname; 412 char vfsfilename[MAXPATHLEN]; 413 struct vfstab vfsbuf; 414 FILE *vfstab; 415 struct statvfs vfs_stat; 416 int is_root = 0; 417 int is_usr = 0; 418 int is_block = 0; 419 420 if (stat64(filename, &stbd) < 0) { 421 fprintf(stderr, 422 "ufs fsck: sanity check failed : cannot stat %s\n", filename); 423 exit(34); 424 } 425 426 if ((stbd.st_mode & S_IFMT) == S_IFBLK) 427 is_block = 1; 428 else if ((stbd.st_mode & S_IFMT) == S_IFCHR) 429 is_block = 0; 430 else { 431 fprintf(stderr, 432 "ufs fsck: sanity check failed: %s not block or character device\n", 433 filename); 434 exit(34); 435 } 436 437 /* 438 * Determine if this is the root file system via vfstab. Give up 439 * silently on failures. The whole point of this is not to care 440 * if the root file system is already mounted. 441 * 442 * XXX - similar for /usr. This should be fixed to simply return 443 * a new code indicating, mounted and needs to be checked. 444 */ 445 if ((vfstab = fopen(VFSTAB, "r")) != 0) { 446 if (getvfsfile(vfstab, &vfsbuf, "/") == 0) { 447 if (is_block) 448 devname = vfsbuf.vfs_special; 449 else 450 devname = vfsbuf.vfs_fsckdev; 451 if (stat64(devname, &stbr) == 0) 452 if (stbr.st_rdev == stbd.st_rdev) 453 is_root = 1; 454 } 455 if (getvfsfile(vfstab, &vfsbuf, "/usr") == 0) { 456 if (is_block) 457 devname = vfsbuf.vfs_special; 458 else 459 devname = vfsbuf.vfs_fsckdev; 460 if (stat64(devname, &stbr) == 0) 461 if (stbr.st_rdev == stbd.st_rdev) 462 is_usr = 1; 463 } 464 } 465 466 467 /* 468 * XXX - only works if filename is a block device or if 469 * character and block device has the same dev_t value 470 */ 471 if (is_root == 0 && is_usr == 0 && ustat(stbd.st_rdev, &usb) == 0) { 472 fprintf(stderr, "ufs fsck: sanity check: %s already mounted\n", 473 filename); 474 exit(33); 475 } 476 477 if (is_root || is_usr) { 478 if (is_root) 479 strcpy(vfsfilename, "/"); 480 else 481 strcpy(vfsfilename, "/usr"); 482 if (statvfs(vfsfilename, &vfs_stat) != 0) { 483 fprintf(stderr, 484 "ufs fsck: Cannot stat %s\n", 485 vfsfilename); 486 exit(34); 487 } 488 489 if (!(vfs_stat.f_flag & ST_RDONLY)) { 490 /* 491 * The file system is mounted read/write 492 * We need to exit saying that / or /usr is 493 * already mounted read/write. If it's only 494 * mounted readonly, we can continue. 495 */ 496 497 fprintf(stderr, 498 "ufs fsck: sanity check:" 499 "%s already mounted read/write\n", 500 filename); 501 exit(33); 502 } 503 } 504 505 /* 506 * We mount the ufs root file system read-only first. After fsck 507 * runs, we remount the root as read-write. Therefore, we no longer 508 * check for different values for fs_state between the root file 509 * system and the rest of file systems. 510 */ 511 if (islog && !islogok) { 512 fprintf(stderr, "ufs fsck: sanity check: %s needs checking\n", 513 filename); 514 exit(32); 515 } 516 if ((sblock.fs_state + (long)sblock.fs_time == FSOKAY) && 517 (sblock.fs_clean == FSCLEAN || sblock.fs_clean == FSSTABLE || 518 (sblock.fs_clean == FSLOG && islog))) { 519 fprintf(stderr, "ufs fsck: sanity check: %s okay\n", filename); 520 } else { 521 fprintf(stderr, "ufs fsck: sanity check: %s needs checking\n", 522 filename); 523 exit(32); 524 } 525 exit(0); 526 } 527 528 char * 529 unrawname(name) 530 char *name; 531 { 532 char *dp; 533 534 extern char *getfullblkname(); 535 536 if ((dp = getfullblkname(name)) == NULL) 537 return (""); 538 return (dp); 539 } 540 541 char * 542 rawname(name) 543 char *name; 544 { 545 char *dp; 546 547 extern char *getfullrawname(); 548 549 if ((dp = getfullrawname(name)) == NULL) 550 return (""); 551 return (dp); 552 } 553 554 char * 555 hasvfsopt(vfs, opt) 556 struct vfstab *vfs; 557 char *opt; 558 { 559 char *f, *opts; 560 static char *tmpopts; 561 562 if (vfs->vfs_mntopts == NULL) 563 return (NULL); 564 if (tmpopts == 0) { 565 tmpopts = (char *)calloc(256, sizeof (char)); 566 if (tmpopts == 0) 567 return (0); 568 } 569 strncpy(tmpopts, vfs->vfs_mntopts, (sizeof (tmpopts) - 1)); 570 opts = tmpopts; 571 f = mntopt(&opts); 572 for (; *f; f = mntopt(&opts)) { 573 if (strncmp(opt, f, strlen(opt)) == 0) 574 return (f - tmpopts + vfs->vfs_mntopts); 575 } 576 return (NULL); 577 } 578 579 580 void 581 usage() 582 { 583 (void) fprintf(stderr, 584 "ufs usage: fsck [-F ufs] [generic options] [-o p,b=#,c,w] [special ....]\n"); 585 exit(31+1); 586 } 587