#include <sys/param.h>
#include <sys/time.h>
#include <sys/mount.h>
#include <ufs/ufs/dinode.h>
#include <ufs/ufs/ufsmount.h>
#include <ufs/ffs/fs.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fstab.h>
#include <string.h>
#include <unistd.h>
#include "fsck.h"
#if REV_ENDIAN_FS
#include "ufs_byte_order.h"
#endif
#define _PATH_SBIN "/sbin"
int returntosingle;
static int argtoi __P((int flag, char *req, char *str, int base));
static int docheck __P((struct fstab *fsp));
static int checkfilesys __P((char *filesys, char *mntpt, char *vfstype,
int child));
static int check_other __P((char *vfstype, char *filesys));
int main __P((int argc, char *argv[]));
char *cdevname;
long dev_bsize;
long secsize;
char nflag;
char yflag;
int bflag;
#ifdef __APPLE__
int qflag;
#endif
int debug;
int fflag=0;
int cvtlevel;
int doinglevel1;
int doinglevel2;
int newinofmt;
char preen;
char havesb;
int fsmodified;
int fsreadfd;
int fswritefd;
ufs_daddr_t maxfsblock;
char *blockmap;
u_int32_t maxino;
u_int32_t lastino;
char *statemap;
u_char *typemap;
short *lncntp;
u_int32_t lfdir;
ufs_daddr_t n_blks;
ufs_daddr_t n_files;
struct dinode zino;
struct bufarea bufhead;
struct bufarea sblk;
struct bufarea cgblk;
struct bufarea *pdirbp;
struct bufarea *pbp;
struct dups *duplist;
struct dups *muldup;
struct zlncnt *zlnhead;
struct inoinfo **inphead, **inpsort;
long numdirs, listmax, inplast;
#if REV_ENDIAN_FS
int rev_endian=0;
#endif
int
main(argc, argv)
int argc;
char *argv[];
{
int ch;
int ret, maxrun = 0;
extern char *optarg;
extern int optind;
sync();
#ifdef __APPLE__
while ((ch = getopt(argc, argv, "dfpqnNyYb:l:m:")) != EOF) {
#else
while ((ch = getopt(argc, argv, "dfpnNyYb:l:m:")) != EOF) {
#endif
switch (ch) {
case 'p':
preen++;
break;
case 'b':
bflag = argtoi('b', "number", optarg, 10);
printf("Alternate super block location: %d\n", bflag);
break;
#if 0
case 'c':
cvtlevel = argtoi('c', "conversion level", optarg, 10);
break;
#endif 0
case 'd':
debug++;
break;
case 'f':
fflag++;
break;
case 'l':
maxrun = argtoi('l', "number", optarg, 10);
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;
#ifdef __APPLE__
case 'q':
qflag++;
break;
#endif
default:
errx(EEXIT, "%c option?", ch);
}
}
argc -= optind;
argv += optind;
if (signal(SIGINT, SIG_IGN) != SIG_IGN)
(void)signal(SIGINT, catch);
if (preen)
(void)signal(SIGQUIT, catchquit);
if (argc) {
ret = 0;
while (argc-- > 0)
ret |= checkfilesys(blockcheck(*argv++), 0, 0, 0);
exit(ret);
}
ret = checkfstab(preen, maxrun, docheck, checkfilesys);
if (returntosingle)
exit(2);
exit(ret);
}
static int
argtoi(flag, req, str, base)
int flag;
char *req, *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);
}
static int
docheck(fsp)
register struct fstab *fsp;
{
int result = 0;
if (strcmp(fsp->fs_vfstype, "ufs") &&
strcmp(fsp->fs_vfstype, "hfs") &&
strcmp(fsp->fs_vfstype, "msdos"))
return 0;
if ((strcmp(fsp->fs_type, FSTAB_RW) && strcmp(fsp->fs_type, FSTAB_RO)) ||
fsp->fs_passno == 0)
return 0;
#define DISKARB_LABEL "LABEL="
#define DISKARB_UUID "UUID="
if ((strncmp(fsp->fs_spec, DISKARB_LABEL, strlen(DISKARB_LABEL)) == 0)
|| (strncmp(fsp->fs_spec, DISKARB_UUID, strlen(DISKARB_UUID)) == 0))
return 0;
return 1;
}
static int
checkfilesys(char *filesys, char *mntpt, char *vfstype, int child)
{
ufs_daddr_t n_ffree, n_bfree;
struct dups *dp;
struct zlncnt *zlnp;
int cylno, flags;
if (preen && child)
(void)signal(SIGQUIT, voidquit);
if (vfstype) {
if (strcmp(vfstype, "ufs") != 0)
return check_other(vfstype, filesys);
}
cdevname = filesys;
if (debug && preen)
pwarn("starting\n");
switch (setup(filesys)) {
case 0:
if (preen)
pfatal("CAN'T CHECK UFS FILE SYSTEM.");
else
return (EEXIT);
case -1:
return (0);
#ifdef __APPLE__
case DIRTYEXIT:
if (qflag)
return (DIRTYEXIT);
break;
case EEXIT:
if (qflag)
return (EEXIT);
break;
case CANTFIXIT:
return (CANTFIXIT);
break;
#endif
}
if (preen == 0) {
printf("** Last Mounted on %s\n", sblock.fs_fsmnt);
if (hotroot)
printf("** Root file system\n");
printf("** Phase 1 - Check Blocks and Sizes\n");
}
pass1();
if (duplist) {
if (preen)
pfatal("INTERNAL ERROR: dups with -p");
printf("** Phase 1b - Rescan For More DUPS\n");
pass1b();
}
if (preen == 0)
printf("** Phase 2 - Check Pathnames\n");
pass2();
if (preen == 0)
printf("** Phase 3 - Check Connectivity\n");
pass3();
if (preen == 0)
printf("** Phase 4 - Check Reference Counts\n");
pass4();
if (preen == 0)
printf("** Phase 5 - Check Cyl groups\n");
pass5();
n_ffree = sblock.fs_cstotal.cs_nffree;
n_bfree = sblock.fs_cstotal.cs_nbfree;
pwarn("%ld files, %ld used, %ld free ",
n_files, n_blks, n_ffree + sblock.fs_frag * n_bfree);
printf("(%u frags, %u blocks, %d.%d%% fragmentation)\n",
n_ffree, n_bfree, (n_ffree * 100) / sblock.fs_dsize,
((n_ffree * 1000 + sblock.fs_dsize / 2) / sblock.fs_dsize) % 10);
if (debug &&
(n_files -= maxino - ROOTINO - sblock.fs_cstotal.cs_nifree))
printf("%u 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("%u blocks missing\n", n_blks);
if (duplist != NULL) {
printf("The following duplicate blocks remain:");
for (dp = duplist; dp; dp = dp->next)
printf(" %u,", 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 = (struct zlncnt *)0;
duplist = (struct dups *)0;
muldup = (struct dups *)0;
inocleanup();
if (fsmodified) {
(void)time(&sblock.fs_time);
sbdirty();
}
if (cvtlevel && sblk.b_dirty) {
for (cylno = 0; cylno < sblock.fs_ncg; cylno++) {
#if REV_ENDIAN_FS
if (rev_endian) byte_swap_sbout(&sblock);
#endif
bwrite(fswritefd, (char *)&sblock,
fsbtodb(&sblock, cgsblock(&sblock, cylno)), SBSIZE);
#if REV_ENDIAN_FS
if (rev_endian) byte_swap_sbin(&sblock);
#endif
}
}
flags = 0;
if (!hotroot) {
ckfini(1);
} else {
struct statfs stfs_buf;
if (statfs("/", &stfs_buf) == 0)
flags = stfs_buf.f_flags;
ckfini(flags & MNT_RDONLY);
}
free(blockmap);
free(statemap);
free((char *)lncntp);
if (!fsmodified)
return (0);
if (!preen)
printf("\n***** FILE SYSTEM WAS MODIFIED *****\n");
if (hotroot) {
struct ufs_args args;
int ret;
if (flags & MNT_RDONLY) {
args.fspec = 0;
flags |= MNT_UPDATE | MNT_RELOAD;
ret = mount("ufs", "/", flags, &args);
if (ret == 0)
return (0);
}
if (!preen)
printf("\n***** REBOOT NOW *****\n");
sync();
return (4);
}
return (0);
}
static int
check_other(char *vfstype, char *filesys)
{
#define ARGC_MAX 4
const char *argv[ARGC_MAX];
int argc;
pid_t pid;
int status = 0;
char options[] = "-pdfnyq";
char progname[NAME_MAX];
char execname[MAXPATHLEN + 1];
bzero(options, sizeof(options));
snprintf(options, sizeof(options), "-%s%s%s%s%s%s",
(preen) ? "p" : "",
(debug) ? "d" : "",
(fflag) ? "f" : "",
(nflag) ? "n" : "",
(yflag) ? "y" : "",
(qflag) ? "q" : ""
);
argc = 0;
snprintf(progname, sizeof(progname), "fsck_%s", vfstype);
argv[argc++] = progname;
if (strlen(options) > 1)
argv[argc++] = options;
argv[argc++] = filesys;
argv[argc] = NULL;
(void)snprintf(execname, sizeof(execname), "%s/fsck_%s", _PATH_SBIN, vfstype);
pid = vfork();
switch (pid) {
case -1:
pfatal("vfork");
return 8;
break;
case 0:
execv(execname, (char * const *)argv);
pfatal("exec %s", execname);
_exit(8);
break;
default:
waitpid(pid, &status, 0);
if (WIFEXITED(status))
status = WEXITSTATUS(status);
else
status = 0;
if (WIFSIGNALED(status)) {
printf("%s (%s) EXITED WITH SIGNAL %d\n", filesys, vfstype, WTERMSIG(status));
status = 8;
}
break;
}
return status;
}