aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@suse.cz>2007-10-17 02:31:01 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-10-17 11:43:03 -0400
commite00d2c2d4aead747d0fbee99001b00612d1082b0 (patch)
treec57228fae55fdb0b91fa6c211431a4ced2435c53
parentc756e0a4d79202535774806f148026e40466a5eb (diff)
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>
-rw-r--r--fs/fuse/dir.c46
-rw-r--r--fs/fuse/inode.c25
2 files changed, 33 insertions, 38 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 35e5cabb3b8c..29fef75f2360 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -980,23 +980,6 @@ static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg)
980 } 980 }
981} 981}
982 982
983static void fuse_vmtruncate(struct inode *inode, loff_t offset)
984{
985 struct fuse_conn *fc = get_fuse_conn(inode);
986 int need_trunc;
987
988 spin_lock(&fc->lock);
989 need_trunc = inode->i_size > offset;
990 i_size_write(inode, offset);
991 spin_unlock(&fc->lock);
992
993 if (need_trunc) {
994 struct address_space *mapping = inode->i_mapping;
995 unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1);
996 truncate_inode_pages(mapping, offset);
997 }
998}
999
1000/* 983/*
1001 * Set attributes, and at the same time refresh them. 984 * Set attributes, and at the same time refresh them.
1002 * 985 *
@@ -1014,7 +997,6 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
1014 struct fuse_setattr_in inarg; 997 struct fuse_setattr_in inarg;
1015 struct fuse_attr_out outarg; 998 struct fuse_attr_out outarg;
1016 int err; 999 int err;
1017 int is_truncate = 0;
1018 1000
1019 if (fc->flags & FUSE_DEFAULT_PERMISSIONS) { 1001 if (fc->flags & FUSE_DEFAULT_PERMISSIONS) {
1020 err = inode_change_ok(inode, attr); 1002 err = inode_change_ok(inode, attr);
@@ -1024,7 +1006,6 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
1024 1006
1025 if (attr->ia_valid & ATTR_SIZE) { 1007 if (attr->ia_valid & ATTR_SIZE) {
1026 unsigned long limit; 1008 unsigned long limit;
1027 is_truncate = 1;
1028 if (IS_SWAPFILE(inode)) 1009 if (IS_SWAPFILE(inode))
1029 return -ETXTBSY; 1010 return -ETXTBSY;
1030 limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur; 1011 limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur;
@@ -1051,21 +1032,20 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
1051 request_send(fc, req); 1032 request_send(fc, req);
1052 err = req->out.h.error; 1033 err = req->out.h.error;
1053 fuse_put_request(fc, req); 1034 fuse_put_request(fc, req);
1054 if (!err) { 1035 if (err) {
1055 if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) { 1036 if (err == -EINTR)
1056 make_bad_inode(inode); 1037 fuse_invalidate_attr(inode);
1057 err = -EIO; 1038 return err;
1058 } else { 1039 }
1059 if (is_truncate)
1060 fuse_vmtruncate(inode, outarg.attr.size);
1061 fuse_change_attributes(inode, &outarg.attr);
1062 fi->i_time = time_to_jiffies(outarg.attr_valid,
1063 outarg.attr_valid_nsec);
1064 }
1065 } else if (err == -EINTR)
1066 fuse_invalidate_attr(inode);
1067 1040
1068 return err; 1041 if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
1042 make_bad_inode(inode);
1043 return -EIO;
1044 }
1045
1046 fuse_change_attributes(inode, &outarg.attr);
1047 fi->i_time = time_to_jiffies(outarg.attr_valid, outarg.attr_valid_nsec);
1048 return 0;
1069} 1049}
1070 1050
1071static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry, 1051static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
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)
109 return 0; 109 return 0;
110} 110}
111 111
112static void fuse_truncate(struct address_space *mapping, loff_t offset)
113{
114 /* See vmtruncate() */
115 unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1);
116 truncate_inode_pages(mapping, offset);
117 unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1);
118}
119
112void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr) 120void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr)
113{ 121{
114 struct fuse_conn *fc = get_fuse_conn(inode); 122 struct fuse_conn *fc = get_fuse_conn(inode);
115 if (S_ISREG(inode->i_mode) && i_size_read(inode) != attr->size) 123 loff_t oldsize;
116 invalidate_mapping_pages(inode->i_mapping, 0, -1);
117 124
118 inode->i_ino = attr->ino; 125 inode->i_ino = attr->ino;
119 inode->i_mode = (inode->i_mode & S_IFMT) + (attr->mode & 07777); 126 inode->i_mode = (inode->i_mode & S_IFMT) + (attr->mode & 07777);
120 inode->i_nlink = attr->nlink; 127 inode->i_nlink = attr->nlink;
121 inode->i_uid = attr->uid; 128 inode->i_uid = attr->uid;
122 inode->i_gid = attr->gid; 129 inode->i_gid = attr->gid;
123 spin_lock(&fc->lock);
124 i_size_write(inode, attr->size);
125 spin_unlock(&fc->lock);
126 inode->i_blocks = attr->blocks; 130 inode->i_blocks = attr->blocks;
127 inode->i_atime.tv_sec = attr->atime; 131 inode->i_atime.tv_sec = attr->atime;
128 inode->i_atime.tv_nsec = attr->atimensec; 132 inode->i_atime.tv_nsec = attr->atimensec;
@@ -130,6 +134,17 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr)
130 inode->i_mtime.tv_nsec = attr->mtimensec; 134 inode->i_mtime.tv_nsec = attr->mtimensec;
131 inode->i_ctime.tv_sec = attr->ctime; 135 inode->i_ctime.tv_sec = attr->ctime;
132 inode->i_ctime.tv_nsec = attr->ctimensec; 136 inode->i_ctime.tv_nsec = attr->ctimensec;
137
138 spin_lock(&fc->lock);
139 oldsize = inode->i_size;
140 i_size_write(inode, attr->size);
141 spin_unlock(&fc->lock);
142
143 if (S_ISREG(inode->i_mode) && oldsize != attr->size) {
144 if (attr->size < oldsize)
145 fuse_truncate(inode->i_mapping, attr->size);
146 invalidate_mapping_pages(inode->i_mapping, 0, -1);
147 }
133} 148}
134 149
135static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr) 150static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)