From 0121b42aca389e437df57a6e6add76a614e8bae2 Mon Sep 17 00:00:00 2001 From: "David E. O'Brien" Date: Mon, 9 Jul 2001 10:35:18 +0000 Subject: Add fsck_msdosfs. Obtained from: NetBSD --- sbin/fsck_msdosfs/Makefile | 11 + sbin/fsck_msdosfs/boot.c | 268 ++++++++++ sbin/fsck_msdosfs/check.c | 194 ++++++++ sbin/fsck_msdosfs/dir.c | 1017 ++++++++++++++++++++++++++++++++++++++ sbin/fsck_msdosfs/dosfs.h | 144 ++++++ sbin/fsck_msdosfs/ext.h | 144 ++++++ sbin/fsck_msdosfs/fat.c | 659 ++++++++++++++++++++++++ sbin/fsck_msdosfs/fsck_msdosfs.8 | 114 +++++ sbin/fsck_msdosfs/main.c | 165 +++++++ 9 files changed, 2716 insertions(+) create mode 100644 sbin/fsck_msdosfs/Makefile create mode 100644 sbin/fsck_msdosfs/boot.c create mode 100644 sbin/fsck_msdosfs/check.c create mode 100644 sbin/fsck_msdosfs/dir.c create mode 100644 sbin/fsck_msdosfs/dosfs.h create mode 100644 sbin/fsck_msdosfs/ext.h create mode 100644 sbin/fsck_msdosfs/fat.c create mode 100644 sbin/fsck_msdosfs/fsck_msdosfs.8 create mode 100644 sbin/fsck_msdosfs/main.c (limited to 'sbin/fsck_msdosfs') diff --git a/sbin/fsck_msdosfs/Makefile b/sbin/fsck_msdosfs/Makefile new file mode 100644 index 000000000000..91d4afe226f2 --- /dev/null +++ b/sbin/fsck_msdosfs/Makefile @@ -0,0 +1,11 @@ +# $NetBSD: Makefile,v 1.6 1997/05/08 21:11:11 gwr Exp $ +# $FreeBSD$ + +PROG= fsck_msdosfs +MAN= fsck_msdosfs.8 +SRCS= main.c check.c boot.c fat.c dir.c fsutil.c +FSCK= ${.CURDIR}/../fsck +CFLAGS+= -I${FSCK} +.PATH: ${FSCK} + +.include diff --git a/sbin/fsck_msdosfs/boot.c b/sbin/fsck_msdosfs/boot.c new file mode 100644 index 000000000000..5c1ae5ad1cc3 --- /dev/null +++ b/sbin/fsck_msdosfs/boot.c @@ -0,0 +1,268 @@ +/* + * Copyright (C) 1995, 1997 Wolfgang Solfrank + * Copyright (c) 1995 Martin Husemann + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Martin Husemann + * and Wolfgang Solfrank. + * 4. 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 BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include +#ifndef lint +__RCSID("$NetBSD: boot.c,v 1.5 1997/10/17 11:19:23 ws Exp $"); +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include +#include +#include +#include +#include + +#include "ext.h" +#include "fsutil.h" + +int +readboot(dosfs, boot) + int dosfs; + struct bootblock *boot; +{ + u_char block[DOSBOOTBLOCKSIZE]; + u_char fsinfo[2 * DOSBOOTBLOCKSIZE]; + u_char backup[DOSBOOTBLOCKSIZE]; + int ret = FSOK; + + if (read(dosfs, block, sizeof block) < sizeof block) { + perror("could not read boot block"); + return FSFATAL; + } + + if (block[510] != 0x55 || block[511] != 0xaa) { + pfatal("Invalid signature in boot block: %02x%02x", block[511], block[510]); + return FSFATAL; + } + + memset(boot, 0, sizeof *boot); + boot->ValidFat = -1; + + /* decode bios parameter block */ + boot->BytesPerSec = block[11] + (block[12] << 8); + boot->SecPerClust = block[13]; + boot->ResSectors = block[14] + (block[15] << 8); + boot->FATs = block[16]; + boot->RootDirEnts = block[17] + (block[18] << 8); + boot->Sectors = block[19] + (block[20] << 8); + boot->Media = block[21]; + boot->FATsmall = block[22] + (block[23] << 8); + boot->SecPerTrack = block[24] + (block[25] << 8); + boot->Heads = block[26] + (block[27] << 8); + boot->HiddenSecs = block[28] + (block[29] << 8) + (block[30] << 16) + (block[31] << 24); + boot->HugeSectors = block[32] + (block[33] << 8) + (block[34] << 16) + (block[35] << 24); + + boot->FATsecs = boot->FATsmall; + + if (!boot->RootDirEnts) + boot->flags |= FAT32; + if (boot->flags & FAT32) { + boot->FATsecs = block[36] + (block[37] << 8) + + (block[38] << 16) + (block[39] << 24); + if (block[40] & 0x80) + boot->ValidFat = block[40] & 0x0f; + + /* check version number: */ + if (block[42] || block[43]) { + /* Correct? XXX */ + pfatal("Unknown filesystem version: %x.%x", + block[43], block[42]); + return FSFATAL; + } + boot->RootCl = block[44] + (block[45] << 8) + + (block[46] << 16) + (block[47] << 24); + boot->FSInfo = block[48] + (block[49] << 8); + boot->Backup = block[50] + (block[51] << 8); + + if (lseek(dosfs, boot->FSInfo * boot->BytesPerSec, SEEK_SET) + != boot->FSInfo * boot->BytesPerSec + || read(dosfs, fsinfo, sizeof fsinfo) + != sizeof fsinfo) { + perror("could not read fsinfo block"); + return FSFATAL; + } + if (memcmp(fsinfo, "RRaA", 4) + || memcmp(fsinfo + 0x1e4, "rrAa", 4) + || fsinfo[0x1fc] + || fsinfo[0x1fd] + || fsinfo[0x1fe] != 0x55 + || fsinfo[0x1ff] != 0xaa + || fsinfo[0x3fc] + || fsinfo[0x3fd] + || fsinfo[0x3fe] != 0x55 + || fsinfo[0x3ff] != 0xaa) { + pwarn("Invalid signature in fsinfo block"); + if (ask(0, "fix")) { + memcpy(fsinfo, "RRaA", 4); + memcpy(fsinfo + 0x1e4, "rrAa", 4); + fsinfo[0x1fc] = fsinfo[0x1fd] = 0; + fsinfo[0x1fe] = 0x55; + fsinfo[0x1ff] = 0xaa; + fsinfo[0x3fc] = fsinfo[0x3fd] = 0; + fsinfo[0x3fe] = 0x55; + fsinfo[0x3ff] = 0xaa; + if (lseek(dosfs, boot->FSInfo * boot->BytesPerSec, SEEK_SET) + != boot->FSInfo * boot->BytesPerSec + || write(dosfs, fsinfo, sizeof fsinfo) + != sizeof fsinfo) { + perror("Unable to write FSInfo"); + return FSFATAL; + } + ret = FSBOOTMOD; + } else + boot->FSInfo = 0; + } + if (boot->FSInfo) { + boot->FSFree = fsinfo[0x1e8] + (fsinfo[0x1e9] << 8) + + (fsinfo[0x1ea] << 16) + + (fsinfo[0x1eb] << 24); + boot->FSNext = fsinfo[0x1ec] + (fsinfo[0x1ed] << 8) + + (fsinfo[0x1ee] << 16) + + (fsinfo[0x1ef] << 24); + } + + if (lseek(dosfs, boot->Backup * boot->BytesPerSec, SEEK_SET) + != boot->Backup * boot->BytesPerSec + || read(dosfs, backup, sizeof backup) != sizeof backup) { + perror("could not read backup bootblock"); + return FSFATAL; + } + if (memcmp(block, backup, DOSBOOTBLOCKSIZE)) { + /* Correct? XXX */ + pfatal("backup doesn't compare to primary bootblock"); + return FSFATAL; + } + /* Check backup FSInfo? XXX */ + } + + boot->ClusterOffset = (boot->RootDirEnts * 32 + boot->BytesPerSec - 1) + / boot->BytesPerSec + + boot->ResSectors + + boot->FATs * boot->FATsecs + - CLUST_FIRST * boot->SecPerClust; + + if (boot->BytesPerSec % DOSBOOTBLOCKSIZE != 0) { + pfatal("Invalid sector size: %u", boot->BytesPerSec); + return FSFATAL; + } + if (boot->SecPerClust == 0) { + pfatal("Invalid cluster size: %u", boot->SecPerClust); + return FSFATAL; + } + if (boot->Sectors) { + boot->HugeSectors = 0; + boot->NumSectors = boot->Sectors; + } else + boot->NumSectors = boot->HugeSectors; + boot->NumClusters = (boot->NumSectors - boot->ClusterOffset) / boot->SecPerClust; + + if (boot->flags&FAT32) + boot->ClustMask = CLUST32_MASK; + else if (boot->NumClusters < (CLUST_RSRVD&CLUST12_MASK)) + boot->ClustMask = CLUST12_MASK; + else if (boot->NumClusters < (CLUST_RSRVD&CLUST16_MASK)) + boot->ClustMask = CLUST16_MASK; + else { + pfatal("Filesystem too big (%u clusters) for non-FAT32 partition", + boot->NumClusters); + return FSFATAL; + } + + switch (boot->ClustMask) { + case CLUST32_MASK: + boot->NumFatEntries = (boot->FATsecs * boot->BytesPerSec) / 4; + break; + case CLUST16_MASK: + boot->NumFatEntries = (boot->FATsecs * boot->BytesPerSec) / 2; + break; + default: + boot->NumFatEntries = (boot->FATsecs * boot->BytesPerSec * 2) / 3; + break; + } + + if (boot->NumFatEntries < boot->NumClusters) { + pfatal("FAT size too small, %u entries won't fit into %u sectors\n", + boot->NumClusters, boot->FATsecs); + return FSFATAL; + } + boot->ClusterSize = boot->BytesPerSec * boot->SecPerClust; + + boot->NumFiles = 1; + boot->NumFree = 0; + + return ret; +} + +int +writefsinfo(dosfs, boot) + int dosfs; + struct bootblock *boot; +{ + u_char fsinfo[2 * DOSBOOTBLOCKSIZE]; + + if (lseek(dosfs, boot->FSInfo * boot->BytesPerSec, SEEK_SET) + != boot->FSInfo * boot->BytesPerSec + || read(dosfs, fsinfo, sizeof fsinfo) != sizeof fsinfo) { + perror("could not read fsinfo block"); + return FSFATAL; + } + fsinfo[0x1e8] = (u_char)boot->FSFree; + fsinfo[0x1e9] = (u_char)(boot->FSFree >> 8); + fsinfo[0x1ea] = (u_char)(boot->FSFree >> 16); + fsinfo[0x1eb] = (u_char)(boot->FSFree >> 24); + fsinfo[0x1ec] = (u_char)boot->FSNext; + fsinfo[0x1ed] = (u_char)(boot->FSNext >> 8); + fsinfo[0x1ee] = (u_char)(boot->FSNext >> 16); + fsinfo[0x1ef] = (u_char)(boot->FSNext >> 24); + if (lseek(dosfs, boot->FSInfo * boot->BytesPerSec, SEEK_SET) + != boot->FSInfo * boot->BytesPerSec + || write(dosfs, fsinfo, sizeof fsinfo) + != sizeof fsinfo) { + perror("Unable to write FSInfo"); + return FSFATAL; + } + /* + * Technically, we should return FSBOOTMOD here. + * + * However, since Win95 OSR2 (the first M$ OS that has + * support for FAT32) doesn't maintain the FSINFO block + * correctly, it has to be fixed pretty often. + * + * Therefor, we handle the FSINFO block only informally, + * fixing it if neccessary, but otherwise ignoring the + * fact that it was incorrect. + */ + return 0; +} diff --git a/sbin/fsck_msdosfs/check.c b/sbin/fsck_msdosfs/check.c new file mode 100644 index 000000000000..2a7ccc71ed01 --- /dev/null +++ b/sbin/fsck_msdosfs/check.c @@ -0,0 +1,194 @@ +/* + * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank + * Copyright (c) 1995 Martin Husemann + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Martin Husemann + * and Wolfgang Solfrank. + * 4. 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 BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include +#ifndef lint +__RCSID("$NetBSD: check.c,v 1.10 2000/04/25 23:02:51 jdolecek Exp $"); +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include + +#include "ext.h" +#include "fsutil.h" + +int +checkfilesys(fname) + const char *fname; +{ + int dosfs; + struct bootblock boot; + struct fatEntry *fat = NULL; + int i, finish_dosdirsection=0; + int mod = 0; + int ret = 8; + + rdonly = alwaysno; + if (!preen) + printf("** %s", fname); + + dosfs = open(fname, rdonly ? O_RDONLY : O_RDWR, 0); + if (dosfs < 0 && !rdonly) { + dosfs = open(fname, O_RDONLY, 0); + if (dosfs >= 0) + pwarn(" (NO WRITE)\n"); + else if (!preen) + printf("\n"); + rdonly = 1; + } else if (!preen) + printf("\n"); + + if (dosfs < 0) { + perror("Can't open"); + return 8; + } + + if (readboot(dosfs, &boot) != FSOK) { + close(dosfs); + printf("\n"); + return 8; + } + + if (!preen) { + if (boot.ValidFat < 0) + printf("** Phase 1 - Read and Compare FATs\n"); + else + printf("** Phase 1 - Read FAT\n"); + } + + mod |= readfat(dosfs, &boot, boot.ValidFat >= 0 ? boot.ValidFat : 0, &fat); + if (mod & FSFATAL) { + close(dosfs); + return 8; + } + + if (boot.ValidFat < 0) + for (i = 1; i < boot.FATs; i++) { + struct fatEntry *currentFat; + + mod |= readfat(dosfs, &boot, i, ¤tFat); + + if (mod & FSFATAL) + goto out; + + mod |= comparefat(&boot, fat, currentFat, i); + free(currentFat); + if (mod & FSFATAL) + goto out; + } + + if (!preen) + printf("** Phase 2 - Check Cluster Chains\n"); + + mod |= checkfat(&boot, fat); + if (mod & FSFATAL) + goto out; + /* delay writing FATs */ + + if (!preen) + printf("** Phase 3 - Checking Directories\n"); + + mod |= resetDosDirSection(&boot, fat); + finish_dosdirsection = 1; + if (mod & FSFATAL) + goto out; + /* delay writing FATs */ + + mod |= handleDirTree(dosfs, &boot, fat); + if (mod & FSFATAL) + goto out; + + if (!preen) + printf("** Phase 4 - Checking for Lost Files\n"); + + mod |= checklost(dosfs, &boot, fat); + if (mod & FSFATAL) + goto out; + + /* now write the FATs */ + if (mod & FSFATMOD) { + if (ask(1, "Update FATs")) { + mod |= writefat(dosfs, &boot, fat, mod & FSFIXFAT); + if (mod & FSFATAL) + goto out; + } else + mod |= FSERROR; + } + + if (boot.NumBad) + pwarn("%d files, %d free (%d clusters), %d bad (%d clusters)\n", + boot.NumFiles, + boot.NumFree * boot.ClusterSize / 1024, boot.NumFree, + boot.NumBad * boot.ClusterSize / 1024, boot.NumBad); + else + pwarn("%d files, %d free (%d clusters)\n", + boot.NumFiles, + boot.NumFree * boot.ClusterSize / 1024, boot.NumFree); + + if (mod && (mod & FSERROR) == 0) { + if (mod & FSDIRTY) { + if (ask(1, "MARK FILE SYSTEM CLEAN") == 0) + mod &= ~FSDIRTY; + + if (mod & FSDIRTY) { + pwarn("MARKING FILE SYSTEM CLEAN\n"); + mod |= writefat(dosfs, &boot, fat, 1); + } else { + pwarn("\n***** FILE SYSTEM IS LEFT MARKED AS DIRTY *****\n"); + mod |= FSERROR; /* file system not clean */ + } + } + } + + if (mod & (FSFATAL | FSERROR)) + goto out; + + ret = 0; + + out: + if (finish_dosdirsection) + finishDosDirSection(); + free(fat); + close(dosfs); + + if (mod & (FSFATMOD|FSDIRMOD)) + pwarn("\n***** FILE SYSTEM WAS MODIFIED *****\n"); + + return ret; +} diff --git a/sbin/fsck_msdosfs/dir.c b/sbin/fsck_msdosfs/dir.c new file mode 100644 index 000000000000..64636cbd0e6f --- /dev/null +++ b/sbin/fsck_msdosfs/dir.c @@ -0,0 +1,1017 @@ +/* + * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank + * Copyright (c) 1995 Martin Husemann + * Some structure declaration borrowed from Paul Popelka + * (paulp@uts.amdahl.com), see /sys/msdosfs/ for reference. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Martin Husemann + * and Wolfgang Solfrank. + * 4. 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 BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include +#ifndef lint +__RCSID("$NetBSD: dir.c,v 1.14 1998/08/25 19:18:15 ross Exp $"); +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ext.h" +#include "fsutil.h" + +#define SLOT_EMPTY 0x00 /* slot has never been used */ +#define SLOT_E5 0x05 /* the real value is 0xe5 */ +#define SLOT_DELETED 0xe5 /* file in this slot deleted */ + +#define ATTR_NORMAL 0x00 /* normal file */ +#define ATTR_READONLY 0x01 /* file is readonly */ +#define ATTR_HIDDEN 0x02 /* file is hidden */ +#define ATTR_SYSTEM 0x04 /* file is a system file */ +#define ATTR_VOLUME 0x08 /* entry is a volume label */ +#define ATTR_DIRECTORY 0x10 /* entry is a directory name */ +#define ATTR_ARCHIVE 0x20 /* file is new or modified */ + +#define ATTR_WIN95 0x0f /* long name record */ + +/* + * This is the format of the contents of the deTime field in the direntry + * structure. + * We don't use bitfields because we don't know how compilers for + * arbitrary machines will lay them out. + */ +#define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */ +#define DT_2SECONDS_SHIFT 0 +#define DT_MINUTES_MASK 0x7E0 /* minutes */ +#define DT_MINUTES_SHIFT 5 +#define DT_HOURS_MASK 0xF800 /* hours */ +#define DT_HOURS_SHIFT 11 + +/* + * This is the format of the contents of the deDate field in the direntry + * structure. + */ +#define DD_DAY_MASK 0x1F /* day of month */ +#define DD_DAY_SHIFT 0 +#define DD_MONTH_MASK 0x1E0 /* month */ +#define DD_MONTH_SHIFT 5 +#define DD_YEAR_MASK 0xFE00 /* year - 1980 */ +#define DD_YEAR_SHIFT 9 + + +/* dir.c */ +static struct dosDirEntry *newDosDirEntry __P((void)); +static void freeDosDirEntry __P((struct dosDirEntry *)); +static struct dirTodoNode *newDirTodo __P((void)); +static void freeDirTodo __P((struct dirTodoNode *)); +static char *fullpath __P((struct dosDirEntry *)); +static u_char calcShortSum __P((u_char *)); +static int delete __P((int, struct bootblock *, struct fatEntry *, cl_t, int, + cl_t, int, int)); +static int removede __P((int, struct bootblock *, struct fatEntry *, u_char *, + u_char *, cl_t, cl_t, cl_t, char *, int)); +static int checksize __P((struct bootblock *, struct fatEntry *, u_char *, + struct dosDirEntry *)); +static int readDosDirSection __P((int, struct bootblock *, struct fatEntry *, + struct dosDirEntry *)); + +/* + * Manage free dosDirEntry structures. + */ +static struct dosDirEntry *freede; + +static struct dosDirEntry * +newDosDirEntry() +{ + struct dosDirEntry *de; + + if (!(de = freede)) { + if (!(de = (struct dosDirEntry *)malloc(sizeof *de))) + return 0; + } else + freede = de->next; + return de; +} + +static void +freeDosDirEntry(de) + struct dosDirEntry *de; +{ + de->next = freede; + freede = de; +} + +/* + * The same for dirTodoNode structures. + */ +static struct dirTodoNode *freedt; + +static struct dirTodoNode * +newDirTodo() +{ + struct dirTodoNode *dt; + + if (!(dt = freedt)) { + if (!(dt = (struct dirTodoNode *)malloc(sizeof *dt))) + return 0; + } else + freedt = dt->next; + return dt; +} + +static void +freeDirTodo(dt) + struct dirTodoNode *dt; +{ + dt->next = freedt; + freedt = dt; +} + +/* + * The stack of unread directories + */ +struct dirTodoNode *pendingDirectories = NULL; + +/* + * Return the full pathname for a directory entry. + */ +static char * +fullpath(dir) + struct dosDirEntry *dir; +{ + static char namebuf[MAXPATHLEN + 1]; + char *cp, *np; + int nl; + + cp = namebuf + sizeof namebuf - 1; + *cp = '\0'; + do { + np = dir->lname[0] ? dir->lname : dir->name; + nl = strlen(np); + if ((cp -= nl) <= namebuf + 1) + break; + memcpy(cp, np, nl); + *--cp = '/'; + } while ((dir = dir->parent) != NULL); + if (dir) + *--cp = '?'; + else + cp++; + return cp; +} + +/* + * Calculate a checksum over an 8.3 alias name + */ +static u_char +calcShortSum(p) + u_char *p; +{ + u_char sum = 0; + int i; + + for (i = 0; i < 11; i++) { + sum = (sum << 7)|(sum >> 1); /* rotate right */ + sum += p[i]; + } + + return sum; +} + +/* + * Global variables temporarily used during a directory scan + */ +static char longName[DOSLONGNAMELEN] = ""; +static u_char *buffer = NULL; +static u_char *delbuf = NULL; + +struct dosDirEntry *rootDir; +static struct dosDirEntry *lostDir; + +/* + * Init internal state for a new directory scan. + */ +int +resetDosDirSection(boot, fat) + struct bootblock *boot; + struct fatEntry *fat; +{ + int b1, b2; + cl_t cl; + int ret = FSOK; + + b1 = boot->RootDirEnts * 32; + b2 = boot->SecPerClust * boot->BytesPerSec; + + if (!(buffer = malloc(b1 > b2 ? b1 : b2)) + || !(delbuf = malloc(b2)) + || !(rootDir = newDosDirEntry())) { + perror("No space for directory"); + return FSFATAL; + } + memset(rootDir, 0, sizeof *rootDir); + if (boot->flags & FAT32) { + if (boot->RootCl < CLUST_FIRST || boot->RootCl >= boot->NumClusters) { + pfatal("Root directory starts with cluster out of range(%u)", + boot->RootCl); + return FSFATAL; + } + cl = fat[boot->RootCl].next; + if (cl < CLUST_FIRST + || (cl >= CLUST_RSRVD && cl< CLUST_EOFS) + || fat[boot->RootCl].head != boot->RootCl) { + if (cl == CLUST_FREE) + pwarn("Root directory starts with free cluster\n"); + else if (cl >= CLUST_RSRVD) + pwarn("Root directory starts with cluster marked %s\n", + rsrvdcltype(cl)); + else { + pfatal("Root directory doesn't start a cluster chain"); + return FSFATAL; + } + if (ask(1, "Fix")) { + fat[boot->RootCl].next = CLUST_FREE; + ret = FSFATMOD; + } else + ret = FSFATAL; + } + + fat[boot->RootCl].flags |= FAT_USED; + rootDir->head = boot->RootCl; + } + + return ret; +} + +/* + * Cleanup after a directory scan + */ +void +finishDosDirSection() +{ + struct dirTodoNode *p, *np; + struct dosDirEntry *d, *nd; + + for (p = pendingDirectories; p; p = np) { + np = p->next; + freeDirTodo(p); + } + pendingDirectories = 0; + for (d = rootDir; d; d = nd) { + if ((nd = d->child) != NULL) { + d->child = 0; + continue; + } + if (!(nd = d->next)) + nd = d->parent; + freeDosDirEntry(d); + } + rootDir = lostDir = NULL; + free(buffer); + free(delbuf); + buffer = NULL; + delbuf = NULL; +} + +/* + * Delete directory entries between startcl, startoff and endcl, endoff. + */ +static int +delete(f, boot, fat, startcl, startoff, endcl, endoff, notlast) + int f; + struct bootblock *boot; + struct fatEntry *fat; + cl_t startcl; + int startoff; + cl_t endcl; + int endoff; + int notlast; +{ + u_char *s, *e; + off_t off; + int clsz = boot->SecPerClust * boot->BytesPerSec; + + s = delbuf + startoff; + e = delbuf + clsz; + while (startcl >= CLUST_FIRST && startcl < boot->NumClusters) { + if (startcl == endcl) { + if (notlast) + break; + e = delbuf + endoff; + } + off = startcl * boot->SecPerClust + boot->ClusterOffset; + off *= boot->BytesPerSec; + if (lseek(f, off, SEEK_SET) != off + || read(f, delbuf, clsz) != clsz) { + perror("Unable to read directory"); + return FSFATAL; + } + while (s < e) { + *s = SLOT_DELETED; + s += 32; + } + if (lseek(f, off, SEEK_SET) != off + || write(f, delbuf, clsz) != clsz) { + perror("Unable to write directory"); + return FSFATAL; + } + if (startcl == endcl) + break; + startcl = fat[startcl].next; + s = delbuf; + } + return FSOK; +} + +static int +removede(f, boot, fat, start, end, startcl, endcl, curcl, path, type) + int f; + struct bootblock *boot; + struct fatEntry *fat; + u_char *start; + u_char *end; + cl_t startcl; + cl_t endcl; + cl_t curcl; + char *path; + int type; +{ + switch (type) { + case 0: + pwarn("Invalid long filename entry for %s\n", path); + break; + case 1: + pwarn("Invalid long filename entry at end of directory %s\n", path); + break; + case 2: + pwarn("Invalid long filename entry for volume label\n"); + break; + } + if (ask(0, "Remove")) { + if (startcl != curcl) { + if (delete(f, boot, fat, + startcl, start - buffer, + endcl, end - buffer, + endcl == curcl) == FSFATAL) + return FSFATAL; + start = buffer; + } + if (endcl == curcl) + for (; start < end; start += 32) + *start = SLOT_DELETED; + return FSDIRMOD; + } + return FSERROR; +} + +/* + * Check an in-memory file entry + */ +static int +checksize(boot, fat, p, dir) + struct bootblock *boot; + struct fatEntry *fat; + u_char *p; + struct dosDirEntry *dir; +{ + /* + * Check size on ordinary files + */ + int32_t physicalSize; + + if (dir->head == CLUST_FREE) + physicalSize = 0; + else { + if (dir->head < CLUST_FIRST || dir->head >= boot->NumClusters) + return FSERROR; + physicalSize = fat[dir->head].length * boot->ClusterSize; + } + if (physicalSize < dir->size) { + pwarn("size of %s is %u, should at most be %u\n", + fullpath(dir), dir->size, physicalSize); + if (ask(1, "Truncate")) { + dir->size = physicalSize; + p[28] = (u_char)physicalSize; + p[29] = (u_char)(physicalSize >> 8); + p[30] = (u_char)(physicalSize >> 16); + p[31] = (u_char)(physicalSize >> 24); + return FSDIRMOD; + } else + return FSERROR; + } else if (physicalSize - dir->size >= boot->ClusterSize) { + pwarn("%s has too many clusters allocated\n", + fullpath(dir)); + if (ask(1, "Drop superfluous clusters")) { + cl_t cl; + u_int32_t sz = 0; + + for (cl = dir->head; (sz += boot->ClusterSize) < dir->size;) + cl = fat[cl].next; + clearchain(boot, fat, fat[cl].next); + fat[cl].next = CLUST_EOF; + return FSFATMOD; + } else + return FSERROR; + } + return FSOK; +} + +/* + * Read a directory and + * - resolve long name records + * - enter file and directory records into the parent's list + * - push directories onto the todo-stack + */ +static int +readDosDirSection(f, boot, fat, dir) + int f; + struct bootblock *boot; + struct fatEntry *fat; + struct dosDirEntry *dir; +{ + struct dosDirEntry dirent, *d; + u_char *p, *vallfn, *invlfn, *empty; + off_t off; + int i, j, k, last; + cl_t cl, valcl = ~0, invcl = ~0, empcl = ~0; + char *t; + u_int lidx = 0; + int shortSum; + int mod = FSOK; +#define THISMOD 0x8000 /* Only used within this routine */ + + cl = dir->head; + if (dir->parent && (cl < CLUST_FIRST || cl >= boot->NumClusters)) { + /* + * Already handled somewhere else. + */ + return FSOK; + } + shortSum = -1; + vallfn = invlfn = empty = NULL; + do { + if (!(boot->flags & FAT32) && !dir->parent) { + last = boot->RootDirEnts * 32; + off = boot->ResSectors + boot->FATs * boot->FATsecs; + } else { + last = boot->SecPerClust * boot->BytesPerSec; + off = cl * boot->SecPerClust + boot->ClusterOffset; + } + + off *= boot->BytesPerSec; + if (lseek(f, off, SEEK_SET) != off + || read(f, buffer, last) != last) { + perror("Unable to read directory"); + return FSFATAL; + } + last /= 32; + /* + * Check `.' and `..' entries here? XXX + */ + for (p = buffer, i = 0; i < last; i++, p += 32) { + if (dir->fsckflags & DIREMPWARN) { + *p = SLOT_EMPTY; + continue; + } + + if (*p == SLOT_EMPTY || *p == SLOT_DELETED) { + if (*p == SLOT_EMPTY) { + dir->fsckflags |= DIREMPTY; + empty = p; + empcl = cl; + } + continue; + } + + if (dir->fsckflags & DIREMPTY) { + if (!(dir->fsckflags & DIREMPWARN)) { + pwarn("%s has entries after end of directory\n", + fullpath(dir)); + if (ask(1, "Extend")) { + u_char *q; + + dir->fsckflags &= ~DIREMPTY; + if (delete(f, boot, fat, + empcl, empty - buffer, + cl, p - buffer, 1) == FSFATAL) + return FSFATAL; + q = empcl == cl ? empty : buffer; + for (; q < p; q += 32) + *q = SLOT_DELETED; + mod |= THISMOD|FSDIRMOD; + } else if (ask(0, "Truncate")) + dir->fsckflags |= DIREMPWARN; + } + if (dir->fsckflags & DIREMPWARN) { + *p = SLOT_DELETED; + mod |= THISMOD|FSDIRMOD; + continue; + } else if (dir->fsckflags & DIREMPTY) + mod |= FSERROR; + empty = NULL; + } + + if (p[11] == ATTR_WIN95) { + if (*p & LRFIRST) { + if (shortSum != -1) { + if (!invlfn) { + invlfn = vallfn; + invcl = valcl; + } + } + memset(longName, 0, sizeof longName); + shortSum = p[13]; + vallfn = p; + valcl = cl; + } else if (shortSum != p[13] + || lidx != (*p & LRNOMASK)) { + if (!invlfn) { + invlfn = vallfn; + invcl = valcl; + } + if (!invlfn) { + invlfn = p; + invcl = cl; + } + vallfn = NULL; + } + lidx = *p & LRNOMASK; + t = longName + --lidx * 13; + for (k = 1; k < 11 && t < longName + sizeof(longName); k += 2) { + if (!p[k] && !p[k + 1]) + break; + *t++ = p[k]; + /* + * Warn about those unusable chars in msdosfs here? XXX + */ + if (p[k + 1]) + t[-1] = '?'; + } + if (k >= 11) + for (k = 14; k < 26 && t < longName + sizeof(longName); k += 2) { + if (!p[k] && !p[k + 1]) + break; + *t++ = p[k]; + if (p[k + 1]) + t[-1] = '?'; + } + if (k >= 26) + for (k = 28; k < 32 && t < longName + sizeof(longName); k += 2) { + if (!p[k] && !p[k + 1]) + break; + *t++ = p[k]; + if (p[k + 1]) + t[-1] = '?'; + } + if (t >= longName + sizeof(longName)) { + pwarn("long filename too long\n"); + if (!invlfn) { + invlfn = vallfn; + invcl = valcl; + } + vallfn = NULL; + } + if (p[26] | (p[27] << 8)) { + pwarn("long filename record cluster start != 0\n"); + if (!invlfn) { + invlfn = vallfn; + invcl = cl; + } + vallfn = NULL; + } + continue; /* long records don't carry further + * information */ + } + + /* + * This is a standard msdosfs directory entry. + */ + memset(&dirent, 0, sizeof dirent); + + /* + * it's a short name record, but we need to know + * more, so get the flags first. + */ + dirent.flags = p[11]; + + /* + * Translate from 850 to ISO here XXX + */ + for (j = 0; j < 8; j++) + dirent.name[j] = p[j]; + dirent.name[8] = '\0'; + for (k = 7; k >= 0 && dirent.name[k] == ' '; k--) + dirent.name[k] = '\0'; + if (dirent.name[k] != '\0') + k++; + if (dirent.name[0] == SLOT_E5) + dirent.name[0] = 0xe5; + + if (dirent.flags & ATTR_VOLUME) { + if (vallfn || invlfn) { + mod |= removede(f, boot, fat, + invlfn ? invlfn : vallfn, p, + invlfn ? invcl : valcl, -1, 0, + fullpath(dir), 2); + vallfn = NULL; + invlfn = NULL; + } + continue; + } + + if (p[8] != ' ') + dirent.name[k++] = '.'; + for (j = 0; j < 3; j++) + dirent.name[k++] = p[j+8]; + dirent.name[k] = '\0'; + for (k--; k >= 0 && dirent.name[k] == ' '; k--) + dirent.name[k] = '\0'; + + if (vallfn && shortSum != calcShortSum(p)) { + if (!invlfn) { + invlfn = vallfn; + invcl = valcl; + } + vallfn = NULL; + } + dirent.head = p[26] | (p[27] << 8); + if (boot->ClustMask == CLUST32_MASK) + dirent.head |= (p[20] << 16) | (p[21] << 24); + dirent.size = p[28] | (p[29] << 8) | (p[30] << 16) | (p[31] << 24); + if (vallfn) { + strcpy(dirent.lname, longName); + longName[0] = '\0'; + shortSum = -1; + } + + dirent.parent = dir; + dirent.next = dir->child; + + if (invlfn) { + mod |= k = removede(f, boot, fat, + invlfn, vallfn ? vallfn : p, + invcl, vallfn ? valcl : cl, cl, + fullpath(&dirent), 0); + if (mod & FSFATAL) + return FSFATAL; + if (vallfn + ? (valcl == cl && vallfn != buffer) + : p != buffer) + if (k & FSDIRMOD) + mod |= THISMOD; + } + + vallfn = NULL; /* not used any longer */ + invlfn = NULL; + + if (dirent.size == 0 && !(dirent.flags & ATTR_DIRECTORY)) { + if (dirent.head != 0) { + pwarn("%s has clusters, but size 0\n", + fullpath(&dirent)); + if (ask(1, "Drop allocated clusters")) { + p[26] = p[27] = 0; + if (boot->ClustMask == CLUST32_MASK) + p[20] = p[21] = 0; + clearchain(boot, fat, dirent.head); + dirent.head = 0; + mod |= THISMOD|FSDIRMOD|FSFATMOD; + } else + mod |= FSERROR; + } + } else if (dirent.head == 0 + && !strcmp(dirent.name, "..") + && dir->parent /* XXX */ + && !dir->parent->parent) { + /* + * Do nothing, the parent is the root + */ + } else if (dirent.head < CLUST_FIRST + || dirent.head >= boot->NumClusters + || fat[dirent.head].next == CLUST_FREE + || (fat[dirent.head].next >= CLUST_RSRVD + && fat[dirent.head].next < CLUST_EOFS) + || fat[dirent.head].head != dirent.head) { + if (dirent.head == 0) + pwarn("%s has no clusters\n", + fullpath(&dirent)); + else if (dirent.head < CLUST_FIRST + || dirent.head >= boot->NumClusters) + pwarn("%s starts with cluster out of range(%u)\n", + fullpath(&dirent), + dirent.head); + else if (fat[dirent.head].next == CLUST_FREE) + pwarn("%s starts with free cluster\n", + fullpath(&dirent)); + else if (fat[dirent.head].next >= CLUST_RSRVD) + pwarn("%s starts with cluster marked %s\n", + fullpath(&dirent), + rsrvdcltype(fat[dirent.head].next)); + else + pwarn("%s doesn't start a cluster chain\n", + fullpath(&dirent)); + if (dirent.flags & ATTR_DIRECTORY) { + if (ask(0, "Remove")) { + *p = SLOT_DELETED; + mod |= THISMOD|FSDIRMOD; + } else + mod |= FSERROR; + continue; + } else { + if (ask(1, "Truncate")) { + p[28] = p[29] = p[30] = p[31] = 0; + p[26] = p[27] = 0; + if (boot->ClustMask == CLUST32_MASK) + p[20] = p[21] = 0; + dirent.size = 0; + mod |= THISMOD|FSDIRMOD; + } else + mod |= FSERROR; + } + } + + if (dirent.head >= CLUST_FIRST && dirent.head < boot->NumClusters) + fat[dirent.head].flags |= FAT_USED; + + if (dirent.flags & ATTR_DIRECTORY) { + /* + * gather more info for directories + */ + struct dirTodoNode *n; + + if (dirent.size) { + pwarn("Directory %s has size != 0\n", + fullpath(&dirent)); + if (ask(1, "Correct")) { + p[28] = p[29] = p[30] = p[31] = 0; + dirent.size = 0; + mod |= THISMOD|FSDIRMOD; + } else + mod |= FSERROR; + } + /* + * handle `.' and `..' specially + */ + if (strcmp(dirent.name, ".") == 0) { + if (dirent.head != dir->head) { + pwarn("`.' entry in %s has incorrect start cluster\n", + fullpath(dir)); + if (ask(1, "Correct")) { + dirent.head = dir->head; + p[26] = (u_char)dirent.head; + p[27] = (u_char)(dirent.head >> 8); + if (boot->ClustMask == CLUST32_MASK) { + p[20] = (u_char)(dirent.head >> 16); + p[21] = (u_char)(dirent.head >> 24); + } + mod |= THISMOD|FSDIRMOD; + } else + mod |= FSERROR; + } + continue; + } + if (strcmp(dirent.name, "..") == 0) { + if (dir->parent) { /* XXX */ + if (!dir->parent->parent) { + if (dirent.head) { + pwarn("`..' entry in %s has non-zero start cluster\n", + fullpath(dir)); + if (ask(1, "Correct")) { + dirent.head = 0; + p[26] = p[27] = 0; + if (boot->ClustMask == CLUST32_MASK) + p[20] = p[21] = 0; + mod |= THISMOD|FSDIRMOD; + } else + mod |= FSERROR; + } + } else if (dirent.head != dir->parent->head) { + pwarn("`..' entry in %s has incorrect start cluster\n", + fullpath(dir)); + if (ask(1, "Correct")) { + dirent.head = dir->parent->head; + p[26] = (u_char)dirent.head; + p[27] = (u_char)(dirent.head >> 8); + if (boot->ClustMask == CLUST32_MASK) { + p[20] = (u_char)(dirent.head >> 16); + p[21] = (u_char)(dirent.head >> 24); + } + mod |= THISMOD|FSDIRMOD; + } else + mod |= FSERROR; + } + } + continue; + } + + /* create directory tree node */ + if (!(d = newDosDirEntry())) { + perror("No space for directory"); + return FSFATAL; + } + memcpy(d, &dirent, sizeof(struct dosDirEntry)); + /* link it into the tree */ + dir->child = d; + + /* Enter this directory into the todo list */ + if (!(n = newDirTodo())) { + perror("No space for todo list"); + return FSFATAL; + } + n->next = pendingDirectories; + n->dir = d; + pendingDirectories = n; + } else { + mod |= k = checksize(boot, fat, p, &dirent); + if (k & FSDIRMOD) + mod |= THISMOD; + } + boot->NumFiles++; + } + if (mod & THISMOD) { + last *= 32; + if (lseek(f, off, SEEK_SET) != off + || write(f, buffer, last) != last) { + perror("Unable to write directory"); + return FSFATAL; + } + mod &= ~THISMOD; + } + } while ((cl = fat[cl].next) >= CLUST_FIRST && cl < boot->NumClusters); + if (invlfn || vallfn) + mod |= removede(f, boot, fat, + invlfn ? invlfn : vallfn, p, + invlfn ? invcl : valcl, -1, 0, + fullpath(dir), 1); + return mod & ~THISMOD; +} + +int +handleDirTree(dosfs, boot, fat) + int dosfs; + struct bootblock *boot; + struct fatEntry *fat; +{ + int mod; + + mod = readDosDirSection(dosfs, boot, fat, rootDir); + if (mod & FSFATAL) + return FSFATAL; + + /* + * process the directory todo list + */ + while (pendingDirectories) { + struct dosDirEntry *dir = pendingDirectories->dir; + struct dirTodoNode *n = pendingDirectories->next; + + /* + * remove TODO entry now, the list might change during + * directory reads + */ + freeDirTodo(pendingDirectories); + pendingDirectories = n; + + /* + * handle subdirectory + */ + mod |= readDosDirSection(dosfs, boot, fat, dir); + if (mod & FSFATAL) + return FSFATAL; + } + + return mod; +} + +/* + * Try to reconnect a FAT chain into dir + */ +static u_char *lfbuf; +static cl_t lfcl; +static off_t lfoff; + +int +reconnect(dosfs, boot, fat, head) + int dosfs; + struct bootblock *boot; + struct fatEntry *fat; + cl_t head; +{ + struct dosDirEntry d; + u_char *p; + + if (!ask(1, "Reconnect")) + return FSERROR; + + if (!lostDir) { + for (lostDir = rootDir->child; lostDir; lostDir = lostDir->next) { + if (!strcmp(lostDir->name, LOSTDIR)) + break; + } + if (!lostDir) { /* Create LOSTDIR? XXX */ + pwarn("No %s directory\n", LOSTDIR); + return FSERROR; + } + } + if (!lfbuf) { + lfbuf = malloc(boot->ClusterSize); + if (!lfbuf) { + perror("No space for buffer"); + return FSFATAL; + } + p = NULL; + } else + p = lfbuf; + while (1) { + if (p) + for (; p < lfbuf + boot->ClusterSize; p += 32) + if (*p == SLOT_EMPTY + || *p == SLOT_DELETED) + break; + if (p && p < lfbuf + boot->ClusterSize) + break; + lfcl = p ? fat[lfcl].next : lostDir->head; + if (lfcl < CLUST_FIRST || lfcl >= boot->NumClusters) { + /* Extend LOSTDIR? XXX */ + pwarn("No space in %s\n", LOSTDIR); + return FSERROR; + } + lfoff = lfcl * boot->ClusterSize + + boot->ClusterOffset * boot->BytesPerSec; + if (lseek(dosfs, lfoff, SEEK_SET) != lfoff + || read(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) { + perror("could not read LOST.DIR"); + return FSFATAL; + } + p = lfbuf; + } + + boot->NumFiles++; + /* Ensure uniqueness of entry here! XXX */ + memset(&d, 0, sizeof d); + (void)snprintf(d.name, sizeof(d.name), "%u", head); + d.flags = 0; + d.head = head; + d.size = fat[head].length * boot->ClusterSize; + + memset(p, 0, 32); + memset(p, ' ', 11); + memcpy(p, d.name, strlen(d.name)); + p[26] = (u_char)d.head; + p[27] = (u_char)(d.head >> 8); + if (boot->ClustMask == CLUST32_MASK) { + p[20] = (u_char)(d.head >> 16); + p[21] = (u_char)(d.head >> 24); + } + p[28] = (u_char)d.size; + p[29] = (u_char)(d.size >> 8); + p[30] = (u_char)(d.size >> 16); + p[31] = (u_char)(d.size >> 24); + fat[head].flags |= FAT_USED; + if (lseek(dosfs, lfoff, SEEK_SET) != lfoff + || write(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) { + perror("could not write LOST.DIR"); + return FSFATAL; + } + return FSDIRMOD; +} + +void +finishlf() +{ + if (lfbuf) + free(lfbuf); + lfbuf = NULL; +} diff --git a/sbin/fsck_msdosfs/dosfs.h b/sbin/fsck_msdosfs/dosfs.h new file mode 100644 index 000000000000..b3abf552e3b8 --- /dev/null +++ b/sbin/fsck_msdosfs/dosfs.h @@ -0,0 +1,144 @@ +/* + * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank + * Copyright (c) 1995 Martin Husemann + * Some structure declaration borrowed from Paul Popelka + * (paulp@uts.amdahl.com), see /sys/msdosfs/ for reference. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Martin Husemann + * and Wolfgang Solfrank. + * 4. 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 BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * $NetBSD: dosfs.h,v 1.4 1997/01/03 14:32:48 ws Exp $ + * $FreeBSD$ + */ + +#ifndef DOSFS_H +#define DOSFS_H + +#define DOSBOOTBLOCKSIZE 512 + +typedef u_int32_t cl_t; /* type holding a cluster number */ + +/* + * architecture independent description of all the info stored in a + * FAT boot block. + */ +struct bootblock { + u_int BytesPerSec; /* bytes per sector */ + u_int SecPerClust; /* sectors per cluster */ + u_int ResSectors; /* number of reserved sectors */ + u_int FATs; /* number of FATs */ + u_int RootDirEnts; /* number of root directory entries */ + u_int Media; /* media descriptor */ + u_int FATsmall; /* number of sectors per FAT */ + u_int SecPerTrack; /* sectors per track */ + u_int Heads; /* number of heads */ + u_int32_t Sectors; /* total number of sectors */ + u_int32_t HiddenSecs; /* # of hidden sectors */ + u_int32_t HugeSectors; /* # of sectors if bpbSectors == 0 */ + u_int FSInfo; /* FSInfo sector */ + u_int Backup; /* Backup of Bootblocks */ + cl_t RootCl; /* Start of Root Directory */ + cl_t FSFree; /* Number of free clusters acc. FSInfo */ + cl_t FSNext; /* Next free cluster acc. FSInfo */ + + /* and some more calculated values */ + u_int flags; /* some flags: */ +#define FAT32 1 /* this is a FAT32 filesystem */ + /* + * Maybe, we should separate out + * various parts of FAT32? XXX + */ + int ValidFat; /* valid fat if FAT32 non-mirrored */ + cl_t ClustMask; /* mask for entries in FAT */ + cl_t NumClusters; /* # of entries in a FAT */ + u_int32_t NumSectors; /* how many sectors are there */ + u_int32_t FATsecs; /* how many sectors are in FAT */ + u_int32_t NumFatEntries; /* how many entries really are there */ + u_int ClusterOffset; /* at what sector would sector 0 start */ + u_int ClusterSize; /* Cluster size in bytes */ + + /* Now some statistics: */ + u_int NumFiles; /* # of plain files */ + u_int NumFree; /* # of free clusters */ + u_int NumBad; /* # of bad clusters */ +}; + +struct fatEntry { + cl_t next; /* pointer to next cluster */ + cl_t head; /* pointer to start of chain */ + u_int32_t length; /* number of clusters on chain */ + int flags; /* see below */ +}; + +#define CLUST_FREE 0 /* 0 means cluster is free */ +#define CLUST_FIRST 2 /* 2 is the minimum valid cluster number */ +#define CLUST_RSRVD 0xfffffff6 /* start of reserved clusters */ +#define CLUST_BAD 0xfffffff7 /* a cluster with a defect */ +#define CLUST_EOFS 0xfffffff8 /* start of EOF indicators */ +#define CLUST_EOF 0xffffffff /* standard value for last cluster */ + +/* + * Masks for cluster values + */ +#define CLUST12_MASK 0xfff +#define CLUST16_MASK 0xffff +#define CLUST32_MASK 0xfffffff + +#define FAT_USED 1 /* This fat chain is used in a file */ + +#define DOSLONGNAMELEN 256 /* long name maximal length */ +#define LRFIRST 0x40 /* first long name record */ +#define LRNOMASK 0x1f /* mask to extract long record + * sequence number */ + +/* + * Architecture independent description of a directory entry + */ +struct dosDirEntry { + struct dosDirEntry + *parent, /* previous tree level */ + *next, /* next brother */ + *child; /* if this is a directory */ + char name[8+1+3+1]; /* alias name first part */ + char lname[DOSLONGNAMELEN]; /* real name */ + uint flags; /* attributes */ + cl_t head; /* cluster no */ + u_int32_t size; /* filesize in bytes */ + uint fsckflags; /* flags during fsck */ +}; +/* Flags in fsckflags: */ +#define DIREMPTY 1 +#define DIREMPWARN 2 + +/* + * TODO-list of unread directories + */ +struct dirTodoNode { + struct dosDirEntry *dir; + struct dirTodoNode *next; +}; + +#endif diff --git a/sbin/fsck_msdosfs/ext.h b/sbin/fsck_msdosfs/ext.h new file mode 100644 index 000000000000..effb546cf4f8 --- /dev/null +++ b/sbin/fsck_msdosfs/ext.h @@ -0,0 +1,144 @@ +/* + * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank + * Copyright (c) 1995 Martin Husemann + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Martin Husemann + * and Wolfgang Solfrank. + * 4. 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 BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * $NetBSD: ext.h,v 1.6 2000/04/25 23:02:51 jdolecek Exp $ + * $FreeBSD$ + */ + +#ifndef EXT_H +#define EXT_H + +#include + +#include "dosfs.h" + +#define LOSTDIR "LOST.DIR" + +/* + * Options: + */ +extern int alwaysno; /* assume "no" for all questions */ +extern int alwaysyes; /* assume "yes" for all questions */ +extern int preen; /* we are preening */ +extern int rdonly; /* device is opened read only (supersedes above) */ + +extern char *fname; /* filesystem currently checked */ + +extern struct dosDirEntry *rootDir; + +/* + * function declarations + */ +int ask __P((int, const char *, ...)) __attribute__((__format__(__printf__,2,3))); + +/* + * Check filesystem given as arg + */ +int checkfilesys __P((const char *)); + +/* + * Return values of various functions + */ +#define FSOK 0 /* Check was OK */ +#define FSBOOTMOD 1 /* Boot block was modified */ +#define FSDIRMOD 2 /* Some directory was modified */ +#define FSFATMOD 4 /* The FAT was modified */ +#define FSERROR 8 /* Some unrecovered error remains */ +#define FSFATAL 16 /* Some unrecoverable error occured */ +#define FSDIRTY 32 /* File system is dirty */ +#define FSFIXFAT 64 /* Fix file system FAT */ + +/* + * read a boot block in a machine independend fashion and translate + * it into our struct bootblock. + */ +int readboot __P((int, struct bootblock *)); + +/* + * Correct the FSInfo block. + */ +int writefsinfo __P((int, struct bootblock *)); + +/* + * Read one of the FAT copies and return a pointer to the new + * allocated array holding our description of it. + */ +int readfat __P((int, struct bootblock *, int, struct fatEntry **)); + +/* + * Check two FAT copies for consistency and merge changes into the + * first if neccessary. + */ +int comparefat __P((struct bootblock *, struct fatEntry *, struct fatEntry *, int)); + +/* + * Check a FAT + */ +int checkfat __P((struct bootblock *, struct fatEntry *)); + +/* + * Write back FAT entries + */ +int writefat __P((int, struct bootblock *, struct fatEntry *, int)); + +/* + * Read a directory + */ +int resetDosDirSection __P((struct bootblock *, struct fatEntry *)); +void finishDosDirSection __P((void)); +int handleDirTree __P((int, struct bootblock *, struct fatEntry *)); + +/* + * Cross-check routines run after everything is completely in memory + */ +/* + * Check for lost cluster chains + */ +int checklost __P((int, struct bootblock *, struct fatEntry *)); +/* + * Try to reconnect a lost cluster chain + */ +int reconnect __P((int, struct bootblock *, struct fatEntry *, cl_t)); +void finishlf __P((void)); + +/* + * Small helper functions + */ +/* + * Return the type of a reserved cluster as text + */ +char *rsrvdcltype __P((cl_t)); + +/* + * Clear a cluster chain in a FAT + */ +void clearchain __P((struct bootblock *, struct fatEntry *, cl_t)); + +#endif diff --git a/sbin/fsck_msdosfs/fat.c b/sbin/fsck_msdosfs/fat.c new file mode 100644 index 000000000000..ff21f41f93bf --- /dev/null +++ b/sbin/fsck_msdosfs/fat.c @@ -0,0 +1,659 @@ +/* + * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank + * Copyright (c) 1995 Martin Husemann + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Martin Husemann + * and Wolfgang Solfrank. + * 4. 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 BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include +#ifndef lint +__RCSID("$NetBSD: fat.c,v 1.12 2000/10/10 20:24:52 is Exp $"); +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include +#include +#include +#include +#include + +#include "ext.h" +#include "fsutil.h" + +static int checkclnum __P((struct bootblock *, int, cl_t, cl_t *)); +static int clustdiffer __P((cl_t, cl_t *, cl_t *, int)); +static int tryclear __P((struct bootblock *, struct fatEntry *, cl_t, cl_t *)); +static int _readfat __P((int, struct bootblock *, int, u_char **)); + +/* + * Check a cluster number for valid value + */ +static int +checkclnum(boot, fat, cl, next) + struct bootblock *boot; + int fat; + cl_t cl; + cl_t *next; +{ + if (*next >= (CLUST_RSRVD&boot->ClustMask)) + *next |= ~boot->ClustMask; + if (*next == CLUST_FREE) { + boot->NumFree++; + return FSOK; + } + if (*next == CLUST_BAD) { + boot->NumBad++; + return FSOK; + } + if (*next < CLUST_FIRST + || (*next >= boot->NumClusters && *next < CLUST_EOFS)) { + pwarn("Cluster %u in FAT %d continues with %s cluster number %u\n", + cl, fat, + *next < CLUST_RSRVD ? "out of range" : "reserved", + *next&boot->ClustMask); + if (ask(0, "Truncate")) { + *next = CLUST_EOF; + return FSFATMOD; + } + return FSERROR; + } + return FSOK; +} + +/* + * Read a FAT from disk. Returns 1 if successful, 0 otherwise. + */ +static int +_readfat(fs, boot, no, buffer) + int fs; + struct bootblock *boot; + int no; + u_char **buffer; +{ + off_t off; + + *buffer = malloc(boot->FATsecs * boot->BytesPerSec); + if (*buffer == NULL) { + perror("No space for FAT"); + return 0; + } + + off = boot->ResSectors + no * boot->FATsecs; + off *= boot->BytesPerSec; + + if (lseek(fs, off, SEEK_SET) != off) { + perror("Unable to read FAT"); + goto err; + } + + if (read(fs, *buffer, boot->FATsecs * boot->BytesPerSec) + != boot->FATsecs * boot->BytesPerSec) { + perror("Unable to read FAT"); + goto err; + } + + return 1; + + err: + free(*buffer); + return 0; +} + +/* + * Read a FAT and decode it into internal format + */ +int +readfat(fs, boot, no, fp) + int fs; + struct bootblock *boot; + int no; + struct fatEntry **fp; +{ + struct fatEntry *fat; + u_char *buffer, *p; + cl_t cl; + int ret = FSOK; + + boot->NumFree = boot->NumBad = 0; + + if (!_readfat(fs, boot, no, &buffer)) + return FSFATAL; + + fat = calloc(boot->NumClusters, sizeof(struct fatEntry)); + if (fat == NULL) { + perror("No space for FAT"); + free(buffer); + return FSFATAL; + } + + if (buffer[0] != boot->Media + || buffer[1] != 0xff || buffer[2] != 0xff + || (boot->ClustMask == CLUST16_MASK && buffer[3] != 0xff) + || (boot->ClustMask == CLUST32_MASK + && ((buffer[3]&0x0f) != 0x0f + || buffer[4] != 0xff || buffer[5] != 0xff + || buffer[6] != 0xff || (buffer[7]&0x0f) != 0x0f))) { + + /* Windows 95 OSR2 (and possibly any later) changes + * the FAT signature to 0xXXffff7f for FAT16 and to + * 0xXXffff0fffffff07 for FAT32 upon boot, to know that the + * filesystem is dirty if it doesn't reboot cleanly. + * Check this special condition before errorring out. + */ + if (buffer[0] == boot->Media && buffer[1] == 0xff + && buffer[2] == 0xff + && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f) + || (boot->ClustMask == CLUST32_MASK + && buffer[3] == 0x0f && buffer[4] == 0xff + && buffer[5] == 0xff && buffer[6] == 0xff + && buffer[7] == 0x07))) + ret |= FSDIRTY; + else { + /* just some odd byte sequence in FAT */ + + switch (boot->ClustMask) { + case CLUST32_MASK: + pwarn("%s (%02x%02x%02x%02x%02x%02x%02x%02x)\n", + "FAT starts with odd byte sequence", + buffer[0], buffer[1], buffer[2], buffer[3], + buffer[4], buffer[5], buffer[6], buffer[7]); + break; + case CLUST16_MASK: + pwarn("%s (%02x%02x%02x%02x)\n", + "FAT starts with odd byte sequence", + buffer[0], buffer[1], buffer[2], buffer[3]); + break; + default: + pwarn("%s (%02x%02x%02x)\n", + "FAT starts with odd byte sequence", + buffer[0], buffer[1], buffer[2]); + break; + } + + + if (ask(1, "Correct")) + ret |= FSFIXFAT; + } + } + switch (boot->ClustMask) { + case CLUST32_MASK: + p = buffer + 8; + break; + case CLUST16_MASK: + p = buffer + 4; + break; + default: + p = buffer + 3; + break; + } + for (cl = CLUST_FIRST; cl < boot->NumClusters;) { + switch (boot->ClustMask) { + case CLUST32_MASK: + fat[cl].next = p[0] + (p[1] << 8) + + (p[2] << 16) + (p[3] << 24); + fat[cl].next &= boot->ClustMask; + ret |= checkclnum(boot, no, cl, &fat[cl].next); + cl++; + p += 4; + break; + case CLUST16_MASK: + fat[cl].next = p[0] + (p[1] << 8); + ret |= checkclnum(boot, no, cl, &fat[cl].next); + cl++; + p += 2; + break; + default: + fat[cl].next = (p[0] + (p[1] << 8)) & 0x0fff; + ret |= checkclnum(boot, no, cl, &fat[cl].next); + cl++; + if (cl >= boot->NumClusters) + break; + fat[cl].next = ((p[1] >> 4) + (p[2] << 4)) & 0x0fff; + ret |= checkclnum(boot, no, cl, &fat[cl].next); + cl++; + p += 3; + break; + } + } + + free(buffer); + *fp = fat; + return ret; +} + +/* + * Get type of reserved cluster + */ +char * +rsrvdcltype(cl) + cl_t cl; +{ + if (cl == CLUST_FREE) + return "free"; + if (cl < CLUST_BAD) + return "reserved"; + if (cl > CLUST_BAD) + return "as EOF"; + return "bad"; +} + +static int +clustdiffer(cl, cp1, cp2, fatnum) + cl_t cl; + cl_t *cp1; + cl_t *cp2; + int fatnum; +{ + if (*cp1 == CLUST_FREE || *cp1 >= CLUST_RSRVD) { + if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) { + if ((*cp1 != CLUST_FREE && *cp1 < CLUST_BAD + && *cp2 != CLUST_FREE && *cp2 < CLUST_BAD) + || (*cp1 > CLUST_BAD && *cp2 > CLUST_BAD)) { + pwarn("Cluster %u is marked %s with different indicators, ", + cl, rsrvdcltype(*cp1)); + if (ask(1, "fix")) { + *cp2 = *cp1; + return FSFATMOD; + } + return FSFATAL; + } + pwarn("Cluster %u is marked %s in FAT 0, %s in FAT %d\n", + cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum); + if (ask(0, "use FAT 0's entry")) { + *cp2 = *cp1; + return FSFATMOD; + } + if (ask(0, "use FAT %d's entry", fatnum)) { + *cp1 = *cp2; + return FSFATMOD; + } + return FSFATAL; + } + pwarn("Cluster %u is marked %s in FAT 0, but continues with cluster %u in FAT %d\n", + cl, rsrvdcltype(*cp1), *cp2, fatnum); + if (ask(0, "Use continuation from FAT %d", fatnum)) { + *cp1 = *cp2; + return FSFATMOD; + } + if (ask(0, "Use mark from FAT 0")) { + *cp2 = *cp1; + return FSFATMOD; + } + return FSFATAL; + } + if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) { + pwarn("Cluster %u continues with cluster %u in FAT 0, but is marked %s in FAT %d\n", + cl, *cp1, rsrvdcltype(*cp2), fatnum); + if (ask(0, "Use continuation from FAT 0")) { + *cp2 = *cp1; + return FSFATMOD; + } + if (ask(0, "Use mark from FAT %d", fatnum)) { + *cp1 = *cp2; + return FSFATMOD; + } + return FSERROR; + } + pwarn("Cluster %u continues with cluster %u in FAT 0, but with cluster %u in FAT %d\n", + cl, *cp1, *cp2, fatnum); + if (ask(0, "Use continuation from FAT 0")) { + *cp2 = *cp1; + return FSFATMOD; + } + if (ask(0, "Use continuation from FAT %d", fatnum)) { + *cp1 = *cp2; + return FSFATMOD; + } + return FSERROR; +} + +/* + * Compare two FAT copies in memory. Resolve any conflicts and merge them + * into the first one. + */ +int +comparefat(boot, first, second, fatnum) + struct bootblock *boot; + struct fatEntry *first; + struct fatEntry *second; + int fatnum; +{ + cl_t cl; + int ret = FSOK; + + for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) + if (first[cl].next != second[cl].next) + ret |= clustdiffer(cl, &first[cl].next, &second[cl].next, fatnum); + return ret; +} + +void +clearchain(boot, fat, head) + struct bootblock *boot; + struct fatEntry *fat; + cl_t head; +{ + cl_t p, q; + + for (p = head; p >= CLUST_FIRST && p < boot->NumClusters; p = q) { + if (fat[p].head != head) + break; + q = fat[p].next; + fat[p].next = fat[p].head = CLUST_FREE; + fat[p].length = 0; + } +} + +int +tryclear(boot, fat, head, trunc) + struct bootblock *boot; + struct fatEntry *fat; + cl_t head; + cl_t *trunc; +{ + if (ask(0, "Clear chain starting at %u", head)) { + clearchain(boot, fat, head); + return FSFATMOD; + } else if (ask(0, "Truncate")) { + *trunc = CLUST_EOF; + return FSFATMOD; + } else + return FSERROR; +} + +/* + * Check a complete FAT in-memory for crosslinks + */ +int +checkfat(boot, fat) + struct bootblock *boot; + struct fatEntry *fat; +{ + cl_t head, p, h, n; + u_int len; + int ret = 0; + int conf; + + /* + * pass 1: figure out the cluster chains. + */ + for (head = CLUST_FIRST; head < boot->NumClusters; head++) { + /* find next untravelled chain */ + if (fat[head].head != 0 /* cluster already belongs to some chain */ + || fat[head].next == CLUST_FREE + || fat[head].next == CLUST_BAD) + continue; /* skip it. */ + + /* follow the chain and mark all clusters on the way */ + for (len = 0, p = head; + p >= CLUST_FIRST && p < boot->NumClusters; + p = fat[p].next) { + fat[p].head = head; + len++; + } + + /* the head record gets the length */ + fat[head].length = fat[head].next == CLUST_FREE ? 0 : len; + } + + /* + * pass 2: check for crosslinked chains (we couldn't do this in pass 1 because + * we didn't know the real start of the chain then - would have treated partial + * chains as interlinked with their main chain) + */ + for (head = CLUST_FIRST; head < boot->NumClusters; head++) { + /* find next untravelled chain */ + if (fat[head].head != head) + continue; + + /* follow the chain to its end (hopefully) */ + for (p = head; + (n = fat[p].next) >= CLUST_FIRST && n < boot->NumClusters; + p = n) + if (fat[n].head != head) + break; + if (n >= CLUST_EOFS) + continue; + + if (n == CLUST_FREE || n >= CLUST_RSRVD) { + pwarn("Cluster chain starting at %u ends with cluster marked %s\n", + head, rsrvdcltype(n)); + ret |= tryclear(boot, fat, head, &fat[p].next); + continue; + } + if (n < CLUST_FIRST || n >= boot->NumClusters) { + pwarn("Cluster chain starting at %u ends with cluster out of range (%u)\n", + head, n); + ret |= tryclear(boot, fat, head, &fat[p].next); + continue; + } + pwarn("Cluster chains starting at %u and %u are linked at cluster %u\n", + head, fat[n].head, n); + conf = tryclear(boot, fat, head, &fat[p].next); + if (ask(0, "Clear chain starting at %u", h = fat[n].head)) { + if (conf == FSERROR) { + /* + * Transfer the common chain to the one not cleared above. + */ + for (p = n; + p >= CLUST_FIRST && p < boot->NumClusters; + p = fat[p].next) { + if (h != fat[p].head) { + /* + * Have to reexamine this chain. + */ + head--; + break; + } + fat[p].head = head; + } + } + clearchain(boot, fat, h); + conf |= FSFATMOD; + } + ret |= conf; + } + + return ret; +} + +/* + * Write out FATs encoding them from the internal format + */ +int +writefat(fs, boot, fat, correct_fat) + int fs; + struct bootblock *boot; + struct fatEntry *fat; + int correct_fat; +{ + u_char *buffer, *p; + cl_t cl; + int i; + u_int32_t fatsz; + off_t off; + int ret = FSOK; + + buffer = malloc(fatsz = boot->FATsecs * boot->BytesPerSec); + if (buffer == NULL) { + perror("No space for FAT"); + return FSFATAL; + } + memset(buffer, 0, fatsz); + boot->NumFree = 0; + p = buffer; + if (correct_fat) { + *p++ = (u_char)boot->Media; + *p++ = 0xff; + *p++ = 0xff; + switch (boot->ClustMask) { + case CLUST16_MASK: + *p++ = 0xff; + break; + case CLUST32_MASK: + *p++ = 0x0f; + *p++ = 0xff; + *p++ = 0xff; + *p++ = 0xff; + *p++ = 0x0f; + break; + } + } else { + /* use same FAT signature as the old FAT has */ + int count; + u_char *old_fat; + + switch (boot->ClustMask) { + case CLUST32_MASK: + count = 8; + break; + case CLUST16_MASK: + count = 4; + break; + default: + count = 3; + break; + } + + if (!_readfat(fs, boot, boot->ValidFat >= 0 ? boot->ValidFat :0, + &old_fat)) { + free(buffer); + return FSFATAL; + } + + memcpy(p, old_fat, count); + free(old_fat); + p += count; + } + + for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) { + switch (boot->ClustMask) { + case CLUST32_MASK: + if (fat[cl].next == CLUST_FREE) + boot->NumFree++; + *p++ = (u_char)fat[cl].next; + *p++ = (u_char)(fat[cl].next >> 8); + *p++ = (u_char)(fat[cl].next >> 16); + *p &= 0xf0; + *p++ |= (fat[cl].next >> 24)&0x0f; + break; + case CLUST16_MASK: + if (fat[cl].next == CLUST_FREE) + boot->NumFree++; + *p++ = (u_char)fat[cl].next; + *p++ = (u_char)(fat[cl].next >> 8); + break; + default: + if (fat[cl].next == CLUST_FREE) + boot->NumFree++; + if (cl + 1 < boot->NumClusters + && fat[cl + 1].next == CLUST_FREE) + boot->NumFree++; + *p++ = (u_char)fat[cl].next; + *p++ = (u_char)((fat[cl].next >> 8) & 0xf) + |(u_char)(fat[cl+1].next << 4); + *p++ = (u_char)(fat[++cl].next >> 4); + break; + } + } + for (i = 0; i < boot->FATs; i++) { + off = boot->ResSectors + i * boot->FATsecs; + off *= boot->BytesPerSec; + if (lseek(fs, off, SEEK_SET) != off + || write(fs, buffer, fatsz) != fatsz) { + perror("Unable to write FAT"); + ret = FSFATAL; /* Return immediately? XXX */ + } + } + free(buffer); + return ret; +} + +/* + * Check a complete in-memory FAT for lost cluster chains + */ +int +checklost(dosfs, boot, fat) + int dosfs; + struct bootblock *boot; + struct fatEntry *fat; +{ + cl_t head; + int mod = FSOK; + int ret; + + for (head = CLUST_FIRST; head < boot->NumClusters; head++) { + /* find next untravelled chain */ + if (fat[head].head != head + || fat[head].next == CLUST_FREE + || (fat[head].next >= CLUST_RSRVD + && fat[head].next < CLUST_EOFS) + || (fat[head].flags & FAT_USED)) + continue; + + pwarn("Lost cluster chain at cluster %u\n%d Cluster(s) lost\n", + head, fat[head].length); + mod |= ret = reconnect(dosfs, boot, fat, head); + if (mod & FSFATAL) + break; + if (ret == FSERROR && ask(0, "Clear")) { + clearchain(boot, fat, head); + mod |= FSFATMOD; + } + } + finishlf(); + + if (boot->FSInfo) { + ret = 0; + if (boot->FSFree != boot->NumFree) { + pwarn("Free space in FSInfo block (%d) not correct (%d)\n", + boot->FSFree, boot->NumFree); + if (ask(1, "fix")) { + boot->FSFree = boot->NumFree; + ret = 1; + } + } + if (boot->NumFree && fat[boot->FSNext].next != CLUST_FREE) { + pwarn("Next free cluster in FSInfo block (%u) not free\n", + boot->FSNext); + if (ask(1, "fix")) + for (head = CLUST_FIRST; head < boot->NumClusters; head++) + if (fat[head].next == CLUST_FREE) { + boot->FSNext = head; + ret = 1; + break; + } + } + if (ret) + mod |= writefsinfo(dosfs, boot); + } + + return mod; +} diff --git a/sbin/fsck_msdosfs/fsck_msdosfs.8 b/sbin/fsck_msdosfs/fsck_msdosfs.8 new file mode 100644 index 000000000000..9d8f56b99d3c --- /dev/null +++ b/sbin/fsck_msdosfs/fsck_msdosfs.8 @@ -0,0 +1,114 @@ +.\" $NetBSD: fsck_msdos.8,v 1.9 1997/10/17 11:19:58 ws Exp $ +.\" $FreeBSD$ +.\" +.\" Copyright (C) 1995 Wolfgang Solfrank +.\" Copyright (c) 1995 Martin Husemann +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Martin Husemann +.\" and Wolfgang Solfrank. +.\" 4. 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 BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" +.Dd August 13, 1995 +.Dt FSCK_MSDOS 8 +.Os NetBSD 1.1a +.Sh NAME +.Nm fsck_msdos +.Nd DOS/Windows (FAT) filesystem consistency checker +.Sh SYNOPSIS +.Nm "" +.Fl p +.Op Fl f +.Ar filesystem ... +.Nm "" +.Op Fl fny +.Ar filesystem ... +.Sh DESCRIPTION +.Pp +The +.Nm +utility verifies and repairs +.Tn FAT +filesystems (more commonly known +as +.Tn DOS +filesystems). +.Pp +The first form of +.Nm +preens the specified filesystems. +It is normally started by +.Xr fsck 8 +run from +.Pa /etc/rc +during automatic reboot, when a FAT filesystem is detected. +When preening file systems, +.Nm +will fix common inconsistencies non-interactively. If +more serious problems are found, +.Nm +does not try to fix them, indicates that it was not +successful, and exits. +.Pp +The second form of +.Nm +checks the specified file systems and tries to repair all +detected inconsistencies, requesting confirmation before +making any changes. +.Pp +The options are as follows: +.Bl -hang -offset indent +.It Fl f +This option is ignored by +.Nm "" , +and is present only for compatibility with programs that +check other file system types for consistency, such as +.Xr fsck_ffs 8 . +.It Fl n +Causes +.Nm +to assume no as the answer to all operator +questions, except +.Dq CONTINUE? . +.It Fl p +Preen the specified filesystems. +.It Fl y +Causes +.Nm +to assume yes as the answer to all operator questions. +.El +.Sh SEE ALSO +.Xr fsck 8 , +.Xr fsck_ffs 8 , +.Xr mount_msdos 8 +.Sh BUGS +.Nm +is still under construction. +.Sh HISTORY +The +.Nm +command first appeared in +.Fx 4.4 . diff --git a/sbin/fsck_msdosfs/main.c b/sbin/fsck_msdosfs/main.c new file mode 100644 index 000000000000..dec3e3958d7e --- /dev/null +++ b/sbin/fsck_msdosfs/main.c @@ -0,0 +1,165 @@ +/* + * Copyright (C) 1995 Wolfgang Solfrank + * Copyright (c) 1995 Martin Husemann + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Martin Husemann + * and Wolfgang Solfrank. + * 4. 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 BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include +#ifndef lint +__RCSID("$NetBSD: main.c,v 1.10 1997/10/01 02:18:14 enami Exp $"); +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#if __STDC__ +#include +#else +#include +#endif + +#include "fsutil.h" +#include "ext.h" + +int alwaysno; /* assume "no" for all questions */ +int alwaysyes; /* assume "yes" for all questions */ +int preen; /* set when preening */ +int rdonly; /* device is opened read only (supersedes above) */ + +static void usage __P((void)); +int main __P((int, char **)); + +static void +usage() +{ + errexit("Usage: fsck_msdos [-fnpy] filesystem ... \n"); +} + +int +main(argc, argv) + int argc; + char **argv; +{ + int ret = 0, erg; + int ch; + + while ((ch = getopt(argc, argv, "pynf")) != -1) { + switch (ch) { + case 'f': + /* + * We are always forced, since we don't + * have a clean flag + */ + break; + case 'n': + alwaysno = 1; + alwaysyes = preen = 0; + break; + case 'y': + alwaysyes = 1; + alwaysno = preen = 0; + break; + + case 'p': + preen = 1; + alwaysyes = alwaysno = 0; + break; + + default: + usage(); + break; + } + } + argc -= optind; + argv += optind; + + if (!argc) + usage(); + + while (--argc >= 0) { + setcdevname(*argv, preen); + erg = checkfilesys(*argv++); + if (erg > ret) + ret = erg; + } + + return ret; +} + + +/*VARARGS*/ +int +#if __STDC__ +ask(int def, const char *fmt, ...) +#else +ask(def, fmt, va_alist) + int def; + char *fmt; + va_dcl +#endif +{ + va_list ap; + + char prompt[256]; + int c; + + if (preen) { + if (rdonly) + def = 0; + if (def) + printf("FIXED\n"); + return def; + } + +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + vsnprintf(prompt, sizeof(prompt), fmt, ap); + if (alwaysyes || rdonly) { + printf("%s? %s\n", prompt, rdonly ? "no" : "yes"); + return !rdonly; + } + do { + printf("%s? [yn] ", prompt); + fflush(stdout); + c = getchar(); + while (c != '\n' && getchar() != '\n') + if (feof(stdin)) + return 0; + } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N'); + return c == 'y' || c == 'Y'; +} -- cgit v1.2.3