/* * 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 #include #include #include #include #include #include #include #include #include #include #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); }