svn commit: r295950 - head/sys/ufs/ffs
Kirk McKusick
mckusick at FreeBSD.org
Wed Feb 24 01:58:42 UTC 2016
Author: mckusick
Date: Wed Feb 24 01:58:40 2016
New Revision: 295950
URL: https://svnweb.freebsd.org/changeset/base/295950
Log:
The UFS filesystem requires that the last block of a file always be
allocated. When shortening the length of a file in which the new end
of the file contains a hole, the hole must have a block allocated.
Reported by: Maxim Sobolev
Reviewed by: kib
Tested by: Peter Holm
Modified:
head/sys/ufs/ffs/ffs_inode.c
Modified: head/sys/ufs/ffs/ffs_inode.c
==============================================================================
--- head/sys/ufs/ffs/ffs_inode.c Wed Feb 24 01:32:12 2016 (r295949)
+++ head/sys/ufs/ffs/ffs_inode.c Wed Feb 24 01:58:40 2016 (r295950)
@@ -184,7 +184,7 @@ ffs_truncate(vp, length, flags, cred)
struct inode *ip;
ufs2_daddr_t bn, lbn, lastblock, lastiblock[NIADDR], indir_lbn[NIADDR];
ufs2_daddr_t oldblks[NDADDR + NIADDR], newblks[NDADDR + NIADDR];
- ufs2_daddr_t count, blocksreleased = 0, datablocks;
+ ufs2_daddr_t count, blocksreleased = 0, datablocks, blkno;
struct bufobj *bo;
struct fs *fs;
struct buf *bp;
@@ -192,7 +192,7 @@ ffs_truncate(vp, length, flags, cred)
int softdeptrunc, journaltrunc;
int needextclean, extblocks;
int offset, size, level, nblocks;
- int i, error, allerror;
+ int i, error, allerror, indiroff;
off_t osize;
ip = VTOI(vp);
@@ -329,16 +329,57 @@ ffs_truncate(vp, length, flags, cred)
ip->i_flag |= IN_CHANGE | IN_UPDATE;
return (ffs_update(vp, !DOINGASYNC(vp)));
}
- if (DOINGSOFTDEP(vp)) {
+ /*
+ * Lookup block number for a given offset. Zero length files
+ * have no blocks, so return a blkno of -1.
+ */
+ lbn = lblkno(fs, length - 1);
+ if (length == 0) {
+ blkno = -1;
+ } else if (lbn < NDADDR) {
+ blkno = DIP(ip, i_db[lbn]);
+ } else {
+ error = UFS_BALLOC(vp, lblktosize(fs, (off_t)lbn), fs->fs_bsize,
+ cred, BA_METAONLY, &bp);
+ if (error)
+ return (error);
+ indiroff = (lbn - NDADDR) % NINDIR(fs);
+ if (ip->i_ump->um_fstype == UFS1)
+ blkno = ((ufs1_daddr_t *)(bp->b_data))[indiroff];
+ else
+ blkno = ((ufs2_daddr_t *)(bp->b_data))[indiroff];
+ /*
+ * If the block number is non-zero, then the indirect block
+ * must have been previously allocated and need not be written.
+ * If the block number is zero, then we may have allocated
+ * the indirect block and hence need to write it out.
+ */
+ if (blkno != 0)
+ brelse(bp);
+ else if (DOINGSOFTDEP(vp) || DOINGASYNC(vp))
+ bdwrite(bp);
+ else
+ bwrite(bp);
+ }
+ /*
+ * If the block number at the new end of the file is zero,
+ * then we must allocate it to ensure that the last block of
+ * the file is allocated. Soft updates does not handle this
+ * case, so here we have to clean up the soft updates data
+ * structures describing the allocation past the truncation
+ * point. Finding and deallocating those structures is a lot of
+ * work. Since partial truncation with a hole at the end occurs
+ * rarely, we solve the problem by syncing the file so that it
+ * will have no soft updates data structures left.
+ */
+ if (blkno == 0 && (error = ffs_syncvnode(vp, MNT_WAIT, 0)) != 0)
+ return (error);
+ if (blkno != 0 && DOINGSOFTDEP(vp)) {
if (softdeptrunc == 0 && journaltrunc == 0) {
/*
- * If a file is only partially truncated, then
- * we have to clean up the data structures
- * describing the allocation past the truncation
- * point. Finding and deallocating those structures
- * is a lot of work. Since partial truncation occurs
- * rarely, we solve the problem by syncing the file
- * so that it will have no data structures left.
+ * If soft updates cannot handle this truncation,
+ * clean up soft dependency data structures and
+ * fall through to the synchronous truncation.
*/
if ((error = ffs_syncvnode(vp, MNT_WAIT, 0)) != 0)
return (error);
@@ -358,15 +399,17 @@ ffs_truncate(vp, length, flags, cred)
}
}
/*
- * Shorten the size of the file. If the file is not being
- * truncated to a block boundary, the contents of the
- * partial block following the end of the file must be
- * zero'ed in case it ever becomes accessible again because
- * of subsequent file growth. Directories however are not
+ * Shorten the size of the file. If the last block of the
+ * shortened file is unallocated, we must allocate it.
+ * Additionally, if the file is not being truncated to a
+ * block boundary, the contents of the partial block
+ * following the end of the file must be zero'ed in
+ * case it ever becomes accessible again because of
+ * subsequent file growth. Directories however are not
* zero'ed as they should grow back initialized to empty.
*/
offset = blkoff(fs, length);
- if (offset == 0) {
+ if (blkno != 0 && offset == 0) {
ip->i_size = length;
DIP_SET(ip, i_size, length);
} else {
@@ -390,7 +433,7 @@ ffs_truncate(vp, length, flags, cred)
ip->i_size = length;
DIP_SET(ip, i_size, length);
size = blksize(fs, ip, lbn);
- if (vp->v_type != VDIR)
+ if (vp->v_type != VDIR && offset != 0)
bzero((char *)bp->b_data + offset,
(u_int)(size - offset));
/* Kirk's code has reallocbuf(bp, size, 1) here */
More information about the svn-src-all
mailing list