#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/ucred.h>
#include <sys/mount.h>
#include <sys/ioctl.h>
#include <sys/disk.h>
#include <hfs/hfs_mount.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include "fsck_hfs.h"
#include "fsck_debug.h"
#include "dfalib/CheckHFS.h"
#define CACHE_IOSIZE 32768
#define CACHE_BLOCKS 128
#define CACHE_HASHSIZE 111
#ifndef F_FREEZE_FS
#define F_FREEZE_FS 53
#define F_THAW_FS 54
#endif // F_FREEZE_FS
const char *cdevname;
char *progname;
char lflag;
char nflag;
char yflag;
char preen;
char force;
char quick;
char debug;
char hotroot;
char guiControl;
char rebuildCatalogBtree;
char modeSetting;
int upgrading;
int lostAndFoundMode = 0;
int fsmodified;
int fsreadfd;
int fswritefd;
Cache_t fscache;
static int checkfilesys __P((char * filesys));
static int setup __P(( char *dev, int *blockDevice_fdPtr, int *canWritePtr ));
static void usage __P((void));
static void getWriteAccess __P(( char *dev, int *blockDevice_fdPtr, int *canWritePtr ));
extern char *unrawname __P((char *name));
int
main(argc, argv)
int argc;
char *argv[];
{
int ch;
int ret;
extern int optind;
extern char *optarg;
if ((progname = strrchr(*argv, '/')))
++progname;
else
progname = *argv;
while ((ch = getopt(argc, argv, "D:dfglm:npqruy")) != EOF) {
switch (ch) {
case 'd':
debug++;
break;
case 'D':
cur_debug_level = strtoul(optarg, NULL, 0);
if (cur_debug_level == 0) {
(void) fprintf (stderr, "%s: invalid debug development argument. Assuming zero\n", progname);
}
break;
case 'f':
force++;
break;
case 'g':
guiControl++;
break;
case 'l':
lflag++;
nflag++;
yflag = 0;
force++;
break;
case 'm':
modeSetting++;
lostAndFoundMode = strtol( optarg, NULL, 8 );
if ( lostAndFoundMode == 0 )
{
(void) fprintf(stderr, "%s: invalid mode argument \n", progname);
usage();
}
break;
case 'n':
nflag++;
yflag = 0;
break;
case 'p':
preen++;
break;
case 'q':
quick++;
break;
case 'r':
rebuildCatalogBtree++; break;
case 'y':
yflag++;
nflag = 0;
break;
case 'u':
case '?':
default:
usage();
}
}
argc -= optind;
argv += optind;
if (guiControl)
debug = 0;
if (signal(SIGINT, SIG_IGN) != SIG_IGN)
(void)signal(SIGINT, catch);
if (argc < 1) {
(void) fprintf(stderr, "%s: missing special-device\n", progname);
usage();
}
ret = 0;
while (argc-- > 0)
ret |= checkfilesys(blockcheck(*argv++));
exit(ret);
}
static int
checkfilesys(char * filesys)
{
int flags;
int result;
int chkLev, repLev, logLev;
int blockDevice_fd, canWrite;
char *unraw, *mntonname;
struct statfs *fsinfo;
int fs_fd=-1;
flags = 0;
cdevname = filesys;
blockDevice_fd = -1;
canWrite = 0;
unraw = NULL;
mntonname = NULL;
if (lflag) {
result = getmntinfo(&fsinfo, MNT_NOWAIT);
while (result--) {
unraw = strdup(cdevname);
unrawname(unraw);
if (unraw != NULL &&
strcmp(unraw, fsinfo[result].f_mntfromname) == 0) {
mntonname = strdup(fsinfo[result].f_mntonname);
}
}
if (mntonname != NULL) {
fs_fd = open(mntonname, O_RDONLY);
if (fs_fd < 0) {
printf("ERROR: could not open %s to freeze the volume.\n", mntonname);
free(mntonname);
free(unraw);
return 0;
}
if (fcntl(fs_fd, F_FREEZE_FS, NULL) != 0) {
free(mntonname);
free(unraw);
printf("ERROR: could not freeze volume (%s)\n", strerror(errno));
return 0;
}
}
}
if (debug && preen)
pwarn("starting\n");
if (setup( filesys, &blockDevice_fd, &canWrite ) == 0) {
if (preen)
pfatal("CAN'T CHECK FILE SYSTEM.");
result = EEXIT;
goto ExitThisRoutine;
}
if (preen == 0) {
if (hotroot && !guiControl)
printf("** Root file system\n");
}
chkLev = kAlwaysCheck;
repLev = kMajorRepairs;
logLev = kVerboseLog;
if (yflag)
repLev = kMajorRepairs;
if (quick) {
chkLev = kNeverCheck;
repLev = kNeverRepair;
logLev = kFatalLog;
} else if (force) {
chkLev = kForceCheck;
}
if (preen) {
repLev = kMinorRepairs;
chkLev = force ? kAlwaysCheck : kDirtyCheck;
logLev = kFatalLog;
}
if (debug)
logLev = kDebugLog;
if (nflag)
repLev = kNeverRepair;
if ( rebuildCatalogBtree ) {
chkLev = kPartialCheck;
repLev = kForceRepairs; }
result = CheckHFS( fsreadfd, fswritefd, chkLev, repLev, logLev,
guiControl, lostAndFoundMode, canWrite, &fsmodified );
if (!hotroot) {
ckfini(1);
if (quick) {
if (result == 0) {
pwarn("QUICKCHECK ONLY; FILESYSTEM CLEAN\n");
result = 0;
goto ExitThisRoutine;
} else if (result == R_Dirty) {
pwarn("QUICKCHECK ONLY; FILESYSTEM DIRTY\n");
result = DIRTYEXIT;
goto ExitThisRoutine;
} else if (result == R_BadSig) {
pwarn("QUICKCHECK ONLY; NO HFS SIGNATURE FOUND\n");
result = DIRTYEXIT;
goto ExitThisRoutine;
} else {
result = EEXIT;
goto ExitThisRoutine;
}
}
} else {
struct statfs stfs_buf;
if (statfs("/", &stfs_buf) == 0)
flags = stfs_buf.f_flags;
else
flags = 0;
ckfini(flags & MNT_RDONLY);
}
if (hotroot && fsmodified) {
struct hfs_mount_args args;
if (!preen)
printf("\n***** FILE SYSTEM WAS MODIFIED *****\n");
if (flags & MNT_RDONLY) {
bzero(&args, sizeof(args));
flags |= MNT_UPDATE | MNT_RELOAD;
if (mount("hfs", "/", flags, &args) == 0) {
result = 0;
goto ExitThisRoutine;
}
}
if (!preen)
printf("\n***** REBOOT NOW *****\n");
sync();
result = FIXEDROOTEXIT;
goto ExitThisRoutine;
}
result = (result == 0) ? 0 : EEXIT;
ExitThisRoutine:
if (lflag) {
fcntl(fs_fd, F_THAW_FS, NULL);
close(fs_fd);
free(unraw);
free(mntonname);
}
if ( blockDevice_fd > 0 ) {
close( blockDevice_fd );
blockDevice_fd = -1;
}
return (result);
}
static int
setup( char *dev, int *blockDevice_fdPtr, int *canWritePtr )
{
struct stat statb;
int devBlockSize;
fswritefd = -1;
*blockDevice_fdPtr = -1;
*canWritePtr = 0;
if (stat(dev, &statb) < 0) {
printf("Can't stat %s: %s\n", dev, strerror(errno));
return (0);
}
if ((statb.st_mode & S_IFMT) != S_IFCHR) {
pfatal("%s is not a character device", dev);
if (reply("CONTINUE") == 0)
return (0);
}
if ((fsreadfd = open(dev, O_RDONLY)) < 0) {
printf("Can't open %s: %s\n", dev, strerror(errno));
return (0);
}
getWriteAccess( dev, blockDevice_fdPtr, canWritePtr );
if (preen == 0 && !guiControl)
printf("** %s", dev);
if (nflag || (fswritefd = open(dev, O_WRONLY)) < 0) {
fswritefd = -1;
if (preen)
pfatal("NO WRITE ACCESS");
if (!guiControl)
printf(" (NO WRITE)");
}
if (preen == 0 && !guiControl)
printf("\n");
if (ioctl(fsreadfd, DKIOCGETBLOCKSIZE, &devBlockSize) < 0) {
pfatal ("Can't get device block size\n");
return (0);
}
if (CacheInit (&fscache, fsreadfd, fswritefd, devBlockSize,
CACHE_IOSIZE, CACHE_BLOCKS, CACHE_HASHSIZE) != EOK) {
pfatal("Can't initialize disk cache\n");
return (0);
}
return (1);
}
static void getWriteAccess( char *dev, int *blockDevice_fdPtr, int *canWritePtr )
{
int i;
int myMountsCount;
void * myPtr;
char * myCharPtr;
struct statfs * myBufPtr;
void * myNamePtr;
myPtr = NULL;
myNamePtr = malloc( strlen(dev) + 2 );
if ( myNamePtr == NULL )
return;
strcpy( (char *)myNamePtr, dev );
if ( (myCharPtr = strrchr( (char *)myNamePtr, '/' )) != 0 ) {
if ( myCharPtr[1] == 'r' ) {
strcpy( &myCharPtr[1], &myCharPtr[2] );
*blockDevice_fdPtr = open( (char *)myNamePtr, O_WRONLY );
}
}
if ( *blockDevice_fdPtr > 0 ) {
*canWritePtr = 1;
goto ExitThisRoutine;
}
myMountsCount = getfsstat( NULL, 0, MNT_NOWAIT );
if ( myMountsCount < 0 )
goto ExitThisRoutine;
myPtr = (void *) malloc( sizeof(struct statfs) * myMountsCount );
if ( myPtr == NULL )
goto ExitThisRoutine;
myMountsCount = getfsstat( myPtr,
(sizeof(struct statfs) * myMountsCount),
MNT_NOWAIT );
if ( myMountsCount < 0 )
goto ExitThisRoutine;
myBufPtr = (struct statfs *) myPtr;
for ( i = 0; i < myMountsCount; i++ )
{
if ( strcmp( myBufPtr->f_mntfromname, myNamePtr ) == 0 ) {
if ( myBufPtr->f_flags & MNT_RDONLY )
*canWritePtr = 1;
goto ExitThisRoutine;
}
myBufPtr++;
}
*canWritePtr = 1;
ExitThisRoutine:
if ( myPtr != NULL )
free( myPtr );
if ( myNamePtr != NULL )
free( myNamePtr );
return;
}
static void
usage()
{
(void) fprintf(stderr, "usage: %s [-dfl m [mode] npqruy] special-device\n", progname);
(void) fprintf(stderr, " d = output debugging info\n");
(void) fprintf(stderr, " f = force fsck even if clean (preen only) \n");
(void) fprintf(stderr, " l = live fsck (lock down and test-only)\n");
(void) fprintf(stderr, " m arg = octal mode used when creating lost+found directory \n");
(void) fprintf(stderr, " n = assume a no response \n");
(void) fprintf(stderr, " p = just fix normal inconsistencies \n");
(void) fprintf(stderr, " q = quick check returns clean, dirty, or failure \n");
(void) fprintf(stderr, " r = rebuild catalog btree \n");
(void) fprintf(stderr, " u = usage \n");
(void) fprintf(stderr, " y = assume a yes response \n");
exit(1);
}