diff options
author | Miklos Szeredi | 2007-10-17 08:31:01 +0200 |
---|---|---|
committer | Linus Torvalds | 2007-10-17 17:43:03 +0200 |
commit | e00d2c2d4aead747d0fbee99001b00612d1082b0 (patch) | |
tree | c57228fae55fdb0b91fa6c211431a4ced2435c53 /fs/fuse/inode.c | |
parent | fuse: add reference counting to fuse_file (diff) | |
download | kernel-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.c | 25 |
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) |