Imported Upstream version 1.0.0 51/83951/1 upstream upstream/1.0.0
authorsilas jeon <silasjeon@samsung.com>
Tue, 16 Aug 2016 03:57:04 +0000 (12:57 +0900)
committersilas jeon <silasjeon@samsung.com>
Tue, 16 Aug 2016 03:58:37 +0000 (12:58 +0900)
Change-Id: Iab341b67a15248d42d05c387b858ab271b4f2996

CMakeLists.txt [new file with mode: 0644]
LICENSE [new file with mode: 0644]
src/boot.c [new file with mode: 0644]
src/check.c [new file with mode: 0644]
src/dir.c [new file with mode: 0644]
src/dosfs.h [new file with mode: 0644]
src/ext.h [new file with mode: 0644]
src/fat.c [new file with mode: 0644]
src/fsutil.h [new file with mode: 0644]
src/main.c [new file with mode: 0644]

diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..048527d
--- /dev/null
@@ -0,0 +1,29 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+PROJECT(fsck_msdosfs C)
+
+SET(SRCS
+               src/boot.c
+               src/check.c
+               src/dir.c
+               src/fat.c
+               src/main.c
+)
+
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
+
+INCLUDE(FindPkgConfig)
+
+FOREACH(flag ${pkgs_CFLAGS})
+       SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -g -fno-omit-frame-pointer -finstrument-functions")
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}")
+
+ADD_DEFINITIONS("-D_GNU_SOURCE")
+
+ADD_EXECUTABLE(${PROJECT_NAME} ${SRCS})
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS})
+
+INSTALL(TARGETS ${PROJECT_NAME} DESTINATION bin)
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..e3c375e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+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.
diff --git a/src/boot.c b/src/boot.c
new file mode 100644 (file)
index 0000000..f51fc6f
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * 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 <sys/cdefs.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#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");
+                exit(2);
+       }
+
+       if (block[510] != 0x55 || block[511] != 0xaa) {
+               pfatal("Invalid signature in boot block: %02x%02x", block[511], block[510]);
+                exit(2);
+       }
+
+       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 file system version: %x.%x",
+                              block[43], block[42]);
+                        exit(2);
+               }
+               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\n");
+                       if (ask(1, "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;
+               }
+               backup[65] = block[65];                         /* XXX */
+               if (memcmp(block + 11, backup + 11, 79)) {
+                        char tmp[255];
+                        int i;
+
+                       /*
+                        * For now, lets not bail out if they don't match
+                        * It seems a lot of sdcards are formatted with
+                        * the backup either empty or containing garbage.
+                        */
+
+                       pwarn("Primary/Backup bootblock miscompare\n");
+
+                        strcpy(tmp, "");
+                        pwarn("Primary:\n");
+                       for (i = 0; i < 79; i++) {
+                               char tmp2[16];
+                                snprintf(tmp2, sizeof(tmp2), "%.2x ", block[11 + i]);
+                               strcat(tmp, tmp2);
+                        }
+                        pwarn("%s\n", tmp);
+
+                       strcpy(tmp, "");
+                        pwarn("Backup:\n");
+                       for (i = 0; i < 79; i++) {
+                               char tmp2[16];
+                                snprintf(tmp2, sizeof(tmp2), "%.2x ", backup[11 + i]);
+                               strcat(tmp, tmp2);
+                        }
+                        pwarn("%s\n", tmp);
+               }
+               /* Check backup FSInfo?                                 XXX */
+       }
+
+       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->BytesPerSec == 0) {
+               pfatal("Invalid sector size: %u", boot->BytesPerSec);
+               return FSFATAL;
+       }
+       if (boot->FATs == 0) {
+               pfatal("Invalid number of FATs: %u", boot->FATs);
+               return FSFATAL;
+       }
+       if (boot->Sectors) {
+               boot->HugeSectors = 0;
+               boot->NumSectors = boot->Sectors;
+       } else
+               boot->NumSectors = boot->HugeSectors;
+
+       boot->ClusterOffset = (boot->RootDirEnts * 32 + boot->BytesPerSec - 1)
+           / boot->BytesPerSec
+           + boot->ResSectors
+           + boot->FATs * boot->FATsecs
+           - CLUST_FIRST * boot->SecPerClust;
+
+       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 necessary, but otherwise ignoring the
+        * fact that it was incorrect.
+        */
+       return 0;
+}
diff --git a/src/check.c b/src/check.c
new file mode 100644 (file)
index 0000000..bb5fbc2
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "ext.h"
+#include "fsutil.h"
+
+/*
+ * If the FAT > this size then skip comparing, lest we risk
+ * OOMing the framework. in the future we need to just re-write
+ * this whole thing and optimize for less memory
+ */
+#define FAT_COMPARE_MAX_KB 4096
+
+int
+checkfilesys(const char *fname)
+{
+       int dosfs;
+       struct bootblock boot;
+       struct fatEntry *fat = NULL;
+       int i, finish_dosdirsection=0;
+       int mod = 0;
+       int ret = 8;
+        int quiet = 0;
+        int skip_fat_compare = 0;
+
+       rdonly = alwaysno;
+       if (!quiet)
+               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 (!quiet)
+                       printf("\n");
+               rdonly = 1;
+       } else if (!quiet)
+               printf("\n");
+
+       if (dosfs < 0) {
+               perror("Can't open");
+               return 8;
+       }
+
+       if (readboot(dosfs, &boot) == FSFATAL) {
+               close(dosfs);
+               printf("\n");
+               return 8;
+       }
+
+       if (skipclean && preen && checkdirty(dosfs, &boot)) {
+               printf("%s: ", fname);
+               printf("FILESYSTEM CLEAN; SKIPPING CHECKS\n");
+               ret = 0;
+               goto out;
+       }
+
+        if (((boot.FATsecs * boot.BytesPerSec) / 1024) > FAT_COMPARE_MAX_KB)
+            skip_fat_compare = 1;
+
+       if (!quiet)  {
+                if (skip_fat_compare)
+                        printf("** Phase 1 - Read FAT (compare skipped)\n");
+               else 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) {
+               printf("Fatal error during readfat()\n");
+               if (fat)
+                       free(fat);
+               close(dosfs);
+               return 8;
+       }
+
+       if (!skip_fat_compare && boot.ValidFat < 0)
+               for (i = 1; i < (int)boot.FATs; i++) {
+                       struct fatEntry *currentFat;
+
+                       mod |= readfat(dosfs, &boot, i, &currentFat);
+
+                       if (mod & FSFATAL) {
+                               printf("Fatal error during readfat() for comparison\n");
+                               goto out;
+                       }
+
+                       mod |= comparefat(&boot, fat, currentFat, i);
+                       free(currentFat);
+                       if (mod & FSFATAL) {
+                               printf("Fatal error during FAT comparison\n");
+                               goto out;
+                       }
+               }
+
+       if (!quiet)
+               printf("** Phase 2 - Check Cluster Chains\n");
+
+       mod |= checkfat(&boot, fat);
+       if (mod & FSFATAL) {
+               printf("Fatal error during FAT check\n");
+               goto out;
+       }
+       /* delay writing FATs */
+
+       if (!quiet)
+               printf("** Phase 3 - Checking Directories\n");
+
+       mod |= resetDosDirSection(&boot, fat);
+       finish_dosdirsection = 1;
+       if (mod & FSFATAL) {
+               printf("Fatal error during resetDosDirSection()\n");
+               goto out;
+       }
+       /* delay writing FATs */
+
+       mod |= handleDirTree(dosfs, &boot, fat);
+       if (mod & FSFATAL)
+               goto out;
+
+       if (!quiet)
+               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) {
+                               printf("Fatal error during writefat()\n");
+                               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 4;
+       }
+
+       return ret;
+}
diff --git a/src/dir.c b/src/dir.c
new file mode 100644 (file)
index 0000000..f5224dd
--- /dev/null
+++ b/src/dir.c
@@ -0,0 +1,1100 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <sys/param.h>
+
+#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(void);
+static void freeDosDirEntry(struct dosDirEntry *);
+static struct dirTodoNode *newDirTodo(void);
+static void freeDirTodo(struct dirTodoNode *);
+static char *fullpath(struct dosDirEntry *);
+static u_char calcShortSum(u_char *);
+static int delete(int, struct bootblock *, struct fatEntry *, cl_t, int,
+    cl_t, int, int);
+static int removede(int, struct bootblock *, struct fatEntry *, u_char *,
+    u_char *, cl_t, cl_t, cl_t, char *, int);
+static int checksize(struct bootblock *, struct fatEntry *, u_char *,
+    struct dosDirEntry *);
+static int readDosDirSection(int, struct bootblock *, struct fatEntry *,
+    struct dosDirEntry *);
+
+/*
+ * Manage free dosDirEntry structures.
+ */
+static struct dosDirEntry *freede;
+
+static struct dosDirEntry *
+newDosDirEntry(void)
+{
+       struct dosDirEntry *de;
+
+       if (!(de = freede)) {
+               if (!(de = (struct dosDirEntry *)malloc(sizeof *de)))
+                       return 0;
+       } else
+               freede = de->next;
+       return de;
+}
+
+static void
+freeDosDirEntry(struct dosDirEntry *de)
+{
+       de->next = freede;
+       freede = de;
+}
+
+/*
+ * The same for dirTodoNode structures.
+ */
+static struct dirTodoNode *freedt;
+
+static struct dirTodoNode *
+newDirTodo(void)
+{
+       struct dirTodoNode *dt;
+
+       if (!(dt = freedt)) {
+               if (!(dt = (struct dirTodoNode *)malloc(sizeof *dt)))
+                       return 0;
+       } else
+               freedt = dt->next;
+       return dt;
+}
+
+static void
+freeDirTodo(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(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(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(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(void)
+{
+       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(int f, struct bootblock *boot, struct fatEntry *fat, cl_t startcl,
+    int startoff, cl_t endcl, int endoff, int notlast)
+{
+       u_char *s, *e;
+       loff_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 (lseek64(f, off, SEEK_SET) != off) {
+                       printf("off = %llu\n", off);
+                       perror("Unable to lseek64");
+                       return FSFATAL;
+               }
+               if (read(f, delbuf, clsz) != clsz) {
+                       perror("Unable to read directory");
+                       return FSFATAL;
+               }
+               while (s < e) {
+                       *s = SLOT_DELETED;
+                       s += 32;
+               }
+               if (lseek64(f, off, SEEK_SET) != off) {
+                       printf("off = %llu\n", off);
+                       perror("Unable to lseek64");
+                       return FSFATAL;
+               }
+               if (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(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(1, "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(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;
+}
+
+
+static u_char  dot_header[16]={0x2E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00};
+static u_char  dot_dot_header[16]={0x2E, 0x2E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00};
+
+/*
+ * Check for missing or broken '.' and '..' entries.
+ */
+static int
+check_dot_dot(int f, struct bootblock *boot, struct fatEntry *fat,struct dosDirEntry *dir)
+{
+       u_char *p, *buf;
+       loff_t off;
+       int last;
+       cl_t cl;
+       int rc=0, n_count;
+
+       int dot, dotdot;
+       dot = dotdot = 0;
+       cl = dir->head;
+
+       if (dir->parent && (cl < CLUST_FIRST || cl >= boot->NumClusters)) {
+               return rc;
+       }
+
+       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;
+               buf = malloc(last);
+               if (!buf) {
+                       perror("Unable to malloc");
+                       return FSFATAL;
+               }
+               if (lseek64(f, off, SEEK_SET) != off) {
+                       printf("off = %llu\n", off);
+                       perror("Unable to lseek64");
+                       free(buf);
+                       return FSFATAL;
+               }
+               if (read(f, buf, last) != last) {
+                       perror("Unable to read");
+                       free(buf);
+                       return FSFATAL;
+               }
+               last /= 32;
+               p = buf;
+               for (n_count=0, rc=0; n_count < 11; n_count++) {
+                       if (dot_header[n_count] != p[n_count]) {
+                               rc=-1;
+                               break;
+                       }
+               }
+                if(!rc)
+                       dot=1;
+
+               for (n_count = 0, rc = 0; n_count < 11; n_count++) {
+                       if (dot_dot_header[n_count] != p[n_count+32]) {
+                               rc=-1;
+                               break;
+                       }
+               }
+               if(!rc)
+                       dotdot=1;
+               free(buf);
+       } while ((cl = fat[cl].next) >= CLUST_FIRST && cl < boot->NumClusters);
+
+       if (!dot || !dotdot) {
+               if (!dot)
+                       pwarn("%s: '.' absent for %s.\n",__func__,dir->name);
+
+               if (!dotdot)
+                       pwarn("%s: '..' absent for %s. \n",__func__,dir->name);
+               return -1;
+       }
+       return 0;
+}
+
+/*
+ * 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(int f, struct bootblock *boot, struct fatEntry *fat,
+    struct dosDirEntry *dir)
+{
+       struct dosDirEntry dirent, *d;
+       u_char *p, *vallfn, *invlfn, *empty;
+       loff_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;
+       int n_count=0;
+       int rc=0;
+#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;
+       int dot,dotdot;
+       dot = dotdot = 0;
+
+       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 (lseek64(f, off, SEEK_SET) != off) {
+                        printf("off = %llu\n", off);
+                       perror("Unable to lseek64");
+                       return FSFATAL;
+                }
+                if (read(f, buffer, last) != last) {
+                       perror("Unable to read");
+                       return FSFATAL;
+                }
+               last /= 32;
+               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(1, "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(1, "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;
+                } else 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;
+                               } else { //only one directory entry can point to dir->head, it's  '.'
+                                       if (dirent.head == dir->head) {
+                                               pwarn("%s entry in %s has incorrect start cluster.remove\n",
+                                                               dirent.name, fullpath(dir));
+                                               //we have to remove this directory entry rigth now rigth here
+                                               if (ask(1, "Remove")) {
+                                                       *p = SLOT_DELETED;
+                                                       mod |= THISMOD|FSDIRMOD;
+                                               } else
+                                                       mod |= FSERROR;
+                                               continue;
+                                       }
+                                       /* Consistency checking. a directory must have at least two entries:
+                                          a dot (.) entry that points to itself, and a dot-dot (..)
+                                          entry that points to its parent.
+                                        */
+                                       if (check_dot_dot(f,boot,fat,&dirent)) {
+                                               //mark directory entry as deleted.
+                                               if (ask(1, "Remove")) {
+                                                       *p = SLOT_DELETED;
+                                                       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;
+#if 0
+                               printf("%s: %s : 0x%02x:head %d, next 0x%0x parent 0x%0x child 0x%0x\n",
+                                               __func__,d->name,d->flags,d->head,d->next,d->parent,d->child);
+#endif
+                               /* 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 (lseek64(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(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 loff_t lfoff;
+
+int
+reconnect(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);
+                       lfcl = (lostDir->head < boot->NumClusters) ? lostDir->head : 0;
+                       return FSERROR;
+               }
+               lfoff = lfcl * boot->ClusterSize
+                   + boot->ClusterOffset * boot->BytesPerSec;
+               if (lseek64(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 (lseek64(dosfs, lfoff, SEEK_SET) != lfoff
+           || write(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) {
+               perror("could not write LOST.DIR");
+               return FSFATAL;
+       }
+       return FSDIRMOD;
+}
+
+void
+finishlf(void)
+{
+       if (lfbuf)
+               free(lfbuf);
+       lfbuf = NULL;
+}
diff --git a/src/dosfs.h b/src/dosfs.h
new file mode 100644 (file)
index 0000000..5420e25
--- /dev/null
@@ -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: src/sbin/fsck_msdosfs/dosfs.h,v 1.3 2003/12/26 17:24:37 trhodes Exp $
+ */
+
+#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 file system */
+                                       /*
+                                        * 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/src/ext.h b/src/ext.h
new file mode 100644 (file)
index 0000000..6d183e9
--- /dev/null
+++ b/src/ext.h
@@ -0,0 +1,150 @@
+/*
+ * 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: src/sbin/fsck_msdosfs/ext.h,v 1.10.20.1 2009/04/15 03:14:26 kensmith Exp $
+ */
+
+#ifndef EXT_H
+#define EXT_H
+
+#include <sys/types.h>
+
+#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 int skipclean;  /* skip clean file systems if preening */
+
+extern struct dosDirEntry *rootDir;
+
+/*
+ * function declarations
+ */
+int ask(int, const char *, ...);
+
+/*
+ * Check the dirty flag.  If the file system is clean, then return 1.
+ * Otherwise, return 0 (this includes the case of FAT12 file systems --
+ * they have no dirty flag, so they must be assumed to be unclean).
+ */
+int checkdirty(int, struct bootblock *);
+
+/*
+ * Check file system given as arg
+ */
+int checkfilesys(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(int, struct bootblock *);
+
+/*
+ * Correct the FSInfo block.
+ */
+int writefsinfo(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(int, struct bootblock *, int, struct fatEntry **);
+
+/*
+ * Check two FAT copies for consistency and merge changes into the
+ * first if neccessary.
+ */
+int comparefat(struct bootblock *, struct fatEntry *, struct fatEntry *, int);
+
+/*
+ * Check a FAT
+ */
+int checkfat(struct bootblock *, struct fatEntry *);
+
+/*
+ * Write back FAT entries
+ */
+int writefat(int, struct bootblock *, struct fatEntry *, int);
+
+/*
+ * Read a directory
+ */
+int resetDosDirSection(struct bootblock *, struct fatEntry *);
+void finishDosDirSection(void);
+int handleDirTree(int, struct bootblock *, struct fatEntry *);
+
+/*
+ * Cross-check routines run after everything is completely in memory
+ */
+/*
+ * Check for lost cluster chains
+ */
+int checklost(int, struct bootblock *, struct fatEntry *);
+/*
+ * Try to reconnect a lost cluster chain
+ */
+int reconnect(int, struct bootblock *, struct fatEntry *, cl_t);
+void finishlf(void);
+
+/*
+ * Small helper functions
+ */
+/*
+ * Return the type of a reserved cluster as text
+ */
+char *rsrvdcltype(cl_t);
+
+/*
+ * Clear a cluster chain in a FAT
+ */
+void clearchain(struct bootblock *, struct fatEntry *, cl_t);
+
+#endif
diff --git a/src/fat.c b/src/fat.c
new file mode 100644 (file)
index 0000000..7be04e5
--- /dev/null
+++ b/src/fat.c
@@ -0,0 +1,735 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "ext.h"
+#include "fsutil.h"
+
+static int checkclnum(struct bootblock *, int, cl_t, cl_t *);
+static int clustdiffer(cl_t, cl_t *, cl_t *, int);
+static int tryclear(struct bootblock *, struct fatEntry *, cl_t, cl_t *);
+static int _readfat(int, struct bootblock *, int, u_char **);
+
+/*-
+ * The first 2 FAT entries contain pseudo-cluster numbers with the following
+ * layout:
+ *
+ * 31...... ........ ........ .......0
+ * rrrr1111 11111111 11111111 mmmmmmmm         FAT32 entry 0
+ * rrrrsh11 11111111 11111111 11111xxx         FAT32 entry 1
+ *
+ *                   11111111 mmmmmmmm         FAT16 entry 0
+ *                   sh111111 11111xxx         FAT16 entry 1
+ *
+ * r = reserved
+ * m = BPB media ID byte
+ * s = clean flag (1 = dismounted; 0 = still mounted)
+ * h = hard error flag (1 = ok; 0 = I/O error)
+ * x = any value ok
+ */
+
+int
+checkdirty(int fs, struct bootblock *boot)
+{
+       off_t off;
+       u_char *buffer;
+       int ret = 0;
+
+       if (boot->ClustMask != CLUST16_MASK && boot->ClustMask != CLUST32_MASK)
+               return 0;
+
+       off = boot->ResSectors;
+       off *= boot->BytesPerSec;
+
+       buffer = malloc(boot->BytesPerSec);
+       if (buffer == NULL) {
+               perror("No space for FAT");
+               return 1;
+       }
+
+       if (lseek(fs, off, SEEK_SET) != off) {
+               perror("Unable to read FAT");
+               goto err;
+       }
+
+       if (read(fs, buffer, boot->BytesPerSec) != boot->BytesPerSec) {
+               perror("Unable to read FAT");
+               goto err;
+       }
+
+       /*
+        * If we don't understand the FAT, then the file system must be
+        * assumed to be unclean.
+        */
+       if (buffer[0] != boot->Media || buffer[1] != 0xff)
+               goto err;
+       if (boot->ClustMask == CLUST16_MASK) {
+               if ((buffer[2] & 0xf8) != 0xf8 || (buffer[3] & 0x3f) != 0x3f)
+                       goto err;
+       } else {
+               if (buffer[2] != 0xff || (buffer[3] & 0x0f) != 0x0f
+                   || (buffer[4] & 0xf8) != 0xf8 || buffer[5] != 0xff
+                   || buffer[6] != 0xff || (buffer[7] & 0x03) != 0x03)
+                       goto err;
+       }
+
+       /*
+        * Now check the actual clean flag (and the no-error flag).
+        */
+       if (boot->ClustMask == CLUST16_MASK) {
+               if ((buffer[3] & 0xc0) == 0xc0)
+                       ret = 1;
+       } else {
+               if ((buffer[7] & 0x0c) == 0x0c)
+                       ret = 1;
+       }
+
+err:
+       free(buffer);
+       return ret;
+}
+
+/*
+ * Check a cluster number for valid value
+ */
+static int
+checkclnum(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(1, "Truncate")) {
+                       *next = CLUST_EOF;
+                       return FSFATMOD;
+               }
+               return FSERROR;
+       }
+       return FSOK;
+}
+
+/*
+ * Read a FAT from disk. Returns 1 if successful, 0 otherwise.
+ */
+static int
+_readfat(int fs, struct bootblock *boot, int no, u_char **buffer)
+{
+       off_t off;
+
+        printf("Attempting to allocate %u KB for FAT\n",
+                (boot->FATsecs * boot->BytesPerSec) / 1024);
+
+       *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(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
+                * file system 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_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_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\n",
+                                     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(1, "Use FAT 0's entry")) {
+                               *cp2 = *cp1;
+                               return FSFATMOD;
+                       }
+                       if (ask(1, "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(1, "Use continuation from FAT %d", fatnum)) {
+                       *cp1 = *cp2;
+                       return FSFATMOD;
+               }
+               if (ask(1, "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(1, "Use continuation from FAT 0")) {
+                       *cp2 = *cp1;
+                       return FSFATMOD;
+               }
+               if (ask(1, "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(1, "Use continuation from FAT 0")) {
+               *cp2 = *cp1;
+               return FSFATMOD;
+       }
+       if (ask(1, "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(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(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(struct bootblock *boot, struct fatEntry *fat, cl_t head, cl_t *trunc)
+{
+       if (ask(1, "Clear chain starting at %u", head)) {
+               clearchain(boot, fat, head);
+               return FSFATMOD;
+       } else if (ask(1, "Truncate")) {
+               *trunc = CLUST_EOF;
+               return FSFATMOD;
+       } else
+               return FSERROR;
+}
+
+/*
+ * Check a complete FAT in-memory for crosslinks
+ */
+int
+checkfat(struct bootblock *boot, struct fatEntry *fat)
+{
+       cl_t head, p, h, n, wdk;
+       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) {
+                               /* we have to check the len, to avoid infinite loop */
+                               if (len > boot->NumClusters) {
+                                       printf("detect cluster chain loop: head %u for p %u\n", head, p);
+                                       break;
+                       }
+
+                       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) */
+               /* also possible infinite loop, that's why I insert wdk counter */
+               for (p = head,wdk=boot->NumClusters;
+                    (n = fat[p].next) >= CLUST_FIRST && n < boot->NumClusters && wdk;
+                                p = n,wdk--) {
+                       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(1, "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(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(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) {
+                       /* If the reconnect failed, then just clear the chain */
+                       pwarn("Error reconnecting chain - clearing\n");
+                       mod &= ~FSFATAL;
+                       clearchain(boot, fat, head);
+                       mod |= FSFATMOD;
+                       continue;
+               }
+               if (ret == FSERROR && ask(1, "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) {
+                       if ((boot->FSNext >= boot->NumClusters) || (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 (boot->FSNext > boot->NumClusters  ) {
+                       pwarn("FSNext block (%d) not correct NumClusters (%d)\n",
+                                       boot->FSNext, boot->NumClusters);
+                       boot->FSNext=CLUST_FIRST; // boot->FSNext can have -1 value.
+           }
+
+               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/src/fsutil.h b/src/fsutil.h
new file mode 100644 (file)
index 0000000..7acfdd6
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef _FS_UTIL_H
+#define _FS_UTIL_H
+
+#define pwarn printf
+#define pfatal printf
+
+#endif
diff --git a/src/main.c b/src/main.c
new file mode 100644 (file)
index 0000000..9539d8f
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdarg.h>
+
+#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) */
+int skipclean;         /* skip clean file systems if preening */
+
+static void usage(void);
+
+static void
+usage(void)
+{
+
+       fprintf(stderr, "%s\n%s\n",
+           "usage: fsck_msdosfs -p [-f] filesystem ...",
+           "       fsck_msdosfs [-ny] filesystem ...");
+       exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+       int ret = 0, erg;
+       int ch;
+
+       skipclean = 1;
+       while ((ch = getopt(argc, argv, "CfFnpy")) != -1) {
+               switch (ch) {
+               case 'C': /* for fsck_ffs compatibility */
+                       break;
+               case 'f':
+                       skipclean = 0;
+                       break;
+               case 'F':
+                       /*
+                        * We can never run in the background.  We must exit
+                        * silently with a nonzero exit code so that fsck(8)
+                        * can probe our support for -F.  The exit code
+                        * doesn't really matter, but we use an unusual one
+                        * in case someone tries -F directly.  The -F flag
+                        * is intentionally left out of the usage message.
+                        */
+                       exit(5);
+               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
+ask(int def, const char *fmt, ...)
+{
+       va_list ap;
+
+       char prompt[256];
+       int c;
+
+       if (preen) {
+               if (rdonly)
+                       def = 0;
+               if (def)
+                       printf("FIXED\n");
+               return def;
+       }
+
+       va_start(ap, fmt);
+       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';
+}