? O
Index: dir.c
===================================================================
RCS file: /cvsroot/src/sbin/fsck_ffs/dir.c,v
retrieving revision 1.59
diff -u -p -u -r1.59 dir.c
--- dir.c	8 Nov 2018 06:34:40 -0000	1.59
+++ dir.c	5 May 2019 02:49:07 -0000
@@ -144,6 +144,23 @@ reparent(ino_t inumber, ino_t parent)
 	propagate(inumber);
 }
 
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+# define NEEDSWAP	(!needswap)
+#else
+# define NEEDSWAP	(needswap)
+#endif
+
+static void
+dirswap(void *dbuf)
+{
+	struct direct *tdp = (struct direct *)dbuf;
+	u_char tmp;
+
+	tmp = tdp->d_namlen;
+	tdp->d_namlen = tdp->d_type;
+	tdp->d_type = tmp;
+}
+
 /*
  * Scan each entry in a directory block.
  */
@@ -201,33 +218,12 @@ dirscan(struct inodesc *idesc)
 		if (dsize > (int)sizeof dbuf)
 			dsize = sizeof dbuf;
 		memmove(dbuf, dp, (size_t)dsize);
-#		if (BYTE_ORDER == LITTLE_ENDIAN)
-			if (!newinofmt && !needswap) {
-#		else
-			if (!newinofmt && needswap) {
-#		endif
-				struct direct *tdp = (struct direct *)dbuf;
-				u_char tmp;
-
-				tmp = tdp->d_namlen;
-				tdp->d_namlen = tdp->d_type;
-				tdp->d_type = tmp;
-			}
+		if (!newinofmt && NEEDSWAP)
+			dirswap(dbuf);
 		idesc->id_dirp = (struct direct *)dbuf;
 		if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
-#			if (BYTE_ORDER == LITTLE_ENDIAN)
-				if (!newinofmt && !doinglevel2 && !needswap) {
-#			else
-				if (!newinofmt && !doinglevel2 && needswap) {
-#			endif
-					struct direct *tdp;
-					u_char tmp;
-
-					tdp = (struct direct *)dbuf;
-					tmp = tdp->d_namlen;
-					tdp->d_namlen = tdp->d_type;
-					tdp->d_type = tmp;
-				}
+			if (!newinofmt && !doinglevel2 && NEEDSWAP)
+				dirswap(dbuf);
 			bp = getdirblk(idesc->id_blkno, blksiz);
 			memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf,
 			    (size_t)dsize);
@@ -255,8 +251,16 @@ fsck_readdir(struct inodesc *idesc)
 	if (idesc->id_loc % dirblksiz == 0 && idesc->id_filesize > 0 &&
 	    idesc->id_loc < blksiz) {
 		dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
-		if (dircheck(idesc, dp))
+		switch (dircheck(idesc, dp)) {
+		case 2:
+			/* mark dirty so we update the zeroed space */
+			dirty(bp);
+			/*FALLTHROUGH*/
+		case 1:
 			goto dpok;
+		default:
+			break;
+		}
 		if (idesc->id_fix == IGNORE)
 			return (0);
 		fix = dofix(idesc, "DIRECTORY CORRUPTED");
@@ -285,21 +289,30 @@ dpok:
 	if ((idesc->id_loc % dirblksiz) == 0)
 		return (dp);
 	ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
-	if (idesc->id_loc < blksiz && idesc->id_filesize > 0 &&
-	    dircheck(idesc, ndp) == 0) {
-		size = dirblksiz - (idesc->id_loc % dirblksiz);
-		idesc->id_loc += size;
-		idesc->id_filesize -= size;
-		if (idesc->id_fix == IGNORE)
-			return (0);
-		fix = dofix(idesc, "DIRECTORY CORRUPTED");
-		bp = getdirblk(idesc->id_blkno, blksiz);
-		dp = (struct direct *)(bp->b_un.b_buf + dploc);
-		dp->d_reclen = iswap16(iswap16(dp->d_reclen) + size);
-		if (fix)
+	if (idesc->id_loc < blksiz && idesc->id_filesize > 0) {
+		switch (dircheck(idesc, ndp)) {
+		case 2:
+			/* mark dirty so we update the zeroed space */
 			dirty(bp);
-		else 
-			markclean = 0;
+			break;
+		case 0:
+			size = dirblksiz - (idesc->id_loc % dirblksiz);
+			idesc->id_loc += size;
+			idesc->id_filesize -= size;
+			if (idesc->id_fix == IGNORE)
+				return (0);
+			fix = dofix(idesc, "DIRECTORY CORRUPTED");
+			bp = getdirblk(idesc->id_blkno, blksiz);
+			dp = (struct direct *)(bp->b_un.b_buf + dploc);
+			dp->d_reclen = iswap16(iswap16(dp->d_reclen) + size);
+			if (fix)
+				dirty(bp);
+			else 
+				markclean = 0;
+			break;
+		default:
+			break;
+		}
 	}
 	return (dp);
 }
@@ -307,6 +320,10 @@ dpok:
 /*
  * Verify that a directory entry is valid.
  * This is a superset of the checks made in the kernel.
+ * Returns:
+ *	2: mark dirty because we zeroed padding
+ *	1: good
+ *	0: bad
  */
 static int
 dircheck(struct inodesc *idesc, struct direct *dp)
@@ -314,39 +331,90 @@ dircheck(struct inodesc *idesc, struct d
 	int size;
 	char *cp;
 	u_char namlen, type;
-	int spaceleft;
+	int spaceleft, modified, unused;
 
+	modified = 0;
 	spaceleft = dirblksiz - (idesc->id_loc % dirblksiz);
 	if (iswap32(dp->d_ino) >= maxino ||
 	    dp->d_reclen == 0 ||
 	    iswap16(dp->d_reclen) > spaceleft ||
-	    (iswap16(dp->d_reclen) & 0x3) != 0) 
-		return (0);
-	if (dp->d_ino == 0)
-		return (1);
-	size = UFS_DIRSIZ(!newinofmt, dp, needswap);
-#	if (BYTE_ORDER == LITTLE_ENDIAN)
-		if (!newinofmt && !needswap) {
-#	else
-		if (!newinofmt && needswap) {
-#	endif
-			type = dp->d_namlen;
-			namlen = dp->d_type;
-		} else {
-			namlen = dp->d_namlen;
-			type = dp->d_type;
+	    (iswap16(dp->d_reclen) & 0x3) != 0)
+		goto bad;
+	if (!newinofmt && NEEDSWAP) {
+		type = dp->d_namlen;
+		namlen = dp->d_type;
+	} else {
+		namlen = dp->d_namlen;
+		type = dp->d_type;
+	}
+	if (dp->d_ino == 0) {
+		/*
+		 * Special case of an unused directory entry. Normally
+		 * the kernel would coalesce unused space with the previous
+		 * entry by extending its d_reclen, but there are situations
+		 * (e.g. fsck) where that doesn't occur.
+		 * If we're clearing out directory cruft (-z flag), then make
+		 * sure this entry gets fully cleared as well.
+		 */
+		if (!zflag || fswritefd < 0)
+			return 1;
+
+		if (dp->d_type != 0) {
+			dp->d_type = 0;
+			modified = 1;
+		}
+		if (dp->d_namlen != 0) {
+			dp->d_namlen = 0;
+			modified = 1;
+		}
+		if (dp->d_name[0] != '\0') {
+			dp->d_name[0] = '\0';
+			modified = 1;
 		}
+		goto good;
+	}
+	size = UFS_DIRSIZ(!newinofmt, dp, needswap);
 	if (iswap16(dp->d_reclen) < size ||
 	    idesc->id_filesize < size ||
 	    /* namlen > MAXNAMLEN || */
 	    type > 15)
-		return (0);
+		goto bad;
 	for (cp = dp->d_name, size = 0; size < namlen; size++)
 		if (*cp == '\0' || (*cp++ == '/'))
-			return (0);
+			goto bad;
 	if (*cp != '\0')
-		return (0);
-	return (1);
+		goto bad;
+
+	if (!zflag || fswritefd < 0)
+		return 1;
+good:
+	/*
+	 * Clear unused directory entry space, including the d_name
+	 * padding.
+	 */
+	/* First figure the number of pad bytes. */
+	unused = roundup2(namlen + 1, DIR_ROUNDUP) - (namlen + 1);
+
+	/* Add in the free space to the end of the record. */
+	unused += dp->d_reclen - UFS_DIRSIZ(0, dp, needswap);
+
+	/*
+	 * Now clear out the unused space, keeping track if we actually
+	 * changed anything.
+	 */
+	for (cp = &dp->d_name[namlen + 1]; unused > 0; unused--, cp++) {
+		if (*cp == '\0')
+			continue;
+		*cp = '\0';
+		modified = 1;
+	}
+	return modified ? 2 : 1;
+bad:
+	if (debug)
+		printf("Bad dir: ino %d reclen %d namlen %d type %d name %s\n",
+		    dp->d_ino, dp->d_reclen, dp->d_namlen, dp->d_type,
+		    dp->d_name);
+	return 0;
 }
 
 void
@@ -469,23 +537,14 @@ mkentry(struct inodesc *idesc)
 		dirp->d_type = 0;
 	dirp->d_namlen = newent.d_namlen;
 	memmove(dirp->d_name, idesc->id_name, (size_t)newent.d_namlen + 1);
-#	if (BYTE_ORDER == LITTLE_ENDIAN)
-		/*
-		 * If the entry was split, dirscan() will only reverse the byte
-		 * order of the original entry, and not the new one, before
-		 * writing it back out.  So, we reverse the byte order here if
-		 * necessary.
-		 */
-		if (oldlen != 0 && !newinofmt && !doinglevel2 && !needswap) {
-#	else
-		if (oldlen != 0 && !newinofmt && !doinglevel2 && needswap) {
-#	endif
-			u_char tmp;
-
-			tmp = dirp->d_namlen;
-			dirp->d_namlen = dirp->d_type;
-			dirp->d_type = tmp;
-		}
+	/*
+	 * If the entry was split, dirscan() will only reverse the byte
+	 * order of the original entry, and not the new one, before
+	 * writing it back out.  So, we reverse the byte order here if
+	 * necessary.
+	 */
+	if (oldlen != 0 && !newinofmt && !doinglevel2 && NEEDSWAP)
+		dirswap(dirp);
 	return (ALTERED|STOP);
 }
 
Index: fsck.h
===================================================================
RCS file: /cvsroot/src/sbin/fsck_ffs/fsck.h,v
retrieving revision 1.52
diff -u -p -u -r1.52 fsck.h
--- fsck.h	8 Feb 2017 18:05:25 -0000	1.52
+++ fsck.h	5 May 2019 02:49:08 -0000
@@ -276,6 +276,7 @@ char	yflag;			/* assume a yes response *
 int	Uflag;			/* resolve user names */
 int	bflag;			/* location of alternate super block */
 int	debug;			/* output debugging info */
+int	zflag;			/* zero unused directory space */
 int	cvtlevel;		/* convert to newer file system format */
 int	doinglevel1;		/* converting to new cylinder group format */
 int	doinglevel2;		/* converting to new inode format */
Index: fsck_ffs.8
===================================================================
RCS file: /cvsroot/src/sbin/fsck_ffs/fsck_ffs.8,v
retrieving revision 1.50
diff -u -p -u -r1.50 fsck_ffs.8
--- fsck_ffs.8	11 Sep 2016 04:07:38 -0000	1.50
+++ fsck_ffs.8	5 May 2019 02:49:08 -0000
@@ -29,7 +29,7 @@
 .\"
 .\"	@(#)fsck.8	8.3 (Berkeley) 11/29/94
 .\"
-.Dd September 11, 2016
+.Dd May 4, 2018
 .Dt FSCK_FFS 8
 .Os
 .Sh NAME
@@ -37,7 +37,7 @@
 .Nd Fast File System consistency check and interactive repair
 .Sh SYNOPSIS
 .Nm
-.Op Fl adFfPpqUX
+.Op Fl adFfPpqUXz
 .Op Fl B Ar byteorder
 .Op Fl b Ar block
 .Op Fl c Ar level
@@ -300,6 +300,9 @@ Assume a yes response to all questions a
 .Nm ;
 this should be used with great caution as this is a free license
 to continue after essentially unlimited trouble has been encountered.
+.It Fl z
+Clear unused directory space.
+The cleared space includes deleted file names and name padding.
 .El
 .Pp
 Inconsistencies checked are as follows:
Index: main.c
===================================================================
RCS file: /cvsroot/src/sbin/fsck_ffs/main.c,v
retrieving revision 1.84
diff -u -p -u -r1.84 main.c
--- main.c	8 Feb 2017 16:11:40 -0000	1.84
+++ main.c	5 May 2019 02:49:08 -0000
@@ -102,7 +102,7 @@ main(int argc, char *argv[])
 #ifndef NO_APPLE_UFS
 	isappleufs = 0;
 #endif
-	while ((ch = getopt(argc, argv, "aB:b:c:dFfm:npPqUyx:X")) != -1) {
+	while ((ch = getopt(argc, argv, "aB:b:c:dFfm:npPqUyx:Xz")) != -1) {
 		switch (ch) {
 #ifndef NO_APPLE_UFS
 		case 'a':
@@ -179,17 +179,23 @@ main(int argc, char *argv[])
 			break;
 #endif
 
-		case 'y':
-			yflag++;
-			nflag = 0;
-			break;
 		case 'x':
 			snap_backup = optarg;
 			break;
+
 		case 'X':
 			snap_internal = 1;
 			break;
 
+		case 'y':
+			yflag++;
+			nflag = 0;
+			break;
+
+		case 'z':
+			zflag++;
+			break;
+
 		default:
 			usage();
 		}