From 52488b514897b5c2c2601008d94bb5a905d6f401 Mon Sep 17 00:00:00 2001 From: Kirk McKusick Date: Fri, 5 Jun 2020 01:00:55 +0000 Subject: Further evaluation of the POSIX spec for fdatasync() shows that it requires that new data on growing files be accessible. Thus, the the fsyncdata() system call must update the on-disk inode when the size of the file has changed. This commit adds another inode update flag, IN_SIZEMOD, that gets set any time that the file size changes. If either the IN_IBLKDATA or the IN_SIZEMOD flag is set when fdatasync() is called, the associated inode is synchronously written to disk. We could have overloaded the IN_IBLKDATA flag to also track size changes since the only (current) use case for these flags are for fsyncdata(), but it does seem useful for possible future uses to separately track the file size changes and the inode block pointer changes. Reviewed by: kib MFC with: -r361785 Differential revision: https://reviews.freebsd.org/D25072 --- sys/ufs/ffs/ffs_alloc.c | 2 +- sys/ufs/ffs/ffs_balloc.c | 9 +++++---- sys/ufs/ffs/ffs_inode.c | 12 ++++++++---- sys/ufs/ffs/ffs_snapshot.c | 2 +- sys/ufs/ffs/ffs_softdep.c | 6 +++++- sys/ufs/ffs/ffs_vnops.c | 7 +++++-- sys/ufs/ufs/inode.h | 10 ++++++---- sys/ufs/ufs/ufs_lookup.c | 5 +++-- sys/ufs/ufs/ufs_vnops.c | 4 ++-- 9 files changed, 36 insertions(+), 21 deletions(-) (limited to 'sys/ufs') diff --git a/sys/ufs/ffs/ffs_alloc.c b/sys/ufs/ffs/ffs_alloc.c index 1805979e6b72..d8ccdec718ae 100644 --- a/sys/ufs/ffs/ffs_alloc.c +++ b/sys/ufs/ffs/ffs_alloc.c @@ -3306,7 +3306,7 @@ sysctl_ffs_fsck(SYSCTL_HANDLER_ARGS) break; ip = VTOI(vp); DIP_SET(ip, i_size, cmd.size); - UFS_INODE_SET_FLAG(ip, IN_CHANGE | IN_MODIFIED); + UFS_INODE_SET_FLAG(ip, IN_SIZEMOD | IN_CHANGE | IN_MODIFIED); error = ffs_update(vp, 1); vput(vp); break; diff --git a/sys/ufs/ffs/ffs_balloc.c b/sys/ufs/ffs/ffs_balloc.c index 3d957ad1a2cb..ba14fb81e7ed 100644 --- a/sys/ufs/ffs/ffs_balloc.c +++ b/sys/ufs/ffs/ffs_balloc.c @@ -155,7 +155,7 @@ ffs_balloc_ufs1(struct vnode *vp, off_t startoffset, int size, dp->di_size = ip->i_size; dp->di_db[nb] = dbtofsb(fs, bp->b_blkno); UFS_INODE_SET_FLAG(ip, - IN_CHANGE | IN_UPDATE | IN_IBLKDATA); + IN_SIZEMOD | IN_CHANGE | IN_UPDATE | IN_IBLKDATA); if (flags & IO_SYNC) bwrite(bp); else if (DOINGASYNC(vp)) @@ -648,7 +648,8 @@ ffs_balloc_ufs2(struct vnode *vp, off_t startoffset, int size, dp->di_extsize = smalllblktosize(fs, nb + 1); dp->di_extb[nb] = dbtofsb(fs, bp->b_blkno); bp->b_xflags |= BX_ALTDATA; - UFS_INODE_SET_FLAG(ip, IN_CHANGE | IN_IBLKDATA); + UFS_INODE_SET_FLAG(ip, + IN_SIZEMOD | IN_CHANGE | IN_IBLKDATA); if (flags & IO_SYNC) bwrite(bp); else @@ -751,8 +752,8 @@ ffs_balloc_ufs2(struct vnode *vp, off_t startoffset, int size, ip->i_size = smalllblktosize(fs, nb + 1); dp->di_size = ip->i_size; dp->di_db[nb] = dbtofsb(fs, bp->b_blkno); - UFS_INODE_SET_FLAG(ip, IN_CHANGE | IN_UPDATE | - IN_IBLKDATA); + UFS_INODE_SET_FLAG(ip, + IN_SIZEMOD |IN_CHANGE | IN_UPDATE | IN_IBLKDATA); if (flags & IO_SYNC) bwrite(bp); else diff --git a/sys/ufs/ffs/ffs_inode.c b/sys/ufs/ffs/ffs_inode.c index b8e637207e4c..dbe3c9b8aa16 100644 --- a/sys/ufs/ffs/ffs_inode.c +++ b/sys/ufs/ffs/ffs_inode.c @@ -279,7 +279,7 @@ ffs_truncate(vp, length, flags, cred) oldblks[i] = ip->i_din2->di_extb[i]; ip->i_din2->di_extb[i] = 0; } - UFS_INODE_SET_FLAG(ip, IN_CHANGE); + UFS_INODE_SET_FLAG(ip, IN_SIZEMOD | IN_CHANGE); if ((error = ffs_update(vp, waitforupdate))) return (error); for (i = 0; i < UFS_NXADDR; i++) { @@ -303,7 +303,7 @@ ffs_truncate(vp, length, flags, cred) bzero(SHORTLINK(ip), (u_int)ip->i_size); ip->i_size = 0; DIP_SET(ip, i_size, 0); - UFS_INODE_SET_FLAG(ip, IN_CHANGE | IN_UPDATE); + UFS_INODE_SET_FLAG(ip, IN_SIZEMOD | IN_CHANGE | IN_UPDATE); if (needextclean) goto extclean; return (ffs_update(vp, waitforupdate)); @@ -343,7 +343,7 @@ ffs_truncate(vp, length, flags, cred) bdwrite(bp); else bawrite(bp); - UFS_INODE_SET_FLAG(ip, IN_CHANGE | IN_UPDATE); + UFS_INODE_SET_FLAG(ip, IN_SIZEMOD | IN_CHANGE | IN_UPDATE); return (ffs_update(vp, waitforupdate)); } /* @@ -429,6 +429,7 @@ ffs_truncate(vp, length, flags, cred) if (blkno != 0 && offset == 0) { ip->i_size = length; DIP_SET(ip, i_size, length); + UFS_INODE_SET_FLAG(ip, IN_SIZEMOD | IN_CHANGE | IN_UPDATE); } else { lbn = lblkno(fs, length); flags |= BA_CLRBUF; @@ -463,6 +464,7 @@ ffs_truncate(vp, length, flags, cred) bdwrite(bp); else bawrite(bp); + UFS_INODE_SET_FLAG(ip, IN_SIZEMOD | IN_CHANGE | IN_UPDATE); } /* * Calculate index into inode's block list of @@ -512,6 +514,7 @@ ffs_truncate(vp, length, flags, cred) } ip->i_size = osize; DIP_SET(ip, i_size, osize); + UFS_INODE_SET_FLAG(ip, IN_SIZEMOD | IN_CHANGE | IN_UPDATE); error = vtruncbuf(vp, length, fs->fs_bsize); if (error && (allerror == 0)) @@ -578,6 +581,7 @@ ffs_truncate(vp, length, flags, cred) oldspace = blksize(fs, ip, lastblock); ip->i_size = length; DIP_SET(ip, i_size, length); + UFS_INODE_SET_FLAG(ip, IN_SIZEMOD | IN_CHANGE | IN_UPDATE); newspace = blksize(fs, ip, lastblock); if (newspace == 0) panic("ffs_truncate: newspace"); @@ -623,7 +627,7 @@ done: DIP_SET(ip, i_blocks, DIP(ip, i_blocks) - blocksreleased); else /* sanity */ DIP_SET(ip, i_blocks, 0); - UFS_INODE_SET_FLAG(ip, IN_CHANGE); + UFS_INODE_SET_FLAG(ip, IN_SIZEMOD | IN_CHANGE); #ifdef QUOTA (void) chkdq(ip, -blocksreleased, NOCRED, FORCE); #endif diff --git a/sys/ufs/ffs/ffs_snapshot.c b/sys/ufs/ffs/ffs_snapshot.c index c0ce8f02f6fd..57903144d33f 100644 --- a/sys/ufs/ffs/ffs_snapshot.c +++ b/sys/ufs/ffs/ffs_snapshot.c @@ -319,7 +319,7 @@ restart: goto out; ip->i_size = lblktosize(fs, (off_t)numblks); DIP_SET(ip, i_size, ip->i_size); - UFS_INODE_SET_FLAG(ip, IN_CHANGE | IN_UPDATE); + UFS_INODE_SET_FLAG(ip, IN_SIZEMOD | IN_CHANGE | IN_UPDATE); error = readblock(vp, bp, numblks - 1); bawrite(bp); if (error != 0) diff --git a/sys/ufs/ffs/ffs_softdep.c b/sys/ufs/ffs/ffs_softdep.c index 47b0fd6b488c..f97b08160dbc 100644 --- a/sys/ufs/ffs/ffs_softdep.c +++ b/sys/ufs/ffs/ffs_softdep.c @@ -6709,6 +6709,7 @@ softdep_journal_freeblocks(ip, cred, length, flags) } ip->i_size = length; DIP_SET(ip, i_size, ip->i_size); + UFS_INODE_SET_FLAG(ip, IN_SIZEMOD | IN_CHANGE); datablocks = DIP(ip, i_blocks) - extblocks; if (length != 0) datablocks = blkcount(fs, datablocks, length); @@ -6719,6 +6720,7 @@ softdep_journal_freeblocks(ip, cred, length, flags) setup_freeext(freeblks, ip, i, needj); ip->i_din2->di_extsize = 0; datablocks += extblocks; + UFS_INODE_SET_FLAG(ip, IN_SIZEMOD | IN_CHANGE); } #ifdef QUOTA /* Reference the quotas in case the block count is wrong in the end. */ @@ -6829,7 +6831,7 @@ softdep_journal_freeblocks(ip, cred, length, flags) } ip->i_size = length; DIP_SET(ip, i_size, length); - UFS_INODE_SET_FLAG(ip, IN_CHANGE | IN_UPDATE); + UFS_INODE_SET_FLAG(ip, IN_SIZEMOD | IN_CHANGE | IN_UPDATE); allocbuf(bp, frags); ffs_update(vp, 0); bawrite(bp); @@ -6976,6 +6978,7 @@ softdep_setup_freeblocks(ip, length, flags) setup_freeindir(freeblks, ip, i, -lbn -i, 0); ip->i_size = 0; DIP_SET(ip, i_size, 0); + UFS_INODE_SET_FLAG(ip, IN_SIZEMOD | IN_CHANGE); datablocks = DIP(ip, i_blocks) - extblocks; } if ((flags & IO_EXT) != 0) { @@ -6983,6 +6986,7 @@ softdep_setup_freeblocks(ip, length, flags) setup_freeext(freeblks, ip, i, 0); ip->i_din2->di_extsize = 0; datablocks += extblocks; + UFS_INODE_SET_FLAG(ip, IN_SIZEMOD | IN_CHANGE); } #ifdef QUOTA /* Reference the quotas in case the block count is wrong in the end. */ diff --git a/sys/ufs/ffs/ffs_vnops.c b/sys/ufs/ffs/ffs_vnops.c index 979497f707ef..f97de5432b67 100644 --- a/sys/ufs/ffs/ffs_vnops.c +++ b/sys/ufs/ffs/ffs_vnops.c @@ -416,7 +416,7 @@ next: error = ffs_update(vp, 1); if (DOINGSUJ(vp)) softdep_journal_fsync(VTOI(vp)); - } else if ((ip->i_flags & IN_IBLKDATA) != 0) { + } else if ((ip->i_flags & (IN_SIZEMOD | IN_IBLKDATA)) != 0) { error = ffs_update(vp, 1); } return (error); @@ -825,6 +825,7 @@ ffs_write(ap) if (uio->uio_offset + xfersize > ip->i_size) { ip->i_size = uio->uio_offset + xfersize; DIP_SET(ip, i_size, ip->i_size); + UFS_INODE_SET_FLAG(ip, IN_SIZEMOD | IN_CHANGE); } size = blksize(fs, ip, lbn) - bp->b_resid; @@ -1108,8 +1109,10 @@ ffs_extwrite(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *ucred) if ((bp->b_flags & B_CACHE) == 0 && fs->fs_bsize <= xfersize) vfs_bio_clrbuf(bp); - if (uio->uio_offset + xfersize > dp->di_extsize) + if (uio->uio_offset + xfersize > dp->di_extsize) { dp->di_extsize = uio->uio_offset + xfersize; + UFS_INODE_SET_FLAG(ip, IN_SIZEMOD | IN_CHANGE); + } size = sblksize(fs, dp->di_extsize, lbn) - bp->b_resid; if (size < xfersize) diff --git a/sys/ufs/ufs/inode.h b/sys/ufs/ufs/inode.h index d0be73c76d44..defa888b17fe 100644 --- a/sys/ufs/ufs/inode.h +++ b/sys/ufs/ufs/inode.h @@ -127,19 +127,21 @@ struct inode { #define IN_LAZYMOD 0x0020 /* Modified, but don't write yet. */ #define IN_LAZYACCESS 0x0040 /* Process IN_ACCESS after the suspension finished */ -#define IN_EA_LOCKED 0x0080 -#define IN_EA_LOCKWAIT 0x0100 +#define IN_EA_LOCKED 0x0080 /* Extended attributes locked */ +#define IN_EA_LOCKWAIT 0x0100 /* Want extended attributes lock */ #define IN_TRUNCATED 0x0200 /* Journaled truncation pending. */ #define IN_UFS2 0x0400 /* UFS2 vs UFS1 */ #define IN_IBLKDATA 0x0800 /* datasync requires inode block update */ +#define IN_SIZEMOD 0x1000 /* Inode size has been modified */ -#define PRINT_INODE_FLAGS "\20\20b16\17b15\16b14\15b13" \ +#define PRINT_INODE_FLAGS "\20\20b16\17b15\16b14\15sizemod" \ "\14iblkdata\13is_ufs2\12truncated\11ea_lockwait\10ea_locked" \ "\7lazyaccess\6lazymod\5needsync\4modified\3update\2change\1access" #define UFS_INODE_FLAG_LAZY_MASK \ - (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE | IN_LAZYMOD | IN_LAZYACCESS) + (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE | IN_LAZYMOD | \ + IN_LAZYACCESS) /* * Some flags can persist a vnode transitioning to 0 hold count and being tkaen * off the list. diff --git a/sys/ufs/ufs/ufs_lookup.c b/sys/ufs/ufs/ufs_lookup.c index 9dd147d489af..ca151037a960 100644 --- a/sys/ufs/ufs/ufs_lookup.c +++ b/sys/ufs/ufs/ufs_lookup.c @@ -556,7 +556,7 @@ found: ufs_dirbad(dp, i_offset, "i_size too small"); dp->i_size = i_offset + DIRSIZ(OFSFMT(vdp), ep); DIP_SET(dp, i_size, dp->i_size); - UFS_INODE_SET_FLAG(dp, IN_CHANGE | IN_UPDATE); + UFS_INODE_SET_FLAG(dp, IN_SIZEMOD | IN_CHANGE | IN_UPDATE); } brelse(bp); @@ -918,7 +918,7 @@ ufs_direnter(dvp, tvp, dirp, cnp, newdirbp, isrename) dp->i_size = dp->i_offset + DIRBLKSIZ; DIP_SET(dp, i_size, dp->i_size); dp->i_endoff = dp->i_size; - UFS_INODE_SET_FLAG(dp, IN_CHANGE | IN_UPDATE); + UFS_INODE_SET_FLAG(dp, IN_SIZEMOD | IN_CHANGE | IN_UPDATE); dirp->d_reclen = DIRBLKSIZ; blkoff = dp->i_offset & (VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_iosize - 1); @@ -1004,6 +1004,7 @@ ufs_direnter(dvp, tvp, dirp, cnp, newdirbp, isrename) if (dp->i_offset + dp->i_count > dp->i_size) { dp->i_size = dp->i_offset + dp->i_count; DIP_SET(dp, i_size, dp->i_size); + UFS_INODE_SET_FLAG(dp, IN_SIZEMOD | IN_MODIFIED); } /* * Get the block containing the space for the new directory entry. diff --git a/sys/ufs/ufs/ufs_vnops.c b/sys/ufs/ufs/ufs_vnops.c index 8ef8c78d654f..dc3535432e3c 100644 --- a/sys/ufs/ufs/ufs_vnops.c +++ b/sys/ufs/ufs/ufs_vnops.c @@ -1932,7 +1932,7 @@ ufs_mkdir(ap) goto bad; ip->i_size = DIRBLKSIZ; DIP_SET(ip, i_size, DIRBLKSIZ); - UFS_INODE_SET_FLAG(ip, IN_CHANGE | IN_UPDATE); + UFS_INODE_SET_FLAG(ip, IN_SIZEMOD | IN_CHANGE | IN_UPDATE); bcopy((caddr_t)&dirtemplate, (caddr_t)bp->b_data, sizeof dirtemplate); if (DOINGSOFTDEP(tvp)) { /* @@ -2119,7 +2119,7 @@ ufs_symlink(ap) bcopy(ap->a_target, SHORTLINK(ip), len); ip->i_size = len; DIP_SET(ip, i_size, len); - UFS_INODE_SET_FLAG(ip, IN_CHANGE | IN_UPDATE); + UFS_INODE_SET_FLAG(ip, IN_SIZEMOD | IN_CHANGE | IN_UPDATE); error = UFS_UPDATE(vp, 0); } else error = vn_rdwr(UIO_WRITE, vp, __DECONST(void *, ap->a_target), -- cgit v1.2.3