1 /* $NetBSD: main.c,v 1.93 2025/12/18 08:21:44 nia Exp $ */ 2 3 /* 4 * Copyright (c) 1980, 1986, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 __COPYRIGHT("@(#) Copyright (c) 1980, 1986, 1993\ 35 The Regents of the University of California. All rights reserved."); 36 #endif /* not lint */ 37 38 #ifndef lint 39 #if 0 40 static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/14/95"; 41 #else 42 __RCSID("$NetBSD: main.c,v 1.93 2025/12/18 08:21:44 nia Exp $"); 43 #endif 44 #endif /* not lint */ 45 46 #include <sys/param.h> 47 #include <sys/time.h> 48 #include <sys/mount.h> 49 #include <sys/resource.h> 50 51 #include <ufs/ufs/dinode.h> 52 #include <ufs/ufs/ufsmount.h> 53 #include <ufs/ffs/fs.h> 54 #include <ufs/ffs/ffs_extern.h> 55 56 #include <ctype.h> 57 #include <err.h> 58 #include <errno.h> 59 #include <endian.h> 60 #include <fstab.h> 61 #include <string.h> 62 #include <time.h> 63 #include <stdio.h> 64 #include <stdlib.h> 65 #include <unistd.h> 66 #include <signal.h> 67 68 #include "fsck.h" 69 #include "extern.h" 70 #include "fsutil.h" 71 #include "exitvalues.h" 72 #include "snapshot.h" 73 74 struct bufarea bufhead; 75 struct bufarea sblk; 76 struct bufarea asblk; 77 struct bufarea cgblk; 78 struct bufarea appleufsblk; 79 struct bufarea *pdirbp; 80 struct bufarea *pbp; 81 struct fs *sblock; 82 struct fs *altsblock; 83 struct cg *cgrp; 84 struct fs *sblocksave; 85 struct dups *duplist; 86 struct dups *muldup; 87 struct zlncnt *zlnhead; 88 struct inoinfo **inphead, **inpsort; 89 long numdirs, dirhash, listmax, inplast; 90 struct uquot_hash *uquot_user_hash; 91 struct uquot_hash *uquot_group_hash; 92 uint8_t q2h_hash_shift; 93 uint16_t q2h_hash_mask; 94 struct inostatlist *inostathead; 95 long dev_bsize; 96 long secsize; 97 char nflag; 98 char yflag; 99 int Uflag; 100 int bflag; 101 int debug; 102 int zflag; 103 int cvtlevel; 104 int doinglevel1; 105 int doinglevel2; 106 int doing2ea; 107 int doing2noea; 108 int newinofmt; 109 char usedsoftdep; 110 int preen; 111 int forceimage; 112 int is_ufs2; 113 int is_ufs2ea; 114 int markclean; 115 char havesb; 116 char skipclean; 117 int fsmodified; 118 int fsreadfd; 119 int fswritefd; 120 int rerun; 121 char resolved; 122 #ifndef NO_FFS_EI 123 int endian; 124 int doswap; 125 int needswap; 126 int do_blkswap; 127 int do_dirswap; 128 #endif 129 #ifndef NO_APPLE_UFS 130 int isappleufs; 131 #endif 132 daddr_t maxfsblock; 133 char *blockmap; 134 ino_t maxino; 135 int dirblksiz; 136 daddr_t n_blks; 137 ino_t n_files; 138 long countdirs; 139 int got_siginfo; 140 struct ufs1_dinode ufs1_zino; 141 struct ufs2_dinode ufs2_zino; 142 143 int progress = 0; 144 145 static int argtoi(int, const char *, const char *, int); 146 static int checkfilesys(const char *, const char *, int); 147 __dead static void usage(void); 148 149 int 150 main(int argc, char *argv[]) 151 { 152 struct rlimit r; 153 int ch; 154 int ret = FSCK_EXIT_OK; 155 char *snap_backup = NULL; 156 int snap_internal = 0; 157 158 ckfinish = ckfini; 159 160 if (getrlimit(RLIMIT_DATA, &r) == 0) { 161 r.rlim_cur = r.rlim_max; 162 (void) setrlimit(RLIMIT_DATA, &r); 163 } 164 sync(); 165 skipclean = 1; 166 markclean = 1; 167 forceimage = 0; 168 #ifndef NO_FFS_EI 169 endian = 0; 170 #endif 171 #ifndef NO_APPLE_UFS 172 isappleufs = 0; 173 #endif 174 while ((ch = getopt(argc, argv, "aB:b:c:dFfm:npPqUyx:Xz")) != -1) { 175 switch (ch) { 176 #ifndef NO_APPLE_UFS 177 case 'a': 178 isappleufs = 1; 179 break; 180 #endif 181 182 #ifndef NO_FFS_EI 183 case 'B': 184 if (strcmp(optarg, "be") == 0) 185 endian = BIG_ENDIAN; 186 else if (strcmp(optarg, "le") == 0) 187 endian = LITTLE_ENDIAN; 188 else 189 usage(); 190 break; 191 #endif 192 193 case 'b': 194 skipclean = 0; 195 bflag = argtoi('b', "number", optarg, 10); 196 printf("Alternate super block location: %d\n", bflag); 197 break; 198 199 case 'c': 200 skipclean = 0; 201 if (strcmp(optarg, "ea") == 0) { 202 doing2ea = 1; 203 break; 204 } 205 if (strcmp(optarg, "no-ea") == 0) { 206 doing2noea = 1; 207 break; 208 } 209 cvtlevel = argtoi('c', "conversion level", optarg, 10); 210 if (cvtlevel > 4) { 211 cvtlevel = 4; 212 warnx("Using maximum conversion level of %d", 213 cvtlevel); 214 } 215 break; 216 217 case 'd': 218 debug++; 219 break; 220 221 case 'F': 222 forceimage = 1; 223 break; 224 225 case 'f': 226 skipclean = 0; 227 break; 228 229 case 'm': 230 lfmode = argtoi('m', "mode", optarg, 8); 231 if (lfmode &~ 07777) 232 errx(FSCK_EXIT_USAGE, "bad mode to -m: %o", 233 lfmode); 234 printf("** lost+found creation mode %o\n", lfmode); 235 break; 236 237 case 'n': 238 nflag++; 239 yflag = 0; 240 break; 241 242 case 'p': 243 preen++; 244 break; 245 246 case 'P': 247 progress = 1; 248 break; 249 250 case 'q': 251 quiet++; 252 break; 253 #ifndef SMALL 254 case 'U': 255 Uflag++; 256 break; 257 #endif 258 259 case 'x': 260 snap_backup = optarg; 261 break; 262 263 case 'X': 264 snap_internal = 1; 265 break; 266 267 case 'y': 268 yflag++; 269 nflag = 0; 270 break; 271 272 case 'z': 273 zflag++; 274 break; 275 276 default: 277 usage(); 278 } 279 } 280 281 if (snap_backup || snap_internal) { 282 if (!nflag || yflag) { 283 warnx("Cannot use -x or -X without -n"); 284 snap_backup = NULL; 285 snap_internal = 0; 286 } 287 } 288 289 argc -= optind; 290 argv += optind; 291 292 if (!argc) 293 usage(); 294 295 if (debug) 296 progress = 0; 297 298 if (signal(SIGINT, SIG_IGN) != SIG_IGN) 299 (void)signal(SIGINT, catch); 300 if (preen) 301 (void)signal(SIGQUIT, catchquit); 302 #ifdef PROGRESS 303 if (progress) { 304 progress_ttywidth(0); 305 (void)signal(SIGWINCH, progress_ttywidth); 306 } 307 #endif /* ! PROGRESS */ 308 signal(SIGINFO, infohandler); 309 310 while (argc-- > 0) { 311 int nret; 312 char *path; 313 314 if (!forceimage) 315 path = strdup(blockcheck(*argv)); 316 else 317 path = strdup(*argv); 318 319 if (path == NULL) 320 pfatal("Can't check %s\n", *argv); 321 322 if (snap_backup || snap_internal) { 323 char *snap_dev; 324 int snapfd; 325 326 snapfd = snap_open(*argv, snap_backup, NULL, &snap_dev); 327 if (snapfd < 0) { 328 warn("can't take snapshot of %s", *argv); 329 goto next; 330 } 331 nret = checkfilesys(blockcheck(snap_dev), path, 0); 332 if (ret < nret) 333 ret = nret; 334 close(snapfd); 335 } else { 336 nret = checkfilesys(path, path, 0); 337 if (ret < nret) 338 ret = nret; 339 } 340 next: 341 free(path); 342 argv++; 343 } 344 345 return returntosingle ? FSCK_EXIT_UNRESOLVED : ret; 346 } 347 348 static int 349 argtoi(int flag, const char *req, const char *str, int base) 350 { 351 char *cp; 352 int ret; 353 354 ret = (int)strtol(str, &cp, base); 355 if (cp == str || *cp) 356 errx(FSCK_EXIT_USAGE, "-%c flag requires a %s", 357 flag, req); 358 return (ret); 359 } 360 361 /* 362 * Check the specified filesystem. 363 */ 364 /* ARGSUSED */ 365 static int 366 checkfilesys(const char *filesys, const char *origfs, int child) 367 { 368 daddr_t n_ffree, n_bfree; 369 struct dups *dp; 370 struct zlncnt *zlnp; 371 uint32_t cylno; 372 #ifdef LITE2BORKEN 373 int flags; 374 #endif 375 #ifdef PROGRESS 376 /* 377 * In prune mode, how far does the progress bar travel during 378 * each pass? (In non-prune mode, each pass has a separate 379 * progress bar that travels from 0 to 100%.) 380 * 381 * The numbers below are percentages, intended to correspond 382 * roughly to the cumulative time up to the end of each pass. 383 * They don't have to be accurate. In reality, on a large 384 * file system, Pass 1 and Pass 2 together are likely to use 385 * significantly more than the 95% reflected below, so users 386 * will get a pleasant surprise when the last 5% of the progress 387 * bar runs more quickly than they had expected. 388 */ 389 static int progress_limits[] = {0, 20, 95, 96, 97, 100}; 390 #endif /* PROGRESS */ 391 392 if (preen && child) 393 (void)signal(SIGQUIT, voidquit); 394 setcdevname(filesys, preen); 395 if (debug && preen) 396 pwarn("starting\n"); 397 switch (setup(filesys, origfs)) { 398 case 0: 399 if (preen) 400 pfatal("CAN'T CHECK FILE SYSTEM."); 401 /* fall through */ 402 case -1: 403 return FSCK_EXIT_OK; 404 } 405 /* 406 * Cleared if any questions answered no. Used to decide if 407 * the superblock should be marked clean. 408 */ 409 resolved = 1; 410 411 #ifdef PROGRESS 412 progress_switch(progress); 413 progress_init(); 414 #endif /* PROGRESS */ 415 416 /* 417 * 1: scan inodes tallying blocks used 418 */ 419 if (preen == 0) { 420 pwarn("** Last Mounted on %s\n", sblock->fs_fsmnt); 421 if (hotroot()) 422 pwarn("** Root file system\n"); 423 pwarn("** Phase 1 - Check Blocks and Sizes\n"); 424 } 425 #ifdef PROGRESS 426 if (preen) 427 progress_setrange(0, progress_limits[1]); 428 #endif /* PROGRESS */ 429 pass1(); 430 431 /* 432 * 1b: locate first references to duplicates, if any 433 */ 434 if (duplist) { 435 if (preen) 436 pfatal("INTERNAL ERROR: dups with -p\n"); 437 if (usedsoftdep) 438 pfatal("INTERNAL ERROR: dups with softdep\n"); 439 pwarn("** Phase 1b - Rescan For More DUPS\n"); 440 pass1b(); 441 } 442 443 /* 444 * 2: traverse directories from root to mark all connected directories 445 */ 446 if (preen == 0) 447 pwarn("** Phase 2 - Check Pathnames\n"); 448 #ifdef PROGRESS 449 if (preen) 450 progress_sethighlim(progress_limits[2]); 451 #endif /* PROGRESS */ 452 pass2(); 453 454 /* 455 * 3: scan inodes looking for disconnected directories 456 */ 457 if (preen == 0) 458 pwarn("** Phase 3 - Check Connectivity\n"); 459 #ifdef PROGRESS 460 if (preen) 461 progress_sethighlim(progress_limits[3]); 462 #endif /* PROGRESS */ 463 pass3(); 464 465 /* 466 * 4: scan inodes looking for disconnected files; check reference counts 467 */ 468 if (preen == 0) 469 pwarn("** Phase 4 - Check Reference Counts\n"); 470 #ifdef PROGRESS 471 if (preen) 472 progress_sethighlim(progress_limits[4]); 473 #endif /* PROGRESS */ 474 pass4(); 475 476 /* 477 * 5: check and repair resource counts in cylinder groups 478 */ 479 if (preen == 0) 480 pwarn("** Phase 5 - Check Cyl groups\n"); 481 #ifdef PROGRESS 482 if (preen) 483 progress_sethighlim(progress_limits[5]); 484 #endif /* PROGRESS */ 485 pass5(); 486 if (uquot_user_hash != NULL) { 487 if (preen == 0) 488 pwarn("** Phase 6 - Check Quotas\n"); 489 pass6(); 490 } 491 492 /* 493 * print out summary statistics 494 */ 495 n_ffree = sblock->fs_cstotal.cs_nffree; 496 n_bfree = sblock->fs_cstotal.cs_nbfree; 497 pwarn("%llu files, %lld used, %lld free ", 498 (unsigned long long)n_files, (long long)n_blks, 499 (long long)(n_ffree + sblock->fs_frag * n_bfree)); 500 printf("(%lld frags, %lld blocks, %lld.%lld%% fragmentation)\n", 501 (long long)n_ffree, (long long)n_bfree, 502 (long long)(n_ffree * 100 / (daddr_t)sblock->fs_dsize), 503 (long long)(((n_ffree * 1000 + (daddr_t)sblock->fs_dsize / 2) 504 / (daddr_t)sblock->fs_dsize) % 10)); 505 if (debug && 506 (n_files -= maxino - UFS_ROOTINO - sblock->fs_cstotal.cs_nifree)) 507 printf("%llu files missing\n", (unsigned long long)n_files); 508 if (debug) { 509 n_blks += sblock->fs_ncg * 510 (cgdmin(sblock, 0) - cgsblock(sblock, 0)); 511 n_blks += cgsblock(sblock, 0) - cgbase(sblock, 0); 512 n_blks += howmany(sblock->fs_cssize, sblock->fs_fsize); 513 if (n_blks -= maxfsblock - (n_ffree + sblock->fs_frag * n_bfree)) 514 printf("%lld blocks missing\n", (long long)n_blks); 515 if (duplist != NULL) { 516 printf("The following duplicate blocks remain:"); 517 for (dp = duplist; dp; dp = dp->next) 518 printf(" %lld,", (long long)dp->dup); 519 printf("\n"); 520 } 521 if (zlnhead != NULL) { 522 printf("The following zero link count inodes remain:"); 523 for (zlnp = zlnhead; zlnp; zlnp = zlnp->next) 524 printf(" %llu,", 525 (unsigned long long)zlnp->zlncnt); 526 printf("\n"); 527 } 528 } 529 zlnhead = (struct zlncnt *)0; 530 duplist = (struct dups *)0; 531 muldup = (struct dups *)0; 532 inocleanup(); 533 if (fsmodified) { 534 sblock->fs_time = time(NULL); 535 sbdirty(); 536 } 537 if (rerun) 538 markclean = 0; 539 #if LITE2BORKEN 540 if (!hotroot()) { 541 ckfini(1); 542 } else { 543 struct statvfs stfs_buf; 544 /* 545 * Check to see if root is mounted read-write. 546 */ 547 if (statvfs("/", &stfs_buf) == 0) 548 flags = stfs_buf.f_flag; 549 else 550 flags = 0; 551 if (markclean) 552 markclean = flags & MNT_RDONLY; 553 ckfini(1); 554 } 555 #else 556 ckfini(1); 557 #endif 558 for (cylno = 0; cylno < sblock->fs_ncg; cylno++) 559 if (inostathead[cylno].il_stat != NULL) 560 free(inostathead[cylno].il_stat); 561 free(inostathead); 562 inostathead = NULL; 563 564 if (!resolved || rerun) { 565 pwarn("\n***** UNRESOLVED INCONSISTENCIES REMAIN *****\n"); 566 returntosingle = 1; 567 } 568 if (!fsmodified) 569 return FSCK_EXIT_OK; 570 if (!preen) 571 pwarn("\n***** FILE SYSTEM WAS MODIFIED *****\n"); 572 if (rerun) 573 pwarn("\n***** PLEASE RERUN FSCK *****\n"); 574 if (hotroot()) { 575 struct statvfs stfs_buf; 576 /* 577 * We modified the root. Do a mount update on 578 * it, unless it is read-write, so we can continue. 579 */ 580 if (statvfs("/", &stfs_buf) == 0) { 581 long flags = stfs_buf.f_flag; 582 struct ufs_args args; 583 584 if (flags & MNT_RDONLY) { 585 args.fspec = 0; 586 flags |= MNT_UPDATE | MNT_RELOAD; 587 if (mount(MOUNT_FFS, "/", flags, 588 &args, sizeof args) == 0) 589 return FSCK_EXIT_OK; 590 } 591 } 592 if (!preen) 593 pwarn("\n***** REBOOT NOW *****\n"); 594 sync(); 595 return FSCK_EXIT_ROOT_CHANGED; 596 } 597 return FSCK_EXIT_OK; 598 } 599 600 static void 601 usage(void) 602 { 603 604 (void) fprintf(stderr, 605 "usage: %s [-" 606 #ifndef NO_APPLE_UFS 607 "a" 608 #endif 609 "dFfPpqUX] " 610 #ifndef NO_FFS_EI 611 "[-B byteorder] " 612 #endif 613 "[-b block] [-c level] [-m mode]\n" 614 "\t[-x snap-backup] [-y | -n] filesystem ...\n", 615 getprogname()); 616 exit(FSCK_EXIT_USAGE); 617 } 618