summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1996-05-22 11:23:57 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1996-05-22 11:23:57 +0000
commit11a765303f21eabf78addcce4a9d0b96915bfa48 (patch)
tree927e2beea471f9f50297e1564f62a1df9ce475c1
parentdcbd4928bd97b1e9011c207e5cab1da61b369a5d (diff)
add fsck_msdos
-rw-r--r--sbin/Makefile6
-rw-r--r--sbin/fsck_msdos/Makefile7
-rw-r--r--sbin/fsck_msdos/boot.c117
-rw-r--r--sbin/fsck_msdos/check.c196
-rw-r--r--sbin/fsck_msdos/dir.c760
-rw-r--r--sbin/fsck_msdos/dosfs.h123
-rw-r--r--sbin/fsck_msdos/ext.h153
-rw-r--r--sbin/fsck_msdos/fat.c514
-rw-r--r--sbin/fsck_msdos/fsck_msdos.876
-rw-r--r--sbin/fsck_msdos/main.c252
10 files changed, 2201 insertions, 3 deletions
diff --git a/sbin/Makefile b/sbin/Makefile
index 0e32ab9c6ff..312cadd00da 100644
--- a/sbin/Makefile
+++ b/sbin/Makefile
@@ -1,5 +1,5 @@
-# $OpenBSD: Makefile,v 1.9 1996/05/17 03:47:30 dm Exp $
-# $NetBSD: Makefile,v 1.28 1996/04/05 01:44:24 cgd Exp $
+# $OpenBSD: Makefile,v 1.10 1996/05/22 11:23:51 deraadt Exp $
+# $NetBSD: Makefile,v 1.29 1996/05/14 17:39:21 ws Exp $
# @(#)Makefile 8.5 (Berkeley) 3/31/94
# Not ported: XNSrouted enpload scsiformat startslip
@@ -18,7 +18,7 @@ SUBDIR+= mount_ffs newfs fsck_ffs fsdb dumpfs dump restore clri tunefs
SUBDIR+= mount_kernfs
SUBDIR+= mount_lfs newlfs dumplfs
# mount_mfs -> newfs
-SUBDIR+= mount_msdos
+SUBDIR+= mount_msdos fsck_msdos
SUBDIR+= mount_nfs
SUBDIR+= mount_null
SUBDIR+= mount_portal
diff --git a/sbin/fsck_msdos/Makefile b/sbin/fsck_msdos/Makefile
new file mode 100644
index 00000000000..1c394d0e16a
--- /dev/null
+++ b/sbin/fsck_msdos/Makefile
@@ -0,0 +1,7 @@
+# $NetBSD: Makefile,v 1.1 1996/05/14 17:39:26 ws Exp $
+
+PROG= fsck_msdos
+MAN= fsck_msdos.8
+SRCS= main.c check.c boot.c fat.c dir.c
+
+.include <bsd.prog.mk>
diff --git a/sbin/fsck_msdos/boot.c b/sbin/fsck_msdos/boot.c
new file mode 100644
index 00000000000..17304504540
--- /dev/null
+++ b/sbin/fsck_msdos/boot.c
@@ -0,0 +1,117 @@
+/* $NetBSD: boot.c,v 1.1 1996/05/14 17:39:28 ws Exp $ */
+
+/*
+ * 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.
+ */
+
+
+#ifndef lint
+static char rcsid[] = "$NetBSD: boot.c,v 1.1 1996/05/14 17:39:28 ws Exp $";
+#endif /* not lint */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "ext.h"
+
+int
+readboot(dosfs, boot)
+ int dosfs;
+ struct bootblock *boot;
+{
+ u_char block[DOSBOOTBLOCKSIZE];
+ int n;
+
+ if ((n = read(dosfs, block, sizeof block)) < (int)sizeof block) {
+ if (n < 0)
+ perror("could not read boot block");
+ else
+ pfatal("Short bootblock?");
+ return FSFATAL;
+ }
+
+ /* 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->FATsecs = 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->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\n", boot->BytesPerSec);
+ return FSFATAL;
+ }
+ if (boot->SecPerClust == 0) {
+ pfatal("Invalid cluster size: %u\n", 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->NumClusters >= MAX12BITCLUSTERS)
+ boot->Is16BitFat = 1;
+ else
+ boot->Is16BitFat = 0;
+
+ if (boot->Is16BitFat)
+ boot->NumFatEntries = (boot->FATsecs * boot->BytesPerSec) / 2;
+ else
+ boot->NumFatEntries = (boot->FATsecs * boot->BytesPerSec * 2) / 3;
+ if (boot->NumFatEntries < boot->NumClusters) {
+ pfatal("FAT size too small, %d 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 FSOK;
+}
diff --git a/sbin/fsck_msdos/check.c b/sbin/fsck_msdos/check.c
new file mode 100644
index 00000000000..adf41300c79
--- /dev/null
+++ b/sbin/fsck_msdos/check.c
@@ -0,0 +1,196 @@
+/* $NetBSD: check.c,v 1.1 1996/05/14 17:39:29 ws Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996 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.
+ */
+
+
+#ifndef lint
+static char rcsid[] = "$NetBSD: check.c,v 1.1 1996/05/14 17:39:29 ws Exp $";
+#endif /* not lint */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "ext.h"
+
+int
+checkfilesys(fname)
+ const char *fname;
+{
+ int dosfs;
+ struct dosDirEntry *rootDir;
+ struct bootblock boot;
+ struct fatEntry * fat = NULL;
+ int i;
+ int mod = 0;
+
+ 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);
+ return 8;
+ }
+
+ if (!preen)
+ printf("** Phase 1 - Read and Compare FATs\n");
+
+ for (i = 0; i < boot.FATs; i++) {
+ struct fatEntry *currentFat;
+
+ mod |= readfat(dosfs, &boot, i, &currentFat);
+
+ if (mod&FSFATAL) {
+ close(dosfs);
+ return 8;
+ }
+
+ if (fat == NULL)
+ fat = currentFat;
+ else {
+ mod |= comparefat(&boot, fat, currentFat, i + 1);
+ if (mod&FSFATAL) {
+ close(dosfs);
+ return 8;
+ }
+ }
+ }
+
+ if (!preen)
+ printf("** Phase 2 - Check Cluster Chains\n");
+
+ mod |= checkfat(&boot, fat);
+ if (mod&FSFATAL) {
+ close(dosfs);
+ return 8;
+ }
+
+ if (mod&FSFATMOD)
+ mod |= writefat(dosfs, &boot, fat); /* delay writing fats? XXX */
+ if (mod&FSFATAL) {
+ close(dosfs);
+ return 8;
+ }
+
+ if (!preen)
+ printf("** Phase 3 - Checking Directories\n");
+
+ rootDir = malloc(sizeof(struct dosDirEntry));
+ memset(rootDir, 0, sizeof(struct dosDirEntry));
+ rootDir->fullpath = strdup("/");
+ if (resetDosDirSection(&boot)&FSFATAL) {
+ close(dosfs);
+ return 8;
+ }
+
+ mod = readDosDirSection(dosfs, &boot, fat, rootDir);
+ if (mod&FSFATAL) {
+ close(dosfs);
+ return 8;
+ }
+
+ if (mod&FSFATMOD)
+ mod |= writefat(dosfs, &boot, fat); /* delay writing fats? XXX */
+ if (mod&FSFATAL) {
+ close(dosfs);
+ return 8;
+ }
+
+ /*
+ * 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
+ */
+ free(pendingDirectories);
+ pendingDirectories = n;
+
+ /*
+ * handle subdirectory
+ */
+ mod |= readDosDirSection(dosfs, &boot, fat, dir);
+ if (mod&FSFATAL) {
+ close(dosfs);
+ return 8;
+ }
+ if (mod&FSFATMOD)
+ mod |= writefat(dosfs, &boot, fat); /* delay writing fats? XXX */
+ if (mod&FSFATAL) {
+ close(dosfs);
+ return 8;
+ }
+ }
+ finishDosDirSection();
+
+ if (!preen)
+ printf("** Phase 4 - Checking for Lost Files\n");
+
+ mod |= checklost(dosfs, &boot, fat, rootDir);
+
+ close(dosfs);
+ pwarn("%d files, %d free (%d clusters)\n",
+ boot.NumFiles, boot.NumFree * boot.ClusterSize / 1024,
+ boot.NumFree);
+ if (mod&(FSFATAL|FSERROR))
+ return 8;
+ if (mod) {
+ pwarn("\n***** FILE SYSTEM WAS MODIFIED *****\n");
+ return 4;
+ }
+ return 0;
+}
diff --git a/sbin/fsck_msdos/dir.c b/sbin/fsck_msdos/dir.c
new file mode 100644
index 00000000000..d0b205cf98f
--- /dev/null
+++ b/sbin/fsck_msdos/dir.c
@@ -0,0 +1,760 @@
+/* $NetBSD: dir.c,v 1.1 1996/05/14 17:39:30 ws Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996 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.
+ */
+
+
+#ifndef lint
+static char rcsid[] = "$NetBSD: dir.c,v 1.1 1996/05/14 17:39:30 ws Exp $";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "ext.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
+
+/*
+ * 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;
+
+/*
+ * Init internal state for a new directory scan.
+ */
+int
+resetDosDirSection(boot)
+ struct bootblock *boot;
+{
+ int b1, b2;
+
+ b1 = boot->RootDirEnts * 32;
+ b2 = boot->SecPerClust * boot->BytesPerSec;
+
+ if (!(buffer = malloc(b1 > b2 ? b1 : b2))
+ || !(delbuf = malloc(b2))) {
+ perror("No space for directory");
+ return FSFATAL;
+ }
+ return FSOK;
+}
+
+/*
+ * Cleanup after a directory scan
+ */
+void
+finishDosDirSection()
+{
+ 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, eof)
+ 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 eof;
+{
+ if (!eof)
+ pwarn("Invalid long filename entry for %s\n", path);
+ else
+ pwarn("Invalid long filename entry at end of directory %s\n", path);
+ 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
+ */
+ u_int32_t physicalSize;
+
+ 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 %lu, should at most be %lu\n",
+ dir->fullpath, 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",
+ dir->fullpath);
+ 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;
+}
+
+/*
+ * The stack of unread directories
+ */
+struct dirTodoNode *pendingDirectories = NULL;
+
+/*
+ * Read a directory and
+ * - resolve long name records
+ * - enter file and directory records into the parent's list
+ * - push directories onto the todo-stack
+ */
+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, invcl, empcl;
+ char *t;
+ u_int lidx = 0;
+ int shortSum;
+ int mod = FSOK;
+#define THISMOD 0x8000 /* Only used within this routine */
+
+ cl = dir->head;
+ if (dir->fullpath[1] && (cl < CLUST_FIRST || cl >= boot->NumClusters)) {
+ /*
+ * Already handled somewhere else.
+ */
+ return FSOK;
+ }
+ shortSum = -1;
+ vallfn = invlfn = empty = NULL;
+ do {
+ if (!dir->fullpath[1]) {
+ 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",
+ dir->fullpath);
+ if (ask(1, "Extend")) {
+ dir->fsckflags &= ~DIREMPTY;
+ if (delete(f, boot, fat,
+ empcl, empty - buffer,
+ cl, p - buffer) == FSFATAL)
+ return FSFATAL;
+ } 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;
+ /*
+ * What about volume names with extensions? XXX
+ */
+ if ((dirent.flags & ATTR_VOLUME) == 0 && 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);
+ dirent.size = p[28] | (p[29] << 8) | (p[30] << 16) | (p[31] << 24);
+ if (vallfn) {
+ strcpy(dirent.lname, longName);
+ longName[0] = '\0';
+ shortSum = -1;
+ }
+
+ k = strlen(dirent.lname[0] ? dirent.lname : dirent.name);
+ k += strlen(dir->fullpath) + 2;
+ dirent.fullpath = malloc(k);
+ strcpy(dirent.fullpath, dir->fullpath);
+ if (dir->fullpath[1])
+ strcat(dirent.fullpath, "/");
+ strcat(dirent.fullpath,
+ dirent.lname[0] ? dirent.lname : dirent.name);
+ if (invlfn) {
+ mod |= k = removede(f, boot, fat,
+ invlfn, vallfn ? vallfn : p,
+ invcl, vallfn ? valcl : cl, cl,
+ dirent.fullpath, 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",
+ dirent.fullpath);
+ if (ask(1, "Drop allocated clusters")) {
+ p[26] = p[27] = 0;
+ clearchain(boot, fat, dirent.head);
+ dirent.head = 0;
+ mod |= THISMOD|FSDIRMOD|FSFATMOD;
+ } else
+ mod |= FSERROR;
+ }
+ } else if (dirent.head == 0
+ && !strcmp(dirent.name, "..")
+ && !strcmp(dir->parent->fullpath, "/")) {
+ /*
+ * 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",
+ dirent.fullpath);
+ else if (dirent.head < CLUST_FIRST
+ || dirent.head >= boot->NumClusters)
+ pwarn("%s starts with cluster out of range(%d)\n",
+ dirent.fullpath,
+ dirent.head);
+ else if (fat[dirent.head].next == CLUST_FREE)
+ pwarn("%s starts with free cluster\n",
+ dirent.fullpath);
+ else if (fat[dirent.head].next >= CLUST_RSRVD)
+ pwarn("%s starts with %s cluster\n",
+ dirent.fullpath,
+ rsrvdcltype(fat[dirent.head].next));
+ else
+ pwarn("%s doesn't start a cluster chain\n",
+ dirent.fullpath);
+ 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;
+ dirent.size = 0;
+ mod |= THISMOD|FSDIRMOD;
+ } else
+ mod |= FSERROR;
+ }
+ }
+
+ /* create directory tree node */
+ d = malloc(sizeof(struct dosDirEntry));
+ memcpy(d, &dirent, sizeof(struct dosDirEntry));
+ /* link it into the directory tree */
+ d->parent = dir;
+ d->next = dir->child;
+ dir->child = d;
+ if (d->head >= CLUST_FIRST && d->head < boot->NumClusters)
+ fat[d->head].dirp = d;
+
+ if (d->flags & ATTR_DIRECTORY) {
+ /*
+ * gather more info for directories
+ */
+ struct dirTodoNode * n;
+
+ if (d->size) {
+ pwarn("Directory %s has size != 0\n",
+ d->fullpath);
+ if (ask(1, "Correct")) {
+ p[28] = p[29] = p[30] = p[31] = 0;
+ d->size = 0;
+ mod |= THISMOD|FSDIRMOD;
+ } else
+ mod |= FSERROR;
+ }
+ /*
+ * handle `.' and `..' specially
+ */
+ if (strcmp(d->name, ".") == 0) {
+ if (d->head != dir->head) {
+ pwarn("`.' entry in %s has incorrect start cluster\n",
+ dir->fullpath);
+ if (ask(1, "Correct")) {
+ d->head = dir->head;
+ p[26] = (u_char)d->head;
+ p[27] = (u_char)(d->head >> 8);
+ mod |= THISMOD|FSDIRMOD;
+ } else
+ mod |= FSERROR;
+ }
+ continue;
+ }
+ if (strcmp(d->name, "..") == 0) {
+ if (d->head != dir->parent->head) {
+ pwarn("`..' entry in %s has incorrect start cluster\n",
+ dir->fullpath);
+ if (ask(1, "Correct")) {
+ d->head = dir->parent->head;
+ p[26] = (u_char)d->head;
+ p[27] = (u_char)(d->head >> 8);
+ mod |= THISMOD|FSDIRMOD;
+ } else
+ mod |= FSERROR;
+ }
+ continue;
+ }
+
+ boot->NumFiles++;
+ /* Enter this directory into the todo list */
+ n = malloc(sizeof(struct dirTodoNode));
+ n->next = pendingDirectories;
+ n->dir = d;
+ pendingDirectories = n;
+ } else {
+ mod |= k = checksize(boot, fat, p, d);
+ 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,
+ dir->fullpath, 1);
+ return mod & ~THISMOD;
+}
+
+/*
+ * 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, dir)
+ int dosfs;
+ struct bootblock *boot;
+ struct fatEntry *fat;
+ cl_t head;
+ struct dosDirEntry *dir;
+{
+ struct dosDirEntry d;
+ u_char *p;
+
+ if (!dir) /* Create lfdir? XXX */
+ 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)
+ while (p < lfbuf + boot->ClusterSize)
+ if (*p == SLOT_EMPTY
+ || *p == SLOT_DELETED)
+ break;
+ if (p && p < lfbuf + boot->ClusterSize)
+ break;
+ lfcl = p ? fat[lfcl].next : dir->head;
+ if (lfcl < CLUST_FIRST || lfcl >= boot->NumClusters) {
+ /* Extend lfdir? 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, buffer, boot->ClusterSize) != boot->ClusterSize) {
+ perror("could not read LOST.DIR");
+ return FSFATAL;
+ }
+ p = lfbuf;
+ }
+
+ if (!ask(0, "Reconnect"))
+ return FSERROR;
+
+ boot->NumFiles++;
+ /* Ensure uniqueness of entry here! XXX */
+ memset(&d, 0, sizeof d);
+ sprintf(d.name, "%d", head);
+ d.fullpath = malloc(strlen(dir->fullpath) + strlen(d.name) + 2);
+ sprintf(d.fullpath, "%s/%s", dir->fullpath, d.name);
+ d.flags = 0;
+ d.head = head;
+ d.size = fat[head].length * boot->ClusterSize;
+ d.parent = dir;
+ d.next = dir->child;
+ dir->child = malloc(sizeof(struct dosDirEntry));
+ memcpy(dir->child, &d, sizeof(struct dosDirEntry));
+
+ memset(p, 0, 32);
+ memset(p, ' ', 11);
+ memcpy(p, dir->name, strlen(dir->name));
+ p[26] = (u_char)dir->head;
+ p[27] = (u_char)(dir->head >> 8);
+ p[28] = (u_char)dir->size;
+ p[29] = (u_char)(dir->size >> 8);
+ p[30] = (u_char)(dir->size >> 16);
+ p[31] = (u_char)(dir->size >> 24);
+ fat[head].dirp = dir;
+ if (lseek(dosfs, lfoff, SEEK_SET) != lfoff
+ || write(dosfs, buffer, 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_msdos/dosfs.h b/sbin/fsck_msdos/dosfs.h
new file mode 100644
index 00000000000..c0295bc7b10
--- /dev/null
+++ b/sbin/fsck_msdos/dosfs.h
@@ -0,0 +1,123 @@
+
+/*
+ * Copyright (C) 1995, 1996 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.
+ */
+
+#ifndef DOSFS_H
+#define DOSFS_H
+
+#define DOSBOOTBLOCKSIZE 512
+
+#define MAX12BITCLUSTERS 4078
+
+typedef u_int16_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_int32_t Sectors; /* total number of sectors */
+ u_int Media; /* media descriptor */
+ u_int FATsecs; /* number of sectors per FAT */
+ u_int SecPerTrack; /* sectors per track */
+ u_int Heads; /* number of heads */
+ u_int32_t HiddenSecs; /* # of hidden sectors */
+ u_int32_t HugeSectors; /* # of sectors if bpbSectors == 0 */
+
+ /* and some more calculated values */
+ int Is16BitFat; /* 0 for 12 bit, 1 for 16 bit */
+ cl_t NumClusters; /* # of entries in a FAT */
+ u_int32_t NumSectors; /* how many sectors are there */
+ 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 */
+};
+
+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 */
+ struct dosDirEntry *dirp; /* corresponding file name */
+};
+
+#define CLUST_FREE 0 /* 0 means cluster is free */
+#define CLUST_FIRST 2 /* 2 is the minimum valid cluster number */
+#define CLUST_RSRVD 0xfff0 /* start of reserved clusters */
+#define CLUST_BAD 0xfff7 /* a cluster with a defect */
+#define CLUST_EOFS 0xfff8 /* start of EOF indicators */
+#define CLUST_EOF 0xffff /* standard value for last cluster */
+
+#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 *fullpath; /* path name from root to this 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_msdos/ext.h b/sbin/fsck_msdos/ext.h
new file mode 100644
index 00000000000..63615e8e195
--- /dev/null
+++ b/sbin/fsck_msdos/ext.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 1995, 1996 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.
+ */
+
+#ifndef EXT_H
+#define EXT_H
+
+#include <sys/types.h>
+
+#if sun
+#define __P(a) a
+
+typedef char int8_t;
+typedef unsigned char u_int8_t;
+typedef short int16_t;
+typedef unsigned short u_int16_t;
+typedef long int32_t;
+typedef unsigned long u_int32_t;
+#endif
+
+#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 */
+
+/*
+ * function declarations
+ */
+void errexit __P((const char *, ...));
+void pfatal __P((const char *, ...));
+void pwarn __P((const char *, ...));
+int ask __P((int, const char *, ...));
+void perror __P((const char *));
+
+/*
+ * Check filesystem given as arg
+ */
+int checkfilesys __P((const char *));
+
+/*
+ * Return values of various functions
+ */
+#define FSOK 0 /* Check was OK */
+#define FSDIRMOD 1 /* Some directory was modified */
+#define FSFATMOD 2 /* The FAT was modified */
+#define FSERROR 4 /* Some unrecovered error remains */
+#define FSFATAL 8 /* Some unrecoverable error occured */
+
+/*
+ * read a boot block in a machine independend fashion and translate
+ * it into our struct bootblock.
+ */
+int readboot __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 *));
+
+/*
+ * Read a directory
+ */
+int resetDosDirSection __P((struct bootblock *));
+void finishDosDirSection __P((void));
+int readDosDirSection __P((int, struct bootblock *, struct fatEntry *, struct dosDirEntry *));
+
+/*
+ * A stack of directories which should be examined later
+ */
+extern struct dirTodoNode *pendingDirectories;
+
+/*
+ * Cross-check routines run after everything is completely in memory
+ */
+/*
+ * Check for lost cluster chains
+ */
+int checklost __P((int, struct bootblock *, struct fatEntry *, struct dosDirEntry *));
+/*
+ * Try to reconnect a lost cluster chain
+ */
+int reconnect __P((int, struct bootblock *, struct fatEntry *, cl_t, struct dosDirEntry *));
+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_msdos/fat.c b/sbin/fsck_msdos/fat.c
new file mode 100644
index 00000000000..6ebb99e529d
--- /dev/null
+++ b/sbin/fsck_msdos/fat.c
@@ -0,0 +1,514 @@
+/* $NetBSD: fat.c,v 1.1 1996/05/14 17:39:34 ws Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996 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.
+ */
+
+
+#ifndef lint
+static char rcsid[] = "$NetBSD: fat.c,v 1.1 1996/05/14 17:39:34 ws Exp $";
+#endif /* not lint */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "ext.h"
+
+/*
+ * 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 (!boot->Is16BitFat && *next >= (CLUST_RSRVD&0xfff))
+ *next |= 0xf000;
+ if (*next == CLUST_FREE) {
+ boot->NumFree++;
+ return FSOK;
+ }
+ if (*next < CLUST_FIRST
+ || (*next >= boot->NumClusters && *next < CLUST_EOFS)) {
+ pwarn("Cluster %d in FAT %d continues with %s cluster number %d\n",
+ cl, fat,
+ *next < CLUST_RSRVD ? "out of range" : "reserved",
+ *next);
+ if (ask(0, "Truncate")) {
+ *next = CLUST_EOF;
+ return FSFATMOD;
+ }
+ return FSERROR;
+ }
+ return FSOK;
+}
+
+/*
+ * 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;
+ off_t off;
+ int size;
+ int ret = FSOK;
+
+ boot->NumFree = 0;
+ fat = malloc(sizeof(struct fatEntry) * boot->NumClusters);
+ buffer = malloc(boot->FATsecs * boot->BytesPerSec);
+ if (fat == NULL || buffer == NULL) {
+ perror("No space for FAT");
+ if (fat)
+ free(fat);
+ return FSFATAL;
+ }
+
+ memset(fat, 0, sizeof(struct fatEntry) * boot->NumClusters);
+
+ off = boot->ResSectors + no * boot->FATsecs;
+ off *= boot->BytesPerSec;
+
+ if (lseek(fs, off, SEEK_SET) != off) {
+ perror("Unable to read FAT");
+ free(buffer);
+ free(fat);
+ return FSFATAL;
+ }
+
+ if ((size = read(fs, buffer, boot->FATsecs * boot->BytesPerSec))
+ != boot->FATsecs * boot->BytesPerSec) {
+ if (size < 0)
+ perror("Unable to read FAT");
+ else
+ pfatal("Short FAT?");
+ free(buffer);
+ free(fat);
+ return FSFATAL;
+ }
+
+ /*
+ * Remember start of FAT to allow keeping it in write_fat.
+ */
+ fat[0].length = buffer[0]|(buffer[1] << 8)|(buffer[2] << 16);
+ if (boot->Is16BitFat)
+ fat[0].length |= buffer[3] << 24;
+ if (buffer[1] != 0xff || buffer[2] != 0xff
+ || (boot->Is16BitFat && buffer[3] != 0xff)) {
+ char *msg = boot->Is16BitFat
+ ? "FAT starts with odd byte sequence (%02x%02x%02x%02x)\n"
+ : "FAT starts with odd byte sequence (%02x%02x%02x)\n";
+ pwarn(msg, buffer[0], buffer[1], buffer[2], buffer[3]);
+ if (ask(1, "Correct")) {
+ fat[0].length = boot->Media|0xffffff;
+ ret |= FSFATMOD;
+ }
+ }
+ p = buffer + (boot->Is16BitFat ? 4 : 3);
+ for (cl = CLUST_FIRST; cl < boot->NumClusters;) {
+ if (boot->Is16BitFat) {
+ fat[cl].next = p[0] + (p[1] << 8);
+ ret |= checkclnum(boot, no, cl, &fat[cl].next);
+ cl++;
+ p += 2;
+ } else {
+ 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;
+ }
+ }
+
+ free(buffer);
+ *fp = fat;
+ return ret;
+}
+
+/*
+ * Get type of reserved cluster
+ */
+char *
+rsrvdcltype(cl)
+ cl_t cl;
+{
+ 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_RSRVD) {
+ if (*cp2 >= CLUST_RSRVD) {
+ if ((*cp1 < CLUST_BAD && *cp2 < CLUST_BAD)
+ || (*cp1 > CLUST_BAD && *cp2 > CLUST_BAD)) {
+ pwarn("Cluster %d is marked %s with different indicators, ",
+ cl, rsrvdcltype(*cp1));
+ if (ask(1, "fix")) {
+ *cp2 = *cp1;
+ return FSFATMOD;
+ }
+ return FSFATAL;
+ }
+ pwarn("Cluster %d is marked %s in FAT 1, %s in FAT %d\n",
+ cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum);
+ if (ask(0, "use FAT #1's entry")) {
+ *cp2 = *cp1;
+ return FSFATMOD;
+ }
+ if (ask(0, "use FAT #%d's entry", fatnum)) {
+ *cp1 = *cp2;
+ return FSFATMOD;
+ }
+ return FSFATAL;
+ }
+ pwarn("Cluster %d is marked %s in FAT 1, but continues with cluster %d 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 1")) {
+ *cp2 = *cp1;
+ return FSFATMOD;
+ }
+ return FSFATAL;
+ }
+ if (*cp2 >= CLUST_RSRVD) {
+ pwarn("Cluster %d continues with cluster %d in FAT 1, but is marked %s in FAT %d\n",
+ cl, *cp1, rsrvdcltype(*cp2), fatnum);
+ if (ask(0, "Use continuation from FAT 1")) {
+ *cp2 = *cp1;
+ return FSFATMOD;
+ }
+ if (ask(0, "Use mark from FAT %d", fatnum)) {
+ *cp1 = *cp2;
+ return FSFATMOD;
+ }
+ return FSERROR;
+ }
+ pwarn("Cluster %d continues with cluster %d in FAT 1, but with cluster %d in FAT %d\n",
+ cl, *cp1, *cp2, fatnum);
+ if (ask(0, "Use continuation from FAT 1")) {
+ *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;
+
+ if (first[0].next != second[0].next) {
+ pwarn("Media bytes in cluster 1(%02x) and %d(%02x) differ\n",
+ first[0].next, fatnum, second[0].next);
+ if (ask(1, "Use media byte from FAT 1")) {
+ second[0].next = first[0].next;
+ ret |= FSFATMOD;
+ } else if (ask(0, "Use media byte from FAT %d", fatnum)) {
+ first[0].next = second[0].next;
+ ret |= FSFATMOD;
+ } else
+ ret |= FSERROR;
+ }
+ 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;
+ }
+}
+
+/*
+ * Check a complete FAT in-memory for crosslinks
+ */
+int
+checkfat(boot, fat)
+ struct bootblock *boot;
+ struct fatEntry *fat;
+{
+ cl_t head, p, h;
+ 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 untraveled chain */
+ if (fat[head].head != 0 /* cluster already belongs to some chain*/
+ || fat[head].next == CLUST_FREE)
+ 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 = 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 untraveled chain */
+ if (fat[head].head != head)
+ continue;
+
+ /* follow the chain to its end (hopefully) */
+ for (p = head;
+ fat[p].next >= CLUST_FIRST && fat[p].next < boot->NumClusters;
+ p = fat[p].next)
+ if (fat[fat[p].next].head != head)
+ break;
+ if (fat[p].next >= CLUST_EOFS)
+ continue;
+
+ if (fat[p].next == 0) {
+ pwarn("Cluster chain starting at %d ends with free cluster\n", head);
+ if (ask(0, "Clear chain starting at %d", head)) {
+ clearchain(boot, fat, head);
+ ret |= FSFATMOD;
+ } else
+ ret |= FSERROR;
+ continue;
+ }
+ if (fat[p].next >= CLUST_RSRVD) {
+ pwarn("Cluster chain starting at %d ends with cluster marked %s\n",
+ head, rsrvdcltype(fat[p].next));
+ if (ask(0, "Clear chain starting at %d", head)) {
+ clearchain(boot, fat, head);
+ ret |= FSFATMOD;
+ } else
+ ret |= FSERROR;
+ continue;
+ }
+ if (fat[p].next < CLUST_FIRST || fat[p].next >= boot->NumClusters) {
+ pwarn("Cluster chain starting at %d ends with cluster out of range (%d)\n",
+ head, fat[p].next);
+ if (ask(0, "Clear chain starting at %d", head)) {
+ clearchain(boot, fat, head);
+ ret |= FSFATMOD;
+ } else
+ ret |= FSERROR;
+ }
+ pwarn("Cluster chains starting at %d and %d are linked at cluster %d\n",
+ head, fat[p].head, p);
+ conf = FSERROR;
+ if (ask(0, "Clear chain starting at %d", head)) {
+ clearchain(boot, fat, head);
+ conf = FSFATMOD;
+ }
+ if (ask(0, "Clear chain starting at %d", h = fat[p].head)) {
+ if (conf == FSERROR) {
+ /*
+ * Transfer the common chain to the one not cleared above.
+ */
+ for (; 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)
+ int fs;
+ struct bootblock *boot;
+ struct fatEntry *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;
+ buffer[0] = (u_char)fat[0].length;
+ buffer[1] = (u_char)(fat[0].length >> 8);
+ if (boot->Is16BitFat)
+ buffer[3] = (u_char)(fat[0].length >> 24);
+ for (cl = CLUST_FIRST, p = buffer; cl < boot->NumClusters;) {
+ if (boot->Is16BitFat) {
+ p[0] = (u_char)fat[cl].next;
+ if (fat[cl].next == CLUST_FREE)
+ boot->NumFree++;
+ p[1] = (u_char)(fat[cl++].next >> 8);
+ p += 2;
+ } else {
+ if (fat[cl].next == CLUST_FREE)
+ boot->NumFree++;
+ if (cl + 1 < boot->NumClusters
+ && fat[cl + 1].next == CLUST_FREE)
+ boot->NumFree++;
+ p[0] = (u_char)fat[cl].next;
+ p[1] = (u_char)((fat[cl].next >> 8) & 0xf)
+ |(u_char)(fat[cl+1].next << 4);
+ p[2] = (u_char)(fat[cl++].next >> 8);
+ p += 3;
+ }
+ }
+ 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, rootDir)
+ int dosfs;
+ struct bootblock *boot;
+ struct fatEntry *fat;
+ struct dosDirEntry *rootDir;
+{
+ cl_t head;
+ struct dosDirEntry *lfdir;
+ int mod = FSOK;
+
+ for (lfdir = rootDir->child; lfdir; lfdir = lfdir->next) {
+ if (!strcmp(lfdir->name, LOSTDIR))
+ break;
+ }
+ for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
+ /* find next untraveled chain */
+ if (fat[head].head != head
+ || fat[head].next == CLUST_FREE
+ || (fat[head].next >= CLUST_RSRVD
+ && fat[head].next < CLUST_EOFS))
+ continue;
+
+ if (fat[head].dirp == NULL) {
+ pwarn("Lost cluster chain at cluster 0x%04x\n%d Cluster(s) lost\n",
+ head, fat[head].length);
+ mod |= reconnect(dosfs, boot, fat, head, lfdir);
+ if (mod&FSFATAL)
+ break;
+ }
+ }
+ finishlf();
+
+ return mod;
+}
diff --git a/sbin/fsck_msdos/fsck_msdos.8 b/sbin/fsck_msdos/fsck_msdos.8
new file mode 100644
index 00000000000..127c4554316
--- /dev/null
+++ b/sbin/fsck_msdos/fsck_msdos.8
@@ -0,0 +1,76 @@
+.\" $NetBSD: fsck_msdos.8,v 1.1 1996/05/14 17:39:35 ws Exp $
+.\"
+.\" 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 fsck_msdos
+.Fl p
+.Ar filesystem
+.Ar ...
+.Nm fsck_msdos
+.Op Fl y
+.Op Fl n
+.Ar filesystem
+.Ar ...
+.Sh DESCRIPTION
+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.
+.Pp
+.Nm fsck_msdos
+verifies a FAT filesystem (more commonly known as DOS filesystem) and tries
+to recover it from inconsistencies.
+.Pp
+Options are:
+.Bl -hang -offset indent
+.It Em -y
+assume yes as answer to all questions.
+.It Em -n
+assume no as answer to all questions.
+.El
+.Sh SEE ALSO
+.Xr fsck 8 ,
+.Xr mount_msdos 8
+.Sh BUGS
+.Nm fsck_msdos
+is still under construction.
diff --git a/sbin/fsck_msdos/main.c b/sbin/fsck_msdos/main.c
new file mode 100644
index 00000000000..67634aa278f
--- /dev/null
+++ b/sbin/fsck_msdos/main.c
@@ -0,0 +1,252 @@
+/* $NetBSD: main.c,v 1.1 1996/05/14 17:39:36 ws Exp $ */
+
+/*
+ * 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.
+ */
+
+
+#ifndef lint
+static char rcsid[] = "$NetBSD: main.c,v 1.1 1996/05/14 17:39:36 ws Exp $";
+#endif /* not lint */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#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) */
+
+char *fname; /* filesystem currently checked */
+
+static void
+usage()
+{
+ errexit("Usage: fsck_msdos [-pny] filesystem ... \n");
+}
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern int optind;
+ int ret = 0, erg;
+ int ch;
+
+ while ((ch = getopt(argc, argv, "vpyn")) != EOF) {
+ switch (ch) {
+ 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) {
+ erg = checkfilesys(fname = *argv++);
+ if (erg > ret)
+ ret = erg;
+ }
+ exit(ret);
+}
+
+/*VARARGS*/
+void
+#if __STDC__
+errexit(const char *fmt, ...)
+#else
+errexit(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ vprintf(fmt, ap);
+ va_end(ap);
+ exit(8);
+}
+
+/*VARARGS*/
+void
+#if __STDC__
+pfatal(const char *fmt, ...)
+#else
+pfatal(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+ if (preen)
+ printf("%s: ", fname);
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ vprintf(fmt, ap);
+ va_end(ap);
+ printf("\n");
+ if (preen)
+ exit(8);
+}
+
+/*VARARGS*/
+void
+#if __STDC__
+pwarn(const char *fmt, ...)
+#else
+pwarn(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+ if (preen)
+ printf("%s: ", fname);
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ vprintf(fmt, ap);
+ va_end(ap);
+}
+
+#if sun
+char *
+strerror(n)
+ int n;
+{
+ extern int sys_nerr;
+ extern char *sys_errlist[];
+ static char alt[80];
+
+ if (n < sys_nerr)
+ return sys_errlist[n];
+ sprintf(alt, "Unknown error %d", n);
+ return alt;
+}
+#endif
+
+void
+perror(s)
+ const char *s;
+{
+ pfatal("%s (%s)", s, strerror(errno));
+}
+
+/*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
+#if sun
+ vsprintf(prompt, fmt, ap);
+#else
+ vsnprintf(prompt, sizeof(prompt), fmt, ap);
+#endif
+ 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';
+}