Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 6 Mar 2019 23:54:55 +0000 (UTC)
From:      Kirk McKusick <mckusick@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-12@freebsd.org
Subject:   svn commit: r344860 - in stable/12: sbin/fsck_ffs sbin/fsdb sys/ufs/ffs
Message-ID:  <201903062354.x26NstKN086003@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: mckusick
Date: Wed Mar  6 23:54:55 2019
New Revision: 344860
URL: https://svnweb.freebsd.org/changeset/base/344860

Log:
  MFC of 344552 and 344732
  
  Have fsck_ffs adjust size for files with hole at end
  
  Tighten last lbn calculation
  
  Sponsored by: Netflix

Modified:
  stable/12/sbin/fsck_ffs/fsck.h
  stable/12/sbin/fsck_ffs/globs.c
  stable/12/sbin/fsck_ffs/inode.c
  stable/12/sbin/fsck_ffs/pass1.c
  stable/12/sbin/fsck_ffs/setup.c
  stable/12/sbin/fsdb/fsdb.c
  stable/12/sys/ufs/ffs/ffs_alloc.c
  stable/12/sys/ufs/ffs/fs.h
Directory Properties:
  stable/12/   (props changed)

Modified: stable/12/sbin/fsck_ffs/fsck.h
==============================================================================
--- stable/12/sbin/fsck_ffs/fsck.h	Wed Mar  6 23:31:42 2019	(r344859)
+++ stable/12/sbin/fsck_ffs/fsck.h	Wed Mar  6 23:54:55 2019	(r344860)
@@ -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: stable/12/sbin/fsck_ffs/globs.c
==============================================================================
--- stable/12/sbin/fsck_ffs/globs.c	Wed Mar  6 23:31:42 2019	(r344859)
+++ stable/12/sbin/fsck_ffs/globs.c	Wed Mar  6 23:54:55 2019	(r344860)
@@ -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: stable/12/sbin/fsck_ffs/inode.c
==============================================================================
--- stable/12/sbin/fsck_ffs/inode.c	Wed Mar  6 23:31:42 2019	(r344859)
+++ stable/12/sbin/fsck_ffs/inode.c	Wed Mar  6 23:54:55 2019	(r344860)
@@ -126,9 +126,9 @@ ckinode(union dinode *dp, struct inodesc *idesc)
 			ret = iblock(idesc, i + 1, remsize, BT_LEVEL1 + i);
 			if (ret & STOP)
 				return (ret);
-		} else {
+		} else if (remsize > 0) {
 			idesc->id_lbn += sizepb / sblock.fs_bsize;
-			if (idesc->id_type == DATA && remsize > 0) {
+			if (idesc->id_type == DATA) {
 				/* An empty block in a directory XXX */
 				getpathname(pathbuf, idesc->id_number,
 						idesc->id_number);

Modified: stable/12/sbin/fsck_ffs/pass1.c
==============================================================================
--- stable/12/sbin/fsck_ffs/pass1.c	Wed Mar  6 23:31:42 2019	(r344859)
+++ stable/12/sbin/fsck_ffs/pass1.c	Wed Mar  6 23:54:55 2019	(r344860)
@@ -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: stable/12/sbin/fsck_ffs/setup.c
==============================================================================
--- stable/12/sbin/fsck_ffs/setup.c	Wed Mar  6 23:31:42 2019	(r344859)
+++ stable/12/sbin/fsck_ffs/setup.c	Wed Mar  6 23:54:55 2019	(r344860)
@@ -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: stable/12/sbin/fsdb/fsdb.c
==============================================================================
--- stable/12/sbin/fsdb/fsdb.c	Wed Mar  6 23:31:42 2019	(r344859)
+++ stable/12/sbin/fsdb/fsdb.c	Wed Mar  6 23:54:55 2019	(r344860)
@@ -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 },
@@ -1013,6 +1015,31 @@ CMDFUNCSTART(chgen)
     }
     DIP_SET(curinode, di_gen, gen);
     inodirty();
+    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: stable/12/sys/ufs/ffs/ffs_alloc.c
==============================================================================
--- stable/12/sys/ufs/ffs/ffs_alloc.c	Wed Mar  6 23:31:42 2019	(r344859)
+++ stable/12/sys/ufs/ffs/ffs_alloc.c	Wed Mar  6 23:54:55 2019	(r344860)
@@ -3020,6 +3020,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]
@@ -3061,6 +3063,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");
 
@@ -3208,6 +3213,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: stable/12/sys/ufs/ffs/fs.h
==============================================================================
--- stable/12/sys/ufs/ffs/fs.h	Wed Mar  6 23:31:42 2019	(r344859)
+++ stable/12/sys/ufs/ffs/fs.h	Wed Mar  6 23:54:55 2019	(r344860)
@@ -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.



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