blob: 065ac0187e242cad9cc77a0f4d0b8730ec9434b6 [file] [log] [blame]
Miklos Szeredi85c74fc2001-10-28 19:44:14 +00001/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001 Miklos Szeredi (mszeredi@inf.bme.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/module.h>
12#include <linux/kernel.h>
13#include <linux/sched.h>
14#include <linux/file.h>
15#include <linux/slab.h>
16
Miklos Szeredib483c932001-10-29 14:57:57 +000017static struct inode_operations fuse_dir_inode_operations;
18static struct inode_operations fuse_file_inode_operations;
19static struct inode_operations fuse_symlink_inode_operations;
20static struct inode_operations fuse_special_inode_operations;
21
22static struct file_operations fuse_dir_operations;
23static struct file_operations fuse_file_operations;
24
25
Miklos Szeredi85c74fc2001-10-28 19:44:14 +000026static void change_attributes(struct inode *inode, struct fuse_attr *attr)
27{
28 inode->i_mode = attr->mode;
29 inode->i_nlink = attr->nlink;
30 inode->i_uid = attr->uid;
31 inode->i_gid = attr->gid;
32 inode->i_size = attr->size;
33 inode->i_blksize = attr->blksize;
34 inode->i_blocks = attr->blocks;
35 inode->i_atime = attr->atime;
36 inode->i_mtime = attr->mtime;
37 inode->i_ctime = attr->ctime;
38}
39
Miklos Szeredib483c932001-10-29 14:57:57 +000040void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
Miklos Szeredi85c74fc2001-10-28 19:44:14 +000041{
42 change_attributes(inode, attr);
43
Miklos Szeredib483c932001-10-29 14:57:57 +000044 if(S_ISREG(inode->i_mode)) {
45 inode->i_op = &fuse_file_inode_operations;
46 inode->i_fop = &fuse_file_operations;
47 }
48 else if(S_ISDIR(inode->i_mode)) {
49 inode->i_op = &fuse_dir_inode_operations;
50 inode->i_fop = &fuse_dir_operations;
51 }
Miklos Szeredi85c74fc2001-10-28 19:44:14 +000052 else if(S_ISLNK(inode->i_mode))
Miklos Szeredib483c932001-10-29 14:57:57 +000053 inode->i_op = &fuse_symlink_inode_operations;
Miklos Szeredi85c74fc2001-10-28 19:44:14 +000054 else {
Miklos Szeredib483c932001-10-29 14:57:57 +000055 inode->i_op = &fuse_special_inode_operations;
Miklos Szeredi85c74fc2001-10-28 19:44:14 +000056 init_special_inode(inode, inode->i_mode, attr->rdev);
57 }
58}
59
60
61static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry)
62{
63 struct fuse_conn *fc = dir->i_sb->u.generic_sbp;
64 struct fuse_in in = FUSE_IN_INIT;
65 struct fuse_out out = FUSE_OUT_INIT;
66 struct fuse_lookup_out arg;
67 struct inode *inode;
68
69 in.h.opcode = FUSE_LOOKUP;
70 in.h.ino = dir->i_ino;
71 in.argsize = entry->d_name.len + 1;
72 in.arg = entry->d_name.name;
73 out.argsize = sizeof(arg);
74 out.arg = &arg;
75 request_send(fc, &in, &out);
76
77 if(out.h.result) {
78 /* Negative dentries are not hashed */
79 if(out.h.result == -ENOENT)
80 return NULL;
81 else
82 return ERR_PTR(out.h.result);
83 }
84
85 inode = iget(dir->i_sb, arg.ino);
86 if(!inode)
87 return ERR_PTR(-ENOMEM);
88
Miklos Szeredib483c932001-10-29 14:57:57 +000089 fuse_init_inode(inode, &arg.attr);
Miklos Szeredi85c74fc2001-10-28 19:44:14 +000090 d_add(entry, inode);
91 return NULL;
92}
93
Miklos Szeredib483c932001-10-29 14:57:57 +000094/* create needs to return a positive entry, so this also does a lookup */
Miklos Szeredi85c74fc2001-10-28 19:44:14 +000095static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode,
96 int rdev)
97{
98 struct fuse_conn *fc = dir->i_sb->u.generic_sbp;
99 struct fuse_in in = FUSE_IN_INIT;
100 struct fuse_out out = FUSE_OUT_INIT;
101 struct fuse_mknod_in *inarg;
102 unsigned int insize;
103 struct fuse_mknod_out outarg;
104 struct inode *inode;
105
106 insize = offsetof(struct fuse_mknod_in, name) + entry->d_name.len + 1;
107 inarg = kmalloc(insize, GFP_KERNEL);
108 if(!inarg)
109 return -ENOMEM;
110
111 inarg->mode = mode;
112 inarg->rdev = rdev;
113 strcpy(inarg->name, entry->d_name.name);
114
115 in.h.opcode = FUSE_MKNOD;
116 in.h.ino = dir->i_ino;
117 in.argsize = insize;
118 in.arg = inarg;
119 out.argsize = sizeof(outarg);
120 out.arg = &outarg;
121 request_send(fc, &in, &out);
122 kfree(inarg);
Miklos Szeredib483c932001-10-29 14:57:57 +0000123
124 if(out.h.result)
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000125 return out.h.result;
126
127 inode = iget(dir->i_sb, outarg.ino);
128 if(!inode)
129 return -ENOMEM;
Miklos Szeredib483c932001-10-29 14:57:57 +0000130
131 fuse_init_inode(inode, &outarg.attr);
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000132 d_add(entry, inode);
Miklos Szeredib483c932001-10-29 14:57:57 +0000133
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000134 return 0;
135}
136
Miklos Szeredib483c932001-10-29 14:57:57 +0000137
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000138static int fuse_create(struct inode *dir, struct dentry *entry, int mode)
139{
140 return fuse_mknod(dir, entry, mode, 0);
141}
142
Miklos Szeredib483c932001-10-29 14:57:57 +0000143static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode)
144{
145 struct fuse_conn *fc = dir->i_sb->u.generic_sbp;
146 struct fuse_in in = FUSE_IN_INIT;
147 struct fuse_out out = FUSE_OUT_INIT;
148 struct fuse_mkdir_in *inarg;
149 unsigned int insize;
150
151 insize = offsetof(struct fuse_mkdir_in, name) + entry->d_name.len + 1;
152 inarg = kmalloc(insize, GFP_KERNEL);
153 if(!inarg)
154 return -ENOMEM;
155
156 inarg->mode = mode;
157 strcpy(inarg->name, entry->d_name.name);
158
159 in.h.opcode = FUSE_MKDIR;
160 in.h.ino = dir->i_ino;
161 in.argsize = insize;
162 in.arg = inarg;
163 request_send(fc, &in, &out);
164 kfree(inarg);
165
166 return out.h.result;
167}
168
169static int fuse_symlink(struct inode *dir, struct dentry *entry,
170 const char *link)
171{
172 struct fuse_conn *fc = dir->i_sb->u.generic_sbp;
173 struct fuse_in in = FUSE_IN_INIT;
174 struct fuse_out out = FUSE_OUT_INIT;
175 char *inarg;
176 unsigned int insize;
177
178 insize = entry->d_name.len + 1 + strlen(link) + 1;
179 inarg = kmalloc(insize, GFP_KERNEL);
180 if(!inarg)
181 return -ENOMEM;
182
183 strcpy(inarg, entry->d_name.name);
184 strcpy(inarg + entry->d_name.len + 1, link);
185
186 in.h.opcode = FUSE_SYMLINK;
187 in.h.ino = dir->i_ino;
188 in.argsize = insize;
189 in.arg = inarg;
190 request_send(fc, &in, &out);
191 kfree(inarg);
192
193 return out.h.result;
194}
195
196static int fuse_remove(struct inode *dir, struct dentry *entry,
197 enum fuse_opcode op)
198{
199 struct fuse_conn *fc = dir->i_sb->u.generic_sbp;
200 struct fuse_in in = FUSE_IN_INIT;
201 struct fuse_out out = FUSE_OUT_INIT;
202
203 in.h.opcode = op;
204 in.h.ino = dir->i_ino;
205 in.argsize = entry->d_name.len + 1;
206 in.arg = entry->d_name.name;
207 request_send(fc, &in, &out);
208 if(!out.h.result) {
209 d_drop(entry);
210 entry->d_inode->i_nlink = 0;
211 }
212
213 return out.h.result;
214}
215
216static int fuse_unlink(struct inode *dir, struct dentry *entry)
217{
218 return fuse_remove(dir, entry, FUSE_UNLINK);
219}
220
221static int fuse_rmdir(struct inode *dir, struct dentry *entry)
222{
223 return fuse_remove(dir, entry, FUSE_RMDIR);
224}
225
226static int fuse_rename(struct inode *olddir, struct dentry *oldent,
227 struct inode *newdir, struct dentry *newent)
228{
229 struct fuse_conn *fc = olddir->i_sb->u.generic_sbp;
230 struct fuse_in in = FUSE_IN_INIT;
231 struct fuse_out out = FUSE_OUT_INIT;
232 struct fuse_rename_in *inarg;
233 unsigned int oldnamsize = oldent->d_name.len + 1;
234 unsigned int newnamsize = newent->d_name.len + 1;
235 unsigned int insize;
236
237 insize = offsetof(struct fuse_rename_in, names) + oldnamsize +
238 newnamsize;
239 inarg = kmalloc(insize, GFP_KERNEL);
240 if(!inarg)
241 return -ENOMEM;
242
243 inarg->newdir = newdir->i_ino;
244 strcpy(inarg->names, oldent->d_name.name);
245 strcpy(inarg->names + oldnamsize, newent->d_name.name);
246
247 in.h.opcode = FUSE_RENAME;
248 in.h.ino = olddir->i_ino;
249 in.argsize = insize;
250 in.arg = inarg;
251 request_send(fc, &in, &out);
252 kfree(inarg);
253
254 return out.h.result;
255}
256
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000257static int fuse_permission(struct inode *inode, int mask)
258{
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000259 return 0;
260}
261
262static int fuse_revalidate(struct dentry *dentry)
263{
264 struct inode *inode = dentry->d_inode;
265 struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
266 struct fuse_in in = FUSE_IN_INIT;
267 struct fuse_out out = FUSE_OUT_INIT;
268 struct fuse_getattr_out arg;
269
270 in.h.opcode = FUSE_GETATTR;
271 in.h.ino = inode->i_ino;
272 out.argsize = sizeof(arg);
273 out.arg = &arg;
274 request_send(fc, &in, &out);
275
276 if(out.h.result == 0)
277 change_attributes(inode, &arg.attr);
Miklos Szeredib483c932001-10-29 14:57:57 +0000278 else
279 d_drop(dentry);
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000280
281 return out.h.result;
282}
283
Miklos Szeredib483c932001-10-29 14:57:57 +0000284static int parse_dirfile(char *buf, size_t nbytes, struct file *file,
285 void *dstbuf, filldir_t filldir)
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000286{
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000287 while(nbytes >= FUSE_NAME_OFFSET) {
Miklos Szeredib483c932001-10-29 14:57:57 +0000288 struct fuse_dirent *dirent = (struct fuse_dirent *) buf;
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000289 size_t reclen = FUSE_DIRENT_SIZE(dirent);
290 int over;
291 if(dirent->namelen > NAME_MAX) {
292 printk("fuse_readdir: name too long\n");
Miklos Szeredib483c932001-10-29 14:57:57 +0000293 return -EPROTO;
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000294 }
295 if(reclen > nbytes)
296 break;
297
298 over = filldir(dstbuf, dirent->name, dirent->namelen,
299 file->f_pos, dirent->ino, dirent->type);
300 if(over)
301 break;
302
Miklos Szeredib483c932001-10-29 14:57:57 +0000303 buf += reclen;
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000304 file->f_pos += reclen;
305 nbytes -= reclen;
306 }
307
Miklos Szeredib483c932001-10-29 14:57:57 +0000308 return 0;
309}
310
311#define DIR_BUFSIZE 2048
312static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
313{
314 struct file *cfile = file->private_data;
315 char *buf;
316 int ret;
317
318 buf = kmalloc(DIR_BUFSIZE, GFP_KERNEL);
319 if(!buf)
320 return -ENOMEM;
321
322 ret = kernel_read(cfile, file->f_pos, buf, DIR_BUFSIZE);
323 if(ret < 0)
324 printk("fuse_readdir: failed to read container file\n");
325 else
326 ret = parse_dirfile(buf, ret, file, dstbuf, filldir);
327
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000328 kfree(buf);
329 return ret;
330}
331
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000332static int read_link(struct dentry *dentry, char **bufp)
333{
334 struct inode *inode = dentry->d_inode;
335 struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
336 struct fuse_in in = FUSE_IN_INIT;
337 struct fuse_out out = FUSE_OUT_INIT;
338 unsigned long page;
339
340 page = __get_free_page(GFP_KERNEL);
341 if(!page)
342 return -ENOMEM;
343
344 in.h.opcode = FUSE_READLINK;
345 in.h.ino = inode->i_ino;
346 out.arg = (void *) page;
347 out.argsize = PAGE_SIZE - 1;
348 out.argvar = 1;
349 request_send(fc, &in, &out);
350 if(out.h.result) {
351 free_page(page);
352 return out.h.result;
353 }
354
355 *bufp = (char *) page;
356 (*bufp)[out.argsize] = '\0';
357 return 0;
358}
359
360static void free_link(char *link)
361{
362 free_page((unsigned long) link);
363}
364
365static int fuse_readlink(struct dentry *dentry, char *buffer, int buflen)
366{
367 int ret;
368 char *link;
369
370 ret = read_link(dentry, &link);
371 if(ret)
372 return ret;
373
374 ret = vfs_readlink(dentry, buffer, buflen, link);
375 free_link(link);
376 return ret;
377}
378
379static int fuse_follow_link(struct dentry *dentry, struct nameidata *nd)
380{
381 int ret;
382 char *link;
383
384 ret = read_link(dentry, &link);
385 if(ret)
386 return ret;
387
388 ret = vfs_follow_link(nd, link);
389 free_link(link);
390 return ret;
391}
392
393static int fuse_dir_open(struct inode *inode, struct file *file)
394{
395 struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
396 struct fuse_in in = FUSE_IN_INIT;
397 struct fuse_out out = FUSE_OUT_INIT;
398 struct fuse_getdir_out outarg;
399
400 if(!(file->f_flags & O_DIRECTORY))
401 return -EISDIR;
402
403 in.h.opcode = FUSE_GETDIR;
404 in.h.ino = inode->i_ino;
405 out.argsize = sizeof(outarg);
406 out.arg = &outarg;
407 request_send(fc, &in, &out);
408 if(out.h.result == 0) {
409 struct file *cfile = outarg.file;
410 struct inode *inode;
411 if(!cfile) {
412 printk("fuse_getdir: invalid file\n");
413 return -EPROTO;
414 }
415 inode = cfile->f_dentry->d_inode;
416 if(!S_ISREG(inode->i_mode)) {
417 printk("fuse_getdir: not a regular file\n");
418 fput(cfile);
419 return -EPROTO;
420 }
421
422 file->private_data = cfile;
423 }
424
425 return out.h.result;
426}
427
428static int fuse_dir_release(struct inode *inode, struct file *file)
429{
430 struct file *cfile = file->private_data;
431
432 if(!cfile)
433 BUG();
434
435 fput(cfile);
436
437 return 0;
438}
439
440static struct inode_operations fuse_dir_inode_operations =
441{
442 lookup: fuse_lookup,
443 create: fuse_create,
444 mknod: fuse_mknod,
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000445 mkdir: fuse_mkdir,
Miklos Szeredib483c932001-10-29 14:57:57 +0000446 symlink: fuse_symlink,
447 unlink: fuse_unlink,
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000448 rmdir: fuse_rmdir,
449 rename: fuse_rename,
Miklos Szeredib483c932001-10-29 14:57:57 +0000450#if 0
451 link: fuse_link,
452 setattr: fuse_setattr,
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000453#endif
454 permission: fuse_permission,
455 revalidate: fuse_revalidate,
456};
457
458static struct file_operations fuse_dir_operations = {
459 read: generic_read_dir,
460 readdir: fuse_readdir,
461 open: fuse_dir_open,
462 release: fuse_dir_release,
463};
464
465static struct inode_operations fuse_file_inode_operations = {
466 permission: fuse_permission,
467 revalidate: fuse_revalidate,
468};
469
470static struct inode_operations fuse_special_inode_operations = {
471 permission: fuse_permission,
472 revalidate: fuse_revalidate,
473};
474
475static struct file_operations fuse_file_operations = {
476};
477
478static struct inode_operations fuse_symlink_inode_operations =
479{
480 readlink: fuse_readlink,
481 follow_link: fuse_follow_link,
482 revalidate: fuse_revalidate,
483};
484
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000485/*
486 * Local Variables:
487 * indent-tabs-mode: t
488 * c-basic-offset: 8
489 * End:
490 */