aboutsummaryrefslogtreecommitdiff
path: root/sbin/fsck_msdosfs/dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'sbin/fsck_msdosfs/dir.c')
-rw-r--r--sbin/fsck_msdosfs/dir.c107
1 files changed, 102 insertions, 5 deletions
diff --git a/sbin/fsck_msdosfs/dir.c b/sbin/fsck_msdosfs/dir.c
index 0a2418eb9d12..e59536f6b5fc 100644
--- a/sbin/fsck_msdosfs/dir.c
+++ b/sbin/fsck_msdosfs/dir.c
@@ -115,7 +115,7 @@ newDosDirEntry(void)
struct dosDirEntry *de;
if (!(de = freede)) {
- if (!(de = (struct dosDirEntry *)malloc(sizeof *de)))
+ if (!(de = malloc(sizeof *de)))
return 0;
} else
freede = de->next;
@@ -140,7 +140,7 @@ newDirTodo(void)
struct dirTodoNode *dt;
if (!(dt = freedt)) {
- if (!(dt = (struct dirTodoNode *)malloc(sizeof *dt)))
+ if (!(dt = malloc(sizeof *dt)))
return 0;
} else
freedt = dt->next;
@@ -444,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
@@ -491,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;
@@ -830,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 */
@@ -973,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