diff options
Diffstat (limited to 'fs/fuse/dir.c')
-rw-r--r-- | fs/fuse/dir.c | 99 |
1 files changed, 90 insertions, 9 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 4c127f2bc814..fead7f49e2ca 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c | |||
@@ -14,6 +14,15 @@ | |||
14 | #include <linux/sched.h> | 14 | #include <linux/sched.h> |
15 | #include <linux/namei.h> | 15 | #include <linux/namei.h> |
16 | 16 | ||
17 | /* | ||
18 | * FUSE caches dentries and attributes with separate timeout. The | ||
19 | * time in jiffies until the dentry/attributes are valid is stored in | ||
20 | * dentry->d_time and fuse_inode->i_time respectively. | ||
21 | */ | ||
22 | |||
23 | /* | ||
24 | * Calculate the time in jiffies until a dentry/attributes are valid | ||
25 | */ | ||
17 | static inline unsigned long time_to_jiffies(unsigned long sec, | 26 | static inline unsigned long time_to_jiffies(unsigned long sec, |
18 | unsigned long nsec) | 27 | unsigned long nsec) |
19 | { | 28 | { |
@@ -21,6 +30,10 @@ static inline unsigned long time_to_jiffies(unsigned long sec, | |||
21 | return jiffies + timespec_to_jiffies(&ts); | 30 | return jiffies + timespec_to_jiffies(&ts); |
22 | } | 31 | } |
23 | 32 | ||
33 | /* | ||
34 | * Set dentry and possibly attribute timeouts from the lookup/mk* | ||
35 | * replies | ||
36 | */ | ||
24 | static void fuse_change_timeout(struct dentry *entry, struct fuse_entry_out *o) | 37 | static void fuse_change_timeout(struct dentry *entry, struct fuse_entry_out *o) |
25 | { | 38 | { |
26 | entry->d_time = time_to_jiffies(o->entry_valid, o->entry_valid_nsec); | 39 | entry->d_time = time_to_jiffies(o->entry_valid, o->entry_valid_nsec); |
@@ -29,16 +42,32 @@ static void fuse_change_timeout(struct dentry *entry, struct fuse_entry_out *o) | |||
29 | time_to_jiffies(o->attr_valid, o->attr_valid_nsec); | 42 | time_to_jiffies(o->attr_valid, o->attr_valid_nsec); |
30 | } | 43 | } |
31 | 44 | ||
45 | /* | ||
46 | * Mark the attributes as stale, so that at the next call to | ||
47 | * ->getattr() they will be fetched from userspace | ||
48 | */ | ||
32 | void fuse_invalidate_attr(struct inode *inode) | 49 | void fuse_invalidate_attr(struct inode *inode) |
33 | { | 50 | { |
34 | get_fuse_inode(inode)->i_time = jiffies - 1; | 51 | get_fuse_inode(inode)->i_time = jiffies - 1; |
35 | } | 52 | } |
36 | 53 | ||
54 | /* | ||
55 | * Just mark the entry as stale, so that a next attempt to look it up | ||
56 | * will result in a new lookup call to userspace | ||
57 | * | ||
58 | * This is called when a dentry is about to become negative and the | ||
59 | * timeout is unknown (unlink, rmdir, rename and in some cases | ||
60 | * lookup) | ||
61 | */ | ||
37 | static void fuse_invalidate_entry_cache(struct dentry *entry) | 62 | static void fuse_invalidate_entry_cache(struct dentry *entry) |
38 | { | 63 | { |
39 | entry->d_time = jiffies - 1; | 64 | entry->d_time = jiffies - 1; |
40 | } | 65 | } |
41 | 66 | ||
67 | /* | ||
68 | * Same as fuse_invalidate_entry_cache(), but also try to remove the | ||
69 | * dentry from the hash | ||
70 | */ | ||
42 | static void fuse_invalidate_entry(struct dentry *entry) | 71 | static void fuse_invalidate_entry(struct dentry *entry) |
43 | { | 72 | { |
44 | d_invalidate(entry); | 73 | d_invalidate(entry); |
@@ -60,6 +89,15 @@ static void fuse_lookup_init(struct fuse_req *req, struct inode *dir, | |||
60 | req->out.args[0].value = outarg; | 89 | req->out.args[0].value = outarg; |
61 | } | 90 | } |
62 | 91 | ||
92 | /* | ||
93 | * Check whether the dentry is still valid | ||
94 | * | ||
95 | * If the entry validity timeout has expired and the dentry is | ||
96 | * positive, try to redo the lookup. If the lookup results in a | ||
97 | * different inode, then let the VFS invalidate the dentry and redo | ||
98 | * the lookup once more. If the lookup results in the same inode, | ||
99 | * then refresh the attributes, timeouts and mark the dentry valid. | ||
100 | */ | ||
63 | static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) | 101 | static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) |
64 | { | 102 | { |
65 | struct inode *inode = entry->d_inode; | 103 | struct inode *inode = entry->d_inode; |
@@ -72,6 +110,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) | |||
72 | struct fuse_conn *fc; | 110 | struct fuse_conn *fc; |
73 | struct fuse_req *req; | 111 | struct fuse_req *req; |
74 | 112 | ||
113 | /* Doesn't hurt to "reset" the validity timeout */ | ||
75 | fuse_invalidate_entry_cache(entry); | 114 | fuse_invalidate_entry_cache(entry); |
76 | if (!inode) | 115 | if (!inode) |
77 | return 0; | 116 | return 0; |
@@ -102,10 +141,13 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) | |||
102 | return 1; | 141 | return 1; |
103 | } | 142 | } |
104 | 143 | ||
144 | /* | ||
145 | * Check if there's already a hashed alias of this directory inode. | ||
146 | * If yes, then lookup and mkdir must not create a new alias. | ||
147 | */ | ||
105 | static int dir_alias(struct inode *inode) | 148 | static int dir_alias(struct inode *inode) |
106 | { | 149 | { |
107 | if (S_ISDIR(inode->i_mode)) { | 150 | if (S_ISDIR(inode->i_mode)) { |
108 | /* Don't allow creating an alias to a directory */ | ||
109 | struct dentry *alias = d_find_alias(inode); | 151 | struct dentry *alias = d_find_alias(inode); |
110 | if (alias) { | 152 | if (alias) { |
111 | dput(alias); | 153 | dput(alias); |
@@ -170,6 +212,12 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | |||
170 | return NULL; | 212 | return NULL; |
171 | } | 213 | } |
172 | 214 | ||
215 | /* | ||
216 | * Atomic create+open operation | ||
217 | * | ||
218 | * If the filesystem doesn't support this, then fall back to separate | ||
219 | * 'mknod' + 'open' requests. | ||
220 | */ | ||
173 | static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, | 221 | static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, |
174 | struct nameidata *nd) | 222 | struct nameidata *nd) |
175 | { | 223 | { |
@@ -236,6 +284,9 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, | |||
236 | if (!inode) { | 284 | if (!inode) { |
237 | flags &= ~(O_CREAT | O_EXCL | O_TRUNC); | 285 | flags &= ~(O_CREAT | O_EXCL | O_TRUNC); |
238 | ff->fh = outopen.fh; | 286 | ff->fh = outopen.fh; |
287 | /* Special release, with inode = NULL, this will | ||
288 | trigger a 'forget' request when the release is | ||
289 | complete */ | ||
239 | fuse_send_release(fc, ff, outentry.nodeid, NULL, flags, 0); | 290 | fuse_send_release(fc, ff, outentry.nodeid, NULL, flags, 0); |
240 | goto out_put_request; | 291 | goto out_put_request; |
241 | } | 292 | } |
@@ -259,6 +310,9 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, | |||
259 | return err; | 310 | return err; |
260 | } | 311 | } |
261 | 312 | ||
313 | /* | ||
314 | * Code shared between mknod, mkdir, symlink and link | ||
315 | */ | ||
262 | static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req, | 316 | static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req, |
263 | struct inode *dir, struct dentry *entry, | 317 | struct inode *dir, struct dentry *entry, |
264 | int mode) | 318 | int mode) |
@@ -576,6 +630,15 @@ static int fuse_allow_task(struct fuse_conn *fc, struct task_struct *task) | |||
576 | return 0; | 630 | return 0; |
577 | } | 631 | } |
578 | 632 | ||
633 | /* | ||
634 | * Check whether the inode attributes are still valid | ||
635 | * | ||
636 | * If the attribute validity timeout has expired, then fetch the fresh | ||
637 | * attributes with a 'getattr' request | ||
638 | * | ||
639 | * I'm not sure why cached attributes are never returned for the root | ||
640 | * inode, this is probably being too cautious. | ||
641 | */ | ||
579 | static int fuse_revalidate(struct dentry *entry) | 642 | static int fuse_revalidate(struct dentry *entry) |
580 | { | 643 | { |
581 | struct inode *inode = entry->d_inode; | 644 | struct inode *inode = entry->d_inode; |
@@ -623,6 +686,19 @@ static int fuse_access(struct inode *inode, int mask) | |||
623 | return err; | 686 | return err; |
624 | } | 687 | } |
625 | 688 | ||
689 | /* | ||
690 | * Check permission. The two basic access models of FUSE are: | ||
691 | * | ||
692 | * 1) Local access checking ('default_permissions' mount option) based | ||
693 | * on file mode. This is the plain old disk filesystem permission | ||
694 | * modell. | ||
695 | * | ||
696 | * 2) "Remote" access checking, where server is responsible for | ||
697 | * checking permission in each inode operation. An exception to this | ||
698 | * is if ->permission() was invoked from sys_access() in which case an | ||
699 | * access request is sent. Execute permission is still checked | ||
700 | * locally based on file mode. | ||
701 | */ | ||
626 | static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) | 702 | static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) |
627 | { | 703 | { |
628 | struct fuse_conn *fc = get_fuse_conn(inode); | 704 | struct fuse_conn *fc = get_fuse_conn(inode); |
@@ -641,14 +717,10 @@ static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) | |||
641 | err = generic_permission(inode, mask, NULL); | 717 | err = generic_permission(inode, mask, NULL); |
642 | } | 718 | } |
643 | 719 | ||
644 | /* FIXME: Need some mechanism to revoke permissions: | 720 | /* Note: the opposite of the above test does not |
645 | currently if the filesystem suddenly changes the | 721 | exist. So if permissions are revoked this won't be |
646 | file mode, we will not be informed about it, and | 722 | noticed immediately, only after the attribute |
647 | continue to allow access to the file/directory. | 723 | timeout has expired */ |
648 | |||
649 | This is actually not so grave, since the user can | ||
650 | simply keep access to the file/directory anyway by | ||
651 | keeping it open... */ | ||
652 | 724 | ||
653 | return err; | 725 | return err; |
654 | } else { | 726 | } else { |
@@ -816,6 +888,15 @@ static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg) | |||
816 | } | 888 | } |
817 | } | 889 | } |
818 | 890 | ||
891 | /* | ||
892 | * Set attributes, and at the same time refresh them. | ||
893 | * | ||
894 | * Truncation is slightly complicated, because the 'truncate' request | ||
895 | * may fail, in which case we don't want to touch the mapping. | ||
896 | * vmtruncate() doesn't allow for this case. So do the rlimit | ||
897 | * checking by hand and call vmtruncate() only after the file has | ||
898 | * actually been truncated. | ||
899 | */ | ||
819 | static int fuse_setattr(struct dentry *entry, struct iattr *attr) | 900 | static int fuse_setattr(struct dentry *entry, struct iattr *attr) |
820 | { | 901 | { |
821 | struct inode *inode = entry->d_inode; | 902 | struct inode *inode = entry->d_inode; |