? 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(); }