Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 1 Jan 2020 09:22:07 +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: r356250 - head/sbin/fsck_msdosfs
Message-ID:  <202001010922.0019M7nD066842@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: delphij
Date: Wed Jan  1 09:22:06 2020
New Revision: 356250
URL: https://svnweb.freebsd.org/changeset/base/356250

Log:
  Revert r356249 for now as it broke GCC builds.

Modified:
  head/sbin/fsck_msdosfs/boot.c
  head/sbin/fsck_msdosfs/check.c
  head/sbin/fsck_msdosfs/dir.c
  head/sbin/fsck_msdosfs/dosfs.h
  head/sbin/fsck_msdosfs/ext.h
  head/sbin/fsck_msdosfs/fat.c
  head/sbin/fsck_msdosfs/main.c

Modified: head/sbin/fsck_msdosfs/boot.c
==============================================================================
--- head/sbin/fsck_msdosfs/boot.c	Wed Jan  1 07:43:08 2020	(r356249)
+++ head/sbin/fsck_msdosfs/boot.c	Wed Jan  1 09:22:06 2020	(r356250)
@@ -152,6 +152,9 @@ readboot(int dosfs, struct bootblock *boot)
 		boot->NumSectors = boot->bpbHugeSectors;
 	}
 
+
+
+
 	if (boot->flags & FAT32) {
 		/* If the OEM Name field is EXFAT, it's not FAT32, so bail */
 		if (!memcmp(&block[3], "EXFAT   ", 8)) {
@@ -269,30 +272,13 @@ readboot(int dosfs, struct bootblock *boot)
 	boot->NumClusters = (boot->NumSectors - boot->FirstCluster) / boot->bpbSecPerClust +
 	    CLUST_FIRST;
 
-	if (boot->flags & FAT32) {
-		if (boot->NumClusters > (CLUST_RSRVD & CLUST32_MASK)) {
-			pfatal("Filesystem too big (%u clusters) for FAT32 partition",
-			    boot->NumClusters);
-			return FSFATAL;
-		}
-		if (boot->NumClusters < (CLUST_RSRVD & CLUST16_MASK)) {
-			pfatal("Filesystem too small (%u clusters) for FAT32 partition",
-			    boot->NumClusters);
-			return FSFATAL;
-		}
+	if (boot->flags & FAT32)
 		boot->ClustMask = CLUST32_MASK;
-
-		if (boot->bpbRootClust < CLUST_FIRST ||
-		    boot->bpbRootClust >= boot->NumClusters) {
-			pfatal("Root directory starts with cluster out of range(%u)",
-			       boot->bpbRootClust);
-			return FSFATAL;
-		}
-	} else if (boot->NumClusters < (CLUST_RSRVD&CLUST12_MASK)) {
+	else if (boot->NumClusters < (CLUST_RSRVD&CLUST12_MASK))
 		boot->ClustMask = CLUST12_MASK;
-	} else if (boot->NumClusters < (CLUST_RSRVD&CLUST16_MASK)) {
+	else if (boot->NumClusters < (CLUST_RSRVD&CLUST16_MASK))
 		boot->ClustMask = CLUST16_MASK;
-	} else {
+	else {
 		pfatal("Filesystem too big (%u clusters) for non-FAT32 partition",
 		       boot->NumClusters);
 		return FSFATAL;

Modified: head/sbin/fsck_msdosfs/check.c
==============================================================================
--- head/sbin/fsck_msdosfs/check.c	Wed Jan  1 07:43:08 2020	(r356249)
+++ head/sbin/fsck_msdosfs/check.c	Wed Jan  1 09:22:06 2020	(r356250)
@@ -47,8 +47,9 @@ checkfilesys(const char *fname)
 {
 	int dosfs;
 	struct bootblock boot;
-	struct fat_descriptor *fat = NULL;
+	struct fatEntry *fat = NULL;
 	int finish_dosdirsection=0;
+	u_int i;
 	int mod = 0;
 	int ret = 8;
 
@@ -87,39 +88,65 @@ checkfilesys(const char *fname)
 	}
 
 	if (!preen)  {
-		printf("** Phase 1 - Read FAT and checking connectivity\n");
+		if (boot.ValidFat < 0)
+			printf("** Phase 1 - Read and Compare FATs\n");
+		else
+			printf("** Phase 1 - Read FAT\n");
 	}
 
-	mod |= readfat(dosfs, &boot, &fat);
+	mod |= readfat(dosfs, &boot, boot.ValidFat >= 0 ? boot.ValidFat : 0, &fat);
 	if (mod & FSFATAL) {
 		close(dosfs);
 		return 8;
 	}
 
+	if (boot.ValidFat < 0)
+		for (i = 1; i < boot.bpbFATs; i++) {
+			struct fatEntry *currentFat;
+
+			mod |= readfat(dosfs, &boot, i, &currentFat);
+
+			if (mod & FSFATAL)
+				goto out;
+
+			mod |= comparefat(&boot, fat, currentFat, i);
+			free(currentFat);
+			if (mod & FSFATAL)
+				goto out;
+		}
+
 	if (!preen)
-		printf("** Phase 2 - Checking Directories\n");
+		printf("** Phase 2 - Check Cluster Chains\n");
 
-	mod |= resetDosDirSection(fat);
+	mod |= checkfat(&boot, fat);
+	if (mod & FSFATAL)
+		goto out;
+	/* delay writing FATs */
+
+	if (!preen)
+		printf("** Phase 3 - Checking Directories\n");
+
+	mod |= resetDosDirSection(&boot, fat);
 	finish_dosdirsection = 1;
 	if (mod & FSFATAL)
 		goto out;
 	/* delay writing FATs */
 
-	mod |= handleDirTree(fat);
+	mod |= handleDirTree(dosfs, &boot, fat);
 	if (mod & FSFATAL)
 		goto out;
 
 	if (!preen)
-		printf("** Phase 3 - Checking for Lost Files\n");
+		printf("** Phase 4 - Checking for Lost Files\n");
 
-	mod |= checklost(fat);
+	mod |= checklost(dosfs, &boot, fat);
 	if (mod & FSFATAL)
 		goto out;
 
 	/* now write the FATs */
-	if (mod & FSFATMOD) {
+	if (mod & (FSFATMOD|FSFIXFAT)) {
 		if (ask(1, "Update FATs")) {
-			mod |= writefat(fat);
+			mod |= writefat(dosfs, &boot, fat, mod & FSFIXFAT);
 			if (mod & FSFATAL)
 				goto out;
 		} else
@@ -143,7 +170,7 @@ checkfilesys(const char *fname)
 
 			if (mod & FSDIRTY) {
 				pwarn("MARKING FILE SYSTEM CLEAN\n");
-				mod |= writefat(fat);
+				mod |= writefat(dosfs, &boot, fat, 1);
 			} else {
 				pwarn("\n***** FILE SYSTEM IS LEFT MARKED AS DIRTY *****\n");
 				mod |= FSERROR; /* file system not clean */

Modified: head/sbin/fsck_msdosfs/dir.c
==============================================================================
--- head/sbin/fsck_msdosfs/dir.c	Wed Jan  1 07:43:08 2020	(r356249)
+++ head/sbin/fsck_msdosfs/dir.c	Wed Jan  1 09:22:06 2020	(r356250)
@@ -1,7 +1,6 @@
 /*-
  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
  *
- * Copyright (c) 2019 Google LLC
  * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank
  * Copyright (c) 1995 Martin Husemann
  * Some structure declaration borrowed from Paul Popelka
@@ -96,11 +95,14 @@ static struct dirTodoNode *newDirTodo(void);
 static void freeDirTodo(struct dirTodoNode *);
 static char *fullpath(struct dosDirEntry *);
 static u_char calcShortSum(u_char *);
-static int delete(struct fat_descriptor *, cl_t, int, cl_t, int, int);
-static int removede(struct fat_descriptor *, u_char *, u_char *,
-    cl_t, cl_t, cl_t, char *, int);
-static int checksize(struct fat_descriptor *, u_char *, struct dosDirEntry *);
-static int readDosDirSection(struct fat_descriptor *, struct dosDirEntry *);
+static int delete(int, struct bootblock *, struct fatEntry *, cl_t, int,
+    cl_t, int, int);
+static int removede(int, struct bootblock *, struct fatEntry *, u_char *,
+    u_char *, cl_t, cl_t, cl_t, char *, int);
+static int checksize(struct bootblock *, struct fatEntry *, u_char *,
+    struct dosDirEntry *);
+static int readDosDirSection(int, struct bootblock *, struct fatEntry *,
+    struct dosDirEntry *);
 
 /*
  * Manage free dosDirEntry structures.
@@ -114,7 +116,7 @@ newDosDirEntry(void)
 
 	if (!(de = freede)) {
 		if (!(de = malloc(sizeof *de)))
-			return (NULL);
+			return 0;
 	} else
 		freede = de->next;
 	return de;
@@ -191,7 +193,7 @@ fullpath(struct dosDirEntry *dir)
 /*
  * Calculate a checksum over an 8.3 alias name
  */
-static inline u_char
+static u_char
 calcShortSum(u_char *p)
 {
 	u_char sum = 0;
@@ -219,24 +221,21 @@ static struct dosDirEntry *lostDir;
  * Init internal state for a new directory scan.
  */
 int
-resetDosDirSection(struct fat_descriptor *fat)
+resetDosDirSection(struct bootblock *boot, struct fatEntry *fat)
 {
-	int rootdir_size, cluster_size;
+	int b1, b2;
 	int ret = FSOK;
 	size_t len;
-	struct bootblock *boot;
 
-	boot = fat_get_boot(fat);
+	b1 = boot->bpbRootDirEnts * 32;
+	b2 = boot->bpbSecPerClust * boot->bpbBytesPerSec;
 
-	rootdir_size = boot->bpbRootDirEnts * 32;
-	cluster_size = boot->bpbSecPerClust * boot->bpbBytesPerSec;
-
-	if ((buffer = malloc(len = MAX(rootdir_size, cluster_size))) == NULL) {
+	if ((buffer = malloc(len = MAX(b1, b2))) == NULL) {
 		perr("No space for directory buffer (%zu)", len);
 		return FSFATAL;
 	}
 
-	if ((delbuf = malloc(len = cluster_size)) == NULL) {
+	if ((delbuf = malloc(len = b2)) == NULL) {
 		free(buffer);
 		perr("No space for directory delbuf (%zu)", len);
 		return FSFATAL;
@@ -251,10 +250,18 @@ resetDosDirSection(struct fat_descriptor *fat)
 
 	memset(rootDir, 0, sizeof *rootDir);
 	if (boot->flags & FAT32) {
-		if (!fat_is_cl_head(fat, boot->bpbRootClust)) {
+		if (boot->bpbRootClust < CLUST_FIRST ||
+		    boot->bpbRootClust >= boot->NumClusters) {
+			pfatal("Root directory starts with cluster out of range(%u)",
+			       boot->bpbRootClust);
+			return FSFATAL;
+		}
+		if (fat[boot->bpbRootClust].head != boot->bpbRootClust) {
 			pfatal("Root directory doesn't start a cluster chain");
 			return FSFATAL;
 		}
+
+		fat[boot->bpbRootClust].flags |= FAT_USED;
 		rootDir->head = boot->bpbRootClust;
 	}
 
@@ -295,21 +302,16 @@ finishDosDirSection(void)
  * Delete directory entries between startcl, startoff and endcl, endoff.
  */
 static int
-delete(struct fat_descriptor *fat, cl_t startcl,
+delete(int f, struct bootblock *boot, struct fatEntry *fat, cl_t startcl,
     int startoff, cl_t endcl, int endoff, int notlast)
 {
 	u_char *s, *e;
 	off_t off;
-	int clsz, fd;
-	struct bootblock *boot;
+	int clsz = boot->bpbSecPerClust * boot->bpbBytesPerSec;
 
-	boot = fat_get_boot(fat);
-	fd = fat_get_fd(fat);
-	clsz = boot->bpbSecPerClust * boot->bpbBytesPerSec;
-
 	s = delbuf + startoff;
 	e = delbuf + clsz;
-	while (fat_is_valid_cl(fat, startcl)) {
+	while (startcl >= CLUST_FIRST && startcl < boot->NumClusters) {
 		if (startcl == endcl) {
 			if (notlast)
 				break;
@@ -318,11 +320,11 @@ delete(struct fat_descriptor *fat, cl_t startcl,
 		off = (startcl - CLUST_FIRST) * boot->bpbSecPerClust + boot->FirstCluster;
 
 		off *= boot->bpbBytesPerSec;
-		if (lseek(fd, off, SEEK_SET) != off) {
+		if (lseek(f, off, SEEK_SET) != off) {
 			perr("Unable to lseek to %" PRId64, off);
 			return FSFATAL;
 		}
-		if (read(fd, delbuf, clsz) != clsz) {
+		if (read(f, delbuf, clsz) != clsz) {
 			perr("Unable to read directory");
 			return FSFATAL;
 		}
@@ -330,26 +332,25 @@ delete(struct fat_descriptor *fat, cl_t startcl,
 			*s = SLOT_DELETED;
 			s += 32;
 		}
-		if (lseek(fd, off, SEEK_SET) != off) {
+		if (lseek(f, off, SEEK_SET) != off) {
 			perr("Unable to lseek to %" PRId64, off);
 			return FSFATAL;
 		}
-		if (write(fd, delbuf, clsz) != clsz) {
+		if (write(f, delbuf, clsz) != clsz) {
 			perr("Unable to write directory");
 			return FSFATAL;
 		}
 		if (startcl == endcl)
 			break;
-		startcl = fat_get_cl_next(fat, startcl);
+		startcl = fat[startcl].next;
 		s = delbuf;
 	}
 	return FSOK;
 }
 
 static int
-removede(struct fat_descriptor *fat, u_char *start,
-    u_char *end, cl_t startcl, cl_t endcl, cl_t curcl,
-    char *path, int type)
+removede(int f, struct bootblock *boot, struct fatEntry *fat, u_char *start,
+    u_char *end, cl_t startcl, cl_t endcl, cl_t curcl, char *path, int type)
 {
 	switch (type) {
 	case 0:
@@ -365,14 +366,14 @@ removede(struct fat_descriptor *fat, u_char *start,
 	}
 	if (ask(0, "Remove")) {
 		if (startcl != curcl) {
-			if (delete(fat,
+			if (delete(f, boot, fat,
 				   startcl, start - buffer,
 				   endcl, end - buffer,
 				   endcl == curcl) == FSFATAL)
 				return FSFATAL;
 			start = buffer;
 		}
-		/* startcl is < CLUST_FIRST for !FAT32 root */
+		/* startcl is < CLUST_FIRST for !fat32 root */
 		if ((endcl == curcl) || (startcl < CLUST_FIRST))
 			for (; start < end; start += 32)
 				*start = SLOT_DELETED;
@@ -385,37 +386,23 @@ removede(struct fat_descriptor *fat, u_char *start,
  * Check an in-memory file entry
  */
 static int
-checksize(struct fat_descriptor *fat, u_char *p, struct dosDirEntry *dir)
+checksize(struct bootblock *boot, struct fatEntry *fat, u_char *p,
+    struct dosDirEntry *dir)
 {
-	int ret = FSOK;
-	size_t physicalSize;
-	struct bootblock *boot;
-
-	boot = fat_get_boot(fat);
-
 	/*
 	 * Check size on ordinary files
 	 */
-	if (dir->head == CLUST_FREE) {
+	u_int32_t physicalSize;
+
+	if (dir->head == CLUST_FREE)
 		physicalSize = 0;
-	} else {
-		if (!fat_is_valid_cl(fat, dir->head))
+	else {
+		if (dir->head < CLUST_FIRST || dir->head >= boot->NumClusters)
 			return FSERROR;
-		ret = checkchain(fat, dir->head, &physicalSize);
-		/*
-		 * Upon return, physicalSize would hold the chain length
-		 * that checkchain() was able to validate, but if the user
-		 * refused the proposed repair, it would be unsafe to
-		 * proceed with directory entry fix, so bail out in that
-		 * case.
-		 */
-		if (ret == FSERROR) {
-			return (FSERROR);
-		}
-		physicalSize *= boot->ClusterSize;
+		physicalSize = fat[dir->head].length * boot->ClusterSize;
 	}
 	if (physicalSize < dir->size) {
-		pwarn("size of %s is %u, should at most be %zu\n",
+		pwarn("size of %s is %u, should at most be %u\n",
 		      fullpath(dir), dir->size, physicalSize);
 		if (ask(1, "Truncate")) {
 			dir->size = physicalSize;
@@ -435,10 +422,11 @@ checksize(struct fat_descriptor *fat, u_char *p, struc
 
 			for (cl = dir->head, len = sz = 0;
 			    (sz += boot->ClusterSize) < dir->size; len++)
-				cl = fat_get_cl_next(fat, cl);
-			clearchain(fat, fat_get_cl_next(fat, cl));
-			ret = fat_set_cl_next(fat, cl, CLUST_EOF);
-			return (FSFATMOD | ret);
+				cl = fat[cl].next;
+			clearchain(boot, fat, fat[cl].next);
+			fat[cl].next = CLUST_EOF;
+			fat[dir->head].length = len;
+			return FSFATMOD;
 		} else
 			return FSERROR;
 	}
@@ -454,20 +442,15 @@ static const u_char dotdot_name[11] = "..         ";
  * when we traverse into it.
  */
 static int
-check_subdirectory(struct fat_descriptor *fat, struct dosDirEntry *dir)
+check_subdirectory(int f, struct bootblock *boot, struct dosDirEntry *dir)
 {
 	u_char *buf, *cp;
 	off_t off;
 	cl_t cl;
 	int retval = FSOK;
-	int fd;
-	struct bootblock *boot;
 
-	boot = fat_get_boot(fat);
-	fd = fat_get_fd(fat);
-
 	cl = dir->head;
-	if (dir->parent && !fat_is_valid_cl(fat, cl)) {
+	if (dir->parent && (cl < CLUST_FIRST || cl >= boot->NumClusters)) {
 		return FSERROR;
 	}
 
@@ -491,8 +474,8 @@ check_subdirectory(struct fat_descriptor *fat, struct 
 	}
 
 	off *= boot->bpbBytesPerSec;
-	if (lseek(fd, off, SEEK_SET) != off ||
-	    read(fd, buf, boot->bpbBytesPerSec) != (ssize_t)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;
@@ -526,27 +509,22 @@ check_subdirectory(struct fat_descriptor *fat, struct 
  *   - push directories onto the todo-stack
  */
 static int
-readDosDirSection(struct fat_descriptor *fat, struct dosDirEntry *dir)
+readDosDirSection(int f, struct bootblock *boot, struct fatEntry *fat,
+    struct dosDirEntry *dir)
 {
-	struct bootblock *boot;
 	struct dosDirEntry dirent, *d;
 	u_char *p, *vallfn, *invlfn, *empty;
 	off_t off;
-	int fd, i, j, k, iosize, entries;
-	bool is_legacyroot;
+	int i, j, k, last;
 	cl_t cl, valcl = ~0, invcl = ~0, empcl = ~0;
 	char *t;
 	u_int lidx = 0;
 	int shortSum;
 	int mod = FSOK;
-	size_t dirclusters;
 #define	THISMOD	0x8000			/* Only used within this routine */
 
-	boot = fat_get_boot(fat);
-	fd = fat_get_fd(fat);
-
 	cl = dir->head;
-	if (dir->parent && (!fat_is_valid_cl(fat, cl))) {
+	if (dir->parent && (cl < CLUST_FIRST || cl >= boot->NumClusters)) {
 		/*
 		 * Already handled somewhere else.
 		 */
@@ -554,50 +532,24 @@ readDosDirSection(struct fat_descriptor *fat, struct d
 	}
 	shortSum = -1;
 	vallfn = invlfn = empty = NULL;
-
-	/*
-	 * If we are checking the legacy root (for FAT12/FAT16),
-	 * we will operate on the whole directory; otherwise, we
-	 * will operate on one cluster at a time, and also take
-	 * this opportunity to examine the chain.
-	 *
-	 * Derive how many entries we are going to encounter from
-	 * the I/O size.
-	 */
-	is_legacyroot = (dir->parent == NULL && !(boot->flags & FAT32));
-	if (is_legacyroot) {
-		iosize = boot->bpbRootDirEnts * 32;
-		entries = boot->bpbRootDirEnts;
-	} else {
-		iosize = boot->bpbSecPerClust * boot->bpbBytesPerSec;
-		entries = iosize / 32;
-		mod |= checkchain(fat, dir->head, &dirclusters);
-	}
-
 	do {
-		if (is_legacyroot) {
-			/*
-			 * Special case for FAT12/FAT16 root -- read
-			 * in the whole root directory.
-			 */
+		if (!(boot->flags & FAT32) && !dir->parent) {
+			last = boot->bpbRootDirEnts * 32;
 			off = boot->bpbResSectors + boot->bpbFATs *
 			    boot->FATsecs;
 		} else {
-			/*
-			 * Otherwise, read in a cluster of the
-			 * directory.
-			 */
+			last = boot->bpbSecPerClust * boot->bpbBytesPerSec;
 			off = (cl - CLUST_FIRST) * boot->bpbSecPerClust + boot->FirstCluster;
 		}
 
 		off *= boot->bpbBytesPerSec;
-		if (lseek(fd, off, SEEK_SET) != off ||
-		    read(fd, buffer, iosize) != iosize) {
+		if (lseek(f, off, SEEK_SET) != off
+		    || read(f, buffer, last) != last) {
 			perr("Unable to read directory");
 			return FSFATAL;
 		}
-
-		for (p = buffer, i = 0; i < entries; i++, p += 32) {
+		last /= 32;
+		for (p = buffer, i = 0; i < last; i++, p += 32) {
 			if (dir->fsckflags & DIREMPWARN) {
 				*p = SLOT_EMPTY;
 				continue;
@@ -620,7 +572,7 @@ readDosDirSection(struct fat_descriptor *fat, struct d
 						u_char *q;
 
 						dir->fsckflags &= ~DIREMPTY;
-						if (delete(fat,
+						if (delete(f, boot, fat,
 							   empcl, empty - buffer,
 							   cl, p - buffer, 1) == FSFATAL)
 							return FSFATAL;
@@ -749,7 +701,7 @@ readDosDirSection(struct fat_descriptor *fat, struct d
 
 			if (dirent.flags & ATTR_VOLUME) {
 				if (vallfn || invlfn) {
-					mod |= removede(fat,
+					mod |= removede(f, boot, fat,
 							invlfn ? invlfn : vallfn, p,
 							invlfn ? invcl : valcl, -1, 0,
 							fullpath(dir), 2);
@@ -789,7 +741,7 @@ readDosDirSection(struct fat_descriptor *fat, struct d
 			dirent.next = dir->child;
 
 			if (invlfn) {
-				mod |= k = removede(fat,
+				mod |= k = removede(f, boot, fat,
 						    invlfn, vallfn ? vallfn : p,
 						    invcl, vallfn ? valcl : cl, cl,
 						    fullpath(&dirent), 0);
@@ -805,61 +757,74 @@ readDosDirSection(struct fat_descriptor *fat, struct d
 			vallfn = NULL; /* not used any longer */
 			invlfn = NULL;
 
-			/*
-			 * Check if the directory entry is sane.
-			 *
-			 * '.' and '..' are skipped, their sanity is
-			 * checked somewhere else.
-			 *
-			 * For everything else, check if we have a new,
-			 * valid cluster chain (beginning of a file or
-			 * directory that was never previously claimed
-			 * by another file) when it's a non-empty file
-			 * or a directory. The sanity of the cluster
-			 * chain is checked at a later time when we
-			 * traverse into the directory, or examine the
-			 * file's directory entry.
-			 *
-			 * The only possible fix is to delete the entry
-			 * if it's a directory; for file, we have to
-			 * truncate the size to 0.
-			 */
-			if (!(dirent.flags & ATTR_DIRECTORY) ||
-			    (strcmp(dirent.name, ".") != 0 &&
-			    strcmp(dirent.name, "..") != 0)) {
-				if ((dirent.size != 0 || (dirent.flags & ATTR_DIRECTORY)) &&
-				    ((!fat_is_valid_cl(fat, dirent.head) ||
-				    !fat_is_cl_head(fat, dirent.head)))) {
-					if (!fat_is_valid_cl(fat, dirent.head)) {
-						pwarn("%s starts with cluster out of range(%u)\n",
-						    fullpath(&dirent),
-						    dirent.head);
-					} else {
-						pwarn("%s doesn't start a new cluster chain\n",
-						    fullpath(&dirent));
-					}
-
-					if (dirent.flags & ATTR_DIRECTORY) {
-						if (ask(0, "Remove")) {
-							*p = SLOT_DELETED;
-							mod |= THISMOD|FSDIRMOD;
-						} else
-							mod |= FSERROR;
-						continue;
-					} else {
-						if (ask(1, "Truncate")) {
-							p[28] = p[29] = p[30] = p[31] = 0;
-							p[26] = p[27] = 0;
-							if (boot->ClustMask == CLUST32_MASK)
-								p[20] = p[21] = 0;
-							dirent.size = 0;
-							dirent.head = 0;
-							mod |= THISMOD|FSDIRMOD;
-						} else
-							mod |= FSERROR;
-					}
+			if (dirent.size == 0 && !(dirent.flags & ATTR_DIRECTORY)) {
+				if (dirent.head != 0) {
+					pwarn("%s has clusters, but size 0\n",
+					      fullpath(&dirent));
+					if (ask(1, "Drop allocated clusters")) {
+						p[26] = p[27] = 0;
+						if (boot->ClustMask == CLUST32_MASK)
+							p[20] = p[21] = 0;
+						clearchain(boot, fat, dirent.head);
+						dirent.head = 0;
+						mod |= THISMOD|FSDIRMOD|FSFATMOD;
+					} else
+						mod |= FSERROR;
 				}
+			} else if (dirent.head == 0
+				   && !strcmp(dirent.name, "..")
+				   && dir->parent			/* XXX */
+				   && !dir->parent->parent) {
+				/*
+				 *  Do nothing, the parent is the root
+				 */
+			} else if (dirent.head < CLUST_FIRST
+				   || dirent.head >= boot->NumClusters
+				   || fat[dirent.head].next == CLUST_FREE
+				   || (fat[dirent.head].next >= CLUST_RSRVD
+				       && fat[dirent.head].next < CLUST_EOFS)
+				   || fat[dirent.head].head != dirent.head) {
+				if (dirent.head == 0)
+					pwarn("%s has no clusters\n",
+					      fullpath(&dirent));
+				else if (dirent.head < CLUST_FIRST
+					 || dirent.head >= boot->NumClusters)
+					pwarn("%s starts with cluster out of range(%u)\n",
+					      fullpath(&dirent),
+					      dirent.head);
+				else if (fat[dirent.head].next == CLUST_FREE)
+					pwarn("%s starts with free cluster\n",
+					      fullpath(&dirent));
+				else if (fat[dirent.head].next >= CLUST_RSRVD)
+					pwarn("%s starts with cluster marked %s\n",
+					      fullpath(&dirent),
+					      rsrvdcltype(fat[dirent.head].next));
+				else
+					pwarn("%s doesn't start a cluster chain\n",
+					      fullpath(&dirent));
+				if (dirent.flags & ATTR_DIRECTORY) {
+					if (ask(0, "Remove")) {
+						*p = SLOT_DELETED;
+						mod |= THISMOD|FSDIRMOD;
+					} else
+						mod |= FSERROR;
+					continue;
+				} else {
+					if (ask(1, "Truncate")) {
+						p[28] = p[29] = p[30] = p[31] = 0;
+						p[26] = p[27] = 0;
+						if (boot->ClustMask == CLUST32_MASK)
+							p[20] = p[21] = 0;
+						dirent.size = 0;
+						mod |= THISMOD|FSDIRMOD;
+					} else
+						mod |= FSERROR;
+				}
 			}
+
+			if (dirent.head >= CLUST_FIRST && dirent.head < boot->NumClusters)
+				fat[dirent.head].flags |= FAT_USED;
+
 			if (dirent.flags & ATTR_DIRECTORY) {
 				/*
 				 * gather more info for directories
@@ -896,7 +861,8 @@ readDosDirSection(struct fat_descriptor *fat, struct d
 							mod |= FSERROR;
 					}
 					continue;
-				} else if (strcmp(dirent.name, "..") == 0) {
+				}
+				if (strcmp(dirent.name, "..") == 0) {
 					if (dir->parent) {		/* XXX */
 						if (!dir->parent->parent) {
 							if (dirent.head) {
@@ -942,7 +908,7 @@ readDosDirSection(struct fat_descriptor *fat, struct d
 						} else
 							mod |= FSERROR;
 						continue;
-					} else if ((check_subdirectory(fat,
+					} else if ((check_subdirectory(f, boot,
 					    &dirent) & FSERROR) == FSERROR) {
 						/*
 						 * A subdirectory should have
@@ -978,43 +944,39 @@ readDosDirSection(struct fat_descriptor *fat, struct d
 				n->dir = d;
 				pendingDirectories = n;
 			} else {
-				mod |= k = checksize(fat, p, &dirent);
+				mod |= k = checksize(boot, fat, p, &dirent);
 				if (k & FSDIRMOD)
 					mod |= THISMOD;
 			}
 			boot->NumFiles++;
 		}
 
-		if (is_legacyroot) {
-			/*
-			 * Don't bother to write back right now because
-			 * we may continue to make modification to the
-			 * non-FAT32 root directory below.
-			 */
+		if (!(boot->flags & FAT32) && !dir->parent)
 			break;
-		} else if (mod & THISMOD) {
-			if (lseek(fd, off, SEEK_SET) != off
-			    || write(fd, buffer, iosize) != iosize) {
+
+		if (mod & THISMOD) {
+			last *= 32;
+			if (lseek(f, off, SEEK_SET) != off
+			    || write(f, buffer, last) != last) {
 				perr("Unable to write directory");
 				return FSFATAL;
 			}
 			mod &= ~THISMOD;
 		}
-	} while (fat_is_valid_cl(fat, (cl = fat_get_cl_next(fat, cl))));
+	} while ((cl = fat[cl].next) >= CLUST_FIRST && cl < boot->NumClusters);
 	if (invlfn || vallfn)
-		mod |= removede(fat,
+		mod |= removede(f, boot, fat,
 				invlfn ? invlfn : vallfn, p,
 				invlfn ? invcl : valcl, -1, 0,
 				fullpath(dir), 1);
 
-	/*
-	 * The root directory of non-FAT32 filesystems is in a special
-	 * area and may have been modified above removede() without
-	 * being written out.
+	/* The root directory of non fat32 filesystems is in a special
+	 * area and may have been modified above without being written out.
 	 */
-	if ((mod & FSDIRMOD) && is_legacyroot) {
-		if (lseek(fd, off, SEEK_SET) != off
-		    || write(fd, buffer, iosize) != iosize) {
+	if ((mod & FSDIRMOD) && !(boot->flags & FAT32) && !dir->parent) {
+		last *= 32;
+		if (lseek(f, off, SEEK_SET) != off
+		    || write(f, buffer, last) != last) {
 			perr("Unable to write directory");
 			return FSFATAL;
 		}
@@ -1024,11 +986,11 @@ readDosDirSection(struct fat_descriptor *fat, struct d
 }
 
 int
-handleDirTree(struct fat_descriptor *fat)
+handleDirTree(int dosfs, struct bootblock *boot, struct fatEntry *fat)
 {
 	int mod;
 
-	mod = readDosDirSection(fat, rootDir);
+	mod = readDosDirSection(dosfs, boot, fat, rootDir);
 	if (mod & FSFATAL)
 		return FSFATAL;
 
@@ -1049,7 +1011,7 @@ handleDirTree(struct fat_descriptor *fat)
 		/*
 		 * handle subdirectory
 		 */
-		mod |= readDosDirSection(fat, dir);
+		mod |= readDosDirSection(dosfs, boot, fat, dir);
 		if (mod & FSFATAL)
 			return FSFATAL;
 	}
@@ -1065,15 +1027,12 @@ static cl_t lfcl;
 static off_t lfoff;
 
 int
-reconnect(struct fat_descriptor *fat, cl_t head, size_t length)
+reconnect(int dosfs, struct bootblock *boot, struct fatEntry *fat, cl_t head)
 {
-	struct bootblock *boot = fat_get_boot(fat);
 	struct dosDirEntry d;
-	int len, dosfs;
+	int len;
 	u_char *p;
 
-	dosfs = fat_get_fd(fat);
-
 	if (!ask(1, "Reconnect"))
 		return FSERROR;
 
@@ -1104,7 +1063,7 @@ reconnect(struct fat_descriptor *fat, cl_t head, size_
 					break;
 		if (p && p < lfbuf + boot->ClusterSize)
 			break;
-		lfcl = p ? fat_get_cl_next(fat, lfcl) : lostDir->head;
+		lfcl = p ? fat[lfcl].next : lostDir->head;
 		if (lfcl < CLUST_FIRST || lfcl >= boot->NumClusters) {
 			/* Extend LOSTDIR?				XXX */
 			pwarn("No space in %s\n", LOSTDIR);
@@ -1129,7 +1088,7 @@ reconnect(struct fat_descriptor *fat, cl_t head, size_
 	len = snprintf(d.name, sizeof(d.name), "%u", head);
 	d.flags = 0;
 	d.head = head;
-	d.size = length * boot->ClusterSize;
+	d.size = fat[head].length * boot->ClusterSize;
 
 	memcpy(p, d.name, len);
 	memset(p + len, ' ', 11 - len);
@@ -1144,6 +1103,7 @@ reconnect(struct fat_descriptor *fat, cl_t head, size_
 	p[29] = (u_char)(d.size >> 8);
 	p[30] = (u_char)(d.size >> 16);
 	p[31] = (u_char)(d.size >> 24);
+	fat[head].flags |= FAT_USED;
 	if (lseek(dosfs, lfoff, SEEK_SET) != lfoff
 	    || (size_t)write(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) {
 		perr("could not write LOST.DIR");

Modified: head/sbin/fsck_msdosfs/dosfs.h
==============================================================================
--- head/sbin/fsck_msdosfs/dosfs.h	Wed Jan  1 07:43:08 2020	(r356249)
+++ head/sbin/fsck_msdosfs/dosfs.h	Wed Jan  1 09:22:06 2020	(r356250)
@@ -83,13 +83,19 @@ struct bootblock {
 	u_int	NumBad;			/* # of bad clusters */
 };
 
+struct fatEntry {
+	cl_t	next;			/* pointer to next cluster */
+	cl_t	head;			/* pointer to start of chain */
+	u_int32_t length;		/* number of clusters on chain */
+	int	flags;			/* see below */
+};
+
 #define	CLUST_FREE	0		/* 0 means cluster is free */
 #define	CLUST_FIRST	2		/* 2 is the minimum valid cluster number */
 #define	CLUST_RSRVD	0xfffffff6	/* start of reserved clusters */
 #define	CLUST_BAD	0xfffffff7	/* a cluster with a defect */
 #define	CLUST_EOFS	0xfffffff8	/* start of EOF indicators */
 #define	CLUST_EOF	0xffffffff	/* standard value for last cluster */
-#define	CLUST_DEAD	0xfdeadc0d	/* error encountered */
 
 /*
  * Masks for cluster values
@@ -97,6 +103,8 @@ struct bootblock {
 #define	CLUST12_MASK	0xfff
 #define	CLUST16_MASK	0xffff
 #define	CLUST32_MASK	0xfffffff
+
+#define	FAT_USED	1		/* This fat chain is used in a file */
 
 #define	DOSLONGNAMELEN	256		/* long name maximal length */
 #define LRFIRST		0x40		/* first long name record */

Modified: head/sbin/fsck_msdosfs/ext.h
==============================================================================
--- head/sbin/fsck_msdosfs/ext.h	Wed Jan  1 07:43:08 2020	(r356249)
+++ head/sbin/fsck_msdosfs/ext.h	Wed Jan  1 09:22:06 2020	(r356250)
@@ -32,8 +32,6 @@
 
 #include <sys/types.h>
 
-#include <stdbool.h>
-
 #include "dosfs.h"
 
 #define	LOSTDIR	"LOST.DIR"
@@ -46,7 +44,6 @@ extern int alwaysyes;	/* assume "yes" for all question
 extern int preen;	/* we are preening */
 extern int rdonly;	/* device is opened read only (supersedes above) */
 extern int skipclean;	/* skip clean file systems if preening */
-extern int allow_mmap;  /* allow the use of mmap() */
 
 /*
  * function declarations
@@ -75,6 +72,7 @@ int checkfilesys(const char *);
 #define	FSERROR		8		/* Some unrecovered error remains */
 #define	FSFATAL		16		/* Some unrecoverable error occurred */
 #define	FSDIRTY		32		/* File system is dirty */
+#define	FSFIXFAT	64		/* Fix file system FAT */
 
 /*
  * read a boot block in a machine independent fashion and translate
@@ -87,53 +85,46 @@ int readboot(int, struct bootblock *);
  */
 int writefsinfo(int, struct bootblock *);
 
-/* Opaque type */
-struct fat_descriptor;
+/*
+ * Read one of the FAT copies and return a pointer to the new
+ * allocated array holding our description of it.
+ */
+int readfat(int, struct bootblock *, u_int, struct fatEntry **);
 
-void fat_clear_cl_head(struct fat_descriptor *, cl_t);
-bool fat_is_cl_head(struct fat_descriptor *, cl_t);
+/*
+ * Check two FAT copies for consistency and merge changes into the
+ * first if necessary.
+ */
+int comparefat(struct bootblock *, struct fatEntry *, struct fatEntry *, u_int);
 
-cl_t fat_get_cl_next(struct fat_descriptor *, cl_t);
-
-int fat_set_cl_next(struct fat_descriptor *, cl_t, cl_t);
-
-cl_t fat_allocate_cluster(struct fat_descriptor *fat);
-
-struct bootblock* fat_get_boot(struct fat_descriptor *);
-int fat_get_fd(struct fat_descriptor *);
-bool fat_is_valid_cl(struct fat_descriptor *, cl_t);
-
 /*
- * Read the FAT 0 and return a pointer to the newly allocated
- * descriptor of it.
+ * Check a FAT
  */
-int readfat(int, struct bootblock *, struct fat_descriptor **);
+int checkfat(struct bootblock *, struct fatEntry *);
 
 /*
  * Write back FAT entries
  */
-int writefat(struct fat_descriptor *);
+int writefat(int, struct bootblock *, struct fatEntry *, int);
 
 /*
  * Read a directory
  */
-int resetDosDirSection(struct fat_descriptor *);
+int resetDosDirSection(struct bootblock *, struct fatEntry *);
 void finishDosDirSection(void);
-int handleDirTree(struct fat_descriptor *);
+int handleDirTree(int, struct bootblock *, struct fatEntry *);
 
 /*
  * Cross-check routines run after everything is completely in memory
  */
-int checkchain(struct fat_descriptor *, cl_t, size_t *);
-
 /*
  * Check for lost cluster chains
  */
-int checklost(struct fat_descriptor *);
+int checklost(int, struct bootblock *, struct fatEntry *);
 /*
  * Try to reconnect a lost cluster chain
  */
-int reconnect(struct fat_descriptor *, cl_t, size_t);
+int reconnect(int, struct bootblock *, struct fatEntry *, cl_t);
 void finishlf(void);
 
 /*
@@ -147,6 +138,6 @@ const char *rsrvdcltype(cl_t);
 /*
  * Clear a cluster chain in a FAT
  */
-void clearchain(struct fat_descriptor *, cl_t);
+void clearchain(struct bootblock *, struct fatEntry *, cl_t);
 
 #endif

Modified: head/sbin/fsck_msdosfs/fat.c
==============================================================================
--- head/sbin/fsck_msdosfs/fat.c	Wed Jan  1 07:43:08 2020	(r356249)
+++ head/sbin/fsck_msdosfs/fat.c	Wed Jan  1 09:22:06 2020	(r356250)
@@ -1,7 +1,6 @@
 /*-
  * SPDX-License-Identifier: BSD-2-Clause
  *
- * Copyright (c) 2019 Google LLC
  * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank
  * Copyright (c) 1995 Martin Husemann
  *
@@ -34,14 +33,6 @@ static const char rcsid[] =
   "$FreeBSD$";
 #endif /* not lint */
 
-#include <sys/endian.h>
-#include <sys/queue.h>
-#include <sys/limits.h>
-#include <sys/mman.h>
-#include <sys/param.h>
-
-#include <assert.h>
-#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
@@ -51,517 +42,12 @@ static const char rcsid[] =
 #include "ext.h"
 #include "fsutil.h"
 
-static int _readfat(struct fat_descriptor *);
-static inline struct bootblock* boot_of_(struct fat_descriptor *);
-static inline int fd_of_(struct fat_descriptor *);
-static inline bool valid_cl(struct fat_descriptor *, cl_t);
+static int checkclnum(struct bootblock *, u_int, cl_t, cl_t *);
+static int clustdiffer(cl_t, cl_t *, cl_t *, u_int);
+static int tryclear(struct bootblock *, struct fatEntry *, cl_t, cl_t *);
+static int _readfat(int, struct bootblock *, u_int, u_char **);
 
-
-/*
- * Head bitmap for FAT scanning.
- *
- * FAT32 have up to 2^28 = 256M entries, and FAT16/12 have much less.
- * For each cluster, we use 1 bit to represent if it's a head cluster

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



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