svn commit: r344552 - in head: sbin/fsck_ffs sbin/fsdb sys/ufs/ffs

Kirk McKusick mckusick at FreeBSD.org
Mon Feb 25 21:58:22 UTC 2019


Author: mckusick
Date: Mon Feb 25 21:58:19 2019
New Revision: 344552
URL: https://svnweb.freebsd.org/changeset/base/344552

Log:
  After a crash, a file that extends into indirect blocks may end up
  shorter than its size resulting in a hole as its final block (which
  is a violation of the invarients of the UFS filesystem).
  
  Soft updates will always ensure that the file size is correct when
  writing inodes to disk for files that contain only direct block
  pointers. However soft updates does not roll back sizes for files
  with indirect blocks that it has set to unallocated because their
  contents have not yet been written to disk. Hence, the file can
  appear to have a hole at its end because the block pointer has been
  rolled back to zero when its inode was written to disk. Thus,
  fsck_ffs calculates the last allocated block in the file. For files
  that extend into indirect blocks, fsck_ffs checks for a size past
  the last allocated block of the file and if that is found, shortens
  the file to reference the last allocated block thus avoiding having
  it reference a hole at its end.
  
  Submitted by: Chuck Silvers <chs at netflix.com>
  Tested by:    Chuck Silvers <chs at netflix.com>
  MFC after:    1 week
  Sponsored by: Netflix

Modified:
  head/sbin/fsck_ffs/fsck.h
  head/sbin/fsck_ffs/globs.c
  head/sbin/fsck_ffs/pass1.c
  head/sbin/fsck_ffs/setup.c
  head/sbin/fsdb/fsdb.c
  head/sys/ufs/ffs/ffs_alloc.c
  head/sys/ufs/ffs/fs.h

Modified: head/sbin/fsck_ffs/fsck.h
==============================================================================
--- head/sbin/fsck_ffs/fsck.h	Mon Feb 25 19:47:27 2019	(r344551)
+++ head/sbin/fsck_ffs/fsck.h	Mon Feb 25 21:58:19 2019	(r344552)
@@ -232,6 +232,7 @@ struct inodesc {
 	ufs_lbn_t id_lbn;	/* logical block number of current block */
 	ufs2_daddr_t id_blkno;	/* current block number being examined */
 	int id_numfrags;	/* number of frags contained in block */
+	ufs_lbn_t id_lballoc;	/* pass1: last LBN that is allocated */
 	off_t id_filesize;	/* for DATA nodes, the size of the directory */
 	ufs2_daddr_t id_entryno;/* for DATA nodes, current entry number */
 	int id_loc;		/* for DATA nodes, current location in dir */
@@ -291,6 +292,7 @@ extern long countdirs;		/* number of directories we ac
 #define MIBSIZE	3		/* size of fsck sysctl MIBs */
 extern int	adjrefcnt[MIBSIZE];	/* MIB command to adjust inode reference cnt */
 extern int	adjblkcnt[MIBSIZE];	/* MIB command to adjust inode block count */
+extern int	setsize[MIBSIZE];	/* MIB command to set inode size */
 extern int	adjndir[MIBSIZE];	/* MIB command to adjust number of directories */
 extern int	adjnbfree[MIBSIZE];	/* MIB command to adjust number of free blocks */
 extern int	adjnifree[MIBSIZE];	/* MIB command to adjust number of free inodes */

Modified: head/sbin/fsck_ffs/globs.c
==============================================================================
--- head/sbin/fsck_ffs/globs.c	Mon Feb 25 19:47:27 2019	(r344551)
+++ head/sbin/fsck_ffs/globs.c	Mon Feb 25 21:58:19 2019	(r344552)
@@ -63,6 +63,7 @@ unsigned long  numdirs, listmax;
 long countdirs;		/* number of directories we actually found */
 int	adjrefcnt[MIBSIZE];	/* MIB command to adjust inode reference cnt */
 int	adjblkcnt[MIBSIZE];	/* MIB command to adjust inode block count */
+int	setsize[MIBSIZE];	/* MIB command to set inode size */
 int	adjndir[MIBSIZE];	/* MIB command to adjust number of directories */
 int	adjnbfree[MIBSIZE];	/* MIB command to adjust number of free blocks */
 int	adjnifree[MIBSIZE];	/* MIB command to adjust number of free inodes */
@@ -131,6 +132,7 @@ fsckinit(void)
 	countdirs = 0;
 	bzero(adjrefcnt, sizeof(int) * MIBSIZE);
 	bzero(adjblkcnt, sizeof(int) * MIBSIZE);
+	bzero(setsize, sizeof(int) * MIBSIZE);
 	bzero(adjndir, sizeof(int) * MIBSIZE);
 	bzero(adjnbfree, sizeof(int) * MIBSIZE);
 	bzero(adjnifree, sizeof(int) * MIBSIZE);

Modified: head/sbin/fsck_ffs/pass1.c
==============================================================================
--- head/sbin/fsck_ffs/pass1.c	Mon Feb 25 19:47:27 2019	(r344551)
+++ head/sbin/fsck_ffs/pass1.c	Mon Feb 25 21:58:19 2019	(r344552)
@@ -247,6 +247,7 @@ checkinode(ino_t inumber, struct inodesc *idesc, int r
 	off_t kernmaxfilesize;
 	ufs2_daddr_t ndb;
 	mode_t mode;
+	uintmax_t fixsize;
 	int j, ret, offset;
 
 	if ((dp = getnextinode(inumber, rebuildcg)) == NULL)
@@ -377,6 +378,7 @@ checkinode(ino_t inumber, struct inodesc *idesc, int r
 		idesc->id_type = SNAP;
 	else
 		idesc->id_type = ADDR;
+	idesc->id_lballoc = -1;
 	(void)ckinode(dp, idesc);
 	if (sblock.fs_magic == FS_UFS2_MAGIC && dp->dp2.di_extsize > 0) {
 		idesc->id_type = ADDR;
@@ -422,6 +424,46 @@ checkinode(ino_t inumber, struct inodesc *idesc, int r
 				rwerror("ADJUST INODE BLOCK COUNT", cmd.value);
 		}
 	}
+	/*
+	 * Soft updates will always ensure that the file size is correct
+	 * for files that contain only direct block pointers. However
+	 * soft updates does not roll back sizes for files with indirect
+	 * blocks that it has set to unallocated because their contents
+	 * have not yet been written to disk. Hence, the file can appear
+	 * to have a hole at its end because the block pointer has been
+	 * rolled back to zero. Thus, id_lballoc tracks the last allocated
+	 * block in the file. Here, for files that extend into indirect
+	 * blocks, we check for a size past the last allocated block of
+	 * the file and if that is found, shorten the file to reference
+	 * the last allocated block to avoid having it reference a hole
+	 * at its end.
+	 */
+	if (DIP(dp, di_size) > UFS_NDADDR * sblock.fs_bsize &&
+	    idesc->id_lballoc != lblkno(&sblock, DIP(dp, di_size) - 1)) {
+		fixsize = lblktosize(&sblock, idesc->id_lballoc + 1);
+		pwarn("INODE %lu: FILE SIZE %ju BEYOND END OF ALLOCATED FILE, "
+		      "SIZE SHOULD BE %ju", (u_long)inumber,
+		      (uintmax_t)DIP(dp, di_size), fixsize);
+		if (preen)
+			printf(" (ADJUSTED)\n");
+		else if (reply("ADJUST") == 0)
+			return (1);
+		if (bkgrdflag == 0) {
+			dp = ginode(inumber);
+			DIP_SET(dp, di_size, fixsize);
+			inodirty(dp);
+		} else {
+			cmd.value = idesc->id_number;
+			cmd.size = fixsize;
+			if (debug)
+				printf("setsize ino %ju size set to %ju\n",
+				    (uintmax_t)cmd.value, (uintmax_t)cmd.size);
+			if (sysctl(setsize, MIBSIZE, 0, 0,
+			    &cmd, sizeof cmd) == -1)
+				rwerror("SET INODE SIZE", cmd.value);
+		}
+
+	}
 	return (1);
 unknown:
 	pfatal("UNKNOWN FILE TYPE I=%lu", (u_long)inumber);
@@ -523,5 +565,7 @@ pass1check(struct inodesc *idesc)
 		 */
 		idesc->id_entryno++;
 	}
+	if (idesc->id_lballoc == -1 || idesc->id_lballoc < idesc->id_lbn)
+		idesc->id_lballoc = idesc->id_lbn;
 	return (res);
 }

Modified: head/sbin/fsck_ffs/setup.c
==============================================================================
--- head/sbin/fsck_ffs/setup.c	Mon Feb 25 19:47:27 2019	(r344551)
+++ head/sbin/fsck_ffs/setup.c	Mon Feb 25 21:58:19 2019	(r344552)
@@ -140,6 +140,7 @@ setup(char *dev)
 		size = MIBSIZE;
 		if (sysctlnametomib("vfs.ffs.adjrefcnt", adjrefcnt, &size) < 0||
 		    sysctlnametomib("vfs.ffs.adjblkcnt", adjblkcnt, &size) < 0||
+		    sysctlnametomib("vfs.ffs.setsize", setsize, &size) < 0 ||
 		    sysctlnametomib("vfs.ffs.freefiles", freefiles, &size) < 0||
 		    sysctlnametomib("vfs.ffs.freedirs", freedirs, &size) < 0 ||
 		    sysctlnametomib("vfs.ffs.freeblks", freeblks, &size) < 0) {

Modified: head/sbin/fsdb/fsdb.c
==============================================================================
--- head/sbin/fsdb/fsdb.c	Mon Feb 25 19:47:27 2019	(r344551)
+++ head/sbin/fsdb/fsdb.c	Mon Feb 25 21:58:19 2019	(r344552)
@@ -157,6 +157,7 @@ CMDFUNC(chctime);			/* Change ctime */
 CMDFUNC(chatime);			/* Change atime */
 CMDFUNC(chinum);			/* Change inode # of dirent */
 CMDFUNC(chname);			/* Change dirname of dirent */
+CMDFUNC(chsize);			/* Change size */
 
 struct cmdtable cmds[] = {
 	{ "help", "Print out help", 1, 1, FL_RO, helpfn },
@@ -186,6 +187,7 @@ struct cmdtable cmds[] = {
 	{ "chgrp", "Change group of current inode to GROUP", 2, 2, FL_WR, chgroup },
 	{ "chflags", "Change flags of current inode to FLAGS", 2, 2, FL_WR, chaflags },
 	{ "chgen", "Change generation number of current inode to GEN", 2, 2, FL_WR, chgen },
+	{ "chsize", "Change size of current inode to SIZE", 2, 2, FL_WR, chsize },
 	{ "btime", "Change btime of current inode to BTIME", 2, 2, FL_WR, chbtime },
 	{ "mtime", "Change mtime of current inode to MTIME", 2, 2, FL_WR, chmtime },
 	{ "ctime", "Change ctime of current inode to CTIME", 2, 2, FL_WR, chctime },
@@ -1012,6 +1014,31 @@ CMDFUNCSTART(chgen)
 	return(1);
     }
     DIP_SET(curinode, di_gen, gen);
+    inodirty(curinode);
+    printactive(0);
+    return rval;
+}
+
+CMDFUNCSTART(chsize)
+{
+    int rval = 1;
+    off_t size;
+    char *cp;
+
+    if (!checkactive())
+	return 1;
+
+    size = strtoll(argv[1], &cp, 0);
+    if (cp == argv[1] || *cp != '\0') {
+	warnx("bad size `%s'", argv[1]);
+	return 1;
+    }
+
+    if (size < 0) {
+	warnx("size set to negative (%jd)\n", (intmax_t)size);
+	return(1);
+    }
+    DIP_SET(curinode, di_size, size);
     inodirty(curinode);
     printactive(0);
     return rval;

Modified: head/sys/ufs/ffs/ffs_alloc.c
==============================================================================
--- head/sys/ufs/ffs/ffs_alloc.c	Mon Feb 25 19:47:27 2019	(r344551)
+++ head/sys/ufs/ffs/ffs_alloc.c	Mon Feb 25 21:58:19 2019	(r344552)
@@ -3037,6 +3037,8 @@ ffs_fserr(fs, inum, cp)
  *	the count to zero will cause the inode to be freed.
  * adjblkcnt(inode, amt) - adjust the number of blocks used by the
  *	inode by the specified amount.
+ * adjsize(inode, size) - set the size of the inode to the
+ *	specified size.
  * adjndir, adjbfree, adjifree, adjffree, adjnumclusters(amt) -
  *	adjust the superblock summary.
  * freedirs(inode, count) - directory inodes [inode..inode + count - 1]
@@ -3078,6 +3080,9 @@ SYSCTL_PROC(_vfs_ffs, FFS_ADJ_REFCNT, adjrefcnt, CTLFL
 static SYSCTL_NODE(_vfs_ffs, FFS_ADJ_BLKCNT, adjblkcnt, CTLFLAG_WR,
 	sysctl_ffs_fsck, "Adjust Inode Used Blocks Count");
 
+static SYSCTL_NODE(_vfs_ffs, FFS_SET_SIZE, setsize, CTLFLAG_WR,
+	sysctl_ffs_fsck, "Set the inode size");
+
 static SYSCTL_NODE(_vfs_ffs, FFS_ADJ_NDIR, adjndir, CTLFLAG_WR,
 	sysctl_ffs_fsck, "Adjust number of directories");
 
@@ -3225,6 +3230,23 @@ sysctl_ffs_fsck(SYSCTL_HANDLER_ARGS)
 			break;
 		ip = VTOI(vp);
 		DIP_SET(ip, i_blocks, DIP(ip, i_blocks) + cmd.size);
+		ip->i_flag |= IN_CHANGE | IN_MODIFIED;
+		error = ffs_update(vp, 1);
+		vput(vp);
+		break;
+
+	case FFS_SET_SIZE:
+#ifdef DEBUG
+		if (fsckcmds) {
+			printf("%s: set inode %jd size to %jd\n",
+			    mp->mnt_stat.f_mntonname, (intmax_t)cmd.value,
+			    (intmax_t)cmd.size);
+		}
+#endif /* DEBUG */
+		if ((error = ffs_vget(mp, (ino_t)cmd.value, LK_EXCLUSIVE, &vp)))
+			break;
+		ip = VTOI(vp);
+		DIP_SET(ip, i_size, cmd.size);
 		ip->i_flag |= IN_CHANGE | IN_MODIFIED;
 		error = ffs_update(vp, 1);
 		vput(vp);

Modified: head/sys/ufs/ffs/fs.h
==============================================================================
--- head/sys/ufs/ffs/fs.h	Mon Feb 25 19:47:27 2019	(r344551)
+++ head/sys/ufs/ffs/fs.h	Mon Feb 25 21:58:19 2019	(r344552)
@@ -221,7 +221,8 @@
 #define	FFS_UNLINK		14	/* remove a name in the filesystem */
 #define	FFS_SET_INODE		15	/* update an on-disk inode */
 #define	FFS_SET_BUFOUTPUT	16	/* set buffered writing on descriptor */
-#define	FFS_MAXID		16	/* number of valid ffs ids */
+#define	FFS_SET_SIZE		17	/* set inode size */
+#define	FFS_MAXID		17	/* number of valid ffs ids */
 
 /*
  * Command structure passed in to the filesystem to adjust filesystem values.


More information about the svn-src-all mailing list