From e00d2c2d4aead747d0fbee99001b00612d1082b0 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 16 Oct 2007 23:31:01 -0700 Subject: fuse: truncate on spontaneous size change Memory mappings were only truncated on an explicit truncate, but not when the file size was changed externally. Fix this by moving the truncation code from fuse_setattr to fuse_change_attributes. Yes, there are races between write and and external truncation, but we can't really do anything about them. Signed-off-by: Miklos Szeredi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/fuse/inode.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'fs/fuse/inode.c') diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 95c8a9738ca..b584de33a6a 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -109,20 +109,24 @@ static int fuse_remount_fs(struct super_block *sb, int *flags, char *data) return 0; } +static void fuse_truncate(struct address_space *mapping, loff_t offset) +{ + /* See vmtruncate() */ + unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1); + truncate_inode_pages(mapping, offset); + unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1); +} + void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr) { struct fuse_conn *fc = get_fuse_conn(inode); - if (S_ISREG(inode->i_mode) && i_size_read(inode) != attr->size) - invalidate_mapping_pages(inode->i_mapping, 0, -1); + loff_t oldsize; inode->i_ino = attr->ino; inode->i_mode = (inode->i_mode & S_IFMT) + (attr->mode & 07777); inode->i_nlink = attr->nlink; inode->i_uid = attr->uid; inode->i_gid = attr->gid; - spin_lock(&fc->lock); - i_size_write(inode, attr->size); - spin_unlock(&fc->lock); inode->i_blocks = attr->blocks; inode->i_atime.tv_sec = attr->atime; inode->i_atime.tv_nsec = attr->atimensec; @@ -130,6 +134,17 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr) inode->i_mtime.tv_nsec = attr->mtimensec; inode->i_ctime.tv_sec = attr->ctime; inode->i_ctime.tv_nsec = attr->ctimensec; + + spin_lock(&fc->lock); + oldsize = inode->i_size; + i_size_write(inode, attr->size); + spin_unlock(&fc->lock); + + if (S_ISREG(inode->i_mode) && oldsize != attr->size) { + if (attr->size < oldsize) + fuse_truncate(inode->i_mapping, attr->size); + invalidate_mapping_pages(inode->i_mapping, 0, -1); + } } static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr) -- cgit v1.2.3