blob: 0d1438a9dab3538b0371698944d289eeaec55daf [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>
16
17static inline unsigned long time_to_jiffies(unsigned long sec,
18 unsigned long nsec)
19{
20 struct timespec ts = {sec, nsec};
21 return jiffies + timespec_to_jiffies(&ts);
22}
23
Miklos Szeredi0aa7c692006-01-06 00:19:34 -080024static void fuse_change_timeout(struct dentry *entry, struct fuse_entry_out *o)
25{
26 struct fuse_inode *fi = get_fuse_inode(entry->d_inode);
27 entry->d_time = time_to_jiffies(o->entry_valid, o->entry_valid_nsec);
28 fi->i_time = time_to_jiffies(o->attr_valid, o->attr_valid_nsec);
29}
30
Miklos Szeredie5e55582005-09-09 13:10:28 -070031static void fuse_lookup_init(struct fuse_req *req, struct inode *dir,
32 struct dentry *entry,
33 struct fuse_entry_out *outarg)
34{
35 req->in.h.opcode = FUSE_LOOKUP;
36 req->in.h.nodeid = get_node_id(dir);
37 req->inode = dir;
38 req->in.numargs = 1;
39 req->in.args[0].size = entry->d_name.len + 1;
40 req->in.args[0].value = entry->d_name.name;
41 req->out.numargs = 1;
42 req->out.args[0].size = sizeof(struct fuse_entry_out);
43 req->out.args[0].value = outarg;
44}
45
46static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
47{
48 if (!entry->d_inode || is_bad_inode(entry->d_inode))
49 return 0;
50 else if (time_after(jiffies, entry->d_time)) {
51 int err;
Miklos Szeredie5e55582005-09-09 13:10:28 -070052 struct fuse_entry_out outarg;
53 struct inode *inode = entry->d_inode;
54 struct fuse_inode *fi = get_fuse_inode(inode);
55 struct fuse_conn *fc = get_fuse_conn(inode);
Miklos Szeredi7c352bd2005-09-09 13:10:39 -070056 struct fuse_req *req = fuse_get_request(fc);
Miklos Szeredie5e55582005-09-09 13:10:28 -070057 if (!req)
58 return 0;
59
60 fuse_lookup_init(req, entry->d_parent->d_inode, entry, &outarg);
Miklos Szeredi7c352bd2005-09-09 13:10:39 -070061 request_send(fc, req);
Miklos Szeredie5e55582005-09-09 13:10:28 -070062 err = req->out.h.error;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -070063 if (!err) {
64 if (outarg.nodeid != get_node_id(inode)) {
65 fuse_send_forget(fc, req, outarg.nodeid, 1);
66 return 0;
67 }
68 fi->nlookup ++;
69 }
Miklos Szeredie5e55582005-09-09 13:10:28 -070070 fuse_put_request(fc, req);
Miklos Szeredi9e6268d2005-09-09 13:10:29 -070071 if (err || (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
Miklos Szeredie5e55582005-09-09 13:10:28 -070072 return 0;
73
74 fuse_change_attributes(inode, &outarg.attr);
Miklos Szeredi0aa7c692006-01-06 00:19:34 -080075 fuse_change_timeout(entry, &outarg);
Miklos Szeredie5e55582005-09-09 13:10:28 -070076 }
77 return 1;
78}
79
Miklos Szeredif007d5c2005-11-28 13:44:16 -080080static int dir_alias(struct inode *inode)
81{
82 if (S_ISDIR(inode->i_mode)) {
83 /* Don't allow creating an alias to a directory */
84 struct dentry *alias = d_find_alias(inode);
85 if (alias) {
86 dput(alias);
87 return 1;
88 }
89 }
90 return 0;
91}
92
Miklos Szeredi2827d0b22005-11-28 13:44:16 -080093static inline int invalid_nodeid(u64 nodeid)
94{
95 return !nodeid || nodeid == FUSE_ROOT_ID;
96}
97
Miklos Szeredie5e55582005-09-09 13:10:28 -070098static struct dentry_operations fuse_dentry_operations = {
99 .d_revalidate = fuse_dentry_revalidate,
100};
101
Miklos Szeredi0aa7c692006-01-06 00:19:34 -0800102static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
103 struct nameidata *nd)
Miklos Szeredie5e55582005-09-09 13:10:28 -0700104{
105 int err;
Miklos Szeredie5e55582005-09-09 13:10:28 -0700106 struct fuse_entry_out outarg;
107 struct inode *inode = NULL;
108 struct fuse_conn *fc = get_fuse_conn(dir);
109 struct fuse_req *req;
110
111 if (entry->d_name.len > FUSE_NAME_MAX)
Miklos Szeredi0aa7c692006-01-06 00:19:34 -0800112 return ERR_PTR(-ENAMETOOLONG);
Miklos Szeredie5e55582005-09-09 13:10:28 -0700113
114 req = fuse_get_request(fc);
115 if (!req)
Miklos Szeredi0aa7c692006-01-06 00:19:34 -0800116 return ERR_PTR(-EINTR);
Miklos Szeredie5e55582005-09-09 13:10:28 -0700117
118 fuse_lookup_init(req, dir, entry, &outarg);
119 request_send(fc, req);
Miklos Szeredie5e55582005-09-09 13:10:28 -0700120 err = req->out.h.error;
Miklos Szeredi2827d0b22005-11-28 13:44:16 -0800121 if (!err && invalid_nodeid(outarg.nodeid))
Miklos Szerediee4e5272005-09-27 21:45:21 -0700122 err = -EIO;
Miklos Szeredie5e55582005-09-09 13:10:28 -0700123 if (!err) {
124 inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700125 &outarg.attr);
Miklos Szeredie5e55582005-09-09 13:10:28 -0700126 if (!inode) {
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700127 fuse_send_forget(fc, req, outarg.nodeid, 1);
Miklos Szeredi0aa7c692006-01-06 00:19:34 -0800128 return ERR_PTR(-ENOMEM);
Miklos Szeredie5e55582005-09-09 13:10:28 -0700129 }
130 }
131 fuse_put_request(fc, req);
132 if (err && err != -ENOENT)
Miklos Szeredi0aa7c692006-01-06 00:19:34 -0800133 return ERR_PTR(err);
Miklos Szeredie5e55582005-09-09 13:10:28 -0700134
Miklos Szeredi0aa7c692006-01-06 00:19:34 -0800135 if (inode && dir_alias(inode)) {
136 iput(inode);
137 return ERR_PTR(-EIO);
Miklos Szeredie5e55582005-09-09 13:10:28 -0700138 }
Miklos Szeredi0aa7c692006-01-06 00:19:34 -0800139 d_add(entry, inode);
Miklos Szeredie5e55582005-09-09 13:10:28 -0700140 entry->d_op = &fuse_dentry_operations;
Miklos Szeredi0aa7c692006-01-06 00:19:34 -0800141 if (inode)
142 fuse_change_timeout(entry, &outarg);
143 return NULL;
Miklos Szeredie5e55582005-09-09 13:10:28 -0700144}
145
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700146void fuse_invalidate_attr(struct inode *inode)
147{
148 get_fuse_inode(inode)->i_time = jiffies - 1;
149}
150
151static void fuse_invalidate_entry(struct dentry *entry)
152{
153 d_invalidate(entry);
154 entry->d_time = jiffies - 1;
155}
156
Miklos Szeredifd72faa2005-11-07 00:59:51 -0800157static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode,
158 struct nameidata *nd)
159{
160 int err;
161 struct inode *inode;
162 struct fuse_conn *fc = get_fuse_conn(dir);
163 struct fuse_req *req;
164 struct fuse_open_in inarg;
165 struct fuse_open_out outopen;
166 struct fuse_entry_out outentry;
Miklos Szeredifd72faa2005-11-07 00:59:51 -0800167 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);
Miklos Szeredifd72faa2005-11-07 00:59:51 -0800227 d_instantiate(entry, inode);
Miklos Szeredi0aa7c692006-01-06 00:19:34 -0800228 fuse_change_timeout(entry, &outentry);
Miklos Szeredifd72faa2005-11-07 00:59:51 -0800229 file = lookup_instantiate_filp(nd, entry, generic_file_open);
230 if (IS_ERR(file)) {
231 ff->fh = outopen.fh;
232 fuse_send_release(fc, ff, outentry.nodeid, inode, flags, 0);
233 return PTR_ERR(file);
234 }
235 fuse_finish_open(inode, file, ff, &outopen);
236 return 0;
237
238 out_free_ff:
239 fuse_file_free(ff);
240 out_put_request:
241 fuse_put_request(fc, req);
242 out:
243 return err;
244}
245
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700246static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req,
247 struct inode *dir, struct dentry *entry,
248 int mode)
249{
250 struct fuse_entry_out outarg;
251 struct inode *inode;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700252 int err;
253
254 req->in.h.nodeid = get_node_id(dir);
255 req->inode = dir;
256 req->out.numargs = 1;
257 req->out.args[0].size = sizeof(outarg);
258 req->out.args[0].value = &outarg;
259 request_send(fc, req);
260 err = req->out.h.error;
261 if (err) {
262 fuse_put_request(fc, req);
263 return err;
264 }
Miklos Szeredi2827d0b22005-11-28 13:44:16 -0800265 if (invalid_nodeid(outarg.nodeid)) {
Miklos Szerediee4e5272005-09-27 21:45:21 -0700266 fuse_put_request(fc, req);
267 return -EIO;
268 }
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700269 inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
270 &outarg.attr);
271 if (!inode) {
272 fuse_send_forget(fc, req, outarg.nodeid, 1);
273 return -ENOMEM;
274 }
275 fuse_put_request(fc, req);
276
277 /* Don't allow userspace to do really stupid things... */
Miklos Szeredif007d5c2005-11-28 13:44:16 -0800278 if (((inode->i_mode ^ mode) & S_IFMT) || dir_alias(inode)) {
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700279 iput(inode);
280 return -EIO;
281 }
282
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700283 d_instantiate(entry, inode);
Miklos Szeredi0aa7c692006-01-06 00:19:34 -0800284 fuse_change_timeout(entry, &outarg);
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700285 fuse_invalidate_attr(dir);
286 return 0;
287}
288
289static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode,
290 dev_t rdev)
291{
292 struct fuse_mknod_in inarg;
293 struct fuse_conn *fc = get_fuse_conn(dir);
294 struct fuse_req *req = fuse_get_request(fc);
295 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700296 return -EINTR;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700297
298 memset(&inarg, 0, sizeof(inarg));
299 inarg.mode = mode;
300 inarg.rdev = new_encode_dev(rdev);
301 req->in.h.opcode = FUSE_MKNOD;
302 req->in.numargs = 2;
303 req->in.args[0].size = sizeof(inarg);
304 req->in.args[0].value = &inarg;
305 req->in.args[1].size = entry->d_name.len + 1;
306 req->in.args[1].value = entry->d_name.name;
307 return create_new_entry(fc, req, dir, entry, mode);
308}
309
310static int fuse_create(struct inode *dir, struct dentry *entry, int mode,
311 struct nameidata *nd)
312{
Miklos Szeredifd72faa2005-11-07 00:59:51 -0800313 if (nd && (nd->flags & LOOKUP_CREATE)) {
314 int err = fuse_create_open(dir, entry, mode, nd);
315 if (err != -ENOSYS)
316 return err;
317 /* Fall back on mknod */
318 }
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700319 return fuse_mknod(dir, entry, mode, 0);
320}
321
322static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode)
323{
324 struct fuse_mkdir_in inarg;
325 struct fuse_conn *fc = get_fuse_conn(dir);
326 struct fuse_req *req = fuse_get_request(fc);
327 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700328 return -EINTR;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700329
330 memset(&inarg, 0, sizeof(inarg));
331 inarg.mode = mode;
332 req->in.h.opcode = FUSE_MKDIR;
333 req->in.numargs = 2;
334 req->in.args[0].size = sizeof(inarg);
335 req->in.args[0].value = &inarg;
336 req->in.args[1].size = entry->d_name.len + 1;
337 req->in.args[1].value = entry->d_name.name;
338 return create_new_entry(fc, req, dir, entry, S_IFDIR);
339}
340
341static int fuse_symlink(struct inode *dir, struct dentry *entry,
342 const char *link)
343{
344 struct fuse_conn *fc = get_fuse_conn(dir);
345 unsigned len = strlen(link) + 1;
346 struct fuse_req *req;
347
348 if (len > FUSE_SYMLINK_MAX)
349 return -ENAMETOOLONG;
350
351 req = fuse_get_request(fc);
352 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700353 return -EINTR;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700354
355 req->in.h.opcode = FUSE_SYMLINK;
356 req->in.numargs = 2;
357 req->in.args[0].size = entry->d_name.len + 1;
358 req->in.args[0].value = entry->d_name.name;
359 req->in.args[1].size = len;
360 req->in.args[1].value = link;
361 return create_new_entry(fc, req, dir, entry, S_IFLNK);
362}
363
364static int fuse_unlink(struct inode *dir, struct dentry *entry)
365{
366 int err;
367 struct fuse_conn *fc = get_fuse_conn(dir);
368 struct fuse_req *req = fuse_get_request(fc);
369 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700370 return -EINTR;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700371
372 req->in.h.opcode = FUSE_UNLINK;
373 req->in.h.nodeid = get_node_id(dir);
374 req->inode = dir;
375 req->in.numargs = 1;
376 req->in.args[0].size = entry->d_name.len + 1;
377 req->in.args[0].value = entry->d_name.name;
378 request_send(fc, req);
379 err = req->out.h.error;
380 fuse_put_request(fc, req);
381 if (!err) {
382 struct inode *inode = entry->d_inode;
383
384 /* Set nlink to zero so the inode can be cleared, if
385 the inode does have more links this will be
386 discovered at the next lookup/getattr */
387 inode->i_nlink = 0;
388 fuse_invalidate_attr(inode);
389 fuse_invalidate_attr(dir);
390 } else if (err == -EINTR)
391 fuse_invalidate_entry(entry);
392 return err;
393}
394
395static int fuse_rmdir(struct inode *dir, struct dentry *entry)
396{
397 int err;
398 struct fuse_conn *fc = get_fuse_conn(dir);
399 struct fuse_req *req = fuse_get_request(fc);
400 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700401 return -EINTR;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700402
403 req->in.h.opcode = FUSE_RMDIR;
404 req->in.h.nodeid = get_node_id(dir);
405 req->inode = dir;
406 req->in.numargs = 1;
407 req->in.args[0].size = entry->d_name.len + 1;
408 req->in.args[0].value = entry->d_name.name;
409 request_send(fc, req);
410 err = req->out.h.error;
411 fuse_put_request(fc, req);
412 if (!err) {
413 entry->d_inode->i_nlink = 0;
414 fuse_invalidate_attr(dir);
415 } else if (err == -EINTR)
416 fuse_invalidate_entry(entry);
417 return err;
418}
419
420static int fuse_rename(struct inode *olddir, struct dentry *oldent,
421 struct inode *newdir, struct dentry *newent)
422{
423 int err;
424 struct fuse_rename_in inarg;
425 struct fuse_conn *fc = get_fuse_conn(olddir);
426 struct fuse_req *req = fuse_get_request(fc);
427 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700428 return -EINTR;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700429
430 memset(&inarg, 0, sizeof(inarg));
431 inarg.newdir = get_node_id(newdir);
432 req->in.h.opcode = FUSE_RENAME;
433 req->in.h.nodeid = get_node_id(olddir);
434 req->inode = olddir;
435 req->inode2 = newdir;
436 req->in.numargs = 3;
437 req->in.args[0].size = sizeof(inarg);
438 req->in.args[0].value = &inarg;
439 req->in.args[1].size = oldent->d_name.len + 1;
440 req->in.args[1].value = oldent->d_name.name;
441 req->in.args[2].size = newent->d_name.len + 1;
442 req->in.args[2].value = newent->d_name.name;
443 request_send(fc, req);
444 err = req->out.h.error;
445 fuse_put_request(fc, req);
446 if (!err) {
447 fuse_invalidate_attr(olddir);
448 if (olddir != newdir)
449 fuse_invalidate_attr(newdir);
450 } else if (err == -EINTR) {
451 /* If request was interrupted, DEITY only knows if the
452 rename actually took place. If the invalidation
453 fails (e.g. some process has CWD under the renamed
454 directory), then there can be inconsistency between
455 the dcache and the real filesystem. Tough luck. */
456 fuse_invalidate_entry(oldent);
457 if (newent->d_inode)
458 fuse_invalidate_entry(newent);
459 }
460
461 return err;
462}
463
464static int fuse_link(struct dentry *entry, struct inode *newdir,
465 struct dentry *newent)
466{
467 int err;
468 struct fuse_link_in inarg;
469 struct inode *inode = entry->d_inode;
470 struct fuse_conn *fc = get_fuse_conn(inode);
471 struct fuse_req *req = fuse_get_request(fc);
472 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700473 return -EINTR;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700474
475 memset(&inarg, 0, sizeof(inarg));
476 inarg.oldnodeid = get_node_id(inode);
477 req->in.h.opcode = FUSE_LINK;
478 req->inode2 = inode;
479 req->in.numargs = 2;
480 req->in.args[0].size = sizeof(inarg);
481 req->in.args[0].value = &inarg;
482 req->in.args[1].size = newent->d_name.len + 1;
483 req->in.args[1].value = newent->d_name.name;
484 err = create_new_entry(fc, req, newdir, newent, inode->i_mode);
485 /* Contrary to "normal" filesystems it can happen that link
486 makes two "logical" inodes point to the same "physical"
487 inode. We invalidate the attributes of the old one, so it
488 will reflect changes in the backing inode (link count,
489 etc.)
490 */
491 if (!err || err == -EINTR)
492 fuse_invalidate_attr(inode);
493 return err;
494}
495
Miklos Szeredie5e55582005-09-09 13:10:28 -0700496int fuse_do_getattr(struct inode *inode)
497{
498 int err;
499 struct fuse_attr_out arg;
500 struct fuse_conn *fc = get_fuse_conn(inode);
501 struct fuse_req *req = fuse_get_request(fc);
502 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700503 return -EINTR;
Miklos Szeredie5e55582005-09-09 13:10:28 -0700504
505 req->in.h.opcode = FUSE_GETATTR;
506 req->in.h.nodeid = get_node_id(inode);
507 req->inode = inode;
508 req->out.numargs = 1;
509 req->out.args[0].size = sizeof(arg);
510 req->out.args[0].value = &arg;
511 request_send(fc, req);
512 err = req->out.h.error;
513 fuse_put_request(fc, req);
514 if (!err) {
515 if ((inode->i_mode ^ arg.attr.mode) & S_IFMT) {
516 make_bad_inode(inode);
517 err = -EIO;
518 } else {
519 struct fuse_inode *fi = get_fuse_inode(inode);
520 fuse_change_attributes(inode, &arg.attr);
521 fi->i_time = time_to_jiffies(arg.attr_valid,
522 arg.attr_valid_nsec);
523 }
524 }
525 return err;
526}
527
Miklos Szeredi87729a52005-09-09 13:10:34 -0700528/*
529 * Calling into a user-controlled filesystem gives the filesystem
530 * daemon ptrace-like capabilities over the requester process. This
531 * means, that the filesystem daemon is able to record the exact
532 * filesystem operations performed, and can also control the behavior
533 * of the requester process in otherwise impossible ways. For example
534 * it can delay the operation for arbitrary length of time allowing
535 * DoS against the requester.
536 *
537 * For this reason only those processes can call into the filesystem,
538 * for which the owner of the mount has ptrace privilege. This
539 * excludes processes started by other users, suid or sgid processes.
540 */
541static int fuse_allow_task(struct fuse_conn *fc, struct task_struct *task)
542{
543 if (fc->flags & FUSE_ALLOW_OTHER)
544 return 1;
545
546 if (task->euid == fc->user_id &&
547 task->suid == fc->user_id &&
548 task->uid == fc->user_id &&
549 task->egid == fc->group_id &&
550 task->sgid == fc->group_id &&
551 task->gid == fc->group_id)
552 return 1;
553
554 return 0;
555}
556
Miklos Szeredie5e55582005-09-09 13:10:28 -0700557static int fuse_revalidate(struct dentry *entry)
558{
559 struct inode *inode = entry->d_inode;
560 struct fuse_inode *fi = get_fuse_inode(inode);
561 struct fuse_conn *fc = get_fuse_conn(inode);
562
Miklos Szeredi87729a52005-09-09 13:10:34 -0700563 if (!fuse_allow_task(fc, current))
564 return -EACCES;
565 if (get_node_id(inode) != FUSE_ROOT_ID &&
566 time_before_eq(jiffies, fi->i_time))
Miklos Szeredie5e55582005-09-09 13:10:28 -0700567 return 0;
568
569 return fuse_do_getattr(inode);
570}
571
Miklos Szeredi31d40d72005-11-07 00:59:50 -0800572static int fuse_access(struct inode *inode, int mask)
573{
574 struct fuse_conn *fc = get_fuse_conn(inode);
575 struct fuse_req *req;
576 struct fuse_access_in inarg;
577 int err;
578
579 if (fc->no_access)
580 return 0;
581
582 req = fuse_get_request(fc);
583 if (!req)
584 return -EINTR;
585
586 memset(&inarg, 0, sizeof(inarg));
587 inarg.mask = mask;
588 req->in.h.opcode = FUSE_ACCESS;
589 req->in.h.nodeid = get_node_id(inode);
590 req->inode = inode;
591 req->in.numargs = 1;
592 req->in.args[0].size = sizeof(inarg);
593 req->in.args[0].value = &inarg;
594 request_send(fc, req);
595 err = req->out.h.error;
596 fuse_put_request(fc, req);
597 if (err == -ENOSYS) {
598 fc->no_access = 1;
599 err = 0;
600 }
601 return err;
602}
603
Miklos Szeredie5e55582005-09-09 13:10:28 -0700604static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd)
605{
606 struct fuse_conn *fc = get_fuse_conn(inode);
607
Miklos Szeredi87729a52005-09-09 13:10:34 -0700608 if (!fuse_allow_task(fc, current))
Miklos Szeredie5e55582005-09-09 13:10:28 -0700609 return -EACCES;
Miklos Szeredi1e9a4ed2005-09-09 13:10:31 -0700610 else if (fc->flags & FUSE_DEFAULT_PERMISSIONS) {
611 int err = generic_permission(inode, mask, NULL);
612
613 /* If permission is denied, try to refresh file
614 attributes. This is also needed, because the root
615 node will at first have no permissions */
616 if (err == -EACCES) {
617 err = fuse_do_getattr(inode);
618 if (!err)
619 err = generic_permission(inode, mask, NULL);
620 }
621
622 /* FIXME: Need some mechanism to revoke permissions:
623 currently if the filesystem suddenly changes the
624 file mode, we will not be informed about it, and
625 continue to allow access to the file/directory.
626
627 This is actually not so grave, since the user can
628 simply keep access to the file/directory anyway by
629 keeping it open... */
630
631 return err;
632 } else {
Miklos Szeredie5e55582005-09-09 13:10:28 -0700633 int mode = inode->i_mode;
Miklos Szeredie5e55582005-09-09 13:10:28 -0700634 if ((mask & MAY_EXEC) && !S_ISDIR(mode) && !(mode & S_IXUGO))
635 return -EACCES;
Miklos Szeredi31d40d72005-11-07 00:59:50 -0800636
637 if (nd && (nd->flags & LOOKUP_ACCESS))
638 return fuse_access(inode, mask);
Miklos Szeredie5e55582005-09-09 13:10:28 -0700639 return 0;
640 }
641}
642
643static int parse_dirfile(char *buf, size_t nbytes, struct file *file,
644 void *dstbuf, filldir_t filldir)
645{
646 while (nbytes >= FUSE_NAME_OFFSET) {
647 struct fuse_dirent *dirent = (struct fuse_dirent *) buf;
648 size_t reclen = FUSE_DIRENT_SIZE(dirent);
649 int over;
650 if (!dirent->namelen || dirent->namelen > FUSE_NAME_MAX)
651 return -EIO;
652 if (reclen > nbytes)
653 break;
654
655 over = filldir(dstbuf, dirent->name, dirent->namelen,
656 file->f_pos, dirent->ino, dirent->type);
657 if (over)
658 break;
659
660 buf += reclen;
661 nbytes -= reclen;
662 file->f_pos = dirent->off;
663 }
664
665 return 0;
666}
667
Miklos Szeredi04730fe2005-09-09 13:10:36 -0700668static inline size_t fuse_send_readdir(struct fuse_req *req, struct file *file,
669 struct inode *inode, loff_t pos,
670 size_t count)
Miklos Szeredie5e55582005-09-09 13:10:28 -0700671{
Miklos Szeredi04730fe2005-09-09 13:10:36 -0700672 return fuse_send_read_common(req, file, inode, pos, count, 1);
Miklos Szeredie5e55582005-09-09 13:10:28 -0700673}
674
675static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
676{
Miklos Szeredi04730fe2005-09-09 13:10:36 -0700677 int err;
678 size_t nbytes;
679 struct page *page;
680 struct inode *inode = file->f_dentry->d_inode;
681 struct fuse_conn *fc = get_fuse_conn(inode);
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700682 struct fuse_req *req = fuse_get_request(fc);
Miklos Szeredi04730fe2005-09-09 13:10:36 -0700683 if (!req)
684 return -EINTR;
Miklos Szeredie5e55582005-09-09 13:10:28 -0700685
Miklos Szeredi04730fe2005-09-09 13:10:36 -0700686 page = alloc_page(GFP_KERNEL);
687 if (!page) {
688 fuse_put_request(fc, req);
Miklos Szeredie5e55582005-09-09 13:10:28 -0700689 return -ENOMEM;
Miklos Szeredi04730fe2005-09-09 13:10:36 -0700690 }
691 req->num_pages = 1;
692 req->pages[0] = page;
693 nbytes = fuse_send_readdir(req, file, inode, file->f_pos, PAGE_SIZE);
694 err = req->out.h.error;
695 fuse_put_request(fc, req);
696 if (!err)
697 err = parse_dirfile(page_address(page), nbytes, file, dstbuf,
698 filldir);
Miklos Szeredie5e55582005-09-09 13:10:28 -0700699
Miklos Szeredi04730fe2005-09-09 13:10:36 -0700700 __free_page(page);
Miklos Szeredib36c31b2005-09-09 13:10:38 -0700701 fuse_invalidate_attr(inode); /* atime changed */
Miklos Szeredi04730fe2005-09-09 13:10:36 -0700702 return err;
Miklos Szeredie5e55582005-09-09 13:10:28 -0700703}
704
705static char *read_link(struct dentry *dentry)
706{
707 struct inode *inode = dentry->d_inode;
708 struct fuse_conn *fc = get_fuse_conn(inode);
709 struct fuse_req *req = fuse_get_request(fc);
710 char *link;
711
712 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700713 return ERR_PTR(-EINTR);
Miklos Szeredie5e55582005-09-09 13:10:28 -0700714
715 link = (char *) __get_free_page(GFP_KERNEL);
716 if (!link) {
717 link = ERR_PTR(-ENOMEM);
718 goto out;
719 }
720 req->in.h.opcode = FUSE_READLINK;
721 req->in.h.nodeid = get_node_id(inode);
722 req->inode = inode;
723 req->out.argvar = 1;
724 req->out.numargs = 1;
725 req->out.args[0].size = PAGE_SIZE - 1;
726 req->out.args[0].value = link;
727 request_send(fc, req);
728 if (req->out.h.error) {
729 free_page((unsigned long) link);
730 link = ERR_PTR(req->out.h.error);
731 } else
732 link[req->out.args[0].size] = '\0';
733 out:
734 fuse_put_request(fc, req);
Miklos Szeredib36c31b2005-09-09 13:10:38 -0700735 fuse_invalidate_attr(inode); /* atime changed */
Miklos Szeredie5e55582005-09-09 13:10:28 -0700736 return link;
737}
738
739static void free_link(char *link)
740{
741 if (!IS_ERR(link))
742 free_page((unsigned long) link);
743}
744
745static void *fuse_follow_link(struct dentry *dentry, struct nameidata *nd)
746{
747 nd_set_link(nd, read_link(dentry));
748 return NULL;
749}
750
751static void fuse_put_link(struct dentry *dentry, struct nameidata *nd, void *c)
752{
753 free_link(nd_get_link(nd));
754}
755
756static int fuse_dir_open(struct inode *inode, struct file *file)
757{
Miklos Szeredi04730fe2005-09-09 13:10:36 -0700758 return fuse_open_common(inode, file, 1);
Miklos Szeredie5e55582005-09-09 13:10:28 -0700759}
760
761static int fuse_dir_release(struct inode *inode, struct file *file)
762{
Miklos Szeredi04730fe2005-09-09 13:10:36 -0700763 return fuse_release_common(inode, file, 1);
Miklos Szeredie5e55582005-09-09 13:10:28 -0700764}
765
Miklos Szeredi82547982005-09-09 13:10:38 -0700766static int fuse_dir_fsync(struct file *file, struct dentry *de, int datasync)
767{
768 /* nfsd can call this with no file */
769 return file ? fuse_fsync_common(file, de, datasync, 1) : 0;
770}
771
Miklos Szeredibefc6492005-11-07 00:59:52 -0800772static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg)
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700773{
774 unsigned ivalid = iattr->ia_valid;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700775
776 if (ivalid & ATTR_MODE)
Miklos Szeredibefc6492005-11-07 00:59:52 -0800777 arg->valid |= FATTR_MODE, arg->mode = iattr->ia_mode;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700778 if (ivalid & ATTR_UID)
Miklos Szeredibefc6492005-11-07 00:59:52 -0800779 arg->valid |= FATTR_UID, arg->uid = iattr->ia_uid;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700780 if (ivalid & ATTR_GID)
Miklos Szeredibefc6492005-11-07 00:59:52 -0800781 arg->valid |= FATTR_GID, arg->gid = iattr->ia_gid;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700782 if (ivalid & ATTR_SIZE)
Miklos Szeredibefc6492005-11-07 00:59:52 -0800783 arg->valid |= FATTR_SIZE, arg->size = iattr->ia_size;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700784 /* You can only _set_ these together (they may change by themselves) */
785 if ((ivalid & (ATTR_ATIME | ATTR_MTIME)) == (ATTR_ATIME | ATTR_MTIME)) {
Miklos Szeredibefc6492005-11-07 00:59:52 -0800786 arg->valid |= FATTR_ATIME | FATTR_MTIME;
787 arg->atime = iattr->ia_atime.tv_sec;
788 arg->mtime = iattr->ia_mtime.tv_sec;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700789 }
Miklos Szeredibefc6492005-11-07 00:59:52 -0800790 if (ivalid & ATTR_FILE) {
791 struct fuse_file *ff = iattr->ia_file->private_data;
792 arg->valid |= FATTR_FH;
793 arg->fh = ff->fh;
794 }
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700795}
796
797static int fuse_setattr(struct dentry *entry, struct iattr *attr)
798{
799 struct inode *inode = entry->d_inode;
800 struct fuse_conn *fc = get_fuse_conn(inode);
801 struct fuse_inode *fi = get_fuse_inode(inode);
802 struct fuse_req *req;
803 struct fuse_setattr_in inarg;
804 struct fuse_attr_out outarg;
805 int err;
806 int is_truncate = 0;
807
Miklos Szeredi1e9a4ed2005-09-09 13:10:31 -0700808 if (fc->flags & FUSE_DEFAULT_PERMISSIONS) {
809 err = inode_change_ok(inode, attr);
810 if (err)
811 return err;
812 }
813
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700814 if (attr->ia_valid & ATTR_SIZE) {
815 unsigned long limit;
816 is_truncate = 1;
817 limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur;
818 if (limit != RLIM_INFINITY && attr->ia_size > (loff_t) limit) {
819 send_sig(SIGXFSZ, current, 0);
820 return -EFBIG;
821 }
822 }
823
824 req = fuse_get_request(fc);
825 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700826 return -EINTR;
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700827
828 memset(&inarg, 0, sizeof(inarg));
Miklos Szeredibefc6492005-11-07 00:59:52 -0800829 iattr_to_fattr(attr, &inarg);
Miklos Szeredi9e6268d2005-09-09 13:10:29 -0700830 req->in.h.opcode = FUSE_SETATTR;
831 req->in.h.nodeid = get_node_id(inode);
832 req->inode = inode;
833 req->in.numargs = 1;
834 req->in.args[0].size = sizeof(inarg);
835 req->in.args[0].value = &inarg;
836 req->out.numargs = 1;
837 req->out.args[0].size = sizeof(outarg);
838 req->out.args[0].value = &outarg;
839 request_send(fc, req);
840 err = req->out.h.error;
841 fuse_put_request(fc, req);
842 if (!err) {
843 if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
844 make_bad_inode(inode);
845 err = -EIO;
846 } else {
847 if (is_truncate) {
848 loff_t origsize = i_size_read(inode);
849 i_size_write(inode, outarg.attr.size);
850 if (origsize > outarg.attr.size)
851 vmtruncate(inode, outarg.attr.size);
852 }
853 fuse_change_attributes(inode, &outarg.attr);
854 fi->i_time = time_to_jiffies(outarg.attr_valid,
855 outarg.attr_valid_nsec);
856 }
857 } else if (err == -EINTR)
858 fuse_invalidate_attr(inode);
859
860 return err;
861}
862
Miklos Szeredie5e55582005-09-09 13:10:28 -0700863static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
864 struct kstat *stat)
865{
866 struct inode *inode = entry->d_inode;
867 int err = fuse_revalidate(entry);
868 if (!err)
869 generic_fillattr(inode, stat);
870
871 return err;
872}
873
Miklos Szeredi92a87802005-09-09 13:10:31 -0700874static int fuse_setxattr(struct dentry *entry, const char *name,
875 const void *value, size_t size, int flags)
876{
877 struct inode *inode = entry->d_inode;
878 struct fuse_conn *fc = get_fuse_conn(inode);
879 struct fuse_req *req;
880 struct fuse_setxattr_in inarg;
881 int err;
882
883 if (size > FUSE_XATTR_SIZE_MAX)
884 return -E2BIG;
885
886 if (fc->no_setxattr)
887 return -EOPNOTSUPP;
888
889 req = fuse_get_request(fc);
890 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700891 return -EINTR;
Miklos Szeredi92a87802005-09-09 13:10:31 -0700892
893 memset(&inarg, 0, sizeof(inarg));
894 inarg.size = size;
895 inarg.flags = flags;
896 req->in.h.opcode = FUSE_SETXATTR;
897 req->in.h.nodeid = get_node_id(inode);
898 req->inode = inode;
899 req->in.numargs = 3;
900 req->in.args[0].size = sizeof(inarg);
901 req->in.args[0].value = &inarg;
902 req->in.args[1].size = strlen(name) + 1;
903 req->in.args[1].value = name;
904 req->in.args[2].size = size;
905 req->in.args[2].value = value;
906 request_send(fc, req);
907 err = req->out.h.error;
908 fuse_put_request(fc, req);
909 if (err == -ENOSYS) {
910 fc->no_setxattr = 1;
911 err = -EOPNOTSUPP;
912 }
913 return err;
914}
915
916static ssize_t fuse_getxattr(struct dentry *entry, const char *name,
917 void *value, size_t size)
918{
919 struct inode *inode = entry->d_inode;
920 struct fuse_conn *fc = get_fuse_conn(inode);
921 struct fuse_req *req;
922 struct fuse_getxattr_in inarg;
923 struct fuse_getxattr_out outarg;
924 ssize_t ret;
925
926 if (fc->no_getxattr)
927 return -EOPNOTSUPP;
928
929 req = fuse_get_request(fc);
930 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700931 return -EINTR;
Miklos Szeredi92a87802005-09-09 13:10:31 -0700932
933 memset(&inarg, 0, sizeof(inarg));
934 inarg.size = size;
935 req->in.h.opcode = FUSE_GETXATTR;
936 req->in.h.nodeid = get_node_id(inode);
937 req->inode = inode;
938 req->in.numargs = 2;
939 req->in.args[0].size = sizeof(inarg);
940 req->in.args[0].value = &inarg;
941 req->in.args[1].size = strlen(name) + 1;
942 req->in.args[1].value = name;
943 /* This is really two different operations rolled into one */
944 req->out.numargs = 1;
945 if (size) {
946 req->out.argvar = 1;
947 req->out.args[0].size = size;
948 req->out.args[0].value = value;
949 } else {
950 req->out.args[0].size = sizeof(outarg);
951 req->out.args[0].value = &outarg;
952 }
953 request_send(fc, req);
954 ret = req->out.h.error;
955 if (!ret)
956 ret = size ? req->out.args[0].size : outarg.size;
957 else {
958 if (ret == -ENOSYS) {
959 fc->no_getxattr = 1;
960 ret = -EOPNOTSUPP;
961 }
962 }
963 fuse_put_request(fc, req);
964 return ret;
965}
966
967static ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
968{
969 struct inode *inode = entry->d_inode;
970 struct fuse_conn *fc = get_fuse_conn(inode);
971 struct fuse_req *req;
972 struct fuse_getxattr_in inarg;
973 struct fuse_getxattr_out outarg;
974 ssize_t ret;
975
976 if (fc->no_listxattr)
977 return -EOPNOTSUPP;
978
979 req = fuse_get_request(fc);
980 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -0700981 return -EINTR;
Miklos Szeredi92a87802005-09-09 13:10:31 -0700982
983 memset(&inarg, 0, sizeof(inarg));
984 inarg.size = size;
985 req->in.h.opcode = FUSE_LISTXATTR;
986 req->in.h.nodeid = get_node_id(inode);
987 req->inode = inode;
988 req->in.numargs = 1;
989 req->in.args[0].size = sizeof(inarg);
990 req->in.args[0].value = &inarg;
991 /* This is really two different operations rolled into one */
992 req->out.numargs = 1;
993 if (size) {
994 req->out.argvar = 1;
995 req->out.args[0].size = size;
996 req->out.args[0].value = list;
997 } else {
998 req->out.args[0].size = sizeof(outarg);
999 req->out.args[0].value = &outarg;
1000 }
1001 request_send(fc, req);
1002 ret = req->out.h.error;
1003 if (!ret)
1004 ret = size ? req->out.args[0].size : outarg.size;
1005 else {
1006 if (ret == -ENOSYS) {
1007 fc->no_listxattr = 1;
1008 ret = -EOPNOTSUPP;
1009 }
1010 }
1011 fuse_put_request(fc, req);
1012 return ret;
1013}
1014
1015static int fuse_removexattr(struct dentry *entry, const char *name)
1016{
1017 struct inode *inode = entry->d_inode;
1018 struct fuse_conn *fc = get_fuse_conn(inode);
1019 struct fuse_req *req;
1020 int err;
1021
1022 if (fc->no_removexattr)
1023 return -EOPNOTSUPP;
1024
1025 req = fuse_get_request(fc);
1026 if (!req)
Miklos Szeredi7c352bd2005-09-09 13:10:39 -07001027 return -EINTR;
Miklos Szeredi92a87802005-09-09 13:10:31 -07001028
1029 req->in.h.opcode = FUSE_REMOVEXATTR;
1030 req->in.h.nodeid = get_node_id(inode);
1031 req->inode = inode;
1032 req->in.numargs = 1;
1033 req->in.args[0].size = strlen(name) + 1;
1034 req->in.args[0].value = name;
1035 request_send(fc, req);
1036 err = req->out.h.error;
1037 fuse_put_request(fc, req);
1038 if (err == -ENOSYS) {
1039 fc->no_removexattr = 1;
1040 err = -EOPNOTSUPP;
1041 }
1042 return err;
1043}
1044
Miklos Szeredie5e55582005-09-09 13:10:28 -07001045static struct inode_operations fuse_dir_inode_operations = {
1046 .lookup = fuse_lookup,
Miklos Szeredi9e6268d2005-09-09 13:10:29 -07001047 .mkdir = fuse_mkdir,
1048 .symlink = fuse_symlink,
1049 .unlink = fuse_unlink,
1050 .rmdir = fuse_rmdir,
1051 .rename = fuse_rename,
1052 .link = fuse_link,
1053 .setattr = fuse_setattr,
1054 .create = fuse_create,
1055 .mknod = fuse_mknod,
Miklos Szeredie5e55582005-09-09 13:10:28 -07001056 .permission = fuse_permission,
1057 .getattr = fuse_getattr,
Miklos Szeredi92a87802005-09-09 13:10:31 -07001058 .setxattr = fuse_setxattr,
1059 .getxattr = fuse_getxattr,
1060 .listxattr = fuse_listxattr,
1061 .removexattr = fuse_removexattr,
Miklos Szeredie5e55582005-09-09 13:10:28 -07001062};
1063
1064static struct file_operations fuse_dir_operations = {
Miklos Szeredib6aeade2005-09-09 13:10:30 -07001065 .llseek = generic_file_llseek,
Miklos Szeredie5e55582005-09-09 13:10:28 -07001066 .read = generic_read_dir,
1067 .readdir = fuse_readdir,
1068 .open = fuse_dir_open,
1069 .release = fuse_dir_release,
Miklos Szeredi82547982005-09-09 13:10:38 -07001070 .fsync = fuse_dir_fsync,
Miklos Szeredie5e55582005-09-09 13:10:28 -07001071};
1072
1073static struct inode_operations fuse_common_inode_operations = {
Miklos Szeredi9e6268d2005-09-09 13:10:29 -07001074 .setattr = fuse_setattr,
Miklos Szeredie5e55582005-09-09 13:10:28 -07001075 .permission = fuse_permission,
1076 .getattr = fuse_getattr,
Miklos Szeredi92a87802005-09-09 13:10:31 -07001077 .setxattr = fuse_setxattr,
1078 .getxattr = fuse_getxattr,
1079 .listxattr = fuse_listxattr,
1080 .removexattr = fuse_removexattr,
Miklos Szeredie5e55582005-09-09 13:10:28 -07001081};
1082
1083static struct inode_operations fuse_symlink_inode_operations = {
Miklos Szeredi9e6268d2005-09-09 13:10:29 -07001084 .setattr = fuse_setattr,
Miklos Szeredie5e55582005-09-09 13:10:28 -07001085 .follow_link = fuse_follow_link,
1086 .put_link = fuse_put_link,
1087 .readlink = generic_readlink,
1088 .getattr = fuse_getattr,
Miklos Szeredi92a87802005-09-09 13:10:31 -07001089 .setxattr = fuse_setxattr,
1090 .getxattr = fuse_getxattr,
1091 .listxattr = fuse_listxattr,
1092 .removexattr = fuse_removexattr,
Miklos Szeredie5e55582005-09-09 13:10:28 -07001093};
1094
1095void fuse_init_common(struct inode *inode)
1096{
1097 inode->i_op = &fuse_common_inode_operations;
1098}
1099
1100void fuse_init_dir(struct inode *inode)
1101{
1102 inode->i_op = &fuse_dir_inode_operations;
1103 inode->i_fop = &fuse_dir_operations;
1104}
1105
1106void fuse_init_symlink(struct inode *inode)
1107{
1108 inode->i_op = &fuse_symlink_inode_operations;
1109}