/* * 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. * * @(#) Copyright (c) 1980, 1986, 1993 The Regents of the University of California. All rights reserved. * @(#)main.c 8.6 (Berkeley) 5/14/95 * $FreeBSD: src/sbin/fsck/main.c,v 1.21.2.1 2001/01/23 23:11:07 iedowse Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fsck.h" int returntosingle; /* 1 => return to single user mode on exit */ int lastmntonly; /* Output the last mounted on only */ static int argtoi(int flag, char *req, char *str, int base); static int docheck(struct fstab *fsp); static int checkfilesys(char *filesys, char *mntpt, long auxdata, int child); static struct statfs *getmntpt(const char *); int main(int argc, char **argv) { int ch; int ret, maxrun = 0; struct rlimit rlimit; sync(); while ((ch = getopt(argc, argv, "dfLpnNyYb:c:l:m:")) != -1) { switch (ch) { case 'p': preen++; break; case 'b': bflag = argtoi('b', "number", optarg, 10); printf("Alternate super block location: %d\n", bflag); break; case 'c': cvtlevel = argtoi('c', "conversion level", optarg, 10); break; case 'd': debug++; break; case 'f': fflag++; break; case 'l': maxrun = argtoi('l', "number", optarg, 10); break; case 'L': lastmntonly++; break; case 'm': lfmode = argtoi('m', "mode", optarg, 8); if (lfmode &~ 07777) errx(EEXIT, "bad mode to -m: %o", lfmode); printf("** lost+found creation mode %o\n", lfmode); break; case 'n': case 'N': nflag++; yflag = 0; break; case 'y': case 'Y': yflag++; nflag = 0; break; default: errx(EEXIT, "%c option?", ch); } } argc -= optind; argv += optind; if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, catch); if (preen) signal(SIGQUIT, catchquit); signal(SIGINFO, infohandler); /* * Push up our allowed memory limit so we can cope * with huge filesystems. */ if (getrlimit(RLIMIT_DATA, &rlimit) == 0) { rlimit.rlim_cur = rlimit.rlim_max; setrlimit(RLIMIT_DATA, &rlimit); } if (argc) { while (argc-- > 0) { char *path = blockcheck(*argv); if (path == NULL) pfatal("Can't check %s\n", *argv); else checkfilesys(path, 0, 0L, 0); ++argv; } exit(0); } ret = checkfstab(preen, maxrun, docheck, checkfilesys); if (returntosingle) exit(2); exit(ret); } static int argtoi(int flag, char *req, char *str, int base) { char *cp; int ret; ret = (int)strtol(str, &cp, base); if (cp == str || *cp) errx(EEXIT, "-%c flag requires a %s", flag, req); return (ret); } /* * Determine whether a filesystem should be checked. */ static int docheck(struct fstab *fsp) { if (strcmp(fsp->fs_vfstype, "ufs") || (strcmp(fsp->fs_type, FSTAB_RW) && strcmp(fsp->fs_type, FSTAB_RO)) || fsp->fs_passno == 0) return (0); return (1); } /* * Check the specified filesystem. */ /* ARGSUSED */ static int checkfilesys(char *filesys, char *mntpt, long auxdata, int child) { ufs_daddr_t n_ffree, n_bfree; struct dups *dp; struct statfs *mntbuf; struct zlncnt *zlnp; int cylno; if (preen && child) signal(SIGQUIT, voidquit); cdevname = filesys; if (debug && preen) pwarn("starting\n"); switch (setup(filesys)) { case 0: if (preen) pfatal("CAN'T CHECK FILE SYSTEM."); return (0); case -1: pwarn("clean, %d free ", sblock.fs_cstotal.cs_nffree + sblock.fs_frag * sblock.fs_cstotal.cs_nbfree); printf("(%d frags, %d blocks, %.1f%% fragmentation)\n", sblock.fs_cstotal.cs_nffree, sblock.fs_cstotal.cs_nbfree, sblock.fs_cstotal.cs_nffree * 100.0 / sblock.fs_dsize); return (0); } got_siginfo = 0; /* * Get the mount point information of the filesystem, if * it is available. */ mntbuf = getmntpt(filesys); /* * Cleared if any questions answered no. Used to decide if * the superblock should be marked clean. */ resolved = 1; /* * 1: scan inodes tallying blocks used */ if (lastmntonly) { printf("** %s Last Mounted on %s\n", filesys, sblock.fs_fsmnt); return (0); } if (preen == 0) { printf("** Last Mounted on %s\n", sblock.fs_fsmnt); if (mntbuf != NULL && mntbuf->f_flags & MNT_ROOTFS) printf("** Root file system\n"); printf("** Phase 1 - Check Blocks and Sizes\n"); } pass1(); /* * 1b: locate first references to duplicates, if any */ if (duplist) { if (preen || usedsoftdep) pfatal("INTERNAL ERROR: dups with -p"); printf("** Phase 1b - Rescan For More DUPS\n"); pass1b(); } /* * 2: traverse directories from root to mark all connected directories */ if (preen == 0) printf("** Phase 2 - Check Pathnames\n"); pass2(); /* * 3: scan inodes looking for disconnected directories */ if (preen == 0) printf("** Phase 3 - Check Connectivity\n"); pass3(); /* * 4: scan inodes looking for disconnected files; check reference counts */ if (preen == 0) printf("** Phase 4 - Check Reference Counts\n"); pass4(); /* * 5: check and repair resource counts in cylinder groups */ if (preen == 0) printf("** Phase 5 - Check Cyl groups\n"); pass5(); /* * print out summary statistics */ n_ffree = sblock.fs_cstotal.cs_nffree; n_bfree = sblock.fs_cstotal.cs_nbfree; pwarn("%d files, %d used, %d free ", n_files, n_blks, n_ffree + sblock.fs_frag * n_bfree); printf("(%d frags, %d blocks, %.1f%% fragmentation)\n", n_ffree, n_bfree, n_ffree * 100.0 / sblock.fs_dsize); if (debug && (n_files -= maxino - UFS_ROOTINO - sblock.fs_cstotal.cs_nifree)) printf("%d files missing\n", 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("%d blocks missing\n", n_blks); if (duplist != NULL) { printf("The following duplicate blocks remain:"); for (dp = duplist; dp; dp = dp->next) printf(" %d,", dp->dup); printf("\n"); } if (zlnhead != NULL) { printf("The following zero link count inodes remain:"); for (zlnp = zlnhead; zlnp; zlnp = zlnp->next) printf(" %u,", zlnp->zlncnt); printf("\n"); } } zlnhead = NULL; duplist = NULL; muldup = NULL; inocleanup(); if (fsmodified) { sblock.fs_time = time(NULL); sbdirty(); } if (cvtlevel && sblk.b_dirty) { /* * Write out the duplicate super blocks */ for (cylno = 0; cylno < sblock.fs_ncg; cylno++) bwrite(fswritefd, (char *)&sblock, fsbtodb(&sblock, cgsblock(&sblock, cylno)), SBSIZE); } if (rerun) resolved = 0; /* * Check to see if the filesystem is mounted read-write. */ if (mntbuf != NULL && (mntbuf->f_flags & MNT_RDONLY) == 0) resolved = 0; ckfini(resolved); for (cylno = 0; cylno < sblock.fs_ncg; cylno++) if (inostathead[cylno].il_stat != NULL) free((char *)inostathead[cylno].il_stat); free((char *)inostathead); inostathead = NULL; if (fsmodified && !preen) printf("\n***** FILE SYSTEM WAS MODIFIED *****\n"); if (rerun) printf("\n***** PLEASE RERUN FSCK *****\n"); if (mntbuf != NULL) { struct ufs_args args; int ret; /* * We modified a mounted filesystem. Do a mount update on * it unless it is read-write, so we can continue using it * as safely as possible. */ if (mntbuf->f_flags & MNT_RDONLY) { args.fspec = 0; args.export.ex_flags = 0; args.export.ex_root = 0; ret = mount("ufs", mntbuf->f_mntonname, mntbuf->f_flags | MNT_UPDATE | MNT_RELOAD, &args); if (ret == 0) return (0); pwarn("mount reload of '%s' failed: %s\n\n", mntbuf->f_mntonname, strerror(errno)); } if (!fsmodified) return (0); if (!preen) printf("\n***** REBOOT NOW *****\n"); sync(); return (4); } return (0); } /* * Get the directory that the device is mounted on. */ static struct statfs * getmntpt(const char *name) { struct stat devstat, mntdevstat; char device[sizeof(_PATH_DEV) - 1 + MNAMELEN]; char *devname; struct statfs *mntbuf; int i, mntsize; if (stat(name, &devstat) != 0 || !(S_ISCHR(devstat.st_mode) || S_ISBLK(devstat.st_mode))) return (NULL); mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); for (i = 0; i < mntsize; i++) { if (strcmp(mntbuf[i].f_fstypename, "ufs") != 0) continue; devname = mntbuf[i].f_mntfromname; if (*devname != '/') { strcpy(device, _PATH_DEV); strcat(device, devname); devname = device; } if (stat(devname, &mntdevstat) == 0 && mntdevstat.st_rdev == devstat.st_rdev) return (&mntbuf[i]); } return (NULL); }