1 /* $NetBSD: main.c,v 1.85 2019/05/05 14:59:06 christos 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.85 2019/05/05 14:59:06 christos 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 <fstab.h> 60 #include <string.h> 61 #include <time.h> 62 #include <stdio.h> 63 #include <stdlib.h> 64 #include <unistd.h> 65 #include <signal.h> 66 67 #include "fsck.h" 68 #include "extern.h" 69 #include "fsutil.h" 70 #include "exitvalues.h" 71 #include "snapshot.h" 72 73 int progress = 0; 74 volatile sig_atomic_t returntosingle = 0; 75 76 static int argtoi(int, const char *, const char *, int); 77 static int checkfilesys(const char *, const char *, int); 78 __dead static void usage(void); 79 80 int 81 main(int argc, char *argv[]) 82 { 83 struct rlimit r; 84 int ch; 85 int ret = FSCK_EXIT_OK; 86 char *snap_backup = NULL; 87 int snap_internal = 0; 88 89 ckfinish = ckfini; 90 91 if (getrlimit(RLIMIT_DATA, &r) == 0) { 92 r.rlim_cur = r.rlim_max; 93 (void) setrlimit(RLIMIT_DATA, &r); 94 } 95 sync(); 96 skipclean = 1; 97 markclean = 1; 98 forceimage = 0; 99 #ifndef NO_FFS_EI 100 endian = 0; 101 #endif 102 #ifndef NO_APPLE_UFS 103 isappleufs = 0; 104 #endif 105 while ((ch = getopt(argc, argv, "aB:b:c:dFfm:npPqUyx:Xz")) != -1) { 106 switch (ch) { 107 #ifndef NO_APPLE_UFS 108 case 'a': 109 isappleufs = 1; 110 break; 111 #endif 112 113 #ifndef NO_FFS_EI 114 case 'B': 115 if (strcmp(optarg, "be") == 0) 116 endian = BIG_ENDIAN; 117 else if (strcmp(optarg, "le") == 0) 118 endian = LITTLE_ENDIAN; 119 else 120 usage(); 121 break; 122 #endif 123 124 case 'b': 125 skipclean = 0; 126 bflag = argtoi('b', "number", optarg, 10); 127 printf("Alternate super block location: %d\n", bflag); 128 break; 129 130 case 'c': 131 skipclean = 0; 132 cvtlevel = argtoi('c', "conversion level", optarg, 10); 133 if (cvtlevel > 4) { 134 cvtlevel = 4; 135 warnx("Using maximum conversion level of %d", 136 cvtlevel); 137 } 138 break; 139 140 case 'd': 141 debug++; 142 break; 143 144 case 'F': 145 forceimage = 1; 146 break; 147 148 case 'f': 149 skipclean = 0; 150 break; 151 152 case 'm': 153 lfmode = argtoi('m', "mode", optarg, 8); 154 if (lfmode &~ 07777) 155 errx(FSCK_EXIT_USAGE, "bad mode to -m: %o", 156 lfmode); 157 printf("** lost+found creation mode %o\n", lfmode); 158 break; 159 160 case 'n': 161 nflag++; 162 yflag = 0; 163 break; 164 165 case 'p': 166 preen++; 167 break; 168 169 case 'P': 170 progress = 1; 171 break; 172 173 case 'q': 174 quiet++; 175 break; 176 #ifndef SMALL 177 case 'U': 178 Uflag++; 179 break; 180 #endif 181 182 case 'x': 183 snap_backup = optarg; 184 break; 185 186 case 'X': 187 snap_internal = 1; 188 break; 189 190 case 'y': 191 yflag++; 192 nflag = 0; 193 break; 194 195 case 'z': 196 zflag++; 197 break; 198 199 default: 200 usage(); 201 } 202 } 203 204 if (snap_backup || snap_internal) { 205 if (!nflag || yflag) { 206 warnx("Cannot use -x or -X without -n"); 207 snap_backup = NULL; 208 snap_internal = 0; 209 } 210 } 211 212 213 argc -= optind; 214 argv += optind; 215 216 if (!argc) 217 usage(); 218 219 if (debug) 220 progress = 0; 221 222 if (signal(SIGINT, SIG_IGN) != SIG_IGN) 223 (void)signal(SIGINT, catch); 224 if (preen) 225 (void)signal(SIGQUIT, catchquit); 226 #ifdef PROGRESS 227 if (progress) { 228 progress_ttywidth(0); 229 (void)signal(SIGWINCH, progress_ttywidth); 230 } 231 #endif /* ! PROGRESS */ 232 signal(SIGINFO, infohandler); 233 234 while (argc-- > 0) { 235 int nret; 236 char *path; 237 238 if (!forceimage) 239 path = strdup(blockcheck(*argv)); 240 else 241 path = strdup(*argv); 242 243 if (path == NULL) 244 pfatal("Can't check %s\n", *argv); 245 246 if (snap_backup || snap_internal) { 247 char *snap_dev; 248 int snapfd; 249 250 snapfd = snap_open(*argv, snap_backup, NULL, &snap_dev); 251 if (snapfd < 0) { 252 warn("can't take snapshot of %s", *argv); 253 goto next; 254 } 255 nret = checkfilesys(blockcheck(snap_dev), path, 0); 256 if (ret < nret) 257 ret = nret; 258 close(snapfd); 259 } else { 260 nret = checkfilesys(path, path, 0); 261 if (ret < nret) 262 ret = nret; 263 } 264 next: 265 free(path); 266 argv++; 267 } 268 269 return returntosingle ? FSCK_EXIT_UNRESOLVED : ret; 270 } 271 272 static int 273 argtoi(int flag, const char *req, const char *str, int base) 274 { 275 char *cp; 276 int ret; 277 278 ret = (int)strtol(str, &cp, base); 279 if (cp == str || *cp) 280 errx(FSCK_EXIT_USAGE, "-%c flag requires a %s", 281 flag, req); 282 return (ret); 283 } 284 285 /* 286 * Check the specified filesystem. 287 */ 288 /* ARGSUSED */ 289 static int 290 checkfilesys(const char *filesys, const char *origfs, int child) 291 { 292 daddr_t n_ffree, n_bfree; 293 struct dups *dp; 294 struct zlncnt *zlnp; 295 int cylno; 296 #ifdef LITE2BORKEN 297 int flags; 298 #endif 299 #ifdef PROGRESS 300 /* 301 * In prune mode, how far does the progress bar travel during 302 * each pass? (In non-prune mode, each pass has a separate 303 * progress bar that travels from 0 to 100%.) 304 * 305 * The numbers below are percentages, intended to correspond 306 * roughly to the cumulative time up to the end of each pass. 307 * They don't have to be accurate. In reality, on a large 308 * file system, Pass 1 and Pass 2 together are likely to use 309 * significantly more than the 95% reflected below, so users 310 * will get a pleasant surprise when the last 5% of the progress 311 * bar runs more quickly than they had expected. 312 */ 313 static int progress_limits[] = {0, 20, 95, 96, 97, 100}; 314 #endif /* PROGRESS */ 315 316 if (preen && child) 317 (void)signal(SIGQUIT, voidquit); 318 setcdevname(filesys, preen); 319 if (debug && preen) 320 pwarn("starting\n"); 321 switch (setup(filesys, origfs)) { 322 case 0: 323 if (preen) 324 pfatal("CAN'T CHECK FILE SYSTEM."); 325 /* fall through */ 326 case -1: 327 return FSCK_EXIT_OK; 328 } 329 /* 330 * Cleared if any questions answered no. Used to decide if 331 * the superblock should be marked clean. 332 */ 333 resolved = 1; 334 335 #ifdef PROGRESS 336 progress_switch(progress); 337 progress_init(); 338 #endif /* PROGRESS */ 339 340 /* 341 * 1: scan inodes tallying blocks used 342 */ 343 if (preen == 0) { 344 pwarn("** Last Mounted on %s\n", sblock->fs_fsmnt); 345 if (hotroot()) 346 pwarn("** Root file system\n"); 347 pwarn("** Phase 1 - Check Blocks and Sizes\n"); 348 } 349 #ifdef PROGRESS 350 if (preen) 351 progress_setrange(0, progress_limits[1]); 352 #endif /* PROGRESS */ 353 pass1(); 354 355 /* 356 * 1b: locate first references to duplicates, if any 357 */ 358 if (duplist) { 359 if (preen) 360 pfatal("INTERNAL ERROR: dups with -p\n"); 361 if (usedsoftdep) 362 pfatal("INTERNAL ERROR: dups with softdep\n"); 363 pwarn("** Phase 1b - Rescan For More DUPS\n"); 364 pass1b(); 365 } 366 367 /* 368 * 2: traverse directories from root to mark all connected directories 369 */ 370 if (preen == 0) 371 pwarn("** Phase 2 - Check Pathnames\n"); 372 #ifdef PROGRESS 373 if (preen) 374 progress_sethighlim(progress_limits[2]); 375 #endif /* PROGRESS */ 376 pass2(); 377 378 /* 379 * 3: scan inodes looking for disconnected directories 380 */ 381 if (preen == 0) 382 pwarn("** Phase 3 - Check Connectivity\n"); 383 #ifdef PROGRESS 384 if (preen) 385 progress_sethighlim(progress_limits[3]); 386 #endif /* PROGRESS */ 387 pass3(); 388 389 /* 390 * 4: scan inodes looking for disconnected files; check reference counts 391 */ 392 if (preen == 0) 393 pwarn("** Phase 4 - Check Reference Counts\n"); 394 #ifdef PROGRESS 395 if (preen) 396 progress_sethighlim(progress_limits[4]); 397 #endif /* PROGRESS */ 398 pass4(); 399 400 /* 401 * 5: check and repair resource counts in cylinder groups 402 */ 403 if (preen == 0) 404 pwarn("** Phase 5 - Check Cyl groups\n"); 405 #ifdef PROGRESS 406 if (preen) 407 progress_sethighlim(progress_limits[5]); 408 #endif /* PROGRESS */ 409 pass5(); 410 if (uquot_user_hash != NULL) { 411 if (preen == 0) 412 pwarn("** Phase 6 - Check Quotas\n"); 413 pass6(); 414 } 415 416 /* 417 * print out summary statistics 418 */ 419 n_ffree = sblock->fs_cstotal.cs_nffree; 420 n_bfree = sblock->fs_cstotal.cs_nbfree; 421 pwarn("%llu files, %lld used, %lld free ", 422 (unsigned long long)n_files, (long long)n_blks, 423 (long long)(n_ffree + sblock->fs_frag * n_bfree)); 424 printf("(%lld frags, %lld blocks, %lld.%lld%% fragmentation)\n", 425 (long long)n_ffree, (long long)n_bfree, 426 (long long)(n_ffree * 100 / (daddr_t)sblock->fs_dsize), 427 (long long)(((n_ffree * 1000 + (daddr_t)sblock->fs_dsize / 2) 428 / (daddr_t)sblock->fs_dsize) % 10)); 429 if (debug && 430 (n_files -= maxino - UFS_ROOTINO - sblock->fs_cstotal.cs_nifree)) 431 printf("%llu files missing\n", (unsigned long long)n_files); 432 if (debug) { 433 n_blks += sblock->fs_ncg * 434 (cgdmin(sblock, 0) - cgsblock(sblock, 0)); 435 n_blks += cgsblock(sblock, 0) - cgbase(sblock, 0); 436 n_blks += howmany(sblock->fs_cssize, sblock->fs_fsize); 437 if (n_blks -= maxfsblock - (n_ffree + sblock->fs_frag * n_bfree)) 438 printf("%lld blocks missing\n", (long long)n_blks); 439 if (duplist != NULL) { 440 printf("The following duplicate blocks remain:"); 441 for (dp = duplist; dp; dp = dp->next) 442 printf(" %lld,", (long long)dp->dup); 443 printf("\n"); 444 } 445 if (zlnhead != NULL) { 446 printf("The following zero link count inodes remain:"); 447 for (zlnp = zlnhead; zlnp; zlnp = zlnp->next) 448 printf(" %llu,", 449 (unsigned long long)zlnp->zlncnt); 450 printf("\n"); 451 } 452 } 453 zlnhead = (struct zlncnt *)0; 454 duplist = (struct dups *)0; 455 muldup = (struct dups *)0; 456 inocleanup(); 457 if (fsmodified) { 458 sblock->fs_time = time(NULL); 459 sbdirty(); 460 } 461 if (rerun) 462 markclean = 0; 463 #if LITE2BORKEN 464 if (!hotroot()) { 465 ckfini(1); 466 } else { 467 struct statvfs stfs_buf; 468 /* 469 * Check to see if root is mounted read-write. 470 */ 471 if (statvfs("/", &stfs_buf) == 0) 472 flags = stfs_buf.f_flag; 473 else 474 flags = 0; 475 if (markclean) 476 markclean = flags & MNT_RDONLY; 477 ckfini(1); 478 } 479 #else 480 ckfini(1); 481 #endif 482 for (cylno = 0; cylno < sblock->fs_ncg; cylno++) 483 if (inostathead[cylno].il_stat != NULL) 484 free(inostathead[cylno].il_stat); 485 free(inostathead); 486 inostathead = NULL; 487 488 if (!resolved || rerun) { 489 pwarn("\n***** UNRESOLVED INCONSISTENCIES REMAIN *****\n"); 490 returntosingle = 1; 491 } 492 if (!fsmodified) 493 return FSCK_EXIT_OK; 494 if (!preen) 495 pwarn("\n***** FILE SYSTEM WAS MODIFIED *****\n"); 496 if (rerun) 497 pwarn("\n***** PLEASE RERUN FSCK *****\n"); 498 if (hotroot()) { 499 struct statvfs stfs_buf; 500 /* 501 * We modified the root. Do a mount update on 502 * it, unless it is read-write, so we can continue. 503 */ 504 if (statvfs("/", &stfs_buf) == 0) { 505 long flags = stfs_buf.f_flag; 506 struct ufs_args args; 507 508 if (flags & MNT_RDONLY) { 509 args.fspec = 0; 510 flags |= MNT_UPDATE | MNT_RELOAD; 511 if (mount(MOUNT_FFS, "/", flags, 512 &args, sizeof args) == 0) 513 return FSCK_EXIT_OK; 514 } 515 } 516 if (!preen) 517 pwarn("\n***** REBOOT NOW *****\n"); 518 sync(); 519 return FSCK_EXIT_ROOT_CHANGED; 520 } 521 return FSCK_EXIT_OK; 522 } 523 524 static void 525 usage(void) 526 { 527 528 (void) fprintf(stderr, 529 "usage: %s [-" 530 #ifndef NO_APPLE_UFS 531 "a" 532 #endif 533 "dFfPpqUX] " 534 #ifndef NO_FFS_EI 535 "[-B byteorder] " 536 #endif 537 "[-b block] [-c level] [-m mode]\n" 538 "\t[-x snap-backup] [-y | -n] filesystem ...\n", 539 getprogname()); 540 exit(FSCK_EXIT_USAGE); 541 } 542