blob: f033e0406b5b33b31804a369d5a1123d46f3be97 [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#include <linux/fuse.h>
11
12#include <string.h>
13#include <unistd.h>
14#include <errno.h>
Miklos Szeredi19dff1b2001-10-30 15:06:52 +000015#include <assert.h>
Miklos Szeredi85c74fc2001-10-28 19:44:14 +000016
17
18static guint name_hash(const struct node *node)
19{
20 return g_str_hash(node->name) ^ node->parent;
21}
22
23static gint name_compare(const struct node *node1, const struct node *node2)
24{
25 return
26 node1->parent == node2->parent &&
27 strcmp(node1->name, node2->name) == 0;
28}
29
30static struct node *new_node(fino_t parent, const char *name)
31{
32 struct node *node = g_new0(struct node, 1);
Miklos Szeredi19dff1b2001-10-30 15:06:52 +000033 node->name = g_strdup(name);
Miklos Szeredi85c74fc2001-10-28 19:44:14 +000034 node->parent = parent;
35 return node;
36}
37
38static int free_node(struct node *node)
39{
40 g_free(node->name);
41 g_free(node);
42 return 1;
43}
44
45static inline struct node *get_node(fino_t ino)
46{
Miklos Szeredib483c932001-10-29 14:57:57 +000047 return (struct node *) ((ino << 3) + 0x8000000);
Miklos Szeredi85c74fc2001-10-28 19:44:14 +000048}
49
50static inline fino_t get_ino(struct node *node)
51{
Miklos Szeredib483c932001-10-29 14:57:57 +000052 return (((fino_t) node) - 0x8000000) >> 3;
Miklos Szeredi85c74fc2001-10-28 19:44:14 +000053}
54
Miklos Szeredi19dff1b2001-10-30 15:06:52 +000055static struct node *lookup_node(struct fuse *f, fino_t parent,
56 const char *name)
57{
58 struct node tmp;
59
60 tmp.name = (char *) name;
61 tmp.parent = parent;
62
63 return g_hash_table_lookup(f->nametab, &tmp);
64}
65
Miklos Szeredi85c74fc2001-10-28 19:44:14 +000066static fino_t find_node(struct fuse *f, fino_t parent, char *name, int create)
67{
68 struct node *node;
Miklos Szeredi85c74fc2001-10-28 19:44:14 +000069
Miklos Szeredi19dff1b2001-10-30 15:06:52 +000070 node = lookup_node(f, parent, name);
Miklos Szeredi85c74fc2001-10-28 19:44:14 +000071 if(node != NULL)
72 return get_ino(node);
73
74 if(!create)
75 return (fino_t) -1;
76
77 node = new_node(parent, name);
78 g_hash_table_insert(f->nametab, node, node);
79 return get_ino(node);
80}
81
82static char *get_path(fino_t ino)
83{
84 GString *s;
85 char *ss;
86
87 s = g_string_new("");
88 if(ino == FUSE_ROOT_INO)
89 g_string_prepend_c(s, '/');
90 else {
91 struct node *node;
92 for(; ino != FUSE_ROOT_INO; ino = node->parent) {
93 node = get_node(ino);
94 g_string_prepend(s, node->name);
95 g_string_prepend_c(s, '/');
96 }
97 }
98
99 ss = s->str;
100 g_string_free(s, FALSE);
101
102 return ss;
103}
104
Miklos Szeredib483c932001-10-29 14:57:57 +0000105static char *get_path_name(fino_t ino, const char *name)
106{
107 char *path = get_path(ino);
108 char *path2 = g_strconcat(path, "/", name, NULL);
109 g_free(path);
110 return path2;
111}
112
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000113static void remove_node(struct fuse *f, fino_t ino)
114{
115 struct node *node = get_node(ino);
116 g_hash_table_remove(f->nametab, node);
117 free_node(node);
118}
119
Miklos Szeredi19dff1b2001-10-30 15:06:52 +0000120static void rename_node(struct fuse *f, fino_t olddir, const char *oldname,
121 fino_t newdir, const char *newname)
122{
123 struct node *node = lookup_node(f, olddir, oldname);
124 struct node *newnode = lookup_node(f, newdir, newname);
125
126 assert(node != NULL);
127
128 /* The overwritten node is left to dangle until deleted */
129 if(newnode != NULL)
130 g_hash_table_remove(f->nametab, newnode);
131
132 /* The renamed node is not freed, since it's pointer is the key */
133 g_hash_table_remove(f->nametab, node);
134 g_free(node->name);
135 node->name = g_strdup(newname);
136 node->parent = newdir;
137 g_hash_table_insert(f->nametab, node, node);
138}
139
140
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000141static void convert_stat(struct stat *stbuf, struct fuse_attr *attr)
142{
143 attr->mode = stbuf->st_mode;
144 attr->nlink = stbuf->st_nlink;
145 attr->uid = stbuf->st_uid;
146 attr->gid = stbuf->st_gid;
147 attr->rdev = stbuf->st_rdev;
148 attr->size = stbuf->st_size;
149 attr->blksize = stbuf->st_blksize;
150 attr->blocks = stbuf->st_blocks;
151 attr->atime = stbuf->st_atime;
152 attr->mtime = stbuf->st_mtime;
153 attr->ctime = stbuf->st_ctime;
154}
155
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000156static int fill_dir(struct fuse_dh *dh, char *name, int type)
157{
158 struct fuse_dirent dirent;
159 size_t reclen;
160 size_t res;
161
162 dirent.ino = find_node(dh->fuse, dh->dir, name, 0);
163 dirent.namelen = strlen(name);
164 strncpy(dirent.name, name, sizeof(dirent.name));
165 dirent.type = type;
166 reclen = FUSE_DIRENT_SIZE(&dirent);
167 res = fwrite(&dirent, reclen, 1, dh->fp);
168 if(res == 0) {
169 perror("writing directory file");
170 return -EIO;
171 }
172 return 0;
173}
174
Miklos Szeredi19dff1b2001-10-30 15:06:52 +0000175static void send_reply(struct fuse *f, struct fuse_in_header *in, int error,
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000176 void *arg, size_t argsize)
177{
178 int res;
179 char *outbuf;
180 size_t outsize;
181 struct fuse_out_header *out;
182
Miklos Szeredi19dff1b2001-10-30 15:06:52 +0000183 if(error > 0) {
184 fprintf(stderr, "positive error code: %i\n", error);
185 error = -ERANGE;
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000186 }
187
Miklos Szeredi19dff1b2001-10-30 15:06:52 +0000188 if(error)
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000189 argsize = 0;
190
191 outsize = sizeof(struct fuse_out_header) + argsize;
192 outbuf = (char *) g_malloc(outsize);
193 out = (struct fuse_out_header *) outbuf;
194 out->unique = in->unique;
Miklos Szeredi19dff1b2001-10-30 15:06:52 +0000195 out->error = error;
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000196 if(argsize != 0)
197 memcpy(outbuf + sizeof(struct fuse_out_header), arg, argsize);
198
Miklos Szeredi19dff1b2001-10-30 15:06:52 +0000199 printf(" unique: %i, error: %i (%s), outsize: %i\n", out->unique,
200 out->error, strerror(-out->error), outsize);
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000201
202 res = write(f->fd, outbuf, outsize);
203 if(res == -1)
204 perror("writing fuse device");
205
206 g_free(outbuf);
207}
208
209static void do_lookup(struct fuse *f, struct fuse_in_header *in, char *name)
210{
211 int res;
Miklos Szeredi19dff1b2001-10-30 15:06:52 +0000212 char *path;
213 struct stat buf;
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000214 struct fuse_lookup_out arg;
215
Miklos Szeredi19dff1b2001-10-30 15:06:52 +0000216 path = get_path_name(in->ino, name);
217 res = -ENOSYS;
218 if(f->op.getattr)
219 res = f->op.getattr(path, &buf);
220 g_free(path);
221 if(res == 0) {
222 convert_stat(&buf, &arg.attr);
223 arg.ino = find_node(f, in->ino, name, 1);
224 }
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000225 send_reply(f, in, res, &arg, sizeof(arg));
226}
227
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000228static void do_forget(struct fuse *f, unsigned long *inos, size_t num)
229{
230 size_t i;
231
232 for(i = 0; i < num; i++)
233 remove_node(f, inos[i]);
234}
235
236static void do_getattr(struct fuse *f, struct fuse_in_header *in)
237{
238 int res;
Miklos Szeredi19dff1b2001-10-30 15:06:52 +0000239 char *path;
240 struct stat buf;
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000241 struct fuse_getattr_out arg;
242
Miklos Szeredi19dff1b2001-10-30 15:06:52 +0000243 path = get_path(in->ino);
244 res = -ENOSYS;
245 if(f->op.getattr)
246 res = f->op.getattr(path, &buf);
247 g_free(path);
248 if(res == 0)
249 convert_stat(&buf, &arg.attr);
250
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000251 send_reply(f, in, res, &arg, sizeof(arg));
252}
253
254static void do_readlink(struct fuse *f, struct fuse_in_header *in)
255{
256 int res;
257 char link[PATH_MAX + 1];
Miklos Szeredib483c932001-10-29 14:57:57 +0000258 char *path;
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000259
Miklos Szeredib483c932001-10-29 14:57:57 +0000260 path = get_path(in->ino);
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000261 res = -ENOSYS;
Miklos Szeredib483c932001-10-29 14:57:57 +0000262 if(f->op.readlink)
263 res = f->op.readlink(path, link, sizeof(link));
264 g_free(path);
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000265
Miklos Szeredib483c932001-10-29 14:57:57 +0000266 send_reply(f, in, res, link, res == 0 ? strlen(link) : 0);
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000267}
268
269static void do_getdir(struct fuse *f, struct fuse_in_header *in)
270{
271 int res;
272 struct fuse_getdir_out arg;
Miklos Szeredib483c932001-10-29 14:57:57 +0000273 struct fuse_dh dh;
274 char *path;
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000275
Miklos Szeredib483c932001-10-29 14:57:57 +0000276 dh.fuse = f;
277 dh.fp = tmpfile();
278 dh.dir = in->ino;
279
280 path = get_path(in->ino);
281 res = -ENOSYS;
282 if(f->op.getdir)
283 res = f->op.getdir(path, &dh, (dirfiller_t) fill_dir);
284 g_free(path);
285
286 fflush(dh.fp);
287 arg.fd = fileno(dh.fp);
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000288 send_reply(f, in, res, &arg, sizeof(arg));
Miklos Szeredib483c932001-10-29 14:57:57 +0000289 fclose(dh.fp);
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000290}
291
Miklos Szeredib483c932001-10-29 14:57:57 +0000292static void do_mknod(struct fuse *f, struct fuse_in_header *in,
293 struct fuse_mknod_in *inarg)
294{
295 int res;
296 char *path;
297 struct fuse_mknod_out outarg;
298 struct stat buf;
299
Miklos Szeredi19dff1b2001-10-30 15:06:52 +0000300 path = get_path_name(in->ino, inarg->name);
Miklos Szeredib483c932001-10-29 14:57:57 +0000301 res = -ENOSYS;
302 if(f->op.mknod && f->op.getattr) {
303 res = f->op.mknod(path, inarg->mode, inarg->rdev);
304 if(res == 0)
305 res = f->op.getattr(path, &buf);
306 }
307 g_free(path);
Miklos Szeredi19dff1b2001-10-30 15:06:52 +0000308 if(res == 0) {
Miklos Szeredib483c932001-10-29 14:57:57 +0000309 convert_stat(&buf, &outarg.attr);
Miklos Szeredi19dff1b2001-10-30 15:06:52 +0000310 outarg.ino = find_node(f, in->ino, inarg->name, 1);
311 }
Miklos Szeredib483c932001-10-29 14:57:57 +0000312
313 send_reply(f, in, res, &outarg, sizeof(outarg));
314}
315
316static void do_mkdir(struct fuse *f, struct fuse_in_header *in,
317 struct fuse_mkdir_in *inarg)
318{
319 int res;
320 char *path;
321
322 path = get_path_name(in->ino, inarg->name);
323 res = -ENOSYS;
324 if(f->op.mkdir)
325 res = f->op.mkdir(path, inarg->mode);
326 g_free(path);
327 send_reply(f, in, res, NULL, 0);
328}
329
330static void do_remove(struct fuse *f, struct fuse_in_header *in, char *name)
331{
332 int res;
333 char *path;
334
335 path = get_path_name(in->ino, name);
336 res = -ENOSYS;
337 if(in->opcode == FUSE_UNLINK) {
338 if(f->op.unlink)
339 res = f->op.unlink(path);
340 }
341 else {
342 if(f->op.rmdir)
343 res = f->op.rmdir(path);
344 }
345 g_free(path);
346 send_reply(f, in, res, NULL, 0);
347}
348
349static void do_symlink(struct fuse *f, struct fuse_in_header *in, char *name,
350 char *link)
351{
352 int res;
353 char *path;
354
355 path = get_path_name(in->ino, name);
356 res = -ENOSYS;
357 if(f->op.symlink)
358 res = f->op.symlink(link, path);
359 g_free(path);
360 send_reply(f, in, res, NULL, 0);
361}
362
Miklos Szeredi19dff1b2001-10-30 15:06:52 +0000363static void do_rename(struct fuse *f, struct fuse_in_header *in,
364 struct fuse_rename_in *inarg)
365{
366 int res;
367 fino_t olddir = in->ino;
368 fino_t newdir = inarg->newdir;
369 char *oldname = inarg->names;
370 char *newname = inarg->names + strlen(oldname) + 1;
371 char *oldpath = get_path_name(olddir, oldname);
372 char *newpath = get_path_name(newdir, newname);
373
374 res = -ENOSYS;
375 if(f->op.rename)
376 res = f->op.rename(oldpath, newpath);
377 if(res == 0)
378 rename_node(f, olddir, oldname, newdir, newname);
379 send_reply(f, in, res, NULL, 0);
380}
381
382static void do_link(struct fuse *f, struct fuse_in_header *in,
383 struct fuse_link_in *inarg)
384{
385 int res;
386 char *oldpath = get_path(in->ino);
387 char *newpath = get_path_name(inarg->newdir, inarg->name);
388
389 res = -ENOSYS;
390 if(f->op.link)
391 res = f->op.link(oldpath, newpath);
392
393 send_reply(f, in, res, NULL, 0);
394}
395
Miklos Szeredib483c932001-10-29 14:57:57 +0000396
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000397void fuse_loop(struct fuse *f)
398{
399 int res;
400 char inbuf[FUSE_MAX_IN];
401 struct fuse_in_header *in = (struct fuse_in_header *) inbuf;
402 void *inarg = inbuf + sizeof(struct fuse_in_header);
403 size_t insize;
404 size_t argsize;
405
406 while(1) {
407 res = read(f->fd, inbuf, sizeof(inbuf));
408 if(res == -1) {
409 perror("reading fuse device");
410 continue;
411 }
412 insize = res;
413
414 if(insize < sizeof(struct fuse_in_header)) {
415 fprintf(stderr, "short read on fuse device\n");
416 continue;
417 }
418 printf("unique: %i, opcode: %i, ino: %li, insize: %i (%i)\n",
419 in->unique, in->opcode, in->ino, insize,
420 g_hash_table_size(f->nametab));
421
422 argsize = insize - sizeof(struct fuse_in_header);
423
424 switch(in->opcode) {
425 case FUSE_LOOKUP:
426 do_lookup(f, in, (char *) inarg);
427 break;
428
429 case FUSE_FORGET:
430 do_forget(f, (unsigned long *) inarg,
431 argsize / sizeof(unsigned long));
432 break;
433
434 case FUSE_GETATTR:
435 do_getattr(f, in);
436 break;
437
438 case FUSE_READLINK:
439 do_readlink(f, in);
440 break;
441
442 case FUSE_GETDIR:
443 do_getdir(f, in);
444 break;
445
446 case FUSE_MKNOD:
447 do_mknod(f, in, (struct fuse_mknod_in *) inarg);
448 break;
Miklos Szeredib483c932001-10-29 14:57:57 +0000449
450 case FUSE_MKDIR:
451 do_mkdir(f, in, (struct fuse_mkdir_in *) inarg);
452 break;
453
454 case FUSE_UNLINK:
455 case FUSE_RMDIR:
456 do_remove(f, in, (char *) inarg);
457 break;
458
459 case FUSE_SYMLINK:
460 do_symlink(f, in, (char *) inarg,
461 ((char *) inarg) + strlen((char *) inarg) + 1);
462 break;
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000463
Miklos Szeredi19dff1b2001-10-30 15:06:52 +0000464 case FUSE_RENAME:
465 do_rename(f, in, (struct fuse_rename_in *) inarg);
466 break;
467
468 case FUSE_LINK:
469 do_link(f, in, (struct fuse_link_in *) inarg);
470 break;
471
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000472 default:
473 fprintf(stderr, "Operation %i not implemented\n", in->opcode);
474 /* No need to send reply to async requests */
475 if(in->unique != 0)
476 send_reply(f, in, -ENOSYS, NULL, 0);
477 }
478 }
479}
480
481struct fuse *fuse_new()
482{
483 struct fuse *f = g_new0(struct fuse, 1);
484
485 f->fd = -1;
486 f->dir = NULL;
487 f->nametab = g_hash_table_new((GHashFunc) name_hash,
488 (GCompareFunc) name_compare);
489
490 return f;
491}
492
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000493void fuse_set_operations(struct fuse *f, const struct fuse_operations *op)
494{
495 f->op = *op;
496}
497
498void fuse_destroy(struct fuse *f)
499{
500 fuse_unmount(f);
501 g_hash_table_foreach_remove(f->nametab, (GHRFunc) free_node, NULL);
502 g_hash_table_destroy(f->nametab);
503 g_free(f);
504}