diff options
Diffstat (limited to 'fs/fuse/dir.c')
-rw-r--r-- | fs/fuse/dir.c | 278 |
1 files changed, 179 insertions, 99 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 51f5da652771..417bcee466f6 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c | |||
@@ -13,8 +13,16 @@ | |||
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 | ||
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 | */ | ||
18 | static inline unsigned long time_to_jiffies(unsigned long sec, | 26 | static inline unsigned long time_to_jiffies(unsigned long sec, |
19 | unsigned long nsec) | 27 | unsigned long nsec) |
20 | { | 28 | { |
@@ -22,6 +30,50 @@ static inline unsigned long time_to_jiffies(unsigned long sec, | |||
22 | return jiffies + timespec_to_jiffies(&ts); | 30 | return jiffies + timespec_to_jiffies(&ts); |
23 | } | 31 | } |
24 | 32 | ||
33 | /* | ||
34 | * Set dentry and possibly attribute timeouts from the lookup/mk* | ||
35 | * replies | ||
36 | */ | ||
37 | static void fuse_change_timeout(struct dentry *entry, struct fuse_entry_out *o) | ||
38 | { | ||
39 | entry->d_time = time_to_jiffies(o->entry_valid, o->entry_valid_nsec); | ||
40 | if (entry->d_inode) | ||
41 | get_fuse_inode(entry->d_inode)->i_time = | ||
42 | time_to_jiffies(o->attr_valid, o->attr_valid_nsec); | ||
43 | } | ||
44 | |||
45 | /* | ||
46 | * Mark the attributes as stale, so that at the next call to | ||
47 | * ->getattr() they will be fetched from userspace | ||
48 | */ | ||
49 | void fuse_invalidate_attr(struct inode *inode) | ||
50 | { | ||
51 | get_fuse_inode(inode)->i_time = jiffies - 1; | ||
52 | } | ||
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 | */ | ||
62 | static void fuse_invalidate_entry_cache(struct dentry *entry) | ||
63 | { | ||
64 | entry->d_time = jiffies - 1; | ||
65 | } | ||
66 | |||
67 | /* | ||
68 | * Same as fuse_invalidate_entry_cache(), but also try to remove the | ||
69 | * dentry from the hash | ||
70 | */ | ||
71 | static void fuse_invalidate_entry(struct dentry *entry) | ||
72 | { | ||
73 | d_invalidate(entry); | ||
74 | fuse_invalidate_entry_cache(entry); | ||
75 | } | ||
76 | |||
25 | static void fuse_lookup_init(struct fuse_req *req, struct inode *dir, | 77 | static void fuse_lookup_init(struct fuse_req *req, struct inode *dir, |
26 | struct dentry *entry, | 78 | struct dentry *entry, |
27 | struct fuse_entry_out *outarg) | 79 | struct fuse_entry_out *outarg) |
@@ -37,17 +89,34 @@ static void fuse_lookup_init(struct fuse_req *req, struct inode *dir, | |||
37 | req->out.args[0].value = outarg; | 89 | req->out.args[0].value = outarg; |
38 | } | 90 | } |
39 | 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 | */ | ||
40 | static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) | 101 | static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) |
41 | { | 102 | { |
42 | if (!entry->d_inode || is_bad_inode(entry->d_inode)) | 103 | struct inode *inode = entry->d_inode; |
104 | |||
105 | if (inode && is_bad_inode(inode)) | ||
43 | return 0; | 106 | return 0; |
44 | else if (time_after(jiffies, entry->d_time)) { | 107 | else if (time_after(jiffies, entry->d_time)) { |
45 | int err; | 108 | int err; |
46 | struct fuse_entry_out outarg; | 109 | struct fuse_entry_out outarg; |
47 | struct inode *inode = entry->d_inode; | 110 | struct fuse_conn *fc; |
48 | struct fuse_inode *fi = get_fuse_inode(inode); | 111 | struct fuse_req *req; |
49 | struct fuse_conn *fc = get_fuse_conn(inode); | 112 | |
50 | struct fuse_req *req = fuse_get_request(fc); | 113 | /* Doesn't hurt to "reset" the validity timeout */ |
114 | fuse_invalidate_entry_cache(entry); | ||
115 | if (!inode) | ||
116 | return 0; | ||
117 | |||
118 | fc = get_fuse_conn(inode); | ||
119 | req = fuse_get_request(fc); | ||
51 | if (!req) | 120 | if (!req) |
52 | return 0; | 121 | return 0; |
53 | 122 | ||
@@ -55,6 +124,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) | |||
55 | request_send(fc, req); | 124 | request_send(fc, req); |
56 | err = req->out.h.error; | 125 | err = req->out.h.error; |
57 | if (!err) { | 126 | if (!err) { |
127 | struct fuse_inode *fi = get_fuse_inode(inode); | ||
58 | if (outarg.nodeid != get_node_id(inode)) { | 128 | if (outarg.nodeid != get_node_id(inode)) { |
59 | fuse_send_forget(fc, req, outarg.nodeid, 1); | 129 | fuse_send_forget(fc, req, outarg.nodeid, 1); |
60 | return 0; | 130 | return 0; |
@@ -66,18 +136,18 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) | |||
66 | return 0; | 136 | return 0; |
67 | 137 | ||
68 | fuse_change_attributes(inode, &outarg.attr); | 138 | fuse_change_attributes(inode, &outarg.attr); |
69 | entry->d_time = time_to_jiffies(outarg.entry_valid, | 139 | 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 | } | 140 | } |
74 | return 1; | 141 | return 1; |
75 | } | 142 | } |
76 | 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 | */ | ||
77 | static int dir_alias(struct inode *inode) | 148 | static int dir_alias(struct inode *inode) |
78 | { | 149 | { |
79 | if (S_ISDIR(inode->i_mode)) { | 150 | if (S_ISDIR(inode->i_mode)) { |
80 | /* Don't allow creating an alias to a directory */ | ||
81 | struct dentry *alias = d_find_alias(inode); | 151 | struct dentry *alias = d_find_alias(inode); |
82 | if (alias) { | 152 | if (alias) { |
83 | dput(alias); | 153 | dput(alias); |
@@ -96,8 +166,14 @@ static struct dentry_operations fuse_dentry_operations = { | |||
96 | .d_revalidate = fuse_dentry_revalidate, | 166 | .d_revalidate = fuse_dentry_revalidate, |
97 | }; | 167 | }; |
98 | 168 | ||
99 | static int fuse_lookup_iget(struct inode *dir, struct dentry *entry, | 169 | static inline int valid_mode(int m) |
100 | struct inode **inodep) | 170 | { |
171 | return S_ISREG(m) || S_ISDIR(m) || S_ISLNK(m) || S_ISCHR(m) || | ||
172 | S_ISBLK(m) || S_ISFIFO(m) || S_ISSOCK(m); | ||
173 | } | ||
174 | |||
175 | static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | ||
176 | struct nameidata *nd) | ||
101 | { | 177 | { |
102 | int err; | 178 | int err; |
103 | struct fuse_entry_out outarg; | 179 | struct fuse_entry_out outarg; |
@@ -106,53 +182,49 @@ static int fuse_lookup_iget(struct inode *dir, struct dentry *entry, | |||
106 | struct fuse_req *req; | 182 | struct fuse_req *req; |
107 | 183 | ||
108 | if (entry->d_name.len > FUSE_NAME_MAX) | 184 | if (entry->d_name.len > FUSE_NAME_MAX) |
109 | return -ENAMETOOLONG; | 185 | return ERR_PTR(-ENAMETOOLONG); |
110 | 186 | ||
111 | req = fuse_get_request(fc); | 187 | req = fuse_get_request(fc); |
112 | if (!req) | 188 | if (!req) |
113 | return -EINTR; | 189 | return ERR_PTR(-EINTR); |
114 | 190 | ||
115 | fuse_lookup_init(req, dir, entry, &outarg); | 191 | fuse_lookup_init(req, dir, entry, &outarg); |
116 | request_send(fc, req); | 192 | request_send(fc, req); |
117 | err = req->out.h.error; | 193 | err = req->out.h.error; |
118 | if (!err && invalid_nodeid(outarg.nodeid)) | 194 | if (!err && ((outarg.nodeid && invalid_nodeid(outarg.nodeid)) || |
195 | !valid_mode(outarg.attr.mode))) | ||
119 | err = -EIO; | 196 | err = -EIO; |
120 | if (!err) { | 197 | if (!err && outarg.nodeid) { |
121 | inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, | 198 | inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, |
122 | &outarg.attr); | 199 | &outarg.attr); |
123 | if (!inode) { | 200 | if (!inode) { |
124 | fuse_send_forget(fc, req, outarg.nodeid, 1); | 201 | fuse_send_forget(fc, req, outarg.nodeid, 1); |
125 | return -ENOMEM; | 202 | return ERR_PTR(-ENOMEM); |
126 | } | 203 | } |
127 | } | 204 | } |
128 | fuse_put_request(fc, req); | 205 | fuse_put_request(fc, req); |
129 | if (err && err != -ENOENT) | 206 | if (err && err != -ENOENT) |
130 | return err; | 207 | return ERR_PTR(err); |
131 | 208 | ||
132 | if (inode) { | 209 | if (inode && dir_alias(inode)) { |
133 | struct fuse_inode *fi = get_fuse_inode(inode); | 210 | iput(inode); |
134 | entry->d_time = time_to_jiffies(outarg.entry_valid, | 211 | return ERR_PTR(-EIO); |
135 | outarg.entry_valid_nsec); | ||
136 | fi->i_time = time_to_jiffies(outarg.attr_valid, | ||
137 | outarg.attr_valid_nsec); | ||
138 | } | 212 | } |
139 | 213 | d_add(entry, inode); | |
140 | entry->d_op = &fuse_dentry_operations; | 214 | entry->d_op = &fuse_dentry_operations; |
141 | *inodep = inode; | 215 | if (!err) |
142 | return 0; | 216 | fuse_change_timeout(entry, &outarg); |
143 | } | 217 | else |
144 | 218 | fuse_invalidate_entry_cache(entry); | |
145 | void fuse_invalidate_attr(struct inode *inode) | 219 | return NULL; |
146 | { | ||
147 | get_fuse_inode(inode)->i_time = jiffies - 1; | ||
148 | } | ||
149 | |||
150 | static void fuse_invalidate_entry(struct dentry *entry) | ||
151 | { | ||
152 | d_invalidate(entry); | ||
153 | entry->d_time = jiffies - 1; | ||
154 | } | 220 | } |
155 | 221 | ||
222 | /* | ||
223 | * Atomic create+open operation | ||
224 | * | ||
225 | * If the filesystem doesn't support this, then fall back to separate | ||
226 | * 'mknod' + 'open' requests. | ||
227 | */ | ||
156 | static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, | 228 | static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, |
157 | struct nameidata *nd) | 229 | struct nameidata *nd) |
158 | { | 230 | { |
@@ -163,7 +235,6 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, | |||
163 | struct fuse_open_in inarg; | 235 | struct fuse_open_in inarg; |
164 | struct fuse_open_out outopen; | 236 | struct fuse_open_out outopen; |
165 | struct fuse_entry_out outentry; | 237 | struct fuse_entry_out outentry; |
166 | struct fuse_inode *fi; | ||
167 | struct fuse_file *ff; | 238 | struct fuse_file *ff; |
168 | struct file *file; | 239 | struct file *file; |
169 | int flags = nd->intent.open.flags - 1; | 240 | int flags = nd->intent.open.flags - 1; |
@@ -172,10 +243,6 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, | |||
172 | if (fc->no_create) | 243 | if (fc->no_create) |
173 | goto out; | 244 | goto out; |
174 | 245 | ||
175 | err = -ENAMETOOLONG; | ||
176 | if (entry->d_name.len > FUSE_NAME_MAX) | ||
177 | goto out; | ||
178 | |||
179 | err = -EINTR; | 246 | err = -EINTR; |
180 | req = fuse_get_request(fc); | 247 | req = fuse_get_request(fc); |
181 | if (!req) | 248 | if (!req) |
@@ -220,17 +287,15 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, | |||
220 | if (!inode) { | 287 | if (!inode) { |
221 | flags &= ~(O_CREAT | O_EXCL | O_TRUNC); | 288 | flags &= ~(O_CREAT | O_EXCL | O_TRUNC); |
222 | ff->fh = outopen.fh; | 289 | ff->fh = outopen.fh; |
290 | /* Special release, with inode = NULL, this will | ||
291 | trigger a 'forget' request when the release is | ||
292 | complete */ | ||
223 | fuse_send_release(fc, ff, outentry.nodeid, NULL, flags, 0); | 293 | fuse_send_release(fc, ff, outentry.nodeid, NULL, flags, 0); |
224 | goto out_put_request; | 294 | goto out_put_request; |
225 | } | 295 | } |
226 | fuse_put_request(fc, req); | 296 | fuse_put_request(fc, req); |
227 | entry->d_time = time_to_jiffies(outentry.entry_valid, | ||
228 | outentry.entry_valid_nsec); | ||
229 | fi = get_fuse_inode(inode); | ||
230 | fi->i_time = time_to_jiffies(outentry.attr_valid, | ||
231 | outentry.attr_valid_nsec); | ||
232 | |||
233 | d_instantiate(entry, inode); | 297 | d_instantiate(entry, inode); |
298 | fuse_change_timeout(entry, &outentry); | ||
234 | file = lookup_instantiate_filp(nd, entry, generic_file_open); | 299 | file = lookup_instantiate_filp(nd, entry, generic_file_open); |
235 | if (IS_ERR(file)) { | 300 | if (IS_ERR(file)) { |
236 | ff->fh = outopen.fh; | 301 | ff->fh = outopen.fh; |
@@ -248,13 +313,15 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, | |||
248 | return err; | 313 | return err; |
249 | } | 314 | } |
250 | 315 | ||
316 | /* | ||
317 | * Code shared between mknod, mkdir, symlink and link | ||
318 | */ | ||
251 | static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req, | 319 | static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req, |
252 | struct inode *dir, struct dentry *entry, | 320 | struct inode *dir, struct dentry *entry, |
253 | int mode) | 321 | int mode) |
254 | { | 322 | { |
255 | struct fuse_entry_out outarg; | 323 | struct fuse_entry_out outarg; |
256 | struct inode *inode; | 324 | struct inode *inode; |
257 | struct fuse_inode *fi; | ||
258 | int err; | 325 | int err; |
259 | 326 | ||
260 | req->in.h.nodeid = get_node_id(dir); | 327 | req->in.h.nodeid = get_node_id(dir); |
@@ -268,10 +335,13 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req, | |||
268 | fuse_put_request(fc, req); | 335 | fuse_put_request(fc, req); |
269 | return err; | 336 | return err; |
270 | } | 337 | } |
271 | if (invalid_nodeid(outarg.nodeid)) { | 338 | err = -EIO; |
272 | fuse_put_request(fc, req); | 339 | if (invalid_nodeid(outarg.nodeid)) |
273 | return -EIO; | 340 | goto out_put_request; |
274 | } | 341 | |
342 | if ((outarg.attr.mode ^ mode) & S_IFMT) | ||
343 | goto out_put_request; | ||
344 | |||
275 | inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, | 345 | inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, |
276 | &outarg.attr); | 346 | &outarg.attr); |
277 | if (!inode) { | 347 | if (!inode) { |
@@ -280,22 +350,19 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req, | |||
280 | } | 350 | } |
281 | fuse_put_request(fc, req); | 351 | fuse_put_request(fc, req); |
282 | 352 | ||
283 | /* Don't allow userspace to do really stupid things... */ | 353 | if (dir_alias(inode)) { |
284 | if (((inode->i_mode ^ mode) & S_IFMT) || dir_alias(inode)) { | ||
285 | iput(inode); | 354 | iput(inode); |
286 | return -EIO; | 355 | return -EIO; |
287 | } | 356 | } |
288 | 357 | ||
289 | entry->d_time = time_to_jiffies(outarg.entry_valid, | ||
290 | outarg.entry_valid_nsec); | ||
291 | |||
292 | fi = get_fuse_inode(inode); | ||
293 | fi->i_time = time_to_jiffies(outarg.attr_valid, | ||
294 | outarg.attr_valid_nsec); | ||
295 | |||
296 | d_instantiate(entry, inode); | 358 | d_instantiate(entry, inode); |
359 | fuse_change_timeout(entry, &outarg); | ||
297 | fuse_invalidate_attr(dir); | 360 | fuse_invalidate_attr(dir); |
298 | return 0; | 361 | return 0; |
362 | |||
363 | out_put_request: | ||
364 | fuse_put_request(fc, req); | ||
365 | return err; | ||
299 | } | 366 | } |
300 | 367 | ||
301 | static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode, | 368 | static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode, |
@@ -355,12 +422,7 @@ static int fuse_symlink(struct inode *dir, struct dentry *entry, | |||
355 | { | 422 | { |
356 | struct fuse_conn *fc = get_fuse_conn(dir); | 423 | struct fuse_conn *fc = get_fuse_conn(dir); |
357 | unsigned len = strlen(link) + 1; | 424 | unsigned len = strlen(link) + 1; |
358 | struct fuse_req *req; | 425 | struct fuse_req *req = fuse_get_request(fc); |
359 | |||
360 | if (len > FUSE_SYMLINK_MAX) | ||
361 | return -ENAMETOOLONG; | ||
362 | |||
363 | req = fuse_get_request(fc); | ||
364 | if (!req) | 426 | if (!req) |
365 | return -EINTR; | 427 | return -EINTR; |
366 | 428 | ||
@@ -399,6 +461,7 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry) | |||
399 | inode->i_nlink = 0; | 461 | inode->i_nlink = 0; |
400 | fuse_invalidate_attr(inode); | 462 | fuse_invalidate_attr(inode); |
401 | fuse_invalidate_attr(dir); | 463 | fuse_invalidate_attr(dir); |
464 | fuse_invalidate_entry_cache(entry); | ||
402 | } else if (err == -EINTR) | 465 | } else if (err == -EINTR) |
403 | fuse_invalidate_entry(entry); | 466 | fuse_invalidate_entry(entry); |
404 | return err; | 467 | return err; |
@@ -424,6 +487,7 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry) | |||
424 | if (!err) { | 487 | if (!err) { |
425 | entry->d_inode->i_nlink = 0; | 488 | entry->d_inode->i_nlink = 0; |
426 | fuse_invalidate_attr(dir); | 489 | fuse_invalidate_attr(dir); |
490 | fuse_invalidate_entry_cache(entry); | ||
427 | } else if (err == -EINTR) | 491 | } else if (err == -EINTR) |
428 | fuse_invalidate_entry(entry); | 492 | fuse_invalidate_entry(entry); |
429 | return err; | 493 | return err; |
@@ -459,6 +523,10 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent, | |||
459 | fuse_invalidate_attr(olddir); | 523 | fuse_invalidate_attr(olddir); |
460 | if (olddir != newdir) | 524 | if (olddir != newdir) |
461 | fuse_invalidate_attr(newdir); | 525 | fuse_invalidate_attr(newdir); |
526 | |||
527 | /* newent will end up negative */ | ||
528 | if (newent->d_inode) | ||
529 | fuse_invalidate_entry_cache(newent); | ||
462 | } else if (err == -EINTR) { | 530 | } else if (err == -EINTR) { |
463 | /* If request was interrupted, DEITY only knows if the | 531 | /* If request was interrupted, DEITY only knows if the |
464 | rename actually took place. If the invalidation | 532 | rename actually took place. If the invalidation |
@@ -566,6 +634,15 @@ static int fuse_allow_task(struct fuse_conn *fc, struct task_struct *task) | |||
566 | return 0; | 634 | return 0; |
567 | } | 635 | } |
568 | 636 | ||
637 | /* | ||
638 | * Check whether the inode attributes are still valid | ||
639 | * | ||
640 | * If the attribute validity timeout has expired, then fetch the fresh | ||
641 | * attributes with a 'getattr' request | ||
642 | * | ||
643 | * I'm not sure why cached attributes are never returned for the root | ||
644 | * inode, this is probably being too cautious. | ||
645 | */ | ||
569 | static int fuse_revalidate(struct dentry *entry) | 646 | static int fuse_revalidate(struct dentry *entry) |
570 | { | 647 | { |
571 | struct inode *inode = entry->d_inode; | 648 | struct inode *inode = entry->d_inode; |
@@ -613,6 +690,19 @@ static int fuse_access(struct inode *inode, int mask) | |||
613 | return err; | 690 | return err; |
614 | } | 691 | } |
615 | 692 | ||
693 | /* | ||
694 | * Check permission. The two basic access models of FUSE are: | ||
695 | * | ||
696 | * 1) Local access checking ('default_permissions' mount option) based | ||
697 | * on file mode. This is the plain old disk filesystem permission | ||
698 | * modell. | ||
699 | * | ||
700 | * 2) "Remote" access checking, where server is responsible for | ||
701 | * checking permission in each inode operation. An exception to this | ||
702 | * is if ->permission() was invoked from sys_access() in which case an | ||
703 | * access request is sent. Execute permission is still checked | ||
704 | * locally based on file mode. | ||
705 | */ | ||
616 | static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) | 706 | static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) |
617 | { | 707 | { |
618 | struct fuse_conn *fc = get_fuse_conn(inode); | 708 | struct fuse_conn *fc = get_fuse_conn(inode); |
@@ -631,14 +721,10 @@ static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) | |||
631 | err = generic_permission(inode, mask, NULL); | 721 | err = generic_permission(inode, mask, NULL); |
632 | } | 722 | } |
633 | 723 | ||
634 | /* FIXME: Need some mechanism to revoke permissions: | 724 | /* Note: the opposite of the above test does not |
635 | currently if the filesystem suddenly changes the | 725 | exist. So if permissions are revoked this won't be |
636 | file mode, we will not be informed about it, and | 726 | noticed immediately, only after the attribute |
637 | continue to allow access to the file/directory. | 727 | timeout has expired */ |
638 | |||
639 | This is actually not so grave, since the user can | ||
640 | simply keep access to the file/directory anyway by | ||
641 | keeping it open... */ | ||
642 | 728 | ||
643 | return err; | 729 | return err; |
644 | } else { | 730 | } else { |
@@ -691,7 +777,12 @@ static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir) | |||
691 | struct page *page; | 777 | struct page *page; |
692 | struct inode *inode = file->f_dentry->d_inode; | 778 | struct inode *inode = file->f_dentry->d_inode; |
693 | struct fuse_conn *fc = get_fuse_conn(inode); | 779 | struct fuse_conn *fc = get_fuse_conn(inode); |
694 | struct fuse_req *req = fuse_get_request(fc); | 780 | struct fuse_req *req; |
781 | |||
782 | if (is_bad_inode(inode)) | ||
783 | return -EIO; | ||
784 | |||
785 | req = fuse_get_request(fc); | ||
695 | if (!req) | 786 | if (!req) |
696 | return -EINTR; | 787 | return -EINTR; |
697 | 788 | ||
@@ -806,6 +897,15 @@ static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg) | |||
806 | } | 897 | } |
807 | } | 898 | } |
808 | 899 | ||
900 | /* | ||
901 | * Set attributes, and at the same time refresh them. | ||
902 | * | ||
903 | * Truncation is slightly complicated, because the 'truncate' request | ||
904 | * may fail, in which case we don't want to touch the mapping. | ||
905 | * vmtruncate() doesn't allow for this case. So do the rlimit | ||
906 | * checking by hand and call vmtruncate() only after the file has | ||
907 | * actually been truncated. | ||
908 | */ | ||
809 | static int fuse_setattr(struct dentry *entry, struct iattr *attr) | 909 | static int fuse_setattr(struct dentry *entry, struct iattr *attr) |
810 | { | 910 | { |
811 | struct inode *inode = entry->d_inode; | 911 | struct inode *inode = entry->d_inode; |
@@ -883,23 +983,6 @@ static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry, | |||
883 | return err; | 983 | return err; |
884 | } | 984 | } |
885 | 985 | ||
886 | static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | ||
887 | struct nameidata *nd) | ||
888 | { | ||
889 | struct inode *inode; | ||
890 | int err; | ||
891 | |||
892 | err = fuse_lookup_iget(dir, entry, &inode); | ||
893 | if (err) | ||
894 | return ERR_PTR(err); | ||
895 | if (inode && dir_alias(inode)) { | ||
896 | iput(inode); | ||
897 | return ERR_PTR(-EIO); | ||
898 | } | ||
899 | d_add(entry, inode); | ||
900 | return NULL; | ||
901 | } | ||
902 | |||
903 | static int fuse_setxattr(struct dentry *entry, const char *name, | 986 | static int fuse_setxattr(struct dentry *entry, const char *name, |
904 | const void *value, size_t size, int flags) | 987 | const void *value, size_t size, int flags) |
905 | { | 988 | { |
@@ -909,9 +992,6 @@ static int fuse_setxattr(struct dentry *entry, const char *name, | |||
909 | struct fuse_setxattr_in inarg; | 992 | struct fuse_setxattr_in inarg; |
910 | int err; | 993 | int err; |
911 | 994 | ||
912 | if (size > FUSE_XATTR_SIZE_MAX) | ||
913 | return -E2BIG; | ||
914 | |||
915 | if (fc->no_setxattr) | 995 | if (fc->no_setxattr) |
916 | return -EOPNOTSUPP; | 996 | return -EOPNOTSUPP; |
917 | 997 | ||