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/fuse_i.h | |
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/fuse_i.h')
-rw-r--r-- | fs/fuse/fuse_i.h | 12 |
1 files changed, 10 insertions, 2 deletions
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index e7464b8ebbfb..ffbcadaa7d67 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h | |||
@@ -67,6 +67,9 @@ struct fuse_inode { | |||
67 | /** The sticky bit in inode->i_mode may have been removed, so | 67 | /** The sticky bit in inode->i_mode may have been removed, so |
68 | preserve the original mode */ | 68 | preserve the original mode */ |
69 | mode_t orig_i_mode; | 69 | mode_t orig_i_mode; |
70 | |||
71 | /** Version of last attribute change */ | ||
72 | u64 attr_version; | ||
70 | }; | 73 | }; |
71 | 74 | ||
72 | /** FUSE specific file data */ | 75 | /** FUSE specific file data */ |
@@ -387,6 +390,9 @@ struct fuse_conn { | |||
387 | 390 | ||
388 | /** Reserved request for the DESTROY message */ | 391 | /** Reserved request for the DESTROY message */ |
389 | struct fuse_req *destroy_req; | 392 | struct fuse_req *destroy_req; |
393 | |||
394 | /** Version counter for attribute changes */ | ||
395 | u64 attr_version; | ||
390 | }; | 396 | }; |
391 | 397 | ||
392 | static inline struct fuse_conn *get_fuse_conn_super(struct super_block *sb) | 398 | static inline struct fuse_conn *get_fuse_conn_super(struct super_block *sb) |
@@ -416,7 +422,8 @@ extern const struct file_operations fuse_dev_operations; | |||
416 | * Get a filled in inode | 422 | * Get a filled in inode |
417 | */ | 423 | */ |
418 | struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid, | 424 | struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid, |
419 | int generation, struct fuse_attr *attr); | 425 | int generation, struct fuse_attr *attr, |
426 | u64 attr_valid, u64 attr_version); | ||
420 | 427 | ||
421 | /** | 428 | /** |
422 | * Send FORGET command | 429 | * Send FORGET command |
@@ -477,7 +484,8 @@ void fuse_init_symlink(struct inode *inode); | |||
477 | /** | 484 | /** |
478 | * Change attributes of an inode | 485 | * Change attributes of an inode |
479 | */ | 486 | */ |
480 | void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr); | 487 | void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, |
488 | u64 attr_valid, u64 attr_version); | ||
481 | 489 | ||
482 | /** | 490 | /** |
483 | * Initialize the client device | 491 | * Initialize the client device |