1 |
dpavlin |
4 |
/* |
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 |
|
|
*/ |