Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 5 Apr 2019 02:21:16 +0000 (UTC)
From:      Xin LI <delphij@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r345900 - head/sbin/fsck_msdosfs
Message-ID:  <201904050221.x352LGjU015040@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: delphij
Date: Fri Apr  5 02:21:16 2019
New Revision: 345900
URL: https://svnweb.freebsd.org/changeset/base/345900

Log:
  Implement checking of `.' and `..' entries of subdirectory.
  
  Reviewed by:		pfg
  Obtained from:		Android https://android.googlesource.com/platform/external/fsck_msdos/+/b6ee08aadb580341a4d80943741b80de16a88b5d%5E%21/
  MFC after:		2 weeks
  Differential Revision:	https://reviews.freebsd.org/D19824

Modified:
  head/sbin/fsck_msdosfs/dir.c

Modified: head/sbin/fsck_msdosfs/dir.c
==============================================================================
--- head/sbin/fsck_msdosfs/dir.c	Fri Apr  5 01:22:30 2019	(r345899)
+++ head/sbin/fsck_msdosfs/dir.c	Fri Apr  5 02:21:16 2019	(r345900)
@@ -444,7 +444,78 @@ checksize(struct bootblock *boot, struct fatEntry *fat
 	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
  *   - enter file and directory records into the parent's list
@@ -491,9 +562,6 @@ readDosDirSection(int f, struct bootblock *boot, struc
 			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, struc
 						}
 					}
 					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 */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201904050221.x352LGjU015040>