1 |
/* |
2 |
FUSE: Filesystem in Userspace |
3 |
Copyright (C) 2001-2004 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/sched.h> |
13 |
#include <linux/slab.h> |
14 |
#include <linux/file.h> |
15 |
#include <linux/mount.h> |
16 |
#include <linux/proc_fs.h> |
17 |
#include <linux/seq_file.h> |
18 |
#ifdef KERNEL_2_6 |
19 |
#include <linux/parser.h> |
20 |
#include <linux/statfs.h> |
21 |
#else |
22 |
#include "compat/parser.h" |
23 |
#endif |
24 |
|
25 |
|
26 |
static int user_allow_other; |
27 |
static kmem_cache_t *fuse_inode_cachep; |
28 |
|
29 |
#ifdef KERNEL_2_6 |
30 |
#include <linux/moduleparam.h> |
31 |
module_param(user_allow_other, int, 0); |
32 |
#else |
33 |
MODULE_PARM(user_allow_other, "i"); |
34 |
#endif |
35 |
|
36 |
MODULE_PARM_DESC(user_allow_other, "Allow non root user to specify the \"allow_other\" mount option"); |
37 |
|
38 |
|
39 |
#define FUSE_SUPER_MAGIC 0x65735546 |
40 |
|
41 |
#ifndef KERNEL_2_6 |
42 |
#define kstatfs statfs |
43 |
#endif |
44 |
|
45 |
#ifndef FS_SAFE |
46 |
#define FS_SAFE 0 |
47 |
#endif |
48 |
|
49 |
struct fuse_mount_data { |
50 |
int fd; |
51 |
unsigned int rootmode; |
52 |
unsigned int uid; |
53 |
unsigned int flags; |
54 |
unsigned int max_read; |
55 |
}; |
56 |
|
57 |
struct fuse_inode *fuse_inode_alloc(void) |
58 |
{ |
59 |
struct fuse_inode *fi; |
60 |
|
61 |
fi = kmem_cache_alloc(fuse_inode_cachep, SLAB_KERNEL); |
62 |
if (fi) { |
63 |
memset(fi, 0, sizeof(*fi)); |
64 |
fi->forget_req = fuse_request_alloc(); |
65 |
if (!fi->forget_req) { |
66 |
kmem_cache_free(fuse_inode_cachep, fi); |
67 |
fi = NULL; |
68 |
} else { |
69 |
init_rwsem(&fi->write_sem); |
70 |
INIT_LIST_HEAD(&fi->write_files); |
71 |
} |
72 |
} |
73 |
|
74 |
return fi; |
75 |
} |
76 |
|
77 |
static void fuse_inode_free(struct fuse_inode *fi) |
78 |
{ |
79 |
BUG_ON(!list_empty(&fi->write_files)); |
80 |
if (fi->forget_req) |
81 |
fuse_request_free(fi->forget_req); |
82 |
kmem_cache_free(fuse_inode_cachep, fi); |
83 |
} |
84 |
|
85 |
static void fuse_read_inode(struct inode *inode) |
86 |
{ |
87 |
/* No op */ |
88 |
} |
89 |
|
90 |
void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req, ino_t ino, |
91 |
int version) |
92 |
{ |
93 |
struct fuse_forget_in *inarg = &req->misc.forget_in; |
94 |
inarg->version = version; |
95 |
req->in.h.opcode = FUSE_FORGET; |
96 |
req->in.h.ino = ino; |
97 |
req->in.numargs = 1; |
98 |
req->in.args[0].size = sizeof(struct fuse_forget_in); |
99 |
req->in.args[0].value = inarg; |
100 |
request_send_noreply(fc, req); |
101 |
} |
102 |
|
103 |
static void fuse_clear_inode(struct inode *inode) |
104 |
{ |
105 |
struct fuse_conn *fc = INO_FC(inode); |
106 |
struct fuse_inode *fi = INO_FI(inode); |
107 |
|
108 |
if (fi) { |
109 |
if (fc) { |
110 |
fuse_send_forget(fc, fi->forget_req, inode->i_ino, |
111 |
inode->i_version); |
112 |
fi->forget_req = NULL; |
113 |
} |
114 |
fuse_inode_free(fi); |
115 |
} |
116 |
} |
117 |
|
118 |
static void fuse_put_super(struct super_block *sb) |
119 |
{ |
120 |
struct fuse_conn *fc = SB_FC(sb); |
121 |
|
122 |
spin_lock(&fuse_lock); |
123 |
fc->sb = NULL; |
124 |
fc->uid = 0; |
125 |
fc->flags = 0; |
126 |
/* Flush all readers on this fs */ |
127 |
wake_up_all(&fc->waitq); |
128 |
fuse_release_conn(fc); |
129 |
SB_FC(sb) = NULL; |
130 |
spin_unlock(&fuse_lock); |
131 |
} |
132 |
|
133 |
static void convert_fuse_statfs(struct kstatfs *stbuf, struct fuse_kstatfs *attr) |
134 |
{ |
135 |
stbuf->f_type = FUSE_SUPER_MAGIC; |
136 |
stbuf->f_bsize = attr->bsize; |
137 |
stbuf->f_blocks = attr->blocks; |
138 |
stbuf->f_bfree = attr->bfree; |
139 |
stbuf->f_bavail = attr->bavail; |
140 |
stbuf->f_files = attr->files; |
141 |
stbuf->f_ffree = attr->ffree; |
142 |
stbuf->f_namelen = attr->namelen; |
143 |
/* fsid is left zero */ |
144 |
} |
145 |
|
146 |
static int fuse_statfs(struct super_block *sb, struct kstatfs *buf) |
147 |
{ |
148 |
struct fuse_conn *fc = SB_FC(sb); |
149 |
struct fuse_req *req; |
150 |
struct fuse_statfs_out outarg; |
151 |
int err; |
152 |
|
153 |
req = fuse_get_request(fc); |
154 |
if (!req) |
155 |
return -ERESTARTSYS; |
156 |
|
157 |
req->in.numargs = 0; |
158 |
req->in.h.opcode = FUSE_STATFS; |
159 |
req->out.numargs = 1; |
160 |
req->out.args[0].size = sizeof(outarg); |
161 |
req->out.args[0].value = &outarg; |
162 |
request_send(fc, req); |
163 |
err = req->out.h.error; |
164 |
if (!err) |
165 |
convert_fuse_statfs(buf, &outarg.st); |
166 |
fuse_put_request(fc, req); |
167 |
return err; |
168 |
} |
169 |
|
170 |
enum { opt_fd, |
171 |
opt_rootmode, |
172 |
opt_uid, |
173 |
opt_default_permissions, |
174 |
opt_allow_other, |
175 |
opt_kernel_cache, |
176 |
#ifndef KERNEL_2_6 |
177 |
opt_large_read, |
178 |
#endif |
179 |
opt_direct_io, |
180 |
opt_max_read, |
181 |
opt_err }; |
182 |
|
183 |
static match_table_t tokens = { |
184 |
{opt_fd, "fd=%u"}, |
185 |
{opt_rootmode, "rootmode=%o"}, |
186 |
{opt_uid, "uid=%u"}, |
187 |
{opt_default_permissions, "default_permissions"}, |
188 |
{opt_allow_other, "allow_other"}, |
189 |
{opt_kernel_cache, "kernel_cache"}, |
190 |
#ifndef KERNEL_2_6 |
191 |
{opt_large_read, "large_read"}, |
192 |
#endif |
193 |
{opt_direct_io, "direct_io"}, |
194 |
{opt_max_read, "max_read=%u" }, |
195 |
{opt_err, NULL} |
196 |
}; |
197 |
|
198 |
static int parse_fuse_opt(char *opt, struct fuse_mount_data *d) |
199 |
{ |
200 |
char *p; |
201 |
memset(d, 0, sizeof(struct fuse_mount_data)); |
202 |
d->fd = -1; |
203 |
d->max_read = ~0; |
204 |
|
205 |
while ((p = strsep(&opt, ",")) != NULL) { |
206 |
int token; |
207 |
int value; |
208 |
substring_t args[MAX_OPT_ARGS]; |
209 |
if (!*p) |
210 |
continue; |
211 |
|
212 |
token = match_token(p, tokens, args); |
213 |
switch (token) { |
214 |
case opt_fd: |
215 |
if (match_int(&args[0], &value)) |
216 |
return 0; |
217 |
d->fd = value; |
218 |
break; |
219 |
|
220 |
case opt_rootmode: |
221 |
if (match_octal(&args[0], &value)) |
222 |
return 0; |
223 |
d->rootmode = value; |
224 |
break; |
225 |
|
226 |
case opt_uid: |
227 |
if (match_int(&args[0], &value)) |
228 |
return 0; |
229 |
d->uid = value; |
230 |
break; |
231 |
|
232 |
case opt_default_permissions: |
233 |
d->flags |= FUSE_DEFAULT_PERMISSIONS; |
234 |
break; |
235 |
|
236 |
case opt_allow_other: |
237 |
d->flags |= FUSE_ALLOW_OTHER; |
238 |
break; |
239 |
|
240 |
case opt_kernel_cache: |
241 |
d->flags |= FUSE_KERNEL_CACHE; |
242 |
break; |
243 |
|
244 |
#ifndef KERNEL_2_6 |
245 |
case opt_large_read: |
246 |
d->flags |= FUSE_LARGE_READ; |
247 |
break; |
248 |
#endif |
249 |
|
250 |
case opt_direct_io: |
251 |
d->flags |= FUSE_DIRECT_IO; |
252 |
break; |
253 |
|
254 |
case opt_max_read: |
255 |
if (match_int(&args[0], &value)) |
256 |
return 0; |
257 |
d->max_read = value; |
258 |
break; |
259 |
|
260 |
default: |
261 |
return 0; |
262 |
} |
263 |
} |
264 |
if (d->fd == -1) |
265 |
return 0; |
266 |
|
267 |
return 1; |
268 |
} |
269 |
|
270 |
static int fuse_show_options(struct seq_file *m, struct vfsmount *mnt) |
271 |
{ |
272 |
struct fuse_conn *fc = SB_FC(mnt->mnt_sb); |
273 |
|
274 |
seq_printf(m, ",uid=%u", fc->uid); |
275 |
if (fc->flags & FUSE_DEFAULT_PERMISSIONS) |
276 |
seq_puts(m, ",default_permissions"); |
277 |
if (fc->flags & FUSE_ALLOW_OTHER) |
278 |
seq_puts(m, ",allow_other"); |
279 |
if (fc->flags & FUSE_KERNEL_CACHE) |
280 |
seq_puts(m, ",kernel_cache"); |
281 |
#ifndef KERNEL_2_6 |
282 |
if (fc->flags & FUSE_LARGE_READ) |
283 |
seq_puts(m, ",large_read"); |
284 |
#endif |
285 |
if (fc->flags & FUSE_DIRECT_IO) |
286 |
seq_puts(m, ",direct_io"); |
287 |
if (fc->max_read != ~0) |
288 |
seq_printf(m, ",max_read=%u", fc->max_read); |
289 |
return 0; |
290 |
} |
291 |
|
292 |
static struct fuse_conn *get_conn(struct file *file, struct super_block *sb) |
293 |
{ |
294 |
struct fuse_conn *fc; |
295 |
struct inode *ino; |
296 |
|
297 |
ino = file->f_dentry->d_inode; |
298 |
if (!ino || !proc_fuse_dev || proc_fuse_dev->low_ino != ino->i_ino) { |
299 |
printk("FUSE: bad communication file descriptor\n"); |
300 |
return NULL; |
301 |
} |
302 |
fc = file->private_data; |
303 |
if (fc->sb != NULL) { |
304 |
printk("fuse_read_super: connection already mounted\n"); |
305 |
return NULL; |
306 |
} |
307 |
fc->sb = sb; |
308 |
return fc; |
309 |
} |
310 |
|
311 |
static struct inode *get_root_inode(struct super_block *sb, unsigned int mode) |
312 |
{ |
313 |
struct fuse_attr attr; |
314 |
memset(&attr, 0, sizeof(attr)); |
315 |
|
316 |
attr.mode = mode; |
317 |
return fuse_iget(sb, 1, 0, &attr, 0); |
318 |
} |
319 |
|
320 |
|
321 |
#ifdef KERNEL_2_6 |
322 |
|
323 |
static struct dentry *fuse_get_dentry(struct super_block *sb, void *vobjp) |
324 |
{ |
325 |
__u32 *objp = vobjp; |
326 |
unsigned long ino = objp[0]; |
327 |
__u32 generation = objp[1]; |
328 |
struct inode *inode; |
329 |
struct dentry *entry; |
330 |
|
331 |
if (ino == 0) |
332 |
return ERR_PTR(-ESTALE); |
333 |
|
334 |
inode = ilookup(sb, ino); |
335 |
if (!inode || inode->i_generation != generation) |
336 |
return ERR_PTR(-ESTALE); |
337 |
|
338 |
entry = d_alloc_anon(inode); |
339 |
if (!entry) { |
340 |
iput(inode); |
341 |
return ERR_PTR(-ENOMEM); |
342 |
} |
343 |
|
344 |
return entry; |
345 |
} |
346 |
|
347 |
static struct export_operations fuse_export_operations = { |
348 |
.get_dentry = fuse_get_dentry, |
349 |
}; |
350 |
#endif |
351 |
|
352 |
static struct super_operations fuse_super_operations = { |
353 |
.read_inode = fuse_read_inode, |
354 |
.clear_inode = fuse_clear_inode, |
355 |
.put_super = fuse_put_super, |
356 |
.statfs = fuse_statfs, |
357 |
.show_options = fuse_show_options, |
358 |
}; |
359 |
|
360 |
static int fuse_read_super(struct super_block *sb, void *data, int silent) |
361 |
{ |
362 |
struct fuse_conn *fc; |
363 |
struct inode *root; |
364 |
struct fuse_mount_data d; |
365 |
struct file *file; |
366 |
|
367 |
if (!parse_fuse_opt((char *) data, &d)) |
368 |
return -EINVAL; |
369 |
|
370 |
if (!user_allow_other && (d.flags & FUSE_ALLOW_OTHER) && |
371 |
current->uid != 0) |
372 |
return -EPERM; |
373 |
|
374 |
sb->s_blocksize = PAGE_CACHE_SIZE; |
375 |
sb->s_blocksize_bits = PAGE_CACHE_SHIFT; |
376 |
sb->s_magic = FUSE_SUPER_MAGIC; |
377 |
sb->s_op = &fuse_super_operations; |
378 |
sb->s_maxbytes = MAX_LFS_FILESIZE; |
379 |
#ifdef KERNEL_2_6 |
380 |
sb->s_export_op = &fuse_export_operations; |
381 |
#endif |
382 |
|
383 |
file = fget(d.fd); |
384 |
if (!file) |
385 |
return -EINVAL; |
386 |
|
387 |
spin_lock(&fuse_lock); |
388 |
fc = get_conn(file, sb); |
389 |
spin_unlock(&fuse_lock); |
390 |
fput(file); |
391 |
if (fc == NULL) |
392 |
return -EINVAL; |
393 |
|
394 |
fc->flags = d.flags; |
395 |
fc->uid = d.uid; |
396 |
fc->max_read = d.max_read; |
397 |
fc->max_write = FUSE_MAX_IN / 2; |
398 |
|
399 |
/* fc is needed in fuse_init_file_inode which could be called |
400 |
from get_root_inode */ |
401 |
SB_FC(sb) = fc; |
402 |
|
403 |
root = get_root_inode(sb, d.rootmode); |
404 |
if (root == NULL) { |
405 |
printk("fuse_read_super: failed to get root inode\n"); |
406 |
goto err; |
407 |
} |
408 |
|
409 |
sb->s_root = d_alloc_root(root); |
410 |
if (!sb->s_root) { |
411 |
iput(root); |
412 |
goto err; |
413 |
} |
414 |
|
415 |
return 0; |
416 |
|
417 |
err: |
418 |
spin_lock(&fuse_lock); |
419 |
fc->sb = NULL; |
420 |
fuse_release_conn(fc); |
421 |
spin_unlock(&fuse_lock); |
422 |
SB_FC(sb) = NULL; |
423 |
return -EINVAL; |
424 |
} |
425 |
|
426 |
#ifdef KERNEL_2_6 |
427 |
static struct super_block *fuse_get_sb(struct file_system_type *fs_type, |
428 |
int flags, const char *dev_name, |
429 |
void *raw_data) |
430 |
{ |
431 |
return get_sb_nodev(fs_type, flags, raw_data, fuse_read_super); |
432 |
} |
433 |
|
434 |
static struct file_system_type fuse_fs_type = { |
435 |
.owner = THIS_MODULE, |
436 |
.name = "fuse", |
437 |
.get_sb = fuse_get_sb, |
438 |
.kill_sb = kill_anon_super, |
439 |
.fs_flags = FS_SAFE, |
440 |
}; |
441 |
#else |
442 |
static struct super_block *fuse_read_super_compat(struct super_block *sb, |
443 |
void *data, int silent) |
444 |
{ |
445 |
int err = fuse_read_super(sb, data, silent); |
446 |
if (err) |
447 |
return NULL; |
448 |
else |
449 |
return sb; |
450 |
} |
451 |
|
452 |
static DECLARE_FSTYPE(fuse_fs_type, "fuse", fuse_read_super_compat, 0); |
453 |
#endif |
454 |
|
455 |
int fuse_fs_init() |
456 |
{ |
457 |
int err; |
458 |
|
459 |
err = register_filesystem(&fuse_fs_type); |
460 |
if (err) |
461 |
printk("fuse: failed to register filesystem\n"); |
462 |
else { |
463 |
fuse_inode_cachep = kmem_cache_create("fuse_inode", |
464 |
sizeof(struct fuse_inode), |
465 |
0, 0, NULL, NULL); |
466 |
if (!fuse_inode_cachep) { |
467 |
unregister_filesystem(&fuse_fs_type); |
468 |
err = -ENOMEM; |
469 |
} |
470 |
} |
471 |
|
472 |
return err; |
473 |
} |
474 |
|
475 |
void fuse_fs_cleanup() |
476 |
{ |
477 |
unregister_filesystem(&fuse_fs_type); |
478 |
kmem_cache_destroy(fuse_inode_cachep); |
479 |
} |
480 |
|
481 |
/* |
482 |
* Local Variables: |
483 |
* indent-tabs-mode: t |
484 |
* c-basic-offset: 8 |
485 |
* End: |
486 |
*/ |