OpenSolaris_b135/cmd/fs.d/pcfs/fsck/fsck.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 1999,2001 by Sun Microsystems, Inc.
 * All rights reserved.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * fsck_pcfs -- main routines.
 */

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <strings.h>
#include <libintl.h>
#include <locale.h>
#include <sys/fcntl.h>
#include <sys/dktp/fdisk.h>
#include "pcfs_common.h"
#include "fsck_pcfs.h"
#include "pcfs_bpb.h"

int32_t BytesPerCluster;
int32_t TotalClusters;
int32_t LastCluster;
off64_t	FirstClusterOffset;
off64_t	PartitionOffset;
bpb_t	TheBIOSParameterBlock;

/*
 * {Output,Input}Image are the file names where we should write the
 * checked fs image and from which we should read the initial fs.
 * The image capability is designed for debugging purposes.
 */
static char	*OutputImage = NULL;
static char	*InputImage = NULL;
static int	WritableOnly = 0; /* -o w, check writable fs' only */
static int	Mflag = 0;	  /* -m, sanity check if fs is mountable */
static int	Preen = 0;	  /* -o p, preen; non-interactive */
/*
 * By default be quick; skip verify reads.
 * If the user wants more exhaustive checking,
 * they should run with the -o v option.
 */
static int	Quick = 1;

int	ReadOnly = 0;
int	IsFAT32 = 0;
int	Verbose = 0;

int	AlwaysYes = 0;	/* -y or -Y, assume a yes answer to all questions */
int	AlwaysNo = 0;	/* -n or -N, assume a no answer to all questions */

extern	ClusterContents	TheRootDir;

/*
 * Function definitions
 */
static void
passOne(int fd)
{
	if (!Quick)
		findBadClusters(fd);
	scanAndFixMetadata(fd);
}

static void
writeBackChanges(int fd)
{
	writeFATMods(fd);
	if (!IsFAT32)
		writeRootDirMods(fd);
	writeClusterMods(fd);
}

static void
tryOpen(int *fd, char *openMe, int oflag, int exitOnFailure)
{
	int saveError;

	if ((*fd = open(openMe, oflag)) < 0) {
		if (exitOnFailure == RETURN_ON_OPEN_FAILURE)
			return;
		saveError = errno;
		mountSanityCheckFails();
		(void) fprintf(stderr, "%s: ", openMe);
		(void) fprintf(stderr, strerror(saveError));
		(void) fprintf(stderr, "\n");
		exit(1);
	}
}

static void
doOpen(int *inFD, int *outFD, char *name, char *outName)
{
	if (ReadOnly) {
		tryOpen(inFD, name, O_RDONLY, EXIT_ON_OPEN_FAILURE);
		*outFD = -1;
	} else {
		tryOpen(inFD, name, O_RDWR, RETURN_ON_OPEN_FAILURE);
		if (*inFD < 0) {
			if (errno != EACCES || WritableOnly) {
				int saveError = errno;
				mountSanityCheckFails();
				(void) fprintf(stderr,
				    gettext("%s: "), name);
				(void) fprintf(stderr, strerror(saveError));
				(void) fprintf(stderr, "\n");
				exit(2);
			} else {
				tryOpen(inFD, name, O_RDONLY,
				    EXIT_ON_OPEN_FAILURE);
				AlwaysYes = 0;
				AlwaysNo = 1;
				ReadOnly = 1;
				*outFD = -1;
			}
		} else {
			*outFD = *inFD;
		}
	}

	if (outName != NULL) {
		tryOpen(outFD, outName, (O_RDWR | O_CREAT),
		    EXIT_ON_OPEN_FAILURE);
	}

	(void) printf("** %s %s\n", name,
	    ReadOnly ? gettext("(NO WRITE)") : "");
}

static void
openFS(char *special, int *inFD, int *outFD)
{
	struct stat dinfo;
	char *actualDisk = NULL;
	char *suffix = NULL;

	if (Verbose)
		(void) fprintf(stderr, gettext("Opening file system.\n"));

	if (InputImage == NULL) {
		actualDisk = stat_actual_disk(special, &dinfo, &suffix);
		/*
		 *  Destination exists, now find more about it.
		 */
		if (!(S_ISCHR(dinfo.st_mode))) {
			mountSanityCheckFails();
			(void) fprintf(stderr,
			    gettext("\n%s: device name must be a "
			    "character special device.\n"), actualDisk);
			exit(2);
		}
	} else {
		actualDisk = InputImage;
	}
	doOpen(inFD, outFD, actualDisk, OutputImage);
	if (suffix) {
		if ((PartitionOffset =
		    findPartitionOffset(*inFD, suffix)) < 0) {
			mountSanityCheckFails();
			(void) fprintf(stderr,
			    gettext("Unable to find logical drive %s\n"),
			    suffix);
			exit(2);
		} else if (Verbose) {
			(void) fprintf(stderr,
			    gettext("Partition starts at offset %lld\n"),
			    PartitionOffset);
		}
	} else {
		PartitionOffset = 0;
	}
}

void
usage()
{
	(void) fprintf(stderr,
		gettext("pcfs Usage: fsck -F pcfs [-o v|p|w] special-file\n"));
	exit(1);
}

static
char *LegalOpts[] = {
#define	VFLAG 0
	"v",
#define	PFLAG 1
	"p",
#define	WFLAG 2
	"w",
#define	DFLAG 3
	"d",
#define	IFLAG 4
	"i",
#define	OFLAG 5
	"o",
	NULL
};

static void
parseSubOptions(char *optsstr)
{
	char *value;
	int c;

	while (*optsstr != '\0') {
		switch (c = getsubopt(&optsstr, LegalOpts, &value)) {
		case VFLAG:
			Quick = 0;
			break;
		case PFLAG:
			Preen++;
			break;
		case WFLAG:
			WritableOnly++;
			break;
		case DFLAG:
			Verbose++;
			break;
		case IFLAG:
			if (value == NULL) {
				missing_arg(LegalOpts[c]);
			} else {
				InputImage = value;
			}
			break;
		case OFLAG:
			if (value == NULL) {
				missing_arg(LegalOpts[c]);
			} else {
				OutputImage = value;
			}
			break;
		default:
			bad_arg(value);
			break;
		}
	}
}

static void
sanityCheckOpts(void)
{
	if (WritableOnly && ReadOnly) {
		(void) fprintf(stderr,
		    gettext("-w option may not be used with the -n "
		    "or -m options\n"));
		exit(4);
	}
}

static void
confirmMountable(char *special, int fd)
{
	char *printName;
	int okayToMount = 1;

	printName = InputImage ? InputImage : special;

	if (!IsFAT32) {
		/* make sure we can at least read the root directory */
		getRootDirectory(fd);
		if (TheRootDir.bytes == NULL)
			okayToMount = 0;
	} else {
		/* check the bit designed into FAT32 for this purpose */
		okayToMount = checkFAT32CleanBit(fd);
	}
	if (okayToMount) {
		(void) fprintf(stderr,
		    gettext("pcfs fsck: sanity check: %s okay\n"), printName);
		exit(0);
	} else {
		(void) fprintf(stderr,
		    gettext("pcfs fsck: sanity check: %s needs checking\n"),
		    printName);
		exit(32);
	}
}

void
mountSanityCheckFails(void)
{
	if (Mflag) {
		(void) fprintf(stderr,
		    gettext("pcfs fsck: sanity check failed: "));
	}
}

/*
 * preenBail
 *	Routine that other routines can call if they would go into a
 *	state where they need user input.  They can send an optional
 *	message string to be printed before the exit.  Caller should
 *	send a NULL string if they don't have an exit message.
 */
void
preenBail(char *outString)
{
	/*
	 *  If we are running in the 'preen' mode, we got here because
	 *  we reached a situation that would require user intervention.
	 *  We have no choice but to bail at this point.
	 */
	if (Preen) {
		if (outString)
			(void) printf("%s", outString);
		(void) printf(gettext("FILE SYSTEM FIX REQUIRES USER "
		    "INTERVENTION; RUN fsck MANUALLY.\n"));
		exit(36);
	}
}

int
main(int argc, char *argv[])
{
	char *string;
	int  ifd, ofd;
	int  c;

	(void) setlocale(LC_ALL, "");

#if !defined(TEXT_DOMAIN)
#define	TEXT_DOMAIN "SYS_TEST"
#endif
	(void) textdomain(TEXT_DOMAIN);

	if (argc < 2)
		usage();

	while ((c = getopt(argc, argv, "F:VYNynmo:")) != EOF) {
		switch (c) {
		case 'F':
			string = optarg;
			if (strcmp(string, "pcfs") != 0)
				usage();
			break;
		case 'V': {
				char	*opt_text;
				int	opt_count;

				(void) printf(gettext("fsck -F pcfs "));
				for (opt_count = 1; opt_count < argc;
				    opt_count++) {
					opt_text = argv[opt_count];
					if (opt_text)
						(void) printf(" %s ",
						    opt_text);
				}
				(void) printf("\n");
				exit(0);
			}
			break;
		case 'N':
		case 'n':
			AlwaysYes = 0;
			AlwaysNo = 1;
			ReadOnly = 1;
			break;
		case 'Y':
		case 'y':
			AlwaysYes = 1;
			AlwaysNo = 0;
			break;
		case 'm':
			Mflag++;
			ReadOnly = 1;
			break;
		case 'o':
			string = optarg;
			parseSubOptions(string);
			break;
		}
	}

	sanityCheckOpts();
	if (InputImage == NULL && (optind < 0 || optind >= argc))
		usage();

	openFS(argv[optind], &ifd, &ofd);
	readBPB(ifd);

	/*
	 * -m mountable fs check.  This call will not return.
	 */
	if (Mflag)
		confirmMountable(argv[optind], ifd);

	/*
	 *  Pass 1: Find any bad clusters and adjust the FAT and directory
	 *	entries accordingly
	 */
	passOne(ifd);

	/*
	 *  XXX - future passes?
	 *	Ideas:
	 *	    Data relocation for bad clusters with partial read success?
	 *	    Syncing backup FAT copies with main copy?
	 *	    Syncing backup root sector for FAT32?
	 */

	/*
	 *  No problems if we made it this far.
	 */
	printSummary(stdout);
	writeBackChanges(ofd);
	return (0);
}