main.c revision 5b4dc236e7280fc192b049c0987f072ab71e69b6
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Copyright (c) 1980, 1986, 1990 The Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that: (1) source distributions retain this entire copyright
* notice and comment, and (2) distributions including binaries display
* the following acknowledgement: ``This product includes software
* developed by the University of California, Berkeley and its contributors''
* in the documentation or other materials provided with the distribution
* and in all advertising materials mentioning features or use of this
* software. 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 '`AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* In-core structures:
* blockmap[]
* A bitmap of block usage very similar to what's on disk, but
* for the entire filesystem rather than just a cylinder group.
* Zero indicates free, one indicates allocated. Note that this
* is opposite the interpretation of a cylinder group's free block
* bitmap.
*
* statemap[]
* Tracks what is known about each inode in the filesystem.
* The fundamental state value is one of USTATE, FSTATE, DSTATE,
*
* There are optional modifying attributes as well: INZLINK,
* INFOUND, INCLEAR, INORPHAN, and INDELAYD. The IN prefix
* stands for inode. INZLINK declares that no links (di_nlink ==
* 0) to the inode have been found. It is used instead of
* examining di_nlink because we've always got the statemap[] in
* memory, and on average the odds are against having any given
* inode in the cache. INFOUND flags that an inode was
* encountered during the descent of the filesystem. In other
* words, it's reachable, either by name or by being an acl or
* attribute. INCLEAR declares an intent to call clri() on an
* inode.
*
* INORPHAN indicates that the inode has already been seen once
* in pass3 and determined to be an orphan, so any additional
* encounters don't need to waste cycles redetermining that status.
* It also means we don't ask the user about doing something to the
* inode N times.
*
* INDELAYD marks inodes that pass1 determined needed to be truncated.
* They can't be truncated during that pass, because it depends on
* having a stable world for building the block and inode tables from.
*
* The IN flags rarely used directly, but instead are
* pre-combined through the {D,F,S}ZLINK, DFOUND, and
* {D,F,S}CLEAR convenience macros. This mainly matters when
* trying to use grep on the source.
*
* Three state-test macros are provided: S_IS_DUNFOUND(),
* S_IS_DVALID(), and S_IS_ZLINK(). The first is true when an
* inode's state indicates that it is either a simple directory
* (DSTATE without the INFOUND or INCLEAR modifiers) or a
* directory with the INZLINK modifier set. By definition, if a
* directory has zero links, then it can't be found. As for
* S_IS_DVALID(), it decides if a directory inode is alive.
* Effectively, this translates to whether or not it's been
* flagged for clearing. If not, then it's valid for current
* purposes. This is true even if INZLINK is set, as we may find
* a reference to it later. Finally, S_IS_ZLINK() just picks out
* the INZLINK flag from the state.
*
* The S_*() macros all work on a state value. To simplify a
* bit, the INO_IS_{DUNFOUND,DVALID}() macros take an inode
* number argument. The inode is looked up in the statemap[] and
* the result handed off to the corresponding S_*() macro. This
* is partly a holdover from working with different data
* structures (with the same net intent) in the BSD fsck.
*
* lncntp
* Each entry is initialized to the di_link from the on-disk
* inode. Each time we find one of those links, we decrement it.
* Once all the traversing is done, we should have a zero. If we
* have a positive value, then some reference disappeared
* (probably from a directory that got nuked); deal with it by
* fixing the count. If we have a negative value, then we found
* an extra reference. This is a can't-happen, except in the
* special case of when we reconnect a directory to its parent or
* to lost+found. An exact match between lncntp[] and the on-disk
* inode means it's completely unreferenced.
*
* aclphead
* This is a hash table of the acl inodes in the filesystem.
*
* aclpsort
* The same acls as in aclphead, but as a simple linear array.
* It is used to hold the acl pointers for sorting and scanning
* in pass3b.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/int_types.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <ustat.h>
#include <errno.h>
#include "fsck.h"
static void usage(void);
static long argtol(int, char *, char *, int);
static void checkfilesys(char *);
static void check_sanity(char *);
static void report_limbo(const void *, VISIT, int);
static char *subopts[] = {
#define PREEN 0 /* non-interactive mode (parent is parallel) */
"p",
"b",
"d",
"w",
"f",
};
/*
* Filesystems that are `magical' - if they exist in vfstab,
* then they have to be mounted for the system to have gotten
* far enough to be able to run fsck. Thus, don't get all
* bent out of shape if we're asked to check it and it is mounted.
*/
char *magic_fs[] = {
"", /* MAGIC_NONE, for normal filesystems */
"/", /* MAGIC_ROOT */
"/usr", /* MAGIC_USR */
NULL /* MAGIC_LIMIT */
};
int
{
int c;
int wflag = 0;
char *suboptions, *value;
extern int optind;
extern char *optarg;
switch (c) {
case QUICK_CHECK:
mflag++;
break;
case ALL_no:
case ALL_NO:
nflag++;
yflag = 0;
break;
case VERBOSE:
verbose++;
break;
case UFS_OPTS:
/*
* ufs specific options.
*/
usage();
/*
* lint does not believe this, nor does it
* believe #pragma does_not_return(usage)
*/
/* NOTREACHED */
}
suboptions = optarg;
while (*suboptions != '\0') {
&value)) {
case PREEN:
preen++;
break;
case BLOCK:
value, 10);
(void) printf("Alternate super block "
"location: %ld.\n",
(long)bflag);
break;
case DEBUG:
debug++;
verbose++;
break;
case ONLY_WRITES:
/* check only writable filesystems */
wflag++;
break;
case FORCE:
fflag++;
break;
default:
usage();
}
}
break;
case ECHO_CMD:
{
int opt_count;
char *opt_text;
(void) printf("fsck -F ufs ");
opt_count++) {
if (opt_text)
}
(void) printf("\n");
}
break;
case ALL_yes:
case ALL_YES:
yflag++;
nflag = 0;
break;
default:
usage();
}
}
if (argc == 0)
usage();
rflag++; /* check raw devices where we can */
if (preen)
/*
* Push up our allowed memory limit so we can cope
* with huge file systems.
*/
}
/*
* There are a lot of places where we just exit if a problem is
* found. This means that we won't necessarily check everything
* we were asked to. It would be nice to do everything, and
* then provide a summary when we're done. However, the
* interface doesn't really allow us to do that in any useful
* way. So, we'll just bail on the first unrecoverable
* problem encountered. If we've been run by the generic
* wrapper, we were only given one filesystem to check, so the
* multi-fs case implies being run manually; that means the
* user can rerun us on the remaining filesystems when it's
* convenient for them.
*/
while (argc-- > 0) {
argv++;
if (exitstat == 0)
} else {
checkfilesys(*argv++);
}
}
if (interrupted)
}
/*
* A relatively intelligent strtol(). Note that if str is NULL, we'll
* exit, so ret does not actually need to be pre-initialized. Lint
* doesn't believe this, and it's harmless enough to make lint happy here.
*/
static long
{
long ret = -1;
errno = 0;
}
if (errno != 0) {
}
return (ret);
}
/*
* Check the specified file system.
*/
static void
checkfilesys(char *filesys)
{
char *devstr;
int zlinks_printed;
double dbl_nffree, dbl_dsize;
int quiet_dups;
mountfd = -1;
hotroot = 0;
reattached_dir = 0;
broke_dir_link = 0;
islog = 0;
islogok = 0;
overflowed_lf = 0;
limbo_dirs = NULL;
if (!iscorrupt) {
return;
}
if (preen)
pfatal("CAN'T CHECK FILE SYSTEM.");
} else {
}
if (mflag) {
/* NOTREACHED */
}
if (debug)
printclean();
iscorrupt = 0; /* setup() succeeded, assume good filesystem */
/*
* 1: scan inodes tallying blocks used
*/
if (!preen) {
/* hotroot is reported as such in setup() if debug is on */
(void) printf("** Currently Mounted on %s\n",
else
(void) printf("** Last Mounted on %s\n",
(void) printf("** Phase 1 - Check Blocks and Sizes\n");
}
pass1();
/*
* 1b: locate first references to duplicates, if any
*/
if (have_dups()) {
if (preen)
pfatal("INTERNAL ERROR: dups with -o p");
(void) printf("** Phase 1b - Rescan For More DUPS\n");
pass1b();
}
/*
* 2: traverse directories from root to mark all connected directories
*/
if (!preen)
(void) printf("** Phase 2 - Check Pathnames\n");
pass2();
/*
* 3a: scan inodes looking for disconnected directories.
*/
if (!preen)
(void) printf("** Phase 3a - Check Connectivity\n");
pass3a();
/*
* 3b: check acls
*/
if (!preen)
pass3b();
/*
* 4: scan inodes looking for disconnected files; check reference counts
*/
if (!preen)
(void) printf("** Phase 4 - Check Reference Counts\n");
pass4();
/*
* 5: check and repair resource counts in cylinder groups
*/
if (!preen)
(void) printf("** Phase 5 - Check Cylinder Groups\n");
pass5();
if (overflowed_lf) {
iscorrupt = 1;
}
(void) printf("FILESYSTEM MAY STILL BE INCONSISTENT.\n");
rerun = 1;
}
if (have_dups()) {
if (report_dups(quiet_dups) > 0)
iscorrupt = 1;
(void) printf("WARNING: DATA LOSS MAY HAVE OCCURRED DUE TO "
"DUP BLOCKS.\nVERIFY FILE CONTENTS BEFORE USING.\n");
}
if (limbo_dirs != NULL) {
/*
* Don't force iscorrupt, as this is sufficiently
* harmless that the filesystem can be mounted and
*/
pwarn("Orphan directories not cleared or reconnected:\n");
while (limbo_dirs != NULL) {
if (limbo_victim != NULL) {
(void) tdelete((void *)limbo_victim,
}
}
rerun = 1;
}
if (iscorrupt) {
(void) printf("FS IS MOUNTED R/W AND"
" FSCK DID ITS BEST TO FIX"
" INCONSISTENCIES.\n");
else
(void) printf("FILESYSTEM MAY STILL BE"
" INCONSISTENT.\n");
rerun = 1;
}
/*
* iscorrupt must be stable at this point.
* updateclean() returns true when it had to discard the log.
* This can only happen once, since sblock.fs_logbno gets
* cleared as part of that operation.
*/
if (updateclean()) {
if (!preen)
(void) printf(
"Log was discarded, updating cyl groups\n");
goto recount;
}
if (debug)
printclean();
ckfini();
/*
* print out summary statistics
*/
pwarn("Reclaimed: %d directories, %d files, %lld fragments\n",
(longlong_t)blks);
}
dbl_nffree = (double)n_ffree;
if (!verbose) {
/*
* Done as one big string to try for a single write,
* so the output doesn't get interleaved with other
* preening fscks.
*/
pwarn("%ld files, %lld used, %lld free "
"(%lld frags, %lld blocks, %.1f%% fragmentation)\n",
} else {
pwarn("\nFilesystem summary:\n");
pwarn("Total free fragments: %lld\n",
pwarn("Free fragments not in blocks: %lld\n",
pwarn("");
if (files < 0)
if (blks < 0)
zlinks_printed = 0;
if (zlinks_printed == 0) {
pwarn("The following zero "
"link count inodes remain:");
}
if (zlinks_printed) {
if ((zlinks_printed % 9) == 0)
(void) puts(",\n");
else
(void) puts(", ");
}
}
}
(void) putchar('\n');
}
/*
* Clean up after ourselves, so we can do the next filesystem.
*/
inocleanup();
if (fsmodified)
(void) printf("\n***** FILE SYSTEM WAS MODIFIED *****\n");
if (overflowed_lf)
(void) printf("\n***** %s FULL, MUST REMOVE ENTRIES *****\n",
lfname);
if (reattached_dir) {
(void) printf("ORPHANED DIRECTORIES REATTACHED; DIR LINK "
"COUNTS MAY NOT BE CORRECT.\n");
rerun = 1;
}
if (broke_dir_link) {
(void) printf(
"DIRECTORY HARDLINK BROKEN; LOOPS MAY STILL EXIST.\n");
rerun = 1;
}
if (iscorrupt)
(void) printf("***** FILE SYSTEM IS BAD *****\n");
if (rerun) {
(void) printf("\n***** PLEASE RERUN FSCK ON UNMOUNTED"
" FILE SYSTEM *****\n");
else
(void) printf("\n***** PLEASE RERUN FSCK *****\n");
}
if ((exitstat == 0) &&
}
if (!fsmodified)
return;
/*
* _FIOFFS is much more effective than a simple sync().
* Note that the original fswritefd was discarded in
* ckfini().
*/
if (fswritefd != -1) {
}
if (!preen)
(void) printf("\n***** REBOOT NOW *****\n");
}
}
/*
* fsck -m: does the filesystem pass cursory examination
*
* XXX This is very redundant with setup(). The right thing would be
* for setup() to modify its behaviour when mflag is set (less
* chatty, exit instead of return, etc).
*/
void
check_sanity(char *filename)
{
char *devname;
char vfsfilename[MAXPATHLEN];
int found_magic[MAGIC_LIMIT];
int magic_cnt;
int is_magic = 0;
int is_block;
"ufs fsck: sanity check failed : cannot stat %s\n", filename);
}
is_block = 1;
is_block = 0;
} else {
/*
* In !mflag mode, we allow checking the contents
* of a file. Since this is intended primarily for
* speeding up boot-time checks and allowing for a
* file complicates the ok-input tests, we'll disallow
* that option.
*/
"ufs fsck: sanity check failed: "
"%s not block or character device\n",
filename);
}
/*
* Determine if this is the root file system via vfstab. Give up
* silently on failures. The whole point of this is to be tolerant
* of the magic file systems being already mounted.
*/
if (magic_cnt == MAGIC_NONE)
continue;
if (is_block)
else
break;
}
}
}
}
}
/*
* Only works if filename is a block device or if
* character and block device has the same dev_t value.
* This is currently true, but nothing really forces it.
*/
"ufs fsck: sanity check: %s already mounted\n", filename);
}
if (is_magic) {
}
/*
* We need to exit saying this. If it's only
* mounted readonly, we can continue.
*/
"ufs fsck: sanity check:"
filename);
}
}
/*
* We know that at boot, the ufs root file system is mounted
* read-only first. After fsck runs, it is remounted as
* read-write. Therefore, we do not need to check for different
* values for fs_state between the root file system and the
* rest of the file systems.
*/
"ufs fsck: sanity check: %s needs checking\n", filename);
}
"ufs fsck: sanity check: %s okay\n", filename);
} else {
"ufs fsck: sanity check: %s needs checking\n", filename);
}
}
{
return (NULL);
}
void
usage(void)
{
"ufs usage: fsck [-F ufs] [-m] [-n] [-V] [-v] [-y] "
"[-o p,b=#,w,f] [special ....]\n");
}
/*ARGSUSED*/
static void
{
}
}