diff options
author | Miklos Szeredi <mszeredi@suse.cz> | 2007-10-18 06:06:58 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-18 17:37:30 -0400 |
commit | 1fb69e7817296da8a6824804bb206ca1e7f31425 (patch) | |
tree | 51f8ac8ab4f06207a406cb9e917a48d71616235c /fs/fuse/file.c | |
parent | e57ac68378a287d6336d187b26971f35f7ee7251 (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/file.c')
-rw-r--r-- | fs/fuse/file.c | 2 |
1 files changed, 2 insertions, 0 deletions
diff --git a/fs/fuse/file.c b/fs/fuse/file.c index c4b98c03a46e..4a28c3d37732 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c | |||
@@ -478,6 +478,7 @@ static int fuse_buffered_write(struct file *file, struct inode *inode, | |||
478 | int err; | 478 | int err; |
479 | size_t nres; | 479 | size_t nres; |
480 | struct fuse_conn *fc = get_fuse_conn(inode); | 480 | struct fuse_conn *fc = get_fuse_conn(inode); |
481 | struct fuse_inode *fi = get_fuse_inode(inode); | ||
481 | unsigned offset = pos & (PAGE_CACHE_SIZE - 1); | 482 | unsigned offset = pos & (PAGE_CACHE_SIZE - 1); |
482 | struct fuse_req *req; | 483 | struct fuse_req *req; |
483 | 484 | ||
@@ -499,6 +500,7 @@ static int fuse_buffered_write(struct file *file, struct inode *inode, | |||
499 | if (!err) { | 500 | if (!err) { |
500 | pos += nres; | 501 | pos += nres; |
501 | spin_lock(&fc->lock); | 502 | spin_lock(&fc->lock); |
503 | fi->attr_version = ++fc->attr_version; | ||
502 | if (pos > inode->i_size) | 504 | if (pos > inode->i_size) |
503 | i_size_write(inode, pos); | 505 | i_size_write(inode, pos); |
504 | spin_unlock(&fc->lock); | 506 | spin_unlock(&fc->lock); |