summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2019-04-10 13:39:57 -0700
committerXin Li <delphij@google.com>2019-04-10 13:39:57 -0700
commit760f5401a046bf4f03f52300af30d502fc4e6170 (patch)
treed3c95e9e8b7f3fb89c08442947d645ce1e979db5
parent1e1dad6992abea357715fdae4f747ae2051025a0 (diff)
downloadfsck_msdos-760f5401a046bf4f03f52300af30d502fc4e6170.tar.gz
Import revision 76844b29fc04ced003061bba03e7f6c947ed362c from FreeBSD.
Change-Id: I5d376e1121e10536530f66e6aae1df7d96ebfcaf
-rw-r--r--dir.c122
-rw-r--r--fat.c14
2 files changed, 128 insertions, 8 deletions
diff --git a/dir.c b/dir.c
index 38c7014..b990b0c 100644
--- a/dir.c
+++ b/dir.c
@@ -35,6 +35,8 @@ static const char rcsid[] =
"$FreeBSD$";
#endif /* not lint */
+#include <assert.h>
+#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -329,8 +331,11 @@ delete(int f, struct bootblock *boot, struct fatEntry *fat, cl_t startcl,
}
off = startcl * boot->bpbSecPerClust + boot->ClusterOffset;
off *= boot->bpbBytesPerSec;
- if (lseek(f, off, SEEK_SET) != off
- || read(f, delbuf, clsz) != clsz) {
+ if (lseek(f, off, SEEK_SET) != off) {
+ perr("Unable to lseek to %" PRId64, off);
+ return FSFATAL;
+ }
+ if (read(f, delbuf, clsz) != clsz) {
perr("Unable to read directory");
return FSFATAL;
}
@@ -338,8 +343,11 @@ delete(int f, struct bootblock *boot, struct fatEntry *fat, cl_t startcl,
*s = SLOT_DELETED;
s += 32;
}
- if (lseek(f, off, SEEK_SET) != off
- || write(f, delbuf, clsz) != clsz) {
+ if (lseek(f, off, SEEK_SET) != off) {
+ perr("Unable to lseek to %" PRId64, off);
+ return FSFATAL;
+ }
+ if (write(f, delbuf, clsz) != clsz) {
perr("Unable to write directory");
return FSFATAL;
}
@@ -436,6 +444,75 @@ checksize(struct bootblock *boot, struct fatEntry *fat, u_char *p,
return FSOK;
}
+static const u_char dot_name[11] = ". ";
+static const u_char dotdot_name[11] = ".. ";
+
+/*
+ * Basic sanity check if the subdirectory have good '.' and '..' entries,
+ * and they are directory entries. Further sanity checks are performed
+ * when we traverse into it.
+ */
+static int
+check_subdirectory(int f, struct bootblock *boot, struct dosDirEntry *dir)
+{
+ u_char *buf, *cp;
+ off_t off;
+ cl_t cl;
+ int retval = FSOK;
+
+ cl = dir->head;
+ if (dir->parent && (cl < CLUST_FIRST || cl >= boot->NumClusters)) {
+ return FSERROR;
+ }
+
+ if (!(boot->flags & FAT32) && !dir->parent) {
+ off = boot->bpbResSectors + boot->bpbFATs *
+ boot->FATsecs;
+ } else {
+ off = cl * boot->bpbSecPerClust + boot->ClusterOffset;
+ }
+
+ /*
+ * We only need to check the first two entries of the directory,
+ * which is found in the first sector of the directory entry,
+ * so read in only the first sector.
+ */
+ buf = malloc(boot->bpbBytesPerSec);
+ if (buf == NULL) {
+ perr("No space for directory buffer (%u)",
+ boot->bpbBytesPerSec);
+ return FSFATAL;
+ }
+
+ off *= boot->bpbBytesPerSec;
+ if (lseek(f, off, SEEK_SET) != off ||
+ read(f, buf, boot->bpbBytesPerSec) != (ssize_t)boot->bpbBytesPerSec) {
+ perr("Unable to read directory");
+ free(buf);
+ return FSFATAL;
+ }
+
+ /*
+ * Both `.' and `..' must be present and be the first two entries
+ * and be ATTR_DIRECTORY of a valid subdirectory.
+ */
+ cp = buf;
+ if (memcmp(cp, dot_name, sizeof(dot_name)) != 0 ||
+ (cp[11] & ATTR_DIRECTORY) != ATTR_DIRECTORY) {
+ pwarn("%s: Incorrect `.' for %s.\n", __func__, dir->name);
+ retval |= FSERROR;
+ }
+ cp += 32;
+ if (memcmp(cp, dotdot_name, sizeof(dotdot_name)) != 0 ||
+ (cp[11] & ATTR_DIRECTORY) != ATTR_DIRECTORY) {
+ pwarn("%s: Incorrect `..' for %s. \n", __func__, dir->name);
+ retval |= FSERROR;
+ }
+
+ free(buf);
+ return retval;
+}
+
/*
* Read a directory and
* - resolve long name records
@@ -483,9 +560,6 @@ readDosDirSection(int f, struct bootblock *boot, struct fatEntry *fat,
return FSFATAL;
}
last /= 32;
- /*
- * Check `.' and `..' entries here? XXX
- */
for (p = buffer, i = 0; i < last; i++, p += 32) {
if (dir->fsckflags & DIREMPWARN) {
*p = SLOT_EMPTY;
@@ -513,7 +587,8 @@ readDosDirSection(int f, struct bootblock *boot, struct fatEntry *fat,
empcl, empty - buffer,
cl, p - buffer, 1) == FSFATAL)
return FSFATAL;
- q = empcl == cl ? empty : buffer;
+ q = ((empcl == cl) ? empty : buffer);
+ assert(q != NULL);
for (; q < p; q += 32)
*q = SLOT_DELETED;
mod |= THISMOD|FSDIRMOD;
@@ -821,6 +896,36 @@ readDosDirSection(int f, struct bootblock *boot, struct fatEntry *fat,
}
}
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\n",
+ dirent.name, fullpath(dir));
+ if (ask(1, "Remove")) {
+ *p = SLOT_DELETED;
+ mod |= THISMOD|FSDIRMOD;
+ } else
+ mod |= FSERROR;
+ continue;
+ } else if ((check_subdirectory(f, boot,
+ &dirent) & FSERROR) == FSERROR) {
+ /*
+ * A subdirectory should have
+ * a dot (.) entry and a dot-dot
+ * (..) entry of ATTR_DIRECTORY,
+ * we will inspect further when
+ * traversing into it.
+ */
+ if (ask(1, "Remove")) {
+ *p = SLOT_DELETED;
+ mod |= THISMOD|FSDIRMOD;
+ } else
+ mod |= FSERROR;
+ continue;
+ }
}
/* create directory tree node */
@@ -964,6 +1069,7 @@ reconnect(int dosfs, struct bootblock *boot, struct fatEntry *fat, cl_t 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
diff --git a/fat.c b/fat.c
index 8a54837..7ea0274 100644
--- a/fat.c
+++ b/fat.c
@@ -705,6 +705,20 @@ checklost(int dosfs, struct bootblock *boot, struct fatEntry *fat)
ret = 1;
}
}
+ if (boot->FSNext != 0xffffffffU &&
+ (boot->FSNext >= boot->NumClusters ||
+ (boot->NumFree && fat[boot->FSNext].next != CLUST_FREE))) {
+ pwarn("Next free cluster in FSInfo block (%u) %s\n",
+ boot->FSNext,
+ (boot->FSNext >= boot->NumClusters) ? "invalid" : "not free");
+ 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);
}