summaryrefslogtreecommitdiffstats
path: root/fs/fuse/inode.c
diff options
context:
space:
mode:
authorMiklos Szeredi2007-10-17 08:31:01 +0200
committerLinus Torvalds2007-10-17 17:43:03 +0200
commite00d2c2d4aead747d0fbee99001b00612d1082b0 (patch)
treec57228fae55fdb0b91fa6c211431a4ced2435c53 /fs/fuse/inode.c
parentfuse: add reference counting to fuse_file (diff)
downloadkernel-qcow2-linux-e00d2c2d4aead747d0fbee99001b00612d1082b0.tar.gz
kernel-qcow2-linux-e00d2c2d4aead747d0fbee99001b00612d1082b0.tar.xz
kernel-qcow2-linux-e00d2c2d4aead747d0fbee99001b00612d1082b0.zip
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 <mszeredi@suse.cz> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/fuse/inode.c')
-rw-r--r--fs/fuse/inode.c25
1 files changed, 20 insertions, 5 deletions
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 95c8a9738ca7..b584de33a6a7 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)