aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fuse/inode.c
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@suse.cz>2007-10-18 06:06:58 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-10-18 17:37:30 -0400
commit1fb69e7817296da8a6824804bb206ca1e7f31425 (patch)
tree51f8ac8ab4f06207a406cb9e917a48d71616235c /fs/fuse/inode.c
parente57ac68378a287d6336d187b26971f35f7ee7251 (diff)
fuse: fix race between getattr and write
Getattr and lookup operations can be running in parallel to attribute changing operations, such as write and setattr. This means, that if for example getattr was slower than a write, the cached size attribute could be set to a stale value. To prevent this race, introduce a per-filesystem attribute version counter. This counter is incremented whenever cached attributes are modified, and the incremented value stored in the inode. Before storing new attributes in the cache, getattr and lookup check, using the version number, whether the attributes have been modified during the request's lifetime. If so, the returned attributes are not cached, because they might be stale. Thanks to Jakub Bogusz for the bug report and test program. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> Cc: Jakub Bogusz <jakub.bogusz@gemius.pl> 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.c22
1 files changed, 17 insertions, 5 deletions
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 00bb5a255de..2167fc4fcab 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -117,12 +117,22 @@ static void fuse_truncate(struct address_space *mapping, loff_t offset)
117 unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1); 117 unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1);
118} 118}
119 119
120void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr) 120
121void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
122 u64 attr_valid, u64 attr_version)
121{ 123{
122 struct fuse_conn *fc = get_fuse_conn(inode); 124 struct fuse_conn *fc = get_fuse_conn(inode);
123 struct fuse_inode *fi = get_fuse_inode(inode); 125 struct fuse_inode *fi = get_fuse_inode(inode);
124 loff_t oldsize; 126 loff_t oldsize;
125 127
128 spin_lock(&fc->lock);
129 if (attr_version != 0 && fi->attr_version > attr_version) {
130 spin_unlock(&fc->lock);
131 return;
132 }
133 fi->attr_version = ++fc->attr_version;
134 fi->i_time = attr_valid;
135
126 inode->i_ino = attr->ino; 136 inode->i_ino = attr->ino;
127 inode->i_mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777); 137 inode->i_mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777);
128 inode->i_nlink = attr->nlink; 138 inode->i_nlink = attr->nlink;
@@ -145,7 +155,6 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr)
145 if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS)) 155 if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS))
146 inode->i_mode &= ~S_ISVTX; 156 inode->i_mode &= ~S_ISVTX;
147 157
148 spin_lock(&fc->lock);
149 oldsize = inode->i_size; 158 oldsize = inode->i_size;
150 i_size_write(inode, attr->size); 159 i_size_write(inode, attr->size);
151 spin_unlock(&fc->lock); 160 spin_unlock(&fc->lock);
@@ -194,7 +203,8 @@ static int fuse_inode_set(struct inode *inode, void *_nodeidp)
194} 203}
195 204
196struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid, 205struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
197 int generation, struct fuse_attr *attr) 206 int generation, struct fuse_attr *attr,
207 u64 attr_valid, u64 attr_version)
198{ 208{
199 struct inode *inode; 209 struct inode *inode;
200 struct fuse_inode *fi; 210 struct fuse_inode *fi;
@@ -222,7 +232,8 @@ struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
222 spin_lock(&fc->lock); 232 spin_lock(&fc->lock);
223 fi->nlookup ++; 233 fi->nlookup ++;
224 spin_unlock(&fc->lock); 234 spin_unlock(&fc->lock);
225 fuse_change_attributes(inode, attr); 235 fuse_change_attributes(inode, attr, attr_valid, attr_version);
236
226 return inode; 237 return inode;
227} 238}
228 239
@@ -457,6 +468,7 @@ static struct fuse_conn *new_conn(void)
457 } 468 }
458 fc->reqctr = 0; 469 fc->reqctr = 0;
459 fc->blocked = 1; 470 fc->blocked = 1;
471 fc->attr_version = 1;
460 get_random_bytes(&fc->scramble_key, sizeof(fc->scramble_key)); 472 get_random_bytes(&fc->scramble_key, sizeof(fc->scramble_key));
461 } 473 }
462out: 474out:
@@ -488,7 +500,7 @@ static struct inode *get_root_inode(struct super_block *sb, unsigned mode)
488 attr.mode = mode; 500 attr.mode = mode;
489 attr.ino = FUSE_ROOT_ID; 501 attr.ino = FUSE_ROOT_ID;
490 attr.nlink = 1; 502 attr.nlink = 1;
491 return fuse_iget(sb, 1, 0, &attr); 503 return fuse_iget(sb, 1, 0, &attr, 0, 0);
492} 504}
493 505
494static const struct super_operations fuse_super_operations = { 506static const struct super_operations fuse_super_operations = {