aboutsummaryrefslogtreecommitdiff
path: root/sbin/fsck_msdosfs
diff options
context:
space:
mode:
authorXin LI <delphij@FreeBSD.org>2019-04-05 02:21:16 +0000
committerXin LI <delphij@FreeBSD.org>2019-04-05 02:21:16 +0000
commitd78b6f1ee8faa680b8ab7e6b37286028524bf840 (patch)
tree2389b2f4dc743b58d882c439314366810f1316e2 /sbin/fsck_msdosfs
parentbcf55f646341fa440f267a57d333801e3491d4e0 (diff)
downloadsrc-d78b6f1ee8faa680b8ab7e6b37286028524bf840.tar.gz
src-d78b6f1ee8faa680b8ab7e6b37286028524bf840.zip
Implement checking of `.' and `..' entries of subdirectory.
Notes
Notes: svn path=/head/; revision=345900
Diffstat (limited to 'sbin/fsck_msdosfs')
-rw-r--r--sbin/fsck_msdosfs/dir.c104
1 files changed, 101 insertions, 3 deletions
diff --git a/sbin/fsck_msdosfs/dir.c b/sbin/fsck_msdosfs/dir.c
index a468a42b4276..06a58f8c7a04 100644
--- a/sbin/fsck_msdosfs/dir.c
+++ b/sbin/fsck_msdosfs/dir.c
@@ -444,6 +444,77 @@ checksize(struct bootblock *boot, struct fatEntry *fat, u_char *p,
return FSOK;
}
+static const u_char dot_name[] = {
+ '.', ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ' };
+static const u_char dotdot_name[] = {
+ '.', '.',' ',' ',' ',' ',' ',' ',' ',' ',' ' };
+
+/*
+ * 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) != 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 +562,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 +898,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 */