diff options
Diffstat (limited to 'fs/fuse/dir.c')
-rw-r--r-- | fs/fuse/dir.c | 241 |
1 files changed, 180 insertions, 61 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index d1acab931330..3763757f9fe7 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 | */ |
66 | static void fuse_change_timeout(struct dentry *entry, struct fuse_entry_out *o) | 66 | static 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); | 73 | static u64 attr_timeout(struct fuse_attr_out *o) |
74 | { | ||
75 | return time_to_jiffies(o->attr_valid, o->attr_valid_nsec); | ||
76 | } | ||
77 | |||
78 | static 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 | /* |
@@ -108,13 +116,19 @@ static void fuse_lookup_init(struct fuse_req *req, struct inode *dir, | |||
108 | struct dentry *entry, | 116 | struct dentry *entry, |
109 | struct fuse_entry_out *outarg) | 117 | struct fuse_entry_out *outarg) |
110 | { | 118 | { |
119 | struct fuse_conn *fc = get_fuse_conn(dir); | ||
120 | |||
121 | memset(outarg, 0, sizeof(struct fuse_entry_out)); | ||
111 | req->in.h.opcode = FUSE_LOOKUP; | 122 | req->in.h.opcode = FUSE_LOOKUP; |
112 | req->in.h.nodeid = get_node_id(dir); | 123 | req->in.h.nodeid = get_node_id(dir); |
113 | req->in.numargs = 1; | 124 | req->in.numargs = 1; |
114 | req->in.args[0].size = entry->d_name.len + 1; | 125 | req->in.args[0].size = entry->d_name.len + 1; |
115 | req->in.args[0].value = entry->d_name.name; | 126 | req->in.args[0].value = entry->d_name.name; |
116 | req->out.numargs = 1; | 127 | req->out.numargs = 1; |
117 | req->out.args[0].size = sizeof(struct fuse_entry_out); | 128 | if (fc->minor < 9) |
129 | req->out.args[0].size = FUSE_COMPAT_ENTRY_OUT_SIZE; | ||
130 | else | ||
131 | req->out.args[0].size = sizeof(struct fuse_entry_out); | ||
118 | req->out.args[0].value = outarg; | 132 | req->out.args[0].value = outarg; |
119 | } | 133 | } |
120 | 134 | ||
@@ -140,6 +154,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) | |||
140 | struct fuse_req *req; | 154 | struct fuse_req *req; |
141 | struct fuse_req *forget_req; | 155 | struct fuse_req *forget_req; |
142 | struct dentry *parent; | 156 | struct dentry *parent; |
157 | u64 attr_version; | ||
143 | 158 | ||
144 | /* For negative dentries, always do a fresh lookup */ | 159 | /* For negative dentries, always do a fresh lookup */ |
145 | if (!inode) | 160 | if (!inode) |
@@ -156,6 +171,10 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) | |||
156 | return 0; | 171 | return 0; |
157 | } | 172 | } |
158 | 173 | ||
174 | spin_lock(&fc->lock); | ||
175 | attr_version = fc->attr_version; | ||
176 | spin_unlock(&fc->lock); | ||
177 | |||
159 | parent = dget_parent(entry); | 178 | parent = dget_parent(entry); |
160 | fuse_lookup_init(req, parent->d_inode, entry, &outarg); | 179 | fuse_lookup_init(req, parent->d_inode, entry, &outarg); |
161 | request_send(fc, req); | 180 | request_send(fc, req); |
@@ -180,8 +199,10 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) | |||
180 | if (err || (outarg.attr.mode ^ inode->i_mode) & S_IFMT) | 199 | if (err || (outarg.attr.mode ^ inode->i_mode) & S_IFMT) |
181 | return 0; | 200 | return 0; |
182 | 201 | ||
183 | fuse_change_attributes(inode, &outarg.attr); | 202 | fuse_change_attributes(inode, &outarg.attr, |
184 | fuse_change_timeout(entry, &outarg); | 203 | entry_attr_timeout(&outarg), |
204 | attr_version); | ||
205 | fuse_change_entry_timeout(entry, &outarg); | ||
185 | } | 206 | } |
186 | return 1; | 207 | return 1; |
187 | } | 208 | } |
@@ -228,6 +249,7 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | |||
228 | struct fuse_conn *fc = get_fuse_conn(dir); | 249 | struct fuse_conn *fc = get_fuse_conn(dir); |
229 | struct fuse_req *req; | 250 | struct fuse_req *req; |
230 | struct fuse_req *forget_req; | 251 | struct fuse_req *forget_req; |
252 | u64 attr_version; | ||
231 | 253 | ||
232 | if (entry->d_name.len > FUSE_NAME_MAX) | 254 | if (entry->d_name.len > FUSE_NAME_MAX) |
233 | return ERR_PTR(-ENAMETOOLONG); | 255 | return ERR_PTR(-ENAMETOOLONG); |
@@ -242,6 +264,10 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | |||
242 | return ERR_PTR(PTR_ERR(forget_req)); | 264 | return ERR_PTR(PTR_ERR(forget_req)); |
243 | } | 265 | } |
244 | 266 | ||
267 | spin_lock(&fc->lock); | ||
268 | attr_version = fc->attr_version; | ||
269 | spin_unlock(&fc->lock); | ||
270 | |||
245 | fuse_lookup_init(req, dir, entry, &outarg); | 271 | fuse_lookup_init(req, dir, entry, &outarg); |
246 | request_send(fc, req); | 272 | request_send(fc, req); |
247 | err = req->out.h.error; | 273 | err = req->out.h.error; |
@@ -253,7 +279,8 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | |||
253 | err = -EIO; | 279 | err = -EIO; |
254 | if (!err && outarg.nodeid) { | 280 | if (!err && outarg.nodeid) { |
255 | inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, | 281 | inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, |
256 | &outarg.attr); | 282 | &outarg.attr, entry_attr_timeout(&outarg), |
283 | attr_version); | ||
257 | if (!inode) { | 284 | if (!inode) { |
258 | fuse_send_forget(fc, forget_req, outarg.nodeid, 1); | 285 | fuse_send_forget(fc, forget_req, outarg.nodeid, 1); |
259 | return ERR_PTR(-ENOMEM); | 286 | return ERR_PTR(-ENOMEM); |
@@ -276,7 +303,7 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | |||
276 | 303 | ||
277 | entry->d_op = &fuse_dentry_operations; | 304 | entry->d_op = &fuse_dentry_operations; |
278 | if (!err) | 305 | if (!err) |
279 | fuse_change_timeout(entry, &outarg); | 306 | fuse_change_entry_timeout(entry, &outarg); |
280 | else | 307 | else |
281 | fuse_invalidate_entry_cache(entry); | 308 | fuse_invalidate_entry_cache(entry); |
282 | return NULL; | 309 | return NULL; |
@@ -335,6 +362,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, | |||
335 | 362 | ||
336 | flags &= ~O_NOCTTY; | 363 | flags &= ~O_NOCTTY; |
337 | memset(&inarg, 0, sizeof(inarg)); | 364 | memset(&inarg, 0, sizeof(inarg)); |
365 | memset(&outentry, 0, sizeof(outentry)); | ||
338 | inarg.flags = flags; | 366 | inarg.flags = flags; |
339 | inarg.mode = mode; | 367 | inarg.mode = mode; |
340 | req->in.h.opcode = FUSE_CREATE; | 368 | req->in.h.opcode = FUSE_CREATE; |
@@ -345,7 +373,10 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, | |||
345 | req->in.args[1].size = entry->d_name.len + 1; | 373 | req->in.args[1].size = entry->d_name.len + 1; |
346 | req->in.args[1].value = entry->d_name.name; | 374 | req->in.args[1].value = entry->d_name.name; |
347 | req->out.numargs = 2; | 375 | req->out.numargs = 2; |
348 | req->out.args[0].size = sizeof(outentry); | 376 | if (fc->minor < 9) |
377 | req->out.args[0].size = FUSE_COMPAT_ENTRY_OUT_SIZE; | ||
378 | else | ||
379 | req->out.args[0].size = sizeof(outentry); | ||
349 | req->out.args[0].value = &outentry; | 380 | req->out.args[0].value = &outentry; |
350 | req->out.args[1].size = sizeof(outopen); | 381 | req->out.args[1].size = sizeof(outopen); |
351 | req->out.args[1].value = &outopen; | 382 | req->out.args[1].value = &outopen; |
@@ -363,7 +394,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, | |||
363 | 394 | ||
364 | fuse_put_request(fc, req); | 395 | fuse_put_request(fc, req); |
365 | inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation, | 396 | inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation, |
366 | &outentry.attr); | 397 | &outentry.attr, entry_attr_timeout(&outentry), 0); |
367 | if (!inode) { | 398 | if (!inode) { |
368 | flags &= ~(O_CREAT | O_EXCL | O_TRUNC); | 399 | flags &= ~(O_CREAT | O_EXCL | O_TRUNC); |
369 | ff->fh = outopen.fh; | 400 | ff->fh = outopen.fh; |
@@ -373,7 +404,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, | |||
373 | } | 404 | } |
374 | fuse_put_request(fc, forget_req); | 405 | fuse_put_request(fc, forget_req); |
375 | d_instantiate(entry, inode); | 406 | d_instantiate(entry, inode); |
376 | fuse_change_timeout(entry, &outentry); | 407 | fuse_change_entry_timeout(entry, &outentry); |
377 | file = lookup_instantiate_filp(nd, entry, generic_file_open); | 408 | file = lookup_instantiate_filp(nd, entry, generic_file_open); |
378 | if (IS_ERR(file)) { | 409 | if (IS_ERR(file)) { |
379 | ff->fh = outopen.fh; | 410 | ff->fh = outopen.fh; |
@@ -410,9 +441,13 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req, | |||
410 | return PTR_ERR(forget_req); | 441 | return PTR_ERR(forget_req); |
411 | } | 442 | } |
412 | 443 | ||
444 | memset(&outarg, 0, sizeof(outarg)); | ||
413 | req->in.h.nodeid = get_node_id(dir); | 445 | req->in.h.nodeid = get_node_id(dir); |
414 | req->out.numargs = 1; | 446 | req->out.numargs = 1; |
415 | req->out.args[0].size = sizeof(outarg); | 447 | if (fc->minor < 9) |
448 | req->out.args[0].size = FUSE_COMPAT_ENTRY_OUT_SIZE; | ||
449 | else | ||
450 | req->out.args[0].size = sizeof(outarg); | ||
416 | req->out.args[0].value = &outarg; | 451 | req->out.args[0].value = &outarg; |
417 | request_send(fc, req); | 452 | request_send(fc, req); |
418 | err = req->out.h.error; | 453 | err = req->out.h.error; |
@@ -428,7 +463,7 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req, | |||
428 | goto out_put_forget_req; | 463 | goto out_put_forget_req; |
429 | 464 | ||
430 | inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, | 465 | inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, |
431 | &outarg.attr); | 466 | &outarg.attr, entry_attr_timeout(&outarg), 0); |
432 | if (!inode) { | 467 | if (!inode) { |
433 | fuse_send_forget(fc, forget_req, outarg.nodeid, 1); | 468 | fuse_send_forget(fc, forget_req, outarg.nodeid, 1); |
434 | return -ENOMEM; | 469 | return -ENOMEM; |
@@ -451,7 +486,7 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req, | |||
451 | } else | 486 | } else |
452 | d_instantiate(entry, inode); | 487 | d_instantiate(entry, inode); |
453 | 488 | ||
454 | fuse_change_timeout(entry, &outarg); | 489 | fuse_change_entry_timeout(entry, &outarg); |
455 | fuse_invalidate_attr(dir); | 490 | fuse_invalidate_attr(dir); |
456 | return 0; | 491 | return 0; |
457 | 492 | ||
@@ -663,52 +698,84 @@ static int fuse_link(struct dentry *entry, struct inode *newdir, | |||
663 | return err; | 698 | return err; |
664 | } | 699 | } |
665 | 700 | ||
666 | static int fuse_do_getattr(struct inode *inode) | 701 | static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr, |
702 | struct kstat *stat) | ||
703 | { | ||
704 | stat->dev = inode->i_sb->s_dev; | ||
705 | stat->ino = attr->ino; | ||
706 | stat->mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777); | ||
707 | stat->nlink = attr->nlink; | ||
708 | stat->uid = attr->uid; | ||
709 | stat->gid = attr->gid; | ||
710 | stat->rdev = inode->i_rdev; | ||
711 | stat->atime.tv_sec = attr->atime; | ||
712 | stat->atime.tv_nsec = attr->atimensec; | ||
713 | stat->mtime.tv_sec = attr->mtime; | ||
714 | stat->mtime.tv_nsec = attr->mtimensec; | ||
715 | stat->ctime.tv_sec = attr->ctime; | ||
716 | stat->ctime.tv_nsec = attr->ctimensec; | ||
717 | stat->size = attr->size; | ||
718 | stat->blocks = attr->blocks; | ||
719 | stat->blksize = (1 << inode->i_blkbits); | ||
720 | } | ||
721 | |||
722 | static int fuse_do_getattr(struct inode *inode, struct kstat *stat, | ||
723 | struct file *file) | ||
667 | { | 724 | { |
668 | int err; | 725 | int err; |
669 | struct fuse_attr_out arg; | 726 | struct fuse_getattr_in inarg; |
727 | struct fuse_attr_out outarg; | ||
670 | struct fuse_conn *fc = get_fuse_conn(inode); | 728 | struct fuse_conn *fc = get_fuse_conn(inode); |
671 | struct fuse_req *req = fuse_get_req(fc); | 729 | struct fuse_req *req; |
730 | u64 attr_version; | ||
731 | |||
732 | req = fuse_get_req(fc); | ||
672 | if (IS_ERR(req)) | 733 | if (IS_ERR(req)) |
673 | return PTR_ERR(req); | 734 | return PTR_ERR(req); |
674 | 735 | ||
736 | spin_lock(&fc->lock); | ||
737 | attr_version = fc->attr_version; | ||
738 | spin_unlock(&fc->lock); | ||
739 | |||
740 | memset(&inarg, 0, sizeof(inarg)); | ||
741 | memset(&outarg, 0, sizeof(outarg)); | ||
742 | /* Directories have separate file-handle space */ | ||
743 | if (file && S_ISREG(inode->i_mode)) { | ||
744 | struct fuse_file *ff = file->private_data; | ||
745 | |||
746 | inarg.getattr_flags |= FUSE_GETATTR_FH; | ||
747 | inarg.fh = ff->fh; | ||
748 | } | ||
675 | req->in.h.opcode = FUSE_GETATTR; | 749 | req->in.h.opcode = FUSE_GETATTR; |
676 | req->in.h.nodeid = get_node_id(inode); | 750 | req->in.h.nodeid = get_node_id(inode); |
751 | req->in.numargs = 1; | ||
752 | req->in.args[0].size = sizeof(inarg); | ||
753 | req->in.args[0].value = &inarg; | ||
677 | req->out.numargs = 1; | 754 | req->out.numargs = 1; |
678 | req->out.args[0].size = sizeof(arg); | 755 | if (fc->minor < 9) |
679 | req->out.args[0].value = &arg; | 756 | req->out.args[0].size = FUSE_COMPAT_ATTR_OUT_SIZE; |
757 | else | ||
758 | req->out.args[0].size = sizeof(outarg); | ||
759 | req->out.args[0].value = &outarg; | ||
680 | request_send(fc, req); | 760 | request_send(fc, req); |
681 | err = req->out.h.error; | 761 | err = req->out.h.error; |
682 | fuse_put_request(fc, req); | 762 | fuse_put_request(fc, req); |
683 | if (!err) { | 763 | if (!err) { |
684 | if ((inode->i_mode ^ arg.attr.mode) & S_IFMT) { | 764 | if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) { |
685 | make_bad_inode(inode); | 765 | make_bad_inode(inode); |
686 | err = -EIO; | 766 | err = -EIO; |
687 | } else { | 767 | } else { |
688 | struct fuse_inode *fi = get_fuse_inode(inode); | 768 | fuse_change_attributes(inode, &outarg.attr, |
689 | fuse_change_attributes(inode, &arg.attr); | 769 | attr_timeout(&outarg), |
690 | fi->i_time = time_to_jiffies(arg.attr_valid, | 770 | attr_version); |
691 | arg.attr_valid_nsec); | 771 | if (stat) |
772 | fuse_fillattr(inode, &outarg.attr, stat); | ||
692 | } | 773 | } |
693 | } | 774 | } |
694 | return err; | 775 | return err; |
695 | } | 776 | } |
696 | 777 | ||
697 | /* | 778 | /* |
698 | * Check if attributes are still valid, and if not send a GETATTR | ||
699 | * request to refresh them. | ||
700 | */ | ||
701 | static 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 | 779 | * Calling into a user-controlled filesystem gives the filesystem |
713 | * daemon ptrace-like capabilities over the requester process. This | 780 | * daemon ptrace-like capabilities over the requester process. This |
714 | * means, that the filesystem daemon is able to record the exact | 781 | * means, that the filesystem daemon is able to record the exact |
@@ -721,7 +788,7 @@ static int fuse_refresh_attributes(struct inode *inode) | |||
721 | * for which the owner of the mount has ptrace privilege. This | 788 | * for which the owner of the mount has ptrace privilege. This |
722 | * excludes processes started by other users, suid or sgid processes. | 789 | * excludes processes started by other users, suid or sgid processes. |
723 | */ | 790 | */ |
724 | static int fuse_allow_task(struct fuse_conn *fc, struct task_struct *task) | 791 | int fuse_allow_task(struct fuse_conn *fc, struct task_struct *task) |
725 | { | 792 | { |
726 | if (fc->flags & FUSE_ALLOW_OTHER) | 793 | if (fc->flags & FUSE_ALLOW_OTHER) |
727 | return 1; | 794 | return 1; |
@@ -795,11 +862,14 @@ static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) | |||
795 | */ | 862 | */ |
796 | if ((fc->flags & FUSE_DEFAULT_PERMISSIONS) || | 863 | if ((fc->flags & FUSE_DEFAULT_PERMISSIONS) || |
797 | ((mask & MAY_EXEC) && S_ISREG(inode->i_mode))) { | 864 | ((mask & MAY_EXEC) && S_ISREG(inode->i_mode))) { |
798 | err = fuse_refresh_attributes(inode); | 865 | struct fuse_inode *fi = get_fuse_inode(inode); |
799 | if (err) | 866 | if (fi->i_time < get_jiffies_64()) { |
800 | return err; | 867 | err = fuse_do_getattr(inode, NULL, NULL); |
868 | if (err) | ||
869 | return err; | ||
801 | 870 | ||
802 | refreshed = true; | 871 | refreshed = true; |
872 | } | ||
803 | } | 873 | } |
804 | 874 | ||
805 | if (fc->flags & FUSE_DEFAULT_PERMISSIONS) { | 875 | if (fc->flags & FUSE_DEFAULT_PERMISSIONS) { |
@@ -809,7 +879,7 @@ static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) | |||
809 | attributes. This is also needed, because the root | 879 | attributes. This is also needed, because the root |
810 | node will at first have no permissions */ | 880 | node will at first have no permissions */ |
811 | if (err == -EACCES && !refreshed) { | 881 | if (err == -EACCES && !refreshed) { |
812 | err = fuse_do_getattr(inode); | 882 | err = fuse_do_getattr(inode, NULL, NULL); |
813 | if (!err) | 883 | if (!err) |
814 | err = generic_permission(inode, mask, NULL); | 884 | err = generic_permission(inode, mask, NULL); |
815 | } | 885 | } |
@@ -825,7 +895,7 @@ static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) | |||
825 | if (refreshed) | 895 | if (refreshed) |
826 | return -EACCES; | 896 | return -EACCES; |
827 | 897 | ||
828 | err = fuse_do_getattr(inode); | 898 | err = fuse_do_getattr(inode, NULL, NULL); |
829 | if (!err && !(inode->i_mode & S_IXUGO)) | 899 | if (!err && !(inode->i_mode & S_IXUGO)) |
830 | return -EACCES; | 900 | return -EACCES; |
831 | } | 901 | } |
@@ -962,6 +1032,20 @@ static int fuse_dir_fsync(struct file *file, struct dentry *de, int datasync) | |||
962 | return file ? fuse_fsync_common(file, de, datasync, 1) : 0; | 1032 | return file ? fuse_fsync_common(file, de, datasync, 1) : 0; |
963 | } | 1033 | } |
964 | 1034 | ||
1035 | static bool update_mtime(unsigned ivalid) | ||
1036 | { | ||
1037 | /* Always update if mtime is explicitly set */ | ||
1038 | if (ivalid & ATTR_MTIME_SET) | ||
1039 | return true; | ||
1040 | |||
1041 | /* If it's an open(O_TRUNC) or an ftruncate(), don't update */ | ||
1042 | if ((ivalid & ATTR_SIZE) && (ivalid & (ATTR_OPEN | ATTR_FILE))) | ||
1043 | return false; | ||
1044 | |||
1045 | /* In all other cases update */ | ||
1046 | return true; | ||
1047 | } | ||
1048 | |||
965 | static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg) | 1049 | static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg) |
966 | { | 1050 | { |
967 | unsigned ivalid = iattr->ia_valid; | 1051 | unsigned ivalid = iattr->ia_valid; |
@@ -974,16 +1058,19 @@ static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg) | |||
974 | arg->valid |= FATTR_GID, arg->gid = iattr->ia_gid; | 1058 | arg->valid |= FATTR_GID, arg->gid = iattr->ia_gid; |
975 | if (ivalid & ATTR_SIZE) | 1059 | if (ivalid & ATTR_SIZE) |
976 | arg->valid |= FATTR_SIZE, arg->size = iattr->ia_size; | 1060 | arg->valid |= FATTR_SIZE, arg->size = iattr->ia_size; |
977 | /* You can only _set_ these together (they may change by themselves) */ | 1061 | if (ivalid & ATTR_ATIME) { |
978 | if ((ivalid & (ATTR_ATIME | ATTR_MTIME)) == (ATTR_ATIME | ATTR_MTIME)) { | 1062 | arg->valid |= FATTR_ATIME; |
979 | arg->valid |= FATTR_ATIME | FATTR_MTIME; | ||
980 | arg->atime = iattr->ia_atime.tv_sec; | 1063 | arg->atime = iattr->ia_atime.tv_sec; |
981 | arg->mtime = iattr->ia_mtime.tv_sec; | 1064 | arg->atimensec = iattr->ia_atime.tv_nsec; |
1065 | if (!(ivalid & ATTR_ATIME_SET)) | ||
1066 | arg->valid |= FATTR_ATIME_NOW; | ||
982 | } | 1067 | } |
983 | if (ivalid & ATTR_FILE) { | 1068 | if ((ivalid & ATTR_MTIME) && update_mtime(ivalid)) { |
984 | struct fuse_file *ff = iattr->ia_file->private_data; | 1069 | arg->valid |= FATTR_MTIME; |
985 | arg->valid |= FATTR_FH; | 1070 | arg->mtime = iattr->ia_mtime.tv_sec; |
986 | arg->fh = ff->fh; | 1071 | arg->mtimensec = iattr->ia_mtime.tv_nsec; |
1072 | if (!(ivalid & ATTR_MTIME_SET)) | ||
1073 | arg->valid |= FATTR_MTIME_NOW; | ||
987 | } | 1074 | } |
988 | } | 1075 | } |
989 | 1076 | ||
@@ -995,22 +1082,28 @@ static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg) | |||
995 | * vmtruncate() doesn't allow for this case, so do the rlimit checking | 1082 | * vmtruncate() doesn't allow for this case, so do the rlimit checking |
996 | * and the actual truncation by hand. | 1083 | * and the actual truncation by hand. |
997 | */ | 1084 | */ |
998 | static int fuse_setattr(struct dentry *entry, struct iattr *attr) | 1085 | static int fuse_do_setattr(struct dentry *entry, struct iattr *attr, |
1086 | struct file *file) | ||
999 | { | 1087 | { |
1000 | struct inode *inode = entry->d_inode; | 1088 | struct inode *inode = entry->d_inode; |
1001 | struct fuse_conn *fc = get_fuse_conn(inode); | 1089 | struct fuse_conn *fc = get_fuse_conn(inode); |
1002 | struct fuse_inode *fi = get_fuse_inode(inode); | ||
1003 | struct fuse_req *req; | 1090 | struct fuse_req *req; |
1004 | struct fuse_setattr_in inarg; | 1091 | struct fuse_setattr_in inarg; |
1005 | struct fuse_attr_out outarg; | 1092 | struct fuse_attr_out outarg; |
1006 | int err; | 1093 | int err; |
1007 | 1094 | ||
1095 | if (!fuse_allow_task(fc, current)) | ||
1096 | return -EACCES; | ||
1097 | |||
1008 | if (fc->flags & FUSE_DEFAULT_PERMISSIONS) { | 1098 | if (fc->flags & FUSE_DEFAULT_PERMISSIONS) { |
1009 | err = inode_change_ok(inode, attr); | 1099 | err = inode_change_ok(inode, attr); |
1010 | if (err) | 1100 | if (err) |
1011 | return err; | 1101 | return err; |
1012 | } | 1102 | } |
1013 | 1103 | ||
1104 | if ((attr->ia_valid & ATTR_OPEN) && fc->atomic_o_trunc) | ||
1105 | return 0; | ||
1106 | |||
1014 | if (attr->ia_valid & ATTR_SIZE) { | 1107 | if (attr->ia_valid & ATTR_SIZE) { |
1015 | unsigned long limit; | 1108 | unsigned long limit; |
1016 | if (IS_SWAPFILE(inode)) | 1109 | if (IS_SWAPFILE(inode)) |
@@ -1027,14 +1120,28 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr) | |||
1027 | return PTR_ERR(req); | 1120 | return PTR_ERR(req); |
1028 | 1121 | ||
1029 | memset(&inarg, 0, sizeof(inarg)); | 1122 | memset(&inarg, 0, sizeof(inarg)); |
1123 | memset(&outarg, 0, sizeof(outarg)); | ||
1030 | iattr_to_fattr(attr, &inarg); | 1124 | iattr_to_fattr(attr, &inarg); |
1125 | if (file) { | ||
1126 | struct fuse_file *ff = file->private_data; | ||
1127 | inarg.valid |= FATTR_FH; | ||
1128 | inarg.fh = ff->fh; | ||
1129 | } | ||
1130 | if (attr->ia_valid & ATTR_SIZE) { | ||
1131 | /* For mandatory locking in truncate */ | ||
1132 | inarg.valid |= FATTR_LOCKOWNER; | ||
1133 | inarg.lock_owner = fuse_lock_owner_id(fc, current->files); | ||
1134 | } | ||
1031 | req->in.h.opcode = FUSE_SETATTR; | 1135 | req->in.h.opcode = FUSE_SETATTR; |
1032 | req->in.h.nodeid = get_node_id(inode); | 1136 | req->in.h.nodeid = get_node_id(inode); |
1033 | req->in.numargs = 1; | 1137 | req->in.numargs = 1; |
1034 | req->in.args[0].size = sizeof(inarg); | 1138 | req->in.args[0].size = sizeof(inarg); |
1035 | req->in.args[0].value = &inarg; | 1139 | req->in.args[0].value = &inarg; |
1036 | req->out.numargs = 1; | 1140 | req->out.numargs = 1; |
1037 | req->out.args[0].size = sizeof(outarg); | 1141 | if (fc->minor < 9) |
1142 | req->out.args[0].size = FUSE_COMPAT_ATTR_OUT_SIZE; | ||
1143 | else | ||
1144 | req->out.args[0].size = sizeof(outarg); | ||
1038 | req->out.args[0].value = &outarg; | 1145 | req->out.args[0].value = &outarg; |
1039 | request_send(fc, req); | 1146 | request_send(fc, req); |
1040 | err = req->out.h.error; | 1147 | err = req->out.h.error; |
@@ -1050,11 +1157,18 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr) | |||
1050 | return -EIO; | 1157 | return -EIO; |
1051 | } | 1158 | } |
1052 | 1159 | ||
1053 | fuse_change_attributes(inode, &outarg.attr); | 1160 | fuse_change_attributes(inode, &outarg.attr, attr_timeout(&outarg), 0); |
1054 | fi->i_time = time_to_jiffies(outarg.attr_valid, outarg.attr_valid_nsec); | ||
1055 | return 0; | 1161 | return 0; |
1056 | } | 1162 | } |
1057 | 1163 | ||
1164 | static int fuse_setattr(struct dentry *entry, struct iattr *attr) | ||
1165 | { | ||
1166 | if (attr->ia_valid & ATTR_FILE) | ||
1167 | return fuse_do_setattr(entry, attr, attr->ia_file); | ||
1168 | else | ||
1169 | return fuse_do_setattr(entry, attr, NULL); | ||
1170 | } | ||
1171 | |||
1058 | static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry, | 1172 | static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry, |
1059 | struct kstat *stat) | 1173 | struct kstat *stat) |
1060 | { | 1174 | { |
@@ -1066,8 +1180,10 @@ static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry, | |||
1066 | if (!fuse_allow_task(fc, current)) | 1180 | if (!fuse_allow_task(fc, current)) |
1067 | return -EACCES; | 1181 | return -EACCES; |
1068 | 1182 | ||
1069 | err = fuse_refresh_attributes(inode); | 1183 | if (fi->i_time < get_jiffies_64()) |
1070 | if (!err) { | 1184 | err = fuse_do_getattr(inode, stat, NULL); |
1185 | else { | ||
1186 | err = 0; | ||
1071 | generic_fillattr(inode, stat); | 1187 | generic_fillattr(inode, stat); |
1072 | stat->mode = fi->orig_i_mode; | 1188 | stat->mode = fi->orig_i_mode; |
1073 | } | 1189 | } |
@@ -1172,6 +1288,9 @@ static ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size) | |||
1172 | struct fuse_getxattr_out outarg; | 1288 | struct fuse_getxattr_out outarg; |
1173 | ssize_t ret; | 1289 | ssize_t ret; |
1174 | 1290 | ||
1291 | if (!fuse_allow_task(fc, current)) | ||
1292 | return -EACCES; | ||
1293 | |||
1175 | if (fc->no_listxattr) | 1294 | if (fc->no_listxattr) |
1176 | return -EOPNOTSUPP; | 1295 | return -EOPNOTSUPP; |
1177 | 1296 | ||