aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fuse/dir.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/dir.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/dir.c')
-rw-r--r--fs/fuse/dir.c125
1 files changed, 82 insertions, 43 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index b18e06daeffb..a058b664859c 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -63,13 +63,21 @@ static u64 time_to_jiffies(unsigned long sec, unsigned long nsec)
63 * Set dentry and possibly attribute timeouts from the lookup/mk* 63 * Set dentry and possibly attribute timeouts from the lookup/mk*
64 * replies 64 * replies
65 */ 65 */
66static void fuse_change_timeout(struct dentry *entry, struct fuse_entry_out *o) 66static void fuse_change_entry_timeout(struct dentry *entry,
67 struct fuse_entry_out *o)
67{ 68{
68 fuse_dentry_settime(entry, 69 fuse_dentry_settime(entry,
69 time_to_jiffies(o->entry_valid, o->entry_valid_nsec)); 70 time_to_jiffies(o->entry_valid, o->entry_valid_nsec));
70 if (entry->d_inode) 71}
71 get_fuse_inode(entry->d_inode)->i_time = 72
72 time_to_jiffies(o->attr_valid, o->attr_valid_nsec); 73static u64 attr_timeout(struct fuse_attr_out *o)
74{
75 return time_to_jiffies(o->attr_valid, o->attr_valid_nsec);
76}
77
78static u64 entry_attr_timeout(struct fuse_entry_out *o)
79{
80 return time_to_jiffies(o->attr_valid, o->attr_valid_nsec);
73} 81}
74 82
75/* 83/*
@@ -140,6 +148,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
140 struct fuse_req *req; 148 struct fuse_req *req;
141 struct fuse_req *forget_req; 149 struct fuse_req *forget_req;
142 struct dentry *parent; 150 struct dentry *parent;
151 u64 attr_version;
143 152
144 /* For negative dentries, always do a fresh lookup */ 153 /* For negative dentries, always do a fresh lookup */
145 if (!inode) 154 if (!inode)
@@ -156,6 +165,10 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
156 return 0; 165 return 0;
157 } 166 }
158 167
168 spin_lock(&fc->lock);
169 attr_version = fc->attr_version;
170 spin_unlock(&fc->lock);
171
159 parent = dget_parent(entry); 172 parent = dget_parent(entry);
160 fuse_lookup_init(req, parent->d_inode, entry, &outarg); 173 fuse_lookup_init(req, parent->d_inode, entry, &outarg);
161 request_send(fc, req); 174 request_send(fc, req);
@@ -180,8 +193,10 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
180 if (err || (outarg.attr.mode ^ inode->i_mode) & S_IFMT) 193 if (err || (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
181 return 0; 194 return 0;
182 195
183 fuse_change_attributes(inode, &outarg.attr); 196 fuse_change_attributes(inode, &outarg.attr,
184 fuse_change_timeout(entry, &outarg); 197 entry_attr_timeout(&outarg),
198 attr_version);
199 fuse_change_entry_timeout(entry, &outarg);
185 } 200 }
186 return 1; 201 return 1;
187} 202}
@@ -228,6 +243,7 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
228 struct fuse_conn *fc = get_fuse_conn(dir); 243 struct fuse_conn *fc = get_fuse_conn(dir);
229 struct fuse_req *req; 244 struct fuse_req *req;
230 struct fuse_req *forget_req; 245 struct fuse_req *forget_req;
246 u64 attr_version;
231 247
232 if (entry->d_name.len > FUSE_NAME_MAX) 248 if (entry->d_name.len > FUSE_NAME_MAX)
233 return ERR_PTR(-ENAMETOOLONG); 249 return ERR_PTR(-ENAMETOOLONG);
@@ -242,6 +258,10 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
242 return ERR_PTR(PTR_ERR(forget_req)); 258 return ERR_PTR(PTR_ERR(forget_req));
243 } 259 }
244 260
261 spin_lock(&fc->lock);
262 attr_version = fc->attr_version;
263 spin_unlock(&fc->lock);
264
245 fuse_lookup_init(req, dir, entry, &outarg); 265 fuse_lookup_init(req, dir, entry, &outarg);
246 request_send(fc, req); 266 request_send(fc, req);
247 err = req->out.h.error; 267 err = req->out.h.error;
@@ -253,7 +273,8 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
253 err = -EIO; 273 err = -EIO;
254 if (!err && outarg.nodeid) { 274 if (!err && outarg.nodeid) {
255 inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, 275 inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
256 &outarg.attr); 276 &outarg.attr, entry_attr_timeout(&outarg),
277 attr_version);
257 if (!inode) { 278 if (!inode) {
258 fuse_send_forget(fc, forget_req, outarg.nodeid, 1); 279 fuse_send_forget(fc, forget_req, outarg.nodeid, 1);
259 return ERR_PTR(-ENOMEM); 280 return ERR_PTR(-ENOMEM);
@@ -276,7 +297,7 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
276 297
277 entry->d_op = &fuse_dentry_operations; 298 entry->d_op = &fuse_dentry_operations;
278 if (!err) 299 if (!err)
279 fuse_change_timeout(entry, &outarg); 300 fuse_change_entry_timeout(entry, &outarg);
280 else 301 else
281 fuse_invalidate_entry_cache(entry); 302 fuse_invalidate_entry_cache(entry);
282 return NULL; 303 return NULL;
@@ -363,7 +384,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode,
363 384
364 fuse_put_request(fc, req); 385 fuse_put_request(fc, req);
365 inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation, 386 inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation,
366 &outentry.attr); 387 &outentry.attr, entry_attr_timeout(&outentry), 0);
367 if (!inode) { 388 if (!inode) {
368 flags &= ~(O_CREAT | O_EXCL | O_TRUNC); 389 flags &= ~(O_CREAT | O_EXCL | O_TRUNC);
369 ff->fh = outopen.fh; 390 ff->fh = outopen.fh;
@@ -373,7 +394,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode,
373 } 394 }
374 fuse_put_request(fc, forget_req); 395 fuse_put_request(fc, forget_req);
375 d_instantiate(entry, inode); 396 d_instantiate(entry, inode);
376 fuse_change_timeout(entry, &outentry); 397 fuse_change_entry_timeout(entry, &outentry);
377 file = lookup_instantiate_filp(nd, entry, generic_file_open); 398 file = lookup_instantiate_filp(nd, entry, generic_file_open);
378 if (IS_ERR(file)) { 399 if (IS_ERR(file)) {
379 ff->fh = outopen.fh; 400 ff->fh = outopen.fh;
@@ -428,7 +449,7 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req,
428 goto out_put_forget_req; 449 goto out_put_forget_req;
429 450
430 inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, 451 inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
431 &outarg.attr); 452 &outarg.attr, entry_attr_timeout(&outarg), 0);
432 if (!inode) { 453 if (!inode) {
433 fuse_send_forget(fc, forget_req, outarg.nodeid, 1); 454 fuse_send_forget(fc, forget_req, outarg.nodeid, 1);
434 return -ENOMEM; 455 return -ENOMEM;
@@ -451,7 +472,7 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req,
451 } else 472 } else
452 d_instantiate(entry, inode); 473 d_instantiate(entry, inode);
453 474
454 fuse_change_timeout(entry, &outarg); 475 fuse_change_entry_timeout(entry, &outarg);
455 fuse_invalidate_attr(dir); 476 fuse_invalidate_attr(dir);
456 return 0; 477 return 0;
457 478
@@ -663,15 +684,43 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
663 return err; 684 return err;
664} 685}
665 686
666static int fuse_do_getattr(struct inode *inode) 687static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr,
688 struct kstat *stat)
689{
690 stat->dev = inode->i_sb->s_dev;
691 stat->ino = attr->ino;
692 stat->mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777);
693 stat->nlink = attr->nlink;
694 stat->uid = attr->uid;
695 stat->gid = attr->gid;
696 stat->rdev = inode->i_rdev;
697 stat->atime.tv_sec = attr->atime;
698 stat->atime.tv_nsec = attr->atimensec;
699 stat->mtime.tv_sec = attr->mtime;
700 stat->mtime.tv_nsec = attr->mtimensec;
701 stat->ctime.tv_sec = attr->ctime;
702 stat->ctime.tv_nsec = attr->ctimensec;
703 stat->size = attr->size;
704 stat->blocks = attr->blocks;
705 stat->blksize = (1 << inode->i_blkbits);
706}
707
708static int fuse_do_getattr(struct inode *inode, struct kstat *stat)
667{ 709{
668 int err; 710 int err;
669 struct fuse_attr_out arg; 711 struct fuse_attr_out arg;
670 struct fuse_conn *fc = get_fuse_conn(inode); 712 struct fuse_conn *fc = get_fuse_conn(inode);
671 struct fuse_req *req = fuse_get_req(fc); 713 struct fuse_req *req;
714 u64 attr_version;
715
716 req = fuse_get_req(fc);
672 if (IS_ERR(req)) 717 if (IS_ERR(req))
673 return PTR_ERR(req); 718 return PTR_ERR(req);
674 719
720 spin_lock(&fc->lock);
721 attr_version = fc->attr_version;
722 spin_unlock(&fc->lock);
723
675 req->in.h.opcode = FUSE_GETATTR; 724 req->in.h.opcode = FUSE_GETATTR;
676 req->in.h.nodeid = get_node_id(inode); 725 req->in.h.nodeid = get_node_id(inode);
677 req->out.numargs = 1; 726 req->out.numargs = 1;
@@ -685,30 +734,17 @@ static int fuse_do_getattr(struct inode *inode)
685 make_bad_inode(inode); 734 make_bad_inode(inode);
686 err = -EIO; 735 err = -EIO;
687 } else { 736 } else {
688 struct fuse_inode *fi = get_fuse_inode(inode); 737 fuse_change_attributes(inode, &arg.attr,
689 fuse_change_attributes(inode, &arg.attr); 738 attr_timeout(&arg),
690 fi->i_time = time_to_jiffies(arg.attr_valid, 739 attr_version);
691 arg.attr_valid_nsec); 740 if (stat)
741 fuse_fillattr(inode, &arg.attr, stat);
692 } 742 }
693 } 743 }
694 return err; 744 return err;
695} 745}
696 746
697/* 747/*
698 * Check if attributes are still valid, and if not send a GETATTR
699 * request to refresh them.
700 */
701static int fuse_refresh_attributes(struct inode *inode)
702{
703 struct fuse_inode *fi = get_fuse_inode(inode);
704
705 if (fi->i_time < get_jiffies_64())
706 return fuse_do_getattr(inode);
707 else
708 return 0;
709}
710
711/*
712 * Calling into a user-controlled filesystem gives the filesystem 748 * Calling into a user-controlled filesystem gives the filesystem
713 * daemon ptrace-like capabilities over the requester process. This 749 * daemon ptrace-like capabilities over the requester process. This
714 * means, that the filesystem daemon is able to record the exact 750 * means, that the filesystem daemon is able to record the exact
@@ -795,11 +831,14 @@ static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd)
795 */ 831 */
796 if ((fc->flags & FUSE_DEFAULT_PERMISSIONS) || 832 if ((fc->flags & FUSE_DEFAULT_PERMISSIONS) ||
797 ((mask & MAY_EXEC) && S_ISREG(inode->i_mode))) { 833 ((mask & MAY_EXEC) && S_ISREG(inode->i_mode))) {
798 err = fuse_refresh_attributes(inode); 834 struct fuse_inode *fi = get_fuse_inode(inode);
799 if (err) 835 if (fi->i_time < get_jiffies_64()) {
800 return err; 836 err = fuse_do_getattr(inode, NULL);
837 if (err)
838 return err;
801 839
802 refreshed = true; 840 refreshed = true;
841 }
803 } 842 }
804 843
805 if (fc->flags & FUSE_DEFAULT_PERMISSIONS) { 844 if (fc->flags & FUSE_DEFAULT_PERMISSIONS) {
@@ -809,7 +848,7 @@ static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd)
809 attributes. This is also needed, because the root 848 attributes. This is also needed, because the root
810 node will at first have no permissions */ 849 node will at first have no permissions */
811 if (err == -EACCES && !refreshed) { 850 if (err == -EACCES && !refreshed) {
812 err = fuse_do_getattr(inode); 851 err = fuse_do_getattr(inode, NULL);
813 if (!err) 852 if (!err)
814 err = generic_permission(inode, mask, NULL); 853 err = generic_permission(inode, mask, NULL);
815 } 854 }
@@ -825,7 +864,7 @@ static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd)
825 if (refreshed) 864 if (refreshed)
826 return -EACCES; 865 return -EACCES;
827 866
828 err = fuse_do_getattr(inode); 867 err = fuse_do_getattr(inode, NULL);
829 if (!err && !(inode->i_mode & S_IXUGO)) 868 if (!err && !(inode->i_mode & S_IXUGO))
830 return -EACCES; 869 return -EACCES;
831 } 870 }
@@ -999,7 +1038,6 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
999{ 1038{
1000 struct inode *inode = entry->d_inode; 1039 struct inode *inode = entry->d_inode;
1001 struct fuse_conn *fc = get_fuse_conn(inode); 1040 struct fuse_conn *fc = get_fuse_conn(inode);
1002 struct fuse_inode *fi = get_fuse_inode(inode);
1003 struct fuse_req *req; 1041 struct fuse_req *req;
1004 struct fuse_setattr_in inarg; 1042 struct fuse_setattr_in inarg;
1005 struct fuse_attr_out outarg; 1043 struct fuse_attr_out outarg;
@@ -1053,8 +1091,7 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
1053 return -EIO; 1091 return -EIO;
1054 } 1092 }
1055 1093
1056 fuse_change_attributes(inode, &outarg.attr); 1094 fuse_change_attributes(inode, &outarg.attr, attr_timeout(&outarg), 0);
1057 fi->i_time = time_to_jiffies(outarg.attr_valid, outarg.attr_valid_nsec);
1058 return 0; 1095 return 0;
1059} 1096}
1060 1097
@@ -1069,8 +1106,10 @@ static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
1069 if (!fuse_allow_task(fc, current)) 1106 if (!fuse_allow_task(fc, current))
1070 return -EACCES; 1107 return -EACCES;
1071 1108
1072 err = fuse_refresh_attributes(inode); 1109 if (fi->i_time < get_jiffies_64())
1073 if (!err) { 1110 err = fuse_do_getattr(inode, stat);
1111 else {
1112 err = 0;
1074 generic_fillattr(inode, stat); 1113 generic_fillattr(inode, stat);
1075 stat->mode = fi->orig_i_mode; 1114 stat->mode = fi->orig_i_mode;
1076 } 1115 }