diff options
Diffstat (limited to 'fs/fuse/dir.c')
-rw-r--r-- | fs/fuse/dir.c | 315 |
1 files changed, 201 insertions, 114 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index c045cc70c749..21fd59c7bc24 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c | |||
@@ -13,15 +13,66 @@ | |||
13 | #include <linux/gfp.h> | 13 | #include <linux/gfp.h> |
14 | #include <linux/sched.h> | 14 | #include <linux/sched.h> |
15 | #include <linux/namei.h> | 15 | #include <linux/namei.h> |
16 | #include <linux/mount.h> | ||
17 | 16 | ||
18 | static inline unsigned long time_to_jiffies(unsigned long sec, | 17 | /* |
19 | unsigned long nsec) | 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 | */ | ||
26 | static unsigned long time_to_jiffies(unsigned long sec, unsigned long nsec) | ||
20 | { | 27 | { |
21 | struct timespec ts = {sec, nsec}; | 28 | struct timespec ts = {sec, nsec}; |
22 | return jiffies + timespec_to_jiffies(&ts); | 29 | return jiffies + timespec_to_jiffies(&ts); |
23 | } | 30 | } |
24 | 31 | ||
32 | /* | ||
33 | * Set dentry and possibly attribute timeouts from the lookup/mk* | ||
34 | * replies | ||
35 | */ | ||
36 | static void fuse_change_timeout(struct dentry *entry, struct fuse_entry_out *o) | ||
37 | { | ||
38 | entry->d_time = time_to_jiffies(o->entry_valid, o->entry_valid_nsec); | ||
39 | if (entry->d_inode) | ||
40 | get_fuse_inode(entry->d_inode)->i_time = | ||
41 | time_to_jiffies(o->attr_valid, o->attr_valid_nsec); | ||
42 | } | ||
43 | |||
44 | /* | ||
45 | * Mark the attributes as stale, so that at the next call to | ||
46 | * ->getattr() they will be fetched from userspace | ||
47 | */ | ||
48 | void fuse_invalidate_attr(struct inode *inode) | ||
49 | { | ||
50 | get_fuse_inode(inode)->i_time = jiffies - 1; | ||
51 | } | ||
52 | |||
53 | /* | ||
54 | * Just mark the entry as stale, so that a next attempt to look it up | ||
55 | * will result in a new lookup call to userspace | ||
56 | * | ||
57 | * This is called when a dentry is about to become negative and the | ||
58 | * timeout is unknown (unlink, rmdir, rename and in some cases | ||
59 | * lookup) | ||
60 | */ | ||
61 | static void fuse_invalidate_entry_cache(struct dentry *entry) | ||
62 | { | ||
63 | entry->d_time = jiffies - 1; | ||
64 | } | ||
65 | |||
66 | /* | ||
67 | * Same as fuse_invalidate_entry_cache(), but also try to remove the | ||
68 | * dentry from the hash | ||
69 | */ | ||
70 | static void fuse_invalidate_entry(struct dentry *entry) | ||
71 | { | ||
72 | d_invalidate(entry); | ||
73 | fuse_invalidate_entry_cache(entry); | ||
74 | } | ||
75 | |||
25 | static void fuse_lookup_init(struct fuse_req *req, struct inode *dir, | 76 | static void fuse_lookup_init(struct fuse_req *req, struct inode *dir, |
26 | struct dentry *entry, | 77 | struct dentry *entry, |
27 | struct fuse_entry_out *outarg) | 78 | struct fuse_entry_out *outarg) |
@@ -37,17 +88,34 @@ static void fuse_lookup_init(struct fuse_req *req, struct inode *dir, | |||
37 | req->out.args[0].value = outarg; | 88 | req->out.args[0].value = outarg; |
38 | } | 89 | } |
39 | 90 | ||
91 | /* | ||
92 | * Check whether the dentry is still valid | ||
93 | * | ||
94 | * If the entry validity timeout has expired and the dentry is | ||
95 | * positive, try to redo the lookup. If the lookup results in a | ||
96 | * different inode, then let the VFS invalidate the dentry and redo | ||
97 | * the lookup once more. If the lookup results in the same inode, | ||
98 | * then refresh the attributes, timeouts and mark the dentry valid. | ||
99 | */ | ||
40 | static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) | 100 | static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) |
41 | { | 101 | { |
42 | if (!entry->d_inode || is_bad_inode(entry->d_inode)) | 102 | struct inode *inode = entry->d_inode; |
103 | |||
104 | if (inode && is_bad_inode(inode)) | ||
43 | return 0; | 105 | return 0; |
44 | else if (time_after(jiffies, entry->d_time)) { | 106 | else if (time_after(jiffies, entry->d_time)) { |
45 | int err; | 107 | int err; |
46 | struct fuse_entry_out outarg; | 108 | struct fuse_entry_out outarg; |
47 | struct inode *inode = entry->d_inode; | 109 | struct fuse_conn *fc; |
48 | struct fuse_inode *fi = get_fuse_inode(inode); | 110 | struct fuse_req *req; |
49 | struct fuse_conn *fc = get_fuse_conn(inode); | 111 | |
50 | struct fuse_req *req = fuse_get_request(fc); | 112 | /* Doesn't hurt to "reset" the validity timeout */ |
113 | fuse_invalidate_entry_cache(entry); | ||
114 | if (!inode) | ||
115 | return 0; | ||
116 | |||
117 | fc = get_fuse_conn(inode); | ||
118 | req = fuse_get_request(fc); | ||
51 | if (!req) | 119 | if (!req) |
52 | return 0; | 120 | return 0; |
53 | 121 | ||
@@ -55,6 +123,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) | |||
55 | request_send(fc, req); | 123 | request_send(fc, req); |
56 | err = req->out.h.error; | 124 | err = req->out.h.error; |
57 | if (!err) { | 125 | if (!err) { |
126 | struct fuse_inode *fi = get_fuse_inode(inode); | ||
58 | if (outarg.nodeid != get_node_id(inode)) { | 127 | if (outarg.nodeid != get_node_id(inode)) { |
59 | fuse_send_forget(fc, req, outarg.nodeid, 1); | 128 | fuse_send_forget(fc, req, outarg.nodeid, 1); |
60 | return 0; | 129 | return 0; |
@@ -66,20 +135,44 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) | |||
66 | return 0; | 135 | return 0; |
67 | 136 | ||
68 | fuse_change_attributes(inode, &outarg.attr); | 137 | fuse_change_attributes(inode, &outarg.attr); |
69 | entry->d_time = time_to_jiffies(outarg.entry_valid, | 138 | fuse_change_timeout(entry, &outarg); |
70 | outarg.entry_valid_nsec); | ||
71 | fi->i_time = time_to_jiffies(outarg.attr_valid, | ||
72 | outarg.attr_valid_nsec); | ||
73 | } | 139 | } |
74 | return 1; | 140 | return 1; |
75 | } | 141 | } |
76 | 142 | ||
143 | /* | ||
144 | * Check if there's already a hashed alias of this directory inode. | ||
145 | * If yes, then lookup and mkdir must not create a new alias. | ||
146 | */ | ||
147 | static int dir_alias(struct inode *inode) | ||
148 | { | ||
149 | if (S_ISDIR(inode->i_mode)) { | ||
150 | struct dentry *alias = d_find_alias(inode); | ||
151 | if (alias) { | ||
152 | dput(alias); | ||
153 | return 1; | ||
154 | } | ||
155 | } | ||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | static int invalid_nodeid(u64 nodeid) | ||
160 | { | ||
161 | return !nodeid || nodeid == FUSE_ROOT_ID; | ||
162 | } | ||
163 | |||
77 | static struct dentry_operations fuse_dentry_operations = { | 164 | static struct dentry_operations fuse_dentry_operations = { |
78 | .d_revalidate = fuse_dentry_revalidate, | 165 | .d_revalidate = fuse_dentry_revalidate, |
79 | }; | 166 | }; |
80 | 167 | ||
81 | static int fuse_lookup_iget(struct inode *dir, struct dentry *entry, | 168 | static int valid_mode(int m) |
82 | struct inode **inodep) | 169 | { |
170 | return S_ISREG(m) || S_ISDIR(m) || S_ISLNK(m) || S_ISCHR(m) || | ||
171 | S_ISBLK(m) || S_ISFIFO(m) || S_ISSOCK(m); | ||
172 | } | ||
173 | |||
174 | static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | ||
175 | struct nameidata *nd) | ||
83 | { | 176 | { |
84 | int err; | 177 | int err; |
85 | struct fuse_entry_out outarg; | 178 | struct fuse_entry_out outarg; |
@@ -88,53 +181,49 @@ static int fuse_lookup_iget(struct inode *dir, struct dentry *entry, | |||
88 | struct fuse_req *req; | 181 | struct fuse_req *req; |
89 | 182 | ||
90 | if (entry->d_name.len > FUSE_NAME_MAX) | 183 | if (entry->d_name.len > FUSE_NAME_MAX) |
91 | return -ENAMETOOLONG; | 184 | return ERR_PTR(-ENAMETOOLONG); |
92 | 185 | ||
93 | req = fuse_get_request(fc); | 186 | req = fuse_get_request(fc); |
94 | if (!req) | 187 | if (!req) |
95 | return -EINTR; | 188 | return ERR_PTR(-EINTR); |
96 | 189 | ||
97 | fuse_lookup_init(req, dir, entry, &outarg); | 190 | fuse_lookup_init(req, dir, entry, &outarg); |
98 | request_send(fc, req); | 191 | request_send(fc, req); |
99 | err = req->out.h.error; | 192 | err = req->out.h.error; |
100 | if (!err && (!outarg.nodeid || outarg.nodeid == FUSE_ROOT_ID)) | 193 | if (!err && ((outarg.nodeid && invalid_nodeid(outarg.nodeid)) || |
194 | !valid_mode(outarg.attr.mode))) | ||
101 | err = -EIO; | 195 | err = -EIO; |
102 | if (!err) { | 196 | if (!err && outarg.nodeid) { |
103 | inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, | 197 | inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, |
104 | &outarg.attr); | 198 | &outarg.attr); |
105 | if (!inode) { | 199 | if (!inode) { |
106 | fuse_send_forget(fc, req, outarg.nodeid, 1); | 200 | fuse_send_forget(fc, req, outarg.nodeid, 1); |
107 | return -ENOMEM; | 201 | return ERR_PTR(-ENOMEM); |
108 | } | 202 | } |
109 | } | 203 | } |
110 | fuse_put_request(fc, req); | 204 | fuse_put_request(fc, req); |
111 | if (err && err != -ENOENT) | 205 | if (err && err != -ENOENT) |
112 | return err; | 206 | return ERR_PTR(err); |
113 | 207 | ||
114 | if (inode) { | 208 | if (inode && dir_alias(inode)) { |
115 | struct fuse_inode *fi = get_fuse_inode(inode); | 209 | iput(inode); |
116 | entry->d_time = time_to_jiffies(outarg.entry_valid, | 210 | return ERR_PTR(-EIO); |
117 | outarg.entry_valid_nsec); | ||
118 | fi->i_time = time_to_jiffies(outarg.attr_valid, | ||
119 | outarg.attr_valid_nsec); | ||
120 | } | 211 | } |
121 | 212 | d_add(entry, inode); | |
122 | entry->d_op = &fuse_dentry_operations; | 213 | entry->d_op = &fuse_dentry_operations; |
123 | *inodep = inode; | 214 | if (!err) |
124 | return 0; | 215 | fuse_change_timeout(entry, &outarg); |
125 | } | 216 | else |
126 | 217 | fuse_invalidate_entry_cache(entry); | |
127 | void fuse_invalidate_attr(struct inode *inode) | 218 | return NULL; |
128 | { | ||
129 | get_fuse_inode(inode)->i_time = jiffies - 1; | ||
130 | } | ||
131 | |||
132 | static void fuse_invalidate_entry(struct dentry *entry) | ||
133 | { | ||
134 | d_invalidate(entry); | ||
135 | entry->d_time = jiffies - 1; | ||
136 | } | 219 | } |
137 | 220 | ||
221 | /* | ||
222 | * Atomic create+open operation | ||
223 | * | ||
224 | * If the filesystem doesn't support this, then fall back to separate | ||
225 | * 'mknod' + 'open' requests. | ||
226 | */ | ||
138 | static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, | 227 | static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, |
139 | struct nameidata *nd) | 228 | struct nameidata *nd) |
140 | { | 229 | { |
@@ -145,7 +234,6 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, | |||
145 | struct fuse_open_in inarg; | 234 | struct fuse_open_in inarg; |
146 | struct fuse_open_out outopen; | 235 | struct fuse_open_out outopen; |
147 | struct fuse_entry_out outentry; | 236 | struct fuse_entry_out outentry; |
148 | struct fuse_inode *fi; | ||
149 | struct fuse_file *ff; | 237 | struct fuse_file *ff; |
150 | struct file *file; | 238 | struct file *file; |
151 | int flags = nd->intent.open.flags - 1; | 239 | int flags = nd->intent.open.flags - 1; |
@@ -154,10 +242,6 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, | |||
154 | if (fc->no_create) | 242 | if (fc->no_create) |
155 | goto out; | 243 | goto out; |
156 | 244 | ||
157 | err = -ENAMETOOLONG; | ||
158 | if (entry->d_name.len > FUSE_NAME_MAX) | ||
159 | goto out; | ||
160 | |||
161 | err = -EINTR; | 245 | err = -EINTR; |
162 | req = fuse_get_request(fc); | 246 | req = fuse_get_request(fc); |
163 | if (!req) | 247 | if (!req) |
@@ -193,7 +277,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, | |||
193 | } | 277 | } |
194 | 278 | ||
195 | err = -EIO; | 279 | err = -EIO; |
196 | if (!S_ISREG(outentry.attr.mode)) | 280 | if (!S_ISREG(outentry.attr.mode) || invalid_nodeid(outentry.nodeid)) |
197 | goto out_free_ff; | 281 | goto out_free_ff; |
198 | 282 | ||
199 | inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation, | 283 | inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation, |
@@ -202,17 +286,15 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, | |||
202 | if (!inode) { | 286 | if (!inode) { |
203 | flags &= ~(O_CREAT | O_EXCL | O_TRUNC); | 287 | flags &= ~(O_CREAT | O_EXCL | O_TRUNC); |
204 | ff->fh = outopen.fh; | 288 | ff->fh = outopen.fh; |
289 | /* Special release, with inode = NULL, this will | ||
290 | trigger a 'forget' request when the release is | ||
291 | complete */ | ||
205 | fuse_send_release(fc, ff, outentry.nodeid, NULL, flags, 0); | 292 | fuse_send_release(fc, ff, outentry.nodeid, NULL, flags, 0); |
206 | goto out_put_request; | 293 | goto out_put_request; |
207 | } | 294 | } |
208 | fuse_put_request(fc, req); | 295 | fuse_put_request(fc, req); |
209 | entry->d_time = time_to_jiffies(outentry.entry_valid, | ||
210 | outentry.entry_valid_nsec); | ||
211 | fi = get_fuse_inode(inode); | ||
212 | fi->i_time = time_to_jiffies(outentry.attr_valid, | ||
213 | outentry.attr_valid_nsec); | ||
214 | |||
215 | d_instantiate(entry, inode); | 296 | d_instantiate(entry, inode); |
297 | fuse_change_timeout(entry, &outentry); | ||
216 | file = lookup_instantiate_filp(nd, entry, generic_file_open); | 298 | file = lookup_instantiate_filp(nd, entry, generic_file_open); |
217 | if (IS_ERR(file)) { | 299 | if (IS_ERR(file)) { |
218 | ff->fh = outopen.fh; | 300 | ff->fh = outopen.fh; |
@@ -230,13 +312,15 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, | |||
230 | return err; | 312 | return err; |
231 | } | 313 | } |
232 | 314 | ||
315 | /* | ||
316 | * Code shared between mknod, mkdir, symlink and link | ||
317 | */ | ||
233 | static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req, | 318 | static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req, |
234 | struct inode *dir, struct dentry *entry, | 319 | struct inode *dir, struct dentry *entry, |
235 | int mode) | 320 | int mode) |
236 | { | 321 | { |
237 | struct fuse_entry_out outarg; | 322 | struct fuse_entry_out outarg; |
238 | struct inode *inode; | 323 | struct inode *inode; |
239 | struct fuse_inode *fi; | ||
240 | int err; | 324 | int err; |
241 | 325 | ||
242 | req->in.h.nodeid = get_node_id(dir); | 326 | req->in.h.nodeid = get_node_id(dir); |
@@ -250,10 +334,13 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req, | |||
250 | fuse_put_request(fc, req); | 334 | fuse_put_request(fc, req); |
251 | return err; | 335 | return err; |
252 | } | 336 | } |
253 | if (!outarg.nodeid || outarg.nodeid == FUSE_ROOT_ID) { | 337 | err = -EIO; |
254 | fuse_put_request(fc, req); | 338 | if (invalid_nodeid(outarg.nodeid)) |
255 | return -EIO; | 339 | goto out_put_request; |
256 | } | 340 | |
341 | if ((outarg.attr.mode ^ mode) & S_IFMT) | ||
342 | goto out_put_request; | ||
343 | |||
257 | inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, | 344 | inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, |
258 | &outarg.attr); | 345 | &outarg.attr); |
259 | if (!inode) { | 346 | if (!inode) { |
@@ -262,22 +349,19 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req, | |||
262 | } | 349 | } |
263 | fuse_put_request(fc, req); | 350 | fuse_put_request(fc, req); |
264 | 351 | ||
265 | /* Don't allow userspace to do really stupid things... */ | 352 | if (dir_alias(inode)) { |
266 | if ((inode->i_mode ^ mode) & S_IFMT) { | ||
267 | iput(inode); | 353 | iput(inode); |
268 | return -EIO; | 354 | return -EIO; |
269 | } | 355 | } |
270 | 356 | ||
271 | entry->d_time = time_to_jiffies(outarg.entry_valid, | ||
272 | outarg.entry_valid_nsec); | ||
273 | |||
274 | fi = get_fuse_inode(inode); | ||
275 | fi->i_time = time_to_jiffies(outarg.attr_valid, | ||
276 | outarg.attr_valid_nsec); | ||
277 | |||
278 | d_instantiate(entry, inode); | 357 | d_instantiate(entry, inode); |
358 | fuse_change_timeout(entry, &outarg); | ||
279 | fuse_invalidate_attr(dir); | 359 | fuse_invalidate_attr(dir); |
280 | return 0; | 360 | return 0; |
361 | |||
362 | out_put_request: | ||
363 | fuse_put_request(fc, req); | ||
364 | return err; | ||
281 | } | 365 | } |
282 | 366 | ||
283 | static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode, | 367 | static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode, |
@@ -337,12 +421,7 @@ static int fuse_symlink(struct inode *dir, struct dentry *entry, | |||
337 | { | 421 | { |
338 | struct fuse_conn *fc = get_fuse_conn(dir); | 422 | struct fuse_conn *fc = get_fuse_conn(dir); |
339 | unsigned len = strlen(link) + 1; | 423 | unsigned len = strlen(link) + 1; |
340 | struct fuse_req *req; | 424 | struct fuse_req *req = fuse_get_request(fc); |
341 | |||
342 | if (len > FUSE_SYMLINK_MAX) | ||
343 | return -ENAMETOOLONG; | ||
344 | |||
345 | req = fuse_get_request(fc); | ||
346 | if (!req) | 425 | if (!req) |
347 | return -EINTR; | 426 | return -EINTR; |
348 | 427 | ||
@@ -381,6 +460,7 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry) | |||
381 | inode->i_nlink = 0; | 460 | inode->i_nlink = 0; |
382 | fuse_invalidate_attr(inode); | 461 | fuse_invalidate_attr(inode); |
383 | fuse_invalidate_attr(dir); | 462 | fuse_invalidate_attr(dir); |
463 | fuse_invalidate_entry_cache(entry); | ||
384 | } else if (err == -EINTR) | 464 | } else if (err == -EINTR) |
385 | fuse_invalidate_entry(entry); | 465 | fuse_invalidate_entry(entry); |
386 | return err; | 466 | return err; |
@@ -406,6 +486,7 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry) | |||
406 | if (!err) { | 486 | if (!err) { |
407 | entry->d_inode->i_nlink = 0; | 487 | entry->d_inode->i_nlink = 0; |
408 | fuse_invalidate_attr(dir); | 488 | fuse_invalidate_attr(dir); |
489 | fuse_invalidate_entry_cache(entry); | ||
409 | } else if (err == -EINTR) | 490 | } else if (err == -EINTR) |
410 | fuse_invalidate_entry(entry); | 491 | fuse_invalidate_entry(entry); |
411 | return err; | 492 | return err; |
@@ -441,6 +522,10 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent, | |||
441 | fuse_invalidate_attr(olddir); | 522 | fuse_invalidate_attr(olddir); |
442 | if (olddir != newdir) | 523 | if (olddir != newdir) |
443 | fuse_invalidate_attr(newdir); | 524 | fuse_invalidate_attr(newdir); |
525 | |||
526 | /* newent will end up negative */ | ||
527 | if (newent->d_inode) | ||
528 | fuse_invalidate_entry_cache(newent); | ||
444 | } else if (err == -EINTR) { | 529 | } else if (err == -EINTR) { |
445 | /* If request was interrupted, DEITY only knows if the | 530 | /* If request was interrupted, DEITY only knows if the |
446 | rename actually took place. If the invalidation | 531 | rename actually took place. If the invalidation |
@@ -548,6 +633,15 @@ static int fuse_allow_task(struct fuse_conn *fc, struct task_struct *task) | |||
548 | return 0; | 633 | return 0; |
549 | } | 634 | } |
550 | 635 | ||
636 | /* | ||
637 | * Check whether the inode attributes are still valid | ||
638 | * | ||
639 | * If the attribute validity timeout has expired, then fetch the fresh | ||
640 | * attributes with a 'getattr' request | ||
641 | * | ||
642 | * I'm not sure why cached attributes are never returned for the root | ||
643 | * inode, this is probably being too cautious. | ||
644 | */ | ||
551 | static int fuse_revalidate(struct dentry *entry) | 645 | static int fuse_revalidate(struct dentry *entry) |
552 | { | 646 | { |
553 | struct inode *inode = entry->d_inode; | 647 | struct inode *inode = entry->d_inode; |
@@ -595,6 +689,19 @@ static int fuse_access(struct inode *inode, int mask) | |||
595 | return err; | 689 | return err; |
596 | } | 690 | } |
597 | 691 | ||
692 | /* | ||
693 | * Check permission. The two basic access models of FUSE are: | ||
694 | * | ||
695 | * 1) Local access checking ('default_permissions' mount option) based | ||
696 | * on file mode. This is the plain old disk filesystem permission | ||
697 | * modell. | ||
698 | * | ||
699 | * 2) "Remote" access checking, where server is responsible for | ||
700 | * checking permission in each inode operation. An exception to this | ||
701 | * is if ->permission() was invoked from sys_access() in which case an | ||
702 | * access request is sent. Execute permission is still checked | ||
703 | * locally based on file mode. | ||
704 | */ | ||
598 | static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) | 705 | static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) |
599 | { | 706 | { |
600 | struct fuse_conn *fc = get_fuse_conn(inode); | 707 | struct fuse_conn *fc = get_fuse_conn(inode); |
@@ -613,14 +720,10 @@ static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) | |||
613 | err = generic_permission(inode, mask, NULL); | 720 | err = generic_permission(inode, mask, NULL); |
614 | } | 721 | } |
615 | 722 | ||
616 | /* FIXME: Need some mechanism to revoke permissions: | 723 | /* Note: the opposite of the above test does not |
617 | currently if the filesystem suddenly changes the | 724 | exist. So if permissions are revoked this won't be |
618 | file mode, we will not be informed about it, and | 725 | noticed immediately, only after the attribute |
619 | continue to allow access to the file/directory. | 726 | timeout has expired */ |
620 | |||
621 | This is actually not so grave, since the user can | ||
622 | simply keep access to the file/directory anyway by | ||
623 | keeping it open... */ | ||
624 | 727 | ||
625 | return err; | 728 | return err; |
626 | } else { | 729 | } else { |
@@ -659,13 +762,6 @@ static int parse_dirfile(char *buf, size_t nbytes, struct file *file, | |||
659 | return 0; | 762 | return 0; |
660 | } | 763 | } |
661 | 764 | ||
662 | static inline size_t fuse_send_readdir(struct fuse_req *req, struct file *file, | ||
663 | struct inode *inode, loff_t pos, | ||
664 | size_t count) | ||
665 | { | ||
666 | return fuse_send_read_common(req, file, inode, pos, count, 1); | ||
667 | } | ||
668 | |||
669 | static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir) | 765 | static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir) |
670 | { | 766 | { |
671 | int err; | 767 | int err; |
@@ -673,7 +769,12 @@ static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir) | |||
673 | struct page *page; | 769 | struct page *page; |
674 | struct inode *inode = file->f_dentry->d_inode; | 770 | struct inode *inode = file->f_dentry->d_inode; |
675 | struct fuse_conn *fc = get_fuse_conn(inode); | 771 | struct fuse_conn *fc = get_fuse_conn(inode); |
676 | struct fuse_req *req = fuse_get_request(fc); | 772 | struct fuse_req *req; |
773 | |||
774 | if (is_bad_inode(inode)) | ||
775 | return -EIO; | ||
776 | |||
777 | req = fuse_get_request(fc); | ||
677 | if (!req) | 778 | if (!req) |
678 | return -EINTR; | 779 | return -EINTR; |
679 | 780 | ||
@@ -684,7 +785,9 @@ static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir) | |||
684 | } | 785 | } |
685 | req->num_pages = 1; | 786 | req->num_pages = 1; |
686 | req->pages[0] = page; | 787 | req->pages[0] = page; |
687 | nbytes = fuse_send_readdir(req, file, inode, file->f_pos, PAGE_SIZE); | 788 | fuse_read_fill(req, file, inode, file->f_pos, PAGE_SIZE, FUSE_READDIR); |
789 | request_send(fc, req); | ||
790 | nbytes = req->out.args[0].size; | ||
688 | err = req->out.h.error; | 791 | err = req->out.h.error; |
689 | fuse_put_request(fc, req); | 792 | fuse_put_request(fc, req); |
690 | if (!err) | 793 | if (!err) |
@@ -788,6 +891,15 @@ static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg) | |||
788 | } | 891 | } |
789 | } | 892 | } |
790 | 893 | ||
894 | /* | ||
895 | * Set attributes, and at the same time refresh them. | ||
896 | * | ||
897 | * Truncation is slightly complicated, because the 'truncate' request | ||
898 | * may fail, in which case we don't want to touch the mapping. | ||
899 | * vmtruncate() doesn't allow for this case. So do the rlimit | ||
900 | * checking by hand and call vmtruncate() only after the file has | ||
901 | * actually been truncated. | ||
902 | */ | ||
791 | static int fuse_setattr(struct dentry *entry, struct iattr *attr) | 903 | static int fuse_setattr(struct dentry *entry, struct iattr *attr) |
792 | { | 904 | { |
793 | struct inode *inode = entry->d_inode; | 905 | struct inode *inode = entry->d_inode; |
@@ -865,28 +977,6 @@ static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry, | |||
865 | return err; | 977 | return err; |
866 | } | 978 | } |
867 | 979 | ||
868 | static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | ||
869 | struct nameidata *nd) | ||
870 | { | ||
871 | struct inode *inode; | ||
872 | int err; | ||
873 | |||
874 | err = fuse_lookup_iget(dir, entry, &inode); | ||
875 | if (err) | ||
876 | return ERR_PTR(err); | ||
877 | if (inode && S_ISDIR(inode->i_mode)) { | ||
878 | /* Don't allow creating an alias to a directory */ | ||
879 | struct dentry *alias = d_find_alias(inode); | ||
880 | if (alias) { | ||
881 | dput(alias); | ||
882 | iput(inode); | ||
883 | return ERR_PTR(-EIO); | ||
884 | } | ||
885 | } | ||
886 | d_add(entry, inode); | ||
887 | return NULL; | ||
888 | } | ||
889 | |||
890 | static int fuse_setxattr(struct dentry *entry, const char *name, | 980 | static int fuse_setxattr(struct dentry *entry, const char *name, |
891 | const void *value, size_t size, int flags) | 981 | const void *value, size_t size, int flags) |
892 | { | 982 | { |
@@ -896,9 +986,6 @@ static int fuse_setxattr(struct dentry *entry, const char *name, | |||
896 | struct fuse_setxattr_in inarg; | 986 | struct fuse_setxattr_in inarg; |
897 | int err; | 987 | int err; |
898 | 988 | ||
899 | if (size > FUSE_XATTR_SIZE_MAX) | ||
900 | return -E2BIG; | ||
901 | |||
902 | if (fc->no_setxattr) | 989 | if (fc->no_setxattr) |
903 | return -EOPNOTSUPP; | 990 | return -EOPNOTSUPP; |
904 | 991 | ||