diff --git a/fs/fat/fat.c b/fs/fat/fat.c
index bccc3e3ed8fd0929c7f1adf33b2125178be75ccb..a863644d648068749a5803276d82ed5feca17604 100644
--- a/fs/fat/fat.c
+++ b/fs/fat/fat.c
@@ -895,6 +895,7 @@ int do_fat_read_at(const char *filename, loff_t pos, void *buffer,
 	strcpy(fnamecopy, filename);
 	downcase(fnamecopy);
 
+root_reparse:
 	if (*fnamecopy == '\0') {
 		if (!dols)
 			goto exit;
@@ -1180,6 +1181,34 @@ rootdir_done:
 		if (isdir && !(dentptr->attr & ATTR_DIR))
 			goto exit;
 
+		/*
+		 * If we are looking for a directory, and found a directory
+		 * type entry, and the entry is for the root directory (as
+		 * denoted by a cluster number of 0), jump back to the start
+		 * of the function, since at least on FAT12/16, the root dir
+		 * lives in a hard-coded location and needs special handling
+		 * to parse, rather than simply following the cluster linked
+		 * list in the FAT, like other directories.
+		 */
+		if (isdir && (dentptr->attr & ATTR_DIR) && !START(dentptr)) {
+			/*
+			 * Modify the filename to remove the prefix that gets
+			 * back to the root directory, so the initial root dir
+			 * parsing code can continue from where we are without
+			 * confusion.
+			 */
+			strcpy(fnamecopy, nextname ?: "");
+			/*
+			 * Set up state the same way as the function does when
+			 * first started. This is required for the root dir
+			 * parsing code operates in its expected environment.
+			 */
+			subname = "";
+			cursect = mydata->rootdir_sect;
+			isdir = 0;
+			goto root_reparse;
+		}
+
 		if (idx >= 0)
 			subname = nextname;
 	}