blob: 51f5da6527719c31ab3a7316c0ba99812e85809b [file] [log] [blame]
Miklos Szeredie5e55582005-09-09 13:10:28 -07001/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2005 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU GPL.
6 See the file COPYING.
7*/
8
9#include "fuse_i.h"
10
11#include <linux/pagemap.h>
12#include <linux/file.h>
13#include <linux/gfp.h>
14#include <linux/sched.h>
15#include <linux/namei.h>
Miklos Szeredifd72faa2005-11-07 00:59:51 -080016#include <linux/mount.h>
Miklos Szeredie5e55582005-09-09 13:10:28 -070017
18static inline unsigned long time_to_jiffies(unsigned long sec,
19 unsigned long nsec)
20{
21 struct timespec ts = {sec, nsec};
22 return jiffies + timespec_to_jiffies(&ts);
23}
24
25static void fuse_lookup_init(struct fuse_req *req, struct inode *dir,
26 struct dentry *entry,
27 struct fuse_entry_out *outarg)
28{
29 req->in.h.opcode = FUSE_LOOKUP;
30 req->in.h.nodeid = get_node_id(dir);
31 req->inode = dir;
32 req->in.numargs = 1;
33 req->in.args[0].size = entry->d_name.len + 1;
34 req->in.args[0].value = entry->d_name.name;
35 req->out.numargs = 1;
36 req->out.args[0].size = sizeof(struct fuse_entry_out);
37 req->out.args[0].value = outarg;
38}
39
40static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
41{
42 if (!entry->d_inode || is_bad_inode(entry->d_inode))
43 return 0;
44 else if (time_after(jiffies, entry->d_time)) {
45 int err;
Miklos Szeredie5e55582005-09-09 13:10:28 -070046 struct fuse_entry_out outarg;
47 struct inode *inode = entry->d_inode;
48 struct fuse_inode *fi = get_fuse_inode(inode);
49 struct fuse_conn *fc = get_fuse_conn(inode);
Miklos Szeredi7c352bd2005-09-09 13:10:39 -070050 struct fuse_req *req = fuse_get_request(fc);
Miklos Szeredie5e55582005-09-09 13:10:28 -070051 if (!req)
52 return 0;
53
54 fuse_lookup_init(req, entry->d_parent->d_inode, entry, &outarg);
Miklos Szeredi7c352bd2005-09-09 13:10:39 -070055 request_send(fc, req);
Miklos Szeredie5e55582005-09-09 13:10:28 -070056 err = req->out.h.error;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -070057 if (!err) {
58 if (outarg.nodeid != get_node_id(inode)) {
59 fuse_send_forget(fc, req, outarg.nodeid, 1);
60 return 0;
61 }
62 fi->nlookup ++;
63 }
Miklos Szeredie5e55582005-09-09 13:10:28 -070064 fuse_put_request(fc, req);
Miklos Szeredi9e6268d2005-09-09 13:10:29 -070065 if (err || (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
Miklos Szeredie5e55582005-09-09 13:10:28 -070066 return 0;
67
68 fuse_change_attributes(inode, &outarg.attr);
Miklos Szeredie5e55582005-09-09 13:10:28 -070069 entry->d_time = time_to_jiffies(outarg.entry_valid,
70 outarg.entry_valid_nsec);
71 fi->i_time = time_to_jiffies(outarg.attr_valid,
72 outarg.attr_valid_nsec);
73 }
74 return 1;
75}
76
Miklos Szeredif007d5c2005-11-28 13:44:16 -080077static int dir_alias(struct inode *inode)
78{
79 if (S_ISDIR(inode->i_mode)) {
80 /* Don't allow creating an alias to a directory */
81 struct dentry *alias = d_find_alias(inode);
82 if (alias) {
83 dput(alias);
84 return 1;
85 }
86 }
87 return 0;
88}
89
Miklos Szeredi2827d0b22005-11-28 13:44:16 -080090static inline int invalid_nodeid(u64 nodeid)
91{
92 return !nodeid || nodeid == FUSE_ROOT_ID;
93}
94
Miklos Szeredie5e55582005-09-09 13:10:28 -070095static struct dentry_operations fuse_dentry_operations = {
96 .d_revalidate = fuse_dentry_revalidate,
97};
98
99static int fuse_lookup_iget(struct inode *dir, struct dentry *entry,
100 struct inode **inodep)
101{
102 int err;
Miklos Szeredie5e55582005-09-09 13:10:28 -0700103 struct fuse_entry_out outarg;
104 struct inode *inode = NULL;
105 struct fuse_conn *fc = get_fuse_conn(dir);
106 struct fuse_req *req;
107
108 if (entry->d_name.len > FUSE_NAME_MAX)
109 return -ENAMETOOLONG;
110
111 req = fuse_get_request(fc);
112 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700113 return -EINTR;
Miklos Szeredie5e55582005-09-09 13:10:28 -0700114
115 fuse_lookup_init(req, dir, entry, &outarg);
116 request_send(fc, req);
Miklos Szeredie5e55582005-09-09 13:10:28 -0700117 err = req->out.h.error;
Miklos Szeredi2827d0b22005-11-28 13:44:16 -0800118 if (!err && invalid_nodeid(outarg.nodeid))
Miklos Szerediee4e5272005-09-27 21:45:21 -0700119 err = -EIO;
Miklos Szeredie5e55582005-09-09 13:10:28 -0700120 if (!err) {
121 inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700122 &outarg.attr);
Miklos Szeredie5e55582005-09-09 13:10:28 -0700123 if (!inode) {
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700124 fuse_send_forget(fc, req, outarg.nodeid, 1);
Miklos Szeredie5e55582005-09-09 13:10:28 -0700125 return -ENOMEM;
126 }
127 }
128 fuse_put_request(fc, req);
129 if (err && err != -ENOENT)
130 return err;
131
132 if (inode) {
133 struct fuse_inode *fi = get_fuse_inode(inode);
134 entry->d_time = time_to_jiffies(outarg.entry_valid,
135 outarg.entry_valid_nsec);
136 fi->i_time = time_to_jiffies(outarg.attr_valid,
137 outarg.attr_valid_nsec);
138 }
139
140 entry->d_op = &fuse_dentry_operations;
141 *inodep = inode;
142 return 0;
143}
144
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700145void fuse_invalidate_attr(struct inode *inode)
146{
147 get_fuse_inode(inode)->i_time = jiffies - 1;
148}
149
150static void fuse_invalidate_entry(struct dentry *entry)
151{
152 d_invalidate(entry);
153 entry->d_time = jiffies - 1;
154}
155
Miklos Szeredifd72faa2005-11-07 00:59:51 -0800156static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode,
157 struct nameidata *nd)
158{
159 int err;
160 struct inode *inode;
161 struct fuse_conn *fc = get_fuse_conn(dir);
162 struct fuse_req *req;
163 struct fuse_open_in inarg;
164 struct fuse_open_out outopen;
165 struct fuse_entry_out outentry;
166 struct fuse_inode *fi;
167 struct fuse_file *ff;
168 struct file *file;
169 int flags = nd->intent.open.flags - 1;
170
171 err = -ENOSYS;
172 if (fc->no_create)
173 goto out;
174
175 err = -ENAMETOOLONG;
176 if (entry->d_name.len > FUSE_NAME_MAX)
177 goto out;
178
179 err = -EINTR;
180 req = fuse_get_request(fc);
181 if (!req)
182 goto out;
183
184 ff = fuse_file_alloc();
185 if (!ff)
186 goto out_put_request;
187
188 flags &= ~O_NOCTTY;
189 memset(&inarg, 0, sizeof(inarg));
190 inarg.flags = flags;
191 inarg.mode = mode;
192 req->in.h.opcode = FUSE_CREATE;
193 req->in.h.nodeid = get_node_id(dir);
194 req->inode = dir;
195 req->in.numargs = 2;
196 req->in.args[0].size = sizeof(inarg);
197 req->in.args[0].value = &inarg;
198 req->in.args[1].size = entry->d_name.len + 1;
199 req->in.args[1].value = entry->d_name.name;
200 req->out.numargs = 2;
201 req->out.args[0].size = sizeof(outentry);
202 req->out.args[0].value = &outentry;
203 req->out.args[1].size = sizeof(outopen);
204 req->out.args[1].value = &outopen;
205 request_send(fc, req);
206 err = req->out.h.error;
207 if (err) {
208 if (err == -ENOSYS)
209 fc->no_create = 1;
210 goto out_free_ff;
211 }
212
213 err = -EIO;
Miklos Szeredi2827d0b22005-11-28 13:44:16 -0800214 if (!S_ISREG(outentry.attr.mode) || invalid_nodeid(outentry.nodeid))
Miklos Szeredifd72faa2005-11-07 00:59:51 -0800215 goto out_free_ff;
216
217 inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation,
218 &outentry.attr);
219 err = -ENOMEM;
220 if (!inode) {
221 flags &= ~(O_CREAT | O_EXCL | O_TRUNC);
222 ff->fh = outopen.fh;
223 fuse_send_release(fc, ff, outentry.nodeid, NULL, flags, 0);
224 goto out_put_request;
225 }
226 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);
234 file = lookup_instantiate_filp(nd, entry, generic_file_open);
235 if (IS_ERR(file)) {
236 ff->fh = outopen.fh;
237 fuse_send_release(fc, ff, outentry.nodeid, inode, flags, 0);
238 return PTR_ERR(file);
239 }
240 fuse_finish_open(inode, file, ff, &outopen);
241 return 0;
242
243 out_free_ff:
244 fuse_file_free(ff);
245 out_put_request:
246 fuse_put_request(fc, req);
247 out:
248 return err;
249}
250
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700251static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req,
252 struct inode *dir, struct dentry *entry,
253 int mode)
254{
255 struct fuse_entry_out outarg;
256 struct inode *inode;
257 struct fuse_inode *fi;
258 int err;
259
260 req->in.h.nodeid = get_node_id(dir);
261 req->inode = dir;
262 req->out.numargs = 1;
263 req->out.args[0].size = sizeof(outarg);
264 req->out.args[0].value = &outarg;
265 request_send(fc, req);
266 err = req->out.h.error;
267 if (err) {
268 fuse_put_request(fc, req);
269 return err;
270 }
Miklos Szeredi2827d0b22005-11-28 13:44:16 -0800271 if (invalid_nodeid(outarg.nodeid)) {
Miklos Szerediee4e5272005-09-27 21:45:21 -0700272 fuse_put_request(fc, req);
273 return -EIO;
274 }
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700275 inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
276 &outarg.attr);
277 if (!inode) {
278 fuse_send_forget(fc, req, outarg.nodeid, 1);
279 return -ENOMEM;
280 }
281 fuse_put_request(fc, req);
282
283 /* Don't allow userspace to do really stupid things... */
Miklos Szeredif007d5c2005-11-28 13:44:16 -0800284 if (((inode->i_mode ^ mode) & S_IFMT) || dir_alias(inode)) {
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700285 iput(inode);
286 return -EIO;
287 }
288
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);
297 fuse_invalidate_attr(dir);
298 return 0;
299}
300
301static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode,
302 dev_t rdev)
303{
304 struct fuse_mknod_in inarg;
305 struct fuse_conn *fc = get_fuse_conn(dir);
306 struct fuse_req *req = fuse_get_request(fc);
307 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700308 return -EINTR;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700309
310 memset(&inarg, 0, sizeof(inarg));
311 inarg.mode = mode;
312 inarg.rdev = new_encode_dev(rdev);
313 req->in.h.opcode = FUSE_MKNOD;
314 req->in.numargs = 2;
315 req->in.args[0].size = sizeof(inarg);
316 req->in.args[0].value = &inarg;
317 req->in.args[1].size = entry->d_name.len + 1;
318 req->in.args[1].value = entry->d_name.name;
319 return create_new_entry(fc, req, dir, entry, mode);
320}
321
322static int fuse_create(struct inode *dir, struct dentry *entry, int mode,
323 struct nameidata *nd)
324{
Miklos Szeredifd72faa2005-11-07 00:59:51 -0800325 if (nd && (nd->flags & LOOKUP_CREATE)) {
326 int err = fuse_create_open(dir, entry, mode, nd);
327 if (err != -ENOSYS)
328 return err;
329 /* Fall back on mknod */
330 }
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700331 return fuse_mknod(dir, entry, mode, 0);
332}
333
334static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode)
335{
336 struct fuse_mkdir_in inarg;
337 struct fuse_conn *fc = get_fuse_conn(dir);
338 struct fuse_req *req = fuse_get_request(fc);
339 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700340 return -EINTR;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700341
342 memset(&inarg, 0, sizeof(inarg));
343 inarg.mode = mode;
344 req->in.h.opcode = FUSE_MKDIR;
345 req->in.numargs = 2;
346 req->in.args[0].size = sizeof(inarg);
347 req->in.args[0].value = &inarg;
348 req->in.args[1].size = entry->d_name.len + 1;
349 req->in.args[1].value = entry->d_name.name;
350 return create_new_entry(fc, req, dir, entry, S_IFDIR);
351}
352
353static int fuse_symlink(struct inode *dir, struct dentry *entry,
354 const char *link)
355{
356 struct fuse_conn *fc = get_fuse_conn(dir);
357 unsigned len = strlen(link) + 1;
358 struct fuse_req *req;
359
360 if (len > FUSE_SYMLINK_MAX)
361 return -ENAMETOOLONG;
362
363 req = fuse_get_request(fc);
364 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700365 return -EINTR;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700366
367 req->in.h.opcode = FUSE_SYMLINK;
368 req->in.numargs = 2;
369 req->in.args[0].size = entry->d_name.len + 1;
370 req->in.args[0].value = entry->d_name.name;
371 req->in.args[1].size = len;
372 req->in.args[1].value = link;
373 return create_new_entry(fc, req, dir, entry, S_IFLNK);
374}
375
376static int fuse_unlink(struct inode *dir, struct dentry *entry)
377{
378 int err;
379 struct fuse_conn *fc = get_fuse_conn(dir);
380 struct fuse_req *req = fuse_get_request(fc);
381 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700382 return -EINTR;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700383
384 req->in.h.opcode = FUSE_UNLINK;
385 req->in.h.nodeid = get_node_id(dir);
386 req->inode = dir;
387 req->in.numargs = 1;
388 req->in.args[0].size = entry->d_name.len + 1;
389 req->in.args[0].value = entry->d_name.name;
390 request_send(fc, req);
391 err = req->out.h.error;
392 fuse_put_request(fc, req);
393 if (!err) {
394 struct inode *inode = entry->d_inode;
395
396 /* Set nlink to zero so the inode can be cleared, if
397 the inode does have more links this will be
398 discovered at the next lookup/getattr */
399 inode->i_nlink = 0;
400 fuse_invalidate_attr(inode);
401 fuse_invalidate_attr(dir);
402 } else if (err == -EINTR)
403 fuse_invalidate_entry(entry);
404 return err;
405}
406
407static int fuse_rmdir(struct inode *dir, struct dentry *entry)
408{
409 int err;
410 struct fuse_conn *fc = get_fuse_conn(dir);
411 struct fuse_req *req = fuse_get_request(fc);
412 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700413 return -EINTR;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700414
415 req->in.h.opcode = FUSE_RMDIR;
416 req->in.h.nodeid = get_node_id(dir);
417 req->inode = dir;
418 req->in.numargs = 1;
419 req->in.args[0].size = entry->d_name.len + 1;
420 req->in.args[0].value = entry->d_name.name;
421 request_send(fc, req);
422 err = req->out.h.error;
423 fuse_put_request(fc, req);
424 if (!err) {
425 entry->d_inode->i_nlink = 0;
426 fuse_invalidate_attr(dir);
427 } else if (err == -EINTR)
428 fuse_invalidate_entry(entry);
429 return err;
430}
431
432static int fuse_rename(struct inode *olddir, struct dentry *oldent,
433 struct inode *newdir, struct dentry *newent)
434{
435 int err;
436 struct fuse_rename_in inarg;
437 struct fuse_conn *fc = get_fuse_conn(olddir);
438 struct fuse_req *req = fuse_get_request(fc);
439 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700440 return -EINTR;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700441
442 memset(&inarg, 0, sizeof(inarg));
443 inarg.newdir = get_node_id(newdir);
444 req->in.h.opcode = FUSE_RENAME;
445 req->in.h.nodeid = get_node_id(olddir);
446 req->inode = olddir;
447 req->inode2 = newdir;
448 req->in.numargs = 3;
449 req->in.args[0].size = sizeof(inarg);
450 req->in.args[0].value = &inarg;
451 req->in.args[1].size = oldent->d_name.len + 1;
452 req->in.args[1].value = oldent->d_name.name;
453 req->in.args[2].size = newent->d_name.len + 1;
454 req->in.args[2].value = newent->d_name.name;
455 request_send(fc, req);
456 err = req->out.h.error;
457 fuse_put_request(fc, req);
458 if (!err) {
459 fuse_invalidate_attr(olddir);
460 if (olddir != newdir)
461 fuse_invalidate_attr(newdir);
462 } else if (err == -EINTR) {
463 /* If request was interrupted, DEITY only knows if the
464 rename actually took place. If the invalidation
465 fails (e.g. some process has CWD under the renamed
466 directory), then there can be inconsistency between
467 the dcache and the real filesystem. Tough luck. */
468 fuse_invalidate_entry(oldent);
469 if (newent->d_inode)
470 fuse_invalidate_entry(newent);
471 }
472
473 return err;
474}
475
476static int fuse_link(struct dentry *entry, struct inode *newdir,
477 struct dentry *newent)
478{
479 int err;
480 struct fuse_link_in inarg;
481 struct inode *inode = entry->d_inode;
482 struct fuse_conn *fc = get_fuse_conn(inode);
483 struct fuse_req *req = fuse_get_request(fc);
484 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700485 return -EINTR;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700486
487 memset(&inarg, 0, sizeof(inarg));
488 inarg.oldnodeid = get_node_id(inode);
489 req->in.h.opcode = FUSE_LINK;
490 req->inode2 = inode;
491 req->in.numargs = 2;
492 req->in.args[0].size = sizeof(inarg);
493 req->in.args[0].value = &inarg;
494 req->in.args[1].size = newent->d_name.len + 1;
495 req->in.args[1].value = newent->d_name.name;
496 err = create_new_entry(fc, req, newdir, newent, inode->i_mode);
497 /* Contrary to "normal" filesystems it can happen that link
498 makes two "logical" inodes point to the same "physical"
499 inode. We invalidate the attributes of the old one, so it
500 will reflect changes in the backing inode (link count,
501 etc.)
502 */
503 if (!err || err == -EINTR)
504 fuse_invalidate_attr(inode);
505 return err;
506}
507
Miklos Szeredie5e55582005-09-09 13:10:28 -0700508int fuse_do_getattr(struct inode *inode)
509{
510 int err;
511 struct fuse_attr_out arg;
512 struct fuse_conn *fc = get_fuse_conn(inode);
513 struct fuse_req *req = fuse_get_request(fc);
514 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700515 return -EINTR;
Miklos Szeredie5e55582005-09-09 13:10:28 -0700516
517 req->in.h.opcode = FUSE_GETATTR;
518 req->in.h.nodeid = get_node_id(inode);
519 req->inode = inode;
520 req->out.numargs = 1;
521 req->out.args[0].size = sizeof(arg);
522 req->out.args[0].value = &arg;
523 request_send(fc, req);
524 err = req->out.h.error;
525 fuse_put_request(fc, req);
526 if (!err) {
527 if ((inode->i_mode ^ arg.attr.mode) & S_IFMT) {
528 make_bad_inode(inode);
529 err = -EIO;
530 } else {
531 struct fuse_inode *fi = get_fuse_inode(inode);
532 fuse_change_attributes(inode, &arg.attr);
533 fi->i_time = time_to_jiffies(arg.attr_valid,
534 arg.attr_valid_nsec);
535 }
536 }
537 return err;
538}
539
Miklos Szeredi87729a52005-09-09 13:10:34 -0700540/*
541 * Calling into a user-controlled filesystem gives the filesystem
542 * daemon ptrace-like capabilities over the requester process. This
543 * means, that the filesystem daemon is able to record the exact
544 * filesystem operations performed, and can also control the behavior
545 * of the requester process in otherwise impossible ways. For example
546 * it can delay the operation for arbitrary length of time allowing
547 * DoS against the requester.
548 *
549 * For this reason only those processes can call into the filesystem,
550 * for which the owner of the mount has ptrace privilege. This
551 * excludes processes started by other users, suid or sgid processes.
552 */
553static int fuse_allow_task(struct fuse_conn *fc, struct task_struct *task)
554{
555 if (fc->flags & FUSE_ALLOW_OTHER)
556 return 1;
557
558 if (task->euid == fc->user_id &&
559 task->suid == fc->user_id &&
560 task->uid == fc->user_id &&
561 task->egid == fc->group_id &&
562 task->sgid == fc->group_id &&
563 task->gid == fc->group_id)
564 return 1;
565
566 return 0;
567}
568
Miklos Szeredie5e55582005-09-09 13:10:28 -0700569static int fuse_revalidate(struct dentry *entry)
570{
571 struct inode *inode = entry->d_inode;
572 struct fuse_inode *fi = get_fuse_inode(inode);
573 struct fuse_conn *fc = get_fuse_conn(inode);
574
Miklos Szeredi87729a52005-09-09 13:10:34 -0700575 if (!fuse_allow_task(fc, current))
576 return -EACCES;
577 if (get_node_id(inode) != FUSE_ROOT_ID &&
578 time_before_eq(jiffies, fi->i_time))
Miklos Szeredie5e55582005-09-09 13:10:28 -0700579 return 0;
580
581 return fuse_do_getattr(inode);
582}
583
Miklos Szeredi31d40d72005-11-07 00:59:50 -0800584static int fuse_access(struct inode *inode, int mask)
585{
586 struct fuse_conn *fc = get_fuse_conn(inode);
587 struct fuse_req *req;
588 struct fuse_access_in inarg;
589 int err;
590
591 if (fc->no_access)
592 return 0;
593
594 req = fuse_get_request(fc);
595 if (!req)
596 return -EINTR;
597
598 memset(&inarg, 0, sizeof(inarg));
599 inarg.mask = mask;
600 req->in.h.opcode = FUSE_ACCESS;
601 req->in.h.nodeid = get_node_id(inode);
602 req->inode = inode;
603 req->in.numargs = 1;
604 req->in.args[0].size = sizeof(inarg);
605 req->in.args[0].value = &inarg;
606 request_send(fc, req);
607 err = req->out.h.error;
608 fuse_put_request(fc, req);
609 if (err == -ENOSYS) {
610 fc->no_access = 1;
611 err = 0;
612 }
613 return err;
614}
615
Miklos Szeredie5e55582005-09-09 13:10:28 -0700616static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd)
617{
618 struct fuse_conn *fc = get_fuse_conn(inode);
619
Miklos Szeredi87729a52005-09-09 13:10:34 -0700620 if (!fuse_allow_task(fc, current))
Miklos Szeredie5e55582005-09-09 13:10:28 -0700621 return -EACCES;
Miklos Szeredi1e9a4ed2005-09-09 13:10:31 -0700622 else if (fc->flags & FUSE_DEFAULT_PERMISSIONS) {
623 int err = generic_permission(inode, mask, NULL);
624
625 /* If permission is denied, try to refresh file
626 attributes. This is also needed, because the root
627 node will at first have no permissions */
628 if (err == -EACCES) {
629 err = fuse_do_getattr(inode);
630 if (!err)
631 err = generic_permission(inode, mask, NULL);
632 }
633
634 /* FIXME: Need some mechanism to revoke permissions:
635 currently if the filesystem suddenly changes the
636 file mode, we will not be informed about it, and
637 continue to allow access to the file/directory.
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
643 return err;
644 } else {
Miklos Szeredie5e55582005-09-09 13:10:28 -0700645 int mode = inode->i_mode;
Miklos Szeredie5e55582005-09-09 13:10:28 -0700646 if ((mask & MAY_EXEC) && !S_ISDIR(mode) && !(mode & S_IXUGO))
647 return -EACCES;
Miklos Szeredi31d40d72005-11-07 00:59:50 -0800648
649 if (nd && (nd->flags & LOOKUP_ACCESS))
650 return fuse_access(inode, mask);
Miklos Szeredie5e55582005-09-09 13:10:28 -0700651 return 0;
652 }
653}
654
655static int parse_dirfile(char *buf, size_t nbytes, struct file *file,
656 void *dstbuf, filldir_t filldir)
657{
658 while (nbytes >= FUSE_NAME_OFFSET) {
659 struct fuse_dirent *dirent = (struct fuse_dirent *) buf;
660 size_t reclen = FUSE_DIRENT_SIZE(dirent);
661 int over;
662 if (!dirent->namelen || dirent->namelen > FUSE_NAME_MAX)
663 return -EIO;
664 if (reclen > nbytes)
665 break;
666
667 over = filldir(dstbuf, dirent->name, dirent->namelen,
668 file->f_pos, dirent->ino, dirent->type);
669 if (over)
670 break;
671
672 buf += reclen;
673 nbytes -= reclen;
674 file->f_pos = dirent->off;
675 }
676
677 return 0;
678}
679
Miklos Szeredi04730fe2005-09-09 13:10:36 -0700680static inline size_t fuse_send_readdir(struct fuse_req *req, struct file *file,
681 struct inode *inode, loff_t pos,
682 size_t count)
Miklos Szeredie5e55582005-09-09 13:10:28 -0700683{
Miklos Szeredi04730fe2005-09-09 13:10:36 -0700684 return fuse_send_read_common(req, file, inode, pos, count, 1);
Miklos Szeredie5e55582005-09-09 13:10:28 -0700685}
686
687static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
688{
Miklos Szeredi04730fe2005-09-09 13:10:36 -0700689 int err;
690 size_t nbytes;
691 struct page *page;
692 struct inode *inode = file->f_dentry->d_inode;
693 struct fuse_conn *fc = get_fuse_conn(inode);
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700694 struct fuse_req *req = fuse_get_request(fc);
Miklos Szeredi04730fe2005-09-09 13:10:36 -0700695 if (!req)
696 return -EINTR;
Miklos Szeredie5e55582005-09-09 13:10:28 -0700697
Miklos Szeredi04730fe2005-09-09 13:10:36 -0700698 page = alloc_page(GFP_KERNEL);
699 if (!page) {
700 fuse_put_request(fc, req);
Miklos Szeredie5e55582005-09-09 13:10:28 -0700701 return -ENOMEM;
Miklos Szeredi04730fe2005-09-09 13:10:36 -0700702 }
703 req->num_pages = 1;
704 req->pages[0] = page;
705 nbytes = fuse_send_readdir(req, file, inode, file->f_pos, PAGE_SIZE);
706 err = req->out.h.error;
707 fuse_put_request(fc, req);
708 if (!err)
709 err = parse_dirfile(page_address(page), nbytes, file, dstbuf,
710 filldir);
Miklos Szeredie5e55582005-09-09 13:10:28 -0700711
Miklos Szeredi04730fe2005-09-09 13:10:36 -0700712 __free_page(page);
Miklos Szeredib36c31b2005-09-09 13:10:38 -0700713 fuse_invalidate_attr(inode); /* atime changed */
Miklos Szeredi04730fe2005-09-09 13:10:36 -0700714 return err;
Miklos Szeredie5e55582005-09-09 13:10:28 -0700715}
716
717static char *read_link(struct dentry *dentry)
718{
719 struct inode *inode = dentry->d_inode;
720 struct fuse_conn *fc = get_fuse_conn(inode);
721 struct fuse_req *req = fuse_get_request(fc);
722 char *link;
723
724 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700725 return ERR_PTR(-EINTR);
Miklos Szeredie5e55582005-09-09 13:10:28 -0700726
727 link = (char *) __get_free_page(GFP_KERNEL);
728 if (!link) {
729 link = ERR_PTR(-ENOMEM);
730 goto out;
731 }
732 req->in.h.opcode = FUSE_READLINK;
733 req->in.h.nodeid = get_node_id(inode);
734 req->inode = inode;
735 req->out.argvar = 1;
736 req->out.numargs = 1;
737 req->out.args[0].size = PAGE_SIZE - 1;
738 req->out.args[0].value = link;
739 request_send(fc, req);
740 if (req->out.h.error) {
741 free_page((unsigned long) link);
742 link = ERR_PTR(req->out.h.error);
743 } else
744 link[req->out.args[0].size] = '\0';
745 out:
746 fuse_put_request(fc, req);
Miklos Szeredib36c31b2005-09-09 13:10:38 -0700747 fuse_invalidate_attr(inode); /* atime changed */
Miklos Szeredie5e55582005-09-09 13:10:28 -0700748 return link;
749}
750
751static void free_link(char *link)
752{
753 if (!IS_ERR(link))
754 free_page((unsigned long) link);
755}
756
757static void *fuse_follow_link(struct dentry *dentry, struct nameidata *nd)
758{
759 nd_set_link(nd, read_link(dentry));
760 return NULL;
761}
762
763static void fuse_put_link(struct dentry *dentry, struct nameidata *nd, void *c)
764{
765 free_link(nd_get_link(nd));
766}
767
768static int fuse_dir_open(struct inode *inode, struct file *file)
769{
Miklos Szeredi04730fe2005-09-09 13:10:36 -0700770 return fuse_open_common(inode, file, 1);
Miklos Szeredie5e55582005-09-09 13:10:28 -0700771}
772
773static int fuse_dir_release(struct inode *inode, struct file *file)
774{
Miklos Szeredi04730fe2005-09-09 13:10:36 -0700775 return fuse_release_common(inode, file, 1);
Miklos Szeredie5e55582005-09-09 13:10:28 -0700776}
777
Miklos Szeredi82547982005-09-09 13:10:38 -0700778static int fuse_dir_fsync(struct file *file, struct dentry *de, int datasync)
779{
780 /* nfsd can call this with no file */
781 return file ? fuse_fsync_common(file, de, datasync, 1) : 0;
782}
783
Miklos Szeredibefc6492005-11-07 00:59:52 -0800784static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg)
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700785{
786 unsigned ivalid = iattr->ia_valid;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700787
788 if (ivalid & ATTR_MODE)
Miklos Szeredibefc6492005-11-07 00:59:52 -0800789 arg->valid |= FATTR_MODE, arg->mode = iattr->ia_mode;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700790 if (ivalid & ATTR_UID)
Miklos Szeredibefc6492005-11-07 00:59:52 -0800791 arg->valid |= FATTR_UID, arg->uid = iattr->ia_uid;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700792 if (ivalid & ATTR_GID)
Miklos Szeredibefc6492005-11-07 00:59:52 -0800793 arg->valid |= FATTR_GID, arg->gid = iattr->ia_gid;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700794 if (ivalid & ATTR_SIZE)
Miklos Szeredibefc6492005-11-07 00:59:52 -0800795 arg->valid |= FATTR_SIZE, arg->size = iattr->ia_size;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700796 /* You can only _set_ these together (they may change by themselves) */
797 if ((ivalid & (ATTR_ATIME | ATTR_MTIME)) == (ATTR_ATIME | ATTR_MTIME)) {
Miklos Szeredibefc6492005-11-07 00:59:52 -0800798 arg->valid |= FATTR_ATIME | FATTR_MTIME;
799 arg->atime = iattr->ia_atime.tv_sec;
800 arg->mtime = iattr->ia_mtime.tv_sec;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700801 }
Miklos Szeredibefc6492005-11-07 00:59:52 -0800802 if (ivalid & ATTR_FILE) {
803 struct fuse_file *ff = iattr->ia_file->private_data;
804 arg->valid |= FATTR_FH;
805 arg->fh = ff->fh;
806 }
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700807}
808
809static int fuse_setattr(struct dentry *entry, struct iattr *attr)
810{
811 struct inode *inode = entry->d_inode;
812 struct fuse_conn *fc = get_fuse_conn(inode);
813 struct fuse_inode *fi = get_fuse_inode(inode);
814 struct fuse_req *req;
815 struct fuse_setattr_in inarg;
816 struct fuse_attr_out outarg;
817 int err;
818 int is_truncate = 0;
819
Miklos Szeredi1e9a4ed2005-09-09 13:10:31 -0700820 if (fc->flags & FUSE_DEFAULT_PERMISSIONS) {
821 err = inode_change_ok(inode, attr);
822 if (err)
823 return err;
824 }
825
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700826 if (attr->ia_valid & ATTR_SIZE) {
827 unsigned long limit;
828 is_truncate = 1;
829 limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur;
830 if (limit != RLIM_INFINITY && attr->ia_size > (loff_t) limit) {
831 send_sig(SIGXFSZ, current, 0);
832 return -EFBIG;
833 }
834 }
835
836 req = fuse_get_request(fc);
837 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700838 return -EINTR;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700839
840 memset(&inarg, 0, sizeof(inarg));
Miklos Szeredibefc6492005-11-07 00:59:52 -0800841 iattr_to_fattr(attr, &inarg);
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700842 req->in.h.opcode = FUSE_SETATTR;
843 req->in.h.nodeid = get_node_id(inode);
844 req->inode = inode;
845 req->in.numargs = 1;
846 req->in.args[0].size = sizeof(inarg);
847 req->in.args[0].value = &inarg;
848 req->out.numargs = 1;
849 req->out.args[0].size = sizeof(outarg);
850 req->out.args[0].value = &outarg;
851 request_send(fc, req);
852 err = req->out.h.error;
853 fuse_put_request(fc, req);
854 if (!err) {
855 if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
856 make_bad_inode(inode);
857 err = -EIO;
858 } else {
859 if (is_truncate) {
860 loff_t origsize = i_size_read(inode);
861 i_size_write(inode, outarg.attr.size);
862 if (origsize > outarg.attr.size)
863 vmtruncate(inode, outarg.attr.size);
864 }
865 fuse_change_attributes(inode, &outarg.attr);
866 fi->i_time = time_to_jiffies(outarg.attr_valid,
867 outarg.attr_valid_nsec);
868 }
869 } else if (err == -EINTR)
870 fuse_invalidate_attr(inode);
871
872 return err;
873}
874
Miklos Szeredie5e55582005-09-09 13:10:28 -0700875static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
876 struct kstat *stat)
877{
878 struct inode *inode = entry->d_inode;
879 int err = fuse_revalidate(entry);
880 if (!err)
881 generic_fillattr(inode, stat);
882
883 return err;
884}
885
886static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
887 struct nameidata *nd)
888{
889 struct inode *inode;
Miklos Szeredifd72faa2005-11-07 00:59:51 -0800890 int err;
891
892 err = fuse_lookup_iget(dir, entry, &inode);
Miklos Szeredie5e55582005-09-09 13:10:28 -0700893 if (err)
894 return ERR_PTR(err);
Miklos Szeredif007d5c2005-11-28 13:44:16 -0800895 if (inode && dir_alias(inode)) {
896 iput(inode);
897 return ERR_PTR(-EIO);
Miklos Szeredie5e55582005-09-09 13:10:28 -0700898 }
Miklos Szeredif12ec442005-10-30 15:02:25 -0800899 d_add(entry, inode);
900 return NULL;
Miklos Szeredie5e55582005-09-09 13:10:28 -0700901}
902
Miklos Szeredi92a87802005-09-09 13:10:31 -0700903static int fuse_setxattr(struct dentry *entry, const char *name,
904 const void *value, size_t size, int flags)
905{
906 struct inode *inode = entry->d_inode;
907 struct fuse_conn *fc = get_fuse_conn(inode);
908 struct fuse_req *req;
909 struct fuse_setxattr_in inarg;
910 int err;
911
912 if (size > FUSE_XATTR_SIZE_MAX)
913 return -E2BIG;
914
915 if (fc->no_setxattr)
916 return -EOPNOTSUPP;
917
918 req = fuse_get_request(fc);
919 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700920 return -EINTR;
Miklos Szeredi92a87802005-09-09 13:10:31 -0700921
922 memset(&inarg, 0, sizeof(inarg));
923 inarg.size = size;
924 inarg.flags = flags;
925 req->in.h.opcode = FUSE_SETXATTR;
926 req->in.h.nodeid = get_node_id(inode);
927 req->inode = inode;
928 req->in.numargs = 3;
929 req->in.args[0].size = sizeof(inarg);
930 req->in.args[0].value = &inarg;
931 req->in.args[1].size = strlen(name) + 1;
932 req->in.args[1].value = name;
933 req->in.args[2].size = size;
934 req->in.args[2].value = value;
935 request_send(fc, req);
936 err = req->out.h.error;
937 fuse_put_request(fc, req);
938 if (err == -ENOSYS) {
939 fc->no_setxattr = 1;
940 err = -EOPNOTSUPP;
941 }
942 return err;
943}
944
945static ssize_t fuse_getxattr(struct dentry *entry, const char *name,
946 void *value, size_t size)
947{
948 struct inode *inode = entry->d_inode;
949 struct fuse_conn *fc = get_fuse_conn(inode);
950 struct fuse_req *req;
951 struct fuse_getxattr_in inarg;
952 struct fuse_getxattr_out outarg;
953 ssize_t ret;
954
955 if (fc->no_getxattr)
956 return -EOPNOTSUPP;
957
958 req = fuse_get_request(fc);
959 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700960 return -EINTR;
Miklos Szeredi92a87802005-09-09 13:10:31 -0700961
962 memset(&inarg, 0, sizeof(inarg));
963 inarg.size = size;
964 req->in.h.opcode = FUSE_GETXATTR;
965 req->in.h.nodeid = get_node_id(inode);
966 req->inode = inode;
967 req->in.numargs = 2;
968 req->in.args[0].size = sizeof(inarg);
969 req->in.args[0].value = &inarg;
970 req->in.args[1].size = strlen(name) + 1;
971 req->in.args[1].value = name;
972 /* This is really two different operations rolled into one */
973 req->out.numargs = 1;
974 if (size) {
975 req->out.argvar = 1;
976 req->out.args[0].size = size;
977 req->out.args[0].value = value;
978 } else {
979 req->out.args[0].size = sizeof(outarg);
980 req->out.args[0].value = &outarg;
981 }
982 request_send(fc, req);
983 ret = req->out.h.error;
984 if (!ret)
985 ret = size ? req->out.args[0].size : outarg.size;
986 else {
987 if (ret == -ENOSYS) {
988 fc->no_getxattr = 1;
989 ret = -EOPNOTSUPP;
990 }
991 }
992 fuse_put_request(fc, req);
993 return ret;
994}
995
996static ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
997{
998 struct inode *inode = entry->d_inode;
999 struct fuse_conn *fc = get_fuse_conn(inode);
1000 struct fuse_req *req;
1001 struct fuse_getxattr_in inarg;
1002 struct fuse_getxattr_out outarg;
1003 ssize_t ret;
1004
1005 if (fc->no_listxattr)
1006 return -EOPNOTSUPP;
1007
1008 req = fuse_get_request(fc);
1009 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -07001010 return -EINTR;
Miklos Szeredi92a87802005-09-09 13:10:31 -07001011
1012 memset(&inarg, 0, sizeof(inarg));
1013 inarg.size = size;
1014 req->in.h.opcode = FUSE_LISTXATTR;
1015 req->in.h.nodeid = get_node_id(inode);
1016 req->inode = inode;
1017 req->in.numargs = 1;
1018 req->in.args[0].size = sizeof(inarg);
1019 req->in.args[0].value = &inarg;
1020 /* This is really two different operations rolled into one */
1021 req->out.numargs = 1;
1022 if (size) {
1023 req->out.argvar = 1;
1024 req->out.args[0].size = size;
1025 req->out.args[0].value = list;
1026 } else {
1027 req->out.args[0].size = sizeof(outarg);
1028 req->out.args[0].value = &outarg;
1029 }
1030 request_send(fc, req);
1031 ret = req->out.h.error;
1032 if (!ret)
1033 ret = size ? req->out.args[0].size : outarg.size;
1034 else {
1035 if (ret == -ENOSYS) {
1036 fc->no_listxattr = 1;
1037 ret = -EOPNOTSUPP;
1038 }
1039 }
1040 fuse_put_request(fc, req);
1041 return ret;
1042}
1043
1044static int fuse_removexattr(struct dentry *entry, const char *name)
1045{
1046 struct inode *inode = entry->d_inode;
1047 struct fuse_conn *fc = get_fuse_conn(inode);
1048 struct fuse_req *req;
1049 int err;
1050
1051 if (fc->no_removexattr)
1052 return -EOPNOTSUPP;
1053
1054 req = fuse_get_request(fc);
1055 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -07001056 return -EINTR;
Miklos Szeredi92a87802005-09-09 13:10:31 -07001057
1058 req->in.h.opcode = FUSE_REMOVEXATTR;
1059 req->in.h.nodeid = get_node_id(inode);
1060 req->inode = inode;
1061 req->in.numargs = 1;
1062 req->in.args[0].size = strlen(name) + 1;
1063 req->in.args[0].value = name;
1064 request_send(fc, req);
1065 err = req->out.h.error;
1066 fuse_put_request(fc, req);
1067 if (err == -ENOSYS) {
1068 fc->no_removexattr = 1;
1069 err = -EOPNOTSUPP;
1070 }
1071 return err;
1072}
1073
Miklos Szeredie5e55582005-09-09 13:10:28 -07001074static struct inode_operations fuse_dir_inode_operations = {
1075 .lookup = fuse_lookup,
Miklos Szeredi9e6268d2005-09-09 13:10:29 -07001076 .mkdir = fuse_mkdir,
1077 .symlink = fuse_symlink,
1078 .unlink = fuse_unlink,
1079 .rmdir = fuse_rmdir,
1080 .rename = fuse_rename,
1081 .link = fuse_link,
1082 .setattr = fuse_setattr,
1083 .create = fuse_create,
1084 .mknod = fuse_mknod,
Miklos Szeredie5e55582005-09-09 13:10:28 -07001085 .permission = fuse_permission,
1086 .getattr = fuse_getattr,
Miklos Szeredi92a87802005-09-09 13:10:31 -07001087 .setxattr = fuse_setxattr,
1088 .getxattr = fuse_getxattr,
1089 .listxattr = fuse_listxattr,
1090 .removexattr = fuse_removexattr,
Miklos Szeredie5e55582005-09-09 13:10:28 -07001091};
1092
1093static struct file_operations fuse_dir_operations = {
Miklos Szeredib6aeade2005-09-09 13:10:30 -07001094 .llseek = generic_file_llseek,
Miklos Szeredie5e55582005-09-09 13:10:28 -07001095 .read = generic_read_dir,
1096 .readdir = fuse_readdir,
1097 .open = fuse_dir_open,
1098 .release = fuse_dir_release,
Miklos Szeredi82547982005-09-09 13:10:38 -07001099 .fsync = fuse_dir_fsync,
Miklos Szeredie5e55582005-09-09 13:10:28 -07001100};
1101
1102static struct inode_operations fuse_common_inode_operations = {
Miklos Szeredi9e6268d2005-09-09 13:10:29 -07001103 .setattr = fuse_setattr,
Miklos Szeredie5e55582005-09-09 13:10:28 -07001104 .permission = fuse_permission,
1105 .getattr = fuse_getattr,
Miklos Szeredi92a87802005-09-09 13:10:31 -07001106 .setxattr = fuse_setxattr,
1107 .getxattr = fuse_getxattr,
1108 .listxattr = fuse_listxattr,
1109 .removexattr = fuse_removexattr,
Miklos Szeredie5e55582005-09-09 13:10:28 -07001110};
1111
1112static struct inode_operations fuse_symlink_inode_operations = {
Miklos Szeredi9e6268d2005-09-09 13:10:29 -07001113 .setattr = fuse_setattr,
Miklos Szeredie5e55582005-09-09 13:10:28 -07001114 .follow_link = fuse_follow_link,
1115 .put_link = fuse_put_link,
1116 .readlink = generic_readlink,
1117 .getattr = fuse_getattr,
Miklos Szeredi92a87802005-09-09 13:10:31 -07001118 .setxattr = fuse_setxattr,
1119 .getxattr = fuse_getxattr,
1120 .listxattr = fuse_listxattr,
1121 .removexattr = fuse_removexattr,
Miklos Szeredie5e55582005-09-09 13:10:28 -07001122};
1123
1124void fuse_init_common(struct inode *inode)
1125{
1126 inode->i_op = &fuse_common_inode_operations;
1127}
1128
1129void fuse_init_dir(struct inode *inode)
1130{
1131 inode->i_op = &fuse_dir_inode_operations;
1132 inode->i_fop = &fuse_dir_operations;
1133}
1134
1135void fuse_init_symlink(struct inode *inode)
1136{
1137 inode->i_op = &fuse_symlink_inode_operations;
1138}