/* $NetBSD: main.c,v 1.90.2.1 2023/05/13 11:51:14 martin Exp $ */ /* * Copyright (c) 1980, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #ifndef lint __COPYRIGHT("@(#) Copyright (c) 1980, 1986, 1993\ The Regents of the University of California. All rights reserved."); #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/14/95"; #else __RCSID("$NetBSD: main.c,v 1.90.2.1 2023/05/13 11:51:14 martin Exp $"); #endif #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fsck.h" #include "extern.h" #include "fsutil.h" #include "exitvalues.h" #include "snapshot.h" struct bufarea bufhead; struct bufarea sblk; struct bufarea asblk; struct bufarea cgblk; struct bufarea appleufsblk; struct bufarea *pdirbp; struct bufarea *pbp; struct fs *sblock; struct fs *altsblock; struct cg *cgrp; struct fs *sblocksave; struct dups *duplist; struct dups *muldup; struct zlncnt *zlnhead; struct inoinfo **inphead, **inpsort; long numdirs, dirhash, listmax, inplast; struct uquot_hash *uquot_user_hash; struct uquot_hash *uquot_group_hash; uint8_t q2h_hash_shift; uint16_t q2h_hash_mask; struct inostatlist *inostathead; long dev_bsize; long secsize; char nflag; char yflag; int Uflag; int bflag; int debug; int zflag; int cvtlevel; int doinglevel1; int doinglevel2; int doing2ea; int doing2noea; int newinofmt; char usedsoftdep; int preen; int forceimage; int is_ufs2; int is_ufs2ea; int markclean; char havesb; char skipclean; int fsmodified; int fsreadfd; int fswritefd; int rerun; char resolved; #ifndef NO_FFS_EI int endian; int doswap; int needswap; int do_blkswap; int do_dirswap; #endif #ifndef NO_APPLE_UFS int isappleufs; #endif daddr_t maxfsblock; char *blockmap; ino_t maxino; int dirblksiz; daddr_t n_blks; ino_t n_files; long countdirs; int got_siginfo; struct ufs1_dinode ufs1_zino; struct ufs2_dinode ufs2_zino; int progress = 0; static int argtoi(int, const char *, const char *, int); static int checkfilesys(const char *, const char *, int); __dead static void usage(void); int main(int argc, char *argv[]) { struct rlimit r; int ch; int ret = FSCK_EXIT_OK; char *snap_backup = NULL; int snap_internal = 0; ckfinish = ckfini; if (getrlimit(RLIMIT_DATA, &r) == 0) { r.rlim_cur = r.rlim_max; (void) setrlimit(RLIMIT_DATA, &r); } sync(); skipclean = 1; markclean = 1; forceimage = 0; #ifndef NO_FFS_EI endian = 0; #endif #ifndef NO_APPLE_UFS isappleufs = 0; #endif while ((ch = getopt(argc, argv, "aB:b:c:dFfm:npPqUyx:Xz")) != -1) { switch (ch) { #ifndef NO_APPLE_UFS case 'a': isappleufs = 1; break; #endif #ifndef NO_FFS_EI case 'B': if (strcmp(optarg, "be") == 0) endian = BIG_ENDIAN; else if (strcmp(optarg, "le") == 0) endian = LITTLE_ENDIAN; else usage(); break; #endif case 'b': skipclean = 0; bflag = argtoi('b', "number", optarg, 10); printf("Alternate super block location: %d\n", bflag); break; case 'c': skipclean = 0; if (strcmp(optarg, "ea") == 0) { doing2ea = 1; break; } if (strcmp(optarg, "no-ea") == 0) { doing2noea = 1; break; } cvtlevel = argtoi('c', "conversion level", optarg, 10); if (cvtlevel > 4) { cvtlevel = 4; warnx("Using maximum conversion level of %d", cvtlevel); } break; case 'd': debug++; break; case 'F': forceimage = 1; break; case 'f': skipclean = 0; break; case 'm': lfmode = argtoi('m', "mode", optarg, 8); if (lfmode &~ 07777) errx(FSCK_EXIT_USAGE, "bad mode to -m: %o", lfmode); printf("** lost+found creation mode %o\n", lfmode); break; case 'n': nflag++; yflag = 0; break; case 'p': preen++; break; case 'P': progress = 1; break; case 'q': quiet++; break; #ifndef SMALL case 'U': Uflag++; break; #endif case 'x': snap_backup = optarg; break; case 'X': snap_internal = 1; break; case 'y': yflag++; nflag = 0; break; case 'z': zflag++; break; default: usage(); } } if (snap_backup || snap_internal) { if (!nflag || yflag) { warnx("Cannot use -x or -X without -n"); snap_backup = NULL; snap_internal = 0; } } argc -= optind; argv += optind; if (!argc) usage(); if (debug) progress = 0; if (signal(SIGINT, SIG_IGN) != SIG_IGN) (void)signal(SIGINT, catch); if (preen) (void)signal(SIGQUIT, catchquit); #ifdef PROGRESS if (progress) { progress_ttywidth(0); (void)signal(SIGWINCH, progress_ttywidth); } #endif /* ! PROGRESS */ signal(SIGINFO, infohandler); while (argc-- > 0) { int nret; char *path; if (!forceimage) path = strdup(blockcheck(*argv)); else path = strdup(*argv); if (path == NULL) pfatal("Can't check %s\n", *argv); if (snap_backup || snap_internal) { char *snap_dev; int snapfd; snapfd = snap_open(*argv, snap_backup, NULL, &snap_dev); if (snapfd < 0) { warn("can't take snapshot of %s", *argv); goto next; } nret = checkfilesys(blockcheck(snap_dev), path, 0); if (ret < nret) ret = nret; close(snapfd); } else { nret = checkfilesys(path, path, 0); if (ret < nret) ret = nret; } next: free(path); argv++; } return returntosingle ? FSCK_EXIT_UNRESOLVED : ret; } static int argtoi(int flag, const char *req, const char *str, int base) { char *cp; int ret; ret = (int)strtol(str, &cp, base); if (cp == str || *cp) errx(FSCK_EXIT_USAGE, "-%c flag requires a %s", flag, req); return (ret); } /* * Check the specified filesystem. */ /* ARGSUSED */ static int checkfilesys(const char *filesys, const char *origfs, int child) { daddr_t n_ffree, n_bfree; struct dups *dp; struct zlncnt *zlnp; uint32_t cylno; #ifdef LITE2BORKEN int flags; #endif #ifdef PROGRESS /* * In prune mode, how far does the progress bar travel during * each pass? (In non-prune mode, each pass has a separate * progress bar that travels from 0 to 100%.) * * The numbers below are percentages, intended to correspond * roughly to the cumulative time up to the end of each pass. * They don't have to be accurate. In reality, on a large * file system, Pass 1 and Pass 2 together are likely to use * significantly more than the 95% reflected below, so users * will get a pleasant surprise when the last 5% of the progress * bar runs more quickly than they had expected. */ static int progress_limits[] = {0, 20, 95, 96, 97, 100}; #endif /* PROGRESS */ if (preen && child) (void)signal(SIGQUIT, voidquit); setcdevname(filesys, preen); if (debug && preen) pwarn("starting\n"); switch (setup(filesys, origfs)) { case 0: if (preen) pfatal("CAN'T CHECK FILE SYSTEM."); /* fall through */ case -1: return FSCK_EXIT_OK; } /* * Cleared if any questions answered no. Used to decide if * the superblock should be marked clean. */ resolved = 1; #ifdef PROGRESS progress_switch(progress); progress_init(); #endif /* PROGRESS */ /* * 1: scan inodes tallying blocks used */ if (preen == 0) { pwarn("** Last Mounted on %s\n", sblock->fs_fsmnt); if (hotroot()) pwarn("** Root file system\n"); pwarn("** Phase 1 - Check Blocks and Sizes\n"); } #ifdef PROGRESS if (preen) progress_setrange(0, progress_limits[1]); #endif /* PROGRESS */ pass1(); /* * 1b: locate first references to duplicates, if any */ if (duplist) { if (preen) pfatal("INTERNAL ERROR: dups with -p\n"); if (usedsoftdep) pfatal("INTERNAL ERROR: dups with softdep\n"); pwarn("** Phase 1b - Rescan For More DUPS\n"); pass1b(); } /* * 2: traverse directories from root to mark all connected directories */ if (preen == 0) pwarn("** Phase 2 - Check Pathnames\n"); #ifdef PROGRESS if (preen) progress_sethighlim(progress_limits[2]); #endif /* PROGRESS */ pass2(); /* * 3: scan inodes looking for disconnected directories */ if (preen == 0) pwarn("** Phase 3 - Check Connectivity\n"); #ifdef PROGRESS if (preen) progress_sethighlim(progress_limits[3]); #endif /* PROGRESS */ pass3(); /* * 4: scan inodes looking for disconnected files; check reference counts */ if (preen == 0) pwarn("** Phase 4 - Check Reference Counts\n"); #ifdef PROGRESS if (preen) progress_sethighlim(progress_limits[4]); #endif /* PROGRESS */ pass4(); /* * 5: check and repair resource counts in cylinder groups */ if (preen == 0) pwarn("** Phase 5 - Check Cyl groups\n"); #ifdef PROGRESS if (preen) progress_sethighlim(progress_limits[5]); #endif /* PROGRESS */ pass5(); if (uquot_user_hash != NULL) { if (preen == 0) pwarn("** Phase 6 - Check Quotas\n"); pass6(); } /* * print out summary statistics */ n_ffree = sblock->fs_cstotal.cs_nffree; n_bfree = sblock->fs_cstotal.cs_nbfree; pwarn("%llu files, %lld used, %lld free ", (unsigned long long)n_files, (long long)n_blks, (long long)(n_ffree + sblock->fs_frag * n_bfree)); printf("(%lld frags, %lld blocks, %lld.%lld%% fragmentation)\n", (long long)n_ffree, (long long)n_bfree, (long long)(n_ffree * 100 / (daddr_t)sblock->fs_dsize), (long long)(((n_ffree * 1000 + (daddr_t)sblock->fs_dsize / 2) / (daddr_t)sblock->fs_dsize) % 10)); if (debug && (n_files -= maxino - UFS_ROOTINO - sblock->fs_cstotal.cs_nifree)) printf("%llu files missing\n", (unsigned long long)n_files); if (debug) { n_blks += sblock->fs_ncg * (cgdmin(sblock, 0) - cgsblock(sblock, 0)); n_blks += cgsblock(sblock, 0) - cgbase(sblock, 0); n_blks += howmany(sblock->fs_cssize, sblock->fs_fsize); if (n_blks -= maxfsblock - (n_ffree + sblock->fs_frag * n_bfree)) printf("%lld blocks missing\n", (long long)n_blks); if (duplist != NULL) { printf("The following duplicate blocks remain:"); for (dp = duplist; dp; dp = dp->next) printf(" %lld,", (long long)dp->dup); printf("\n"); } if (zlnhead != NULL) { printf("The following zero link count inodes remain:"); for (zlnp = zlnhead; zlnp; zlnp = zlnp->next) printf(" %llu,", (unsigned long long)zlnp->zlncnt); printf("\n"); } } zlnhead = (struct zlncnt *)0; duplist = (struct dups *)0; muldup = (struct dups *)0; inocleanup(); if (fsmodified) { sblock->fs_time = time(NULL); sbdirty(); } if (rerun) markclean = 0; #if LITE2BORKEN if (!hotroot()) { ckfini(1); } else { struct statvfs stfs_buf; /* * Check to see if root is mounted read-write. */ if (statvfs("/", &stfs_buf) == 0) flags = stfs_buf.f_flag; else flags = 0; if (markclean) markclean = flags & MNT_RDONLY; ckfini(1); } #else ckfini(1); #endif for (cylno = 0; cylno < sblock->fs_ncg; cylno++) if (inostathead[cylno].il_stat != NULL) free(inostathead[cylno].il_stat); free(inostathead); inostathead = NULL; if (!resolved || rerun) { pwarn("\n***** UNRESOLVED INCONSISTENCIES REMAIN *****\n"); returntosingle = 1; } if (!fsmodified) return FSCK_EXIT_OK; if (!preen) pwarn("\n***** FILE SYSTEM WAS MODIFIED *****\n"); if (rerun) pwarn("\n***** PLEASE RERUN FSCK *****\n"); if (hotroot()) { struct statvfs stfs_buf; /* * We modified the root. Do a mount update on * it, unless it is read-write, so we can continue. */ if (statvfs("/", &stfs_buf) == 0) { long flags = stfs_buf.f_flag; struct ufs_args args; if (flags & MNT_RDONLY) { args.fspec = 0; flags |= MNT_UPDATE | MNT_RELOAD; if (mount(MOUNT_FFS, "/", flags, &args, sizeof args) == 0) return FSCK_EXIT_OK; } } if (!preen) pwarn("\n***** REBOOT NOW *****\n"); sync(); return FSCK_EXIT_ROOT_CHANGED; } return FSCK_EXIT_OK; } static void usage(void) { (void) fprintf(stderr, "usage: %s [-" #ifndef NO_APPLE_UFS "a" #endif "dFfPpqUX] " #ifndef NO_FFS_EI "[-B byteorder] " #endif "[-b block] [-c level] [-m mode]\n" "\t[-x snap-backup] [-y | -n] filesystem ...\n", getprogname()); exit(FSCK_EXIT_USAGE); }