blob: 62695f0923a1ce5e2248e9441b73b5cf6d27d79c [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>
15
16
17static guint name_hash(const struct node *node)
18{
19 return g_str_hash(node->name) ^ node->parent;
20}
21
22static gint name_compare(const struct node *node1, const struct node *node2)
23{
24 return
25 node1->parent == node2->parent &&
26 strcmp(node1->name, node2->name) == 0;
27}
28
29static struct node *new_node(fino_t parent, const char *name)
30{
31 struct node *node = g_new0(struct node, 1);
32 node->name = strdup(name);
33 node->parent = parent;
34 return node;
35}
36
37static int free_node(struct node *node)
38{
39 g_free(node->name);
40 g_free(node);
41 return 1;
42}
43
44static inline struct node *get_node(fino_t ino)
45{
Miklos Szeredib483c932001-10-29 14:57:57 +000046 return (struct node *) ((ino << 3) + 0x8000000);
Miklos Szeredi85c74fc2001-10-28 19:44:14 +000047}
48
49static inline fino_t get_ino(struct node *node)
50{
Miklos Szeredib483c932001-10-29 14:57:57 +000051 return (((fino_t) node) - 0x8000000) >> 3;
Miklos Szeredi85c74fc2001-10-28 19:44:14 +000052}
53
Miklos Szeredi85c74fc2001-10-28 19:44:14 +000054static fino_t find_node(struct fuse *f, fino_t parent, char *name, int create)
55{
56 struct node *node;
57 struct node tmp;
58
59 tmp.name = name;
60 tmp.parent = parent;
61
62 node = g_hash_table_lookup(f->nametab, &tmp);
63 if(node != NULL)
64 return get_ino(node);
65
66 if(!create)
67 return (fino_t) -1;
68
69 node = new_node(parent, name);
70 g_hash_table_insert(f->nametab, node, node);
71 return get_ino(node);
72}
73
74static char *get_path(fino_t ino)
75{
76 GString *s;
77 char *ss;
78
79 s = g_string_new("");
80 if(ino == FUSE_ROOT_INO)
81 g_string_prepend_c(s, '/');
82 else {
83 struct node *node;
84 for(; ino != FUSE_ROOT_INO; ino = node->parent) {
85 node = get_node(ino);
86 g_string_prepend(s, node->name);
87 g_string_prepend_c(s, '/');
88 }
89 }
90
91 ss = s->str;
92 g_string_free(s, FALSE);
93
94 return ss;
95}
96
Miklos Szeredib483c932001-10-29 14:57:57 +000097static char *get_path_name(fino_t ino, const char *name)
98{
99 char *path = get_path(ino);
100 char *path2 = g_strconcat(path, "/", name, NULL);
101 g_free(path);
102 return path2;
103}
104
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000105static void remove_node(struct fuse *f, fino_t ino)
106{
107 struct node *node = get_node(ino);
108 g_hash_table_remove(f->nametab, node);
109 free_node(node);
110}
111
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000112static void convert_stat(struct stat *stbuf, struct fuse_attr *attr)
113{
114 attr->mode = stbuf->st_mode;
115 attr->nlink = stbuf->st_nlink;
116 attr->uid = stbuf->st_uid;
117 attr->gid = stbuf->st_gid;
118 attr->rdev = stbuf->st_rdev;
119 attr->size = stbuf->st_size;
120 attr->blksize = stbuf->st_blksize;
121 attr->blocks = stbuf->st_blocks;
122 attr->atime = stbuf->st_atime;
123 attr->mtime = stbuf->st_mtime;
124 attr->ctime = stbuf->st_ctime;
125}
126
127static int get_attributes(struct fuse *f, fino_t ino, struct fuse_attr *attr)
128{
129 char *path;
130 struct stat buf;
131 int res;
132
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000133 path = get_path(ino);
Miklos Szeredib483c932001-10-29 14:57:57 +0000134 res = -ENOSYS;
135 if(f->op.getattr)
136 res = f->op.getattr(path, &buf);
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000137 g_free(path);
138 if(res == 0)
139 convert_stat(&buf, attr);
140
141 return res;
142}
143
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000144static int fill_dir(struct fuse_dh *dh, char *name, int type)
145{
146 struct fuse_dirent dirent;
147 size_t reclen;
148 size_t res;
149
150 dirent.ino = find_node(dh->fuse, dh->dir, name, 0);
151 dirent.namelen = strlen(name);
152 strncpy(dirent.name, name, sizeof(dirent.name));
153 dirent.type = type;
154 reclen = FUSE_DIRENT_SIZE(&dirent);
155 res = fwrite(&dirent, reclen, 1, dh->fp);
156 if(res == 0) {
157 perror("writing directory file");
158 return -EIO;
159 }
160 return 0;
161}
162
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000163static void send_reply(struct fuse *f, struct fuse_in_header *in, int result,
164 void *arg, size_t argsize)
165{
166 int res;
167 char *outbuf;
168 size_t outsize;
169 struct fuse_out_header *out;
170
171 if(result > 0) {
172 fprintf(stderr, "positive result to operation %i : %i\n", in->opcode,
173 result);
174 result = -ERANGE;
175 }
176
177 if(result != 0)
178 argsize = 0;
179
180 outsize = sizeof(struct fuse_out_header) + argsize;
181 outbuf = (char *) g_malloc(outsize);
182 out = (struct fuse_out_header *) outbuf;
183 out->unique = in->unique;
184 out->result = result;
185 if(argsize != 0)
186 memcpy(outbuf + sizeof(struct fuse_out_header), arg, argsize);
187
188 printf(" unique: %i, result: %i (%s), outsize: %i\n", out->unique,
189 out->result, strerror(-out->result), outsize);
190
191 res = write(f->fd, outbuf, outsize);
192 if(res == -1)
193 perror("writing fuse device");
194
195 g_free(outbuf);
196}
197
198static void do_lookup(struct fuse *f, struct fuse_in_header *in, char *name)
199{
200 int res;
201 struct fuse_lookup_out arg;
202
203 arg.ino = find_node(f, in->ino, name, 1);
204 res = get_attributes(f, arg.ino, &arg.attr);
205
206 send_reply(f, in, res, &arg, sizeof(arg));
207}
208
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000209static void do_forget(struct fuse *f, unsigned long *inos, size_t num)
210{
211 size_t i;
212
213 for(i = 0; i < num; i++)
214 remove_node(f, inos[i]);
215}
216
217static void do_getattr(struct fuse *f, struct fuse_in_header *in)
218{
219 int res;
220 struct fuse_getattr_out arg;
221
222 res = get_attributes(f, in->ino, &arg.attr);
223 send_reply(f, in, res, &arg, sizeof(arg));
224}
225
226static void do_readlink(struct fuse *f, struct fuse_in_header *in)
227{
228 int res;
229 char link[PATH_MAX + 1];
Miklos Szeredib483c932001-10-29 14:57:57 +0000230 char *path;
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000231
Miklos Szeredib483c932001-10-29 14:57:57 +0000232 path = get_path(in->ino);
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000233 res = -ENOSYS;
Miklos Szeredib483c932001-10-29 14:57:57 +0000234 if(f->op.readlink)
235 res = f->op.readlink(path, link, sizeof(link));
236 g_free(path);
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000237
Miklos Szeredib483c932001-10-29 14:57:57 +0000238 send_reply(f, in, res, link, res == 0 ? strlen(link) : 0);
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000239}
240
241static void do_getdir(struct fuse *f, struct fuse_in_header *in)
242{
243 int res;
244 struct fuse_getdir_out arg;
Miklos Szeredib483c932001-10-29 14:57:57 +0000245 struct fuse_dh dh;
246 char *path;
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000247
Miklos Szeredib483c932001-10-29 14:57:57 +0000248 dh.fuse = f;
249 dh.fp = tmpfile();
250 dh.dir = in->ino;
251
252 path = get_path(in->ino);
253 res = -ENOSYS;
254 if(f->op.getdir)
255 res = f->op.getdir(path, &dh, (dirfiller_t) fill_dir);
256 g_free(path);
257
258 fflush(dh.fp);
259 arg.fd = fileno(dh.fp);
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000260 send_reply(f, in, res, &arg, sizeof(arg));
Miklos Szeredib483c932001-10-29 14:57:57 +0000261 fclose(dh.fp);
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000262}
263
Miklos Szeredib483c932001-10-29 14:57:57 +0000264static void do_mknod(struct fuse *f, struct fuse_in_header *in,
265 struct fuse_mknod_in *inarg)
266{
267 int res;
268 char *path;
269 struct fuse_mknod_out outarg;
270 struct stat buf;
271
272 outarg.ino = find_node(f, in->ino, inarg->name, 1);
273 path = get_path(outarg.ino);
274 res = -ENOSYS;
275 if(f->op.mknod && f->op.getattr) {
276 res = f->op.mknod(path, inarg->mode, inarg->rdev);
277 if(res == 0)
278 res = f->op.getattr(path, &buf);
279 }
280 g_free(path);
281
282 if(res == 0)
283 convert_stat(&buf, &outarg.attr);
284
285 send_reply(f, in, res, &outarg, sizeof(outarg));
286}
287
288static void do_mkdir(struct fuse *f, struct fuse_in_header *in,
289 struct fuse_mkdir_in *inarg)
290{
291 int res;
292 char *path;
293
294 path = get_path_name(in->ino, inarg->name);
295 res = -ENOSYS;
296 if(f->op.mkdir)
297 res = f->op.mkdir(path, inarg->mode);
298 g_free(path);
299 send_reply(f, in, res, NULL, 0);
300}
301
302static void do_remove(struct fuse *f, struct fuse_in_header *in, char *name)
303{
304 int res;
305 char *path;
306
307 path = get_path_name(in->ino, name);
308 res = -ENOSYS;
309 if(in->opcode == FUSE_UNLINK) {
310 if(f->op.unlink)
311 res = f->op.unlink(path);
312 }
313 else {
314 if(f->op.rmdir)
315 res = f->op.rmdir(path);
316 }
317 g_free(path);
318 send_reply(f, in, res, NULL, 0);
319}
320
321static void do_symlink(struct fuse *f, struct fuse_in_header *in, char *name,
322 char *link)
323{
324 int res;
325 char *path;
326
327 path = get_path_name(in->ino, name);
328 res = -ENOSYS;
329 if(f->op.symlink)
330 res = f->op.symlink(link, path);
331 g_free(path);
332 send_reply(f, in, res, NULL, 0);
333}
334
335
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000336void fuse_loop(struct fuse *f)
337{
338 int res;
339 char inbuf[FUSE_MAX_IN];
340 struct fuse_in_header *in = (struct fuse_in_header *) inbuf;
341 void *inarg = inbuf + sizeof(struct fuse_in_header);
342 size_t insize;
343 size_t argsize;
344
345 while(1) {
346 res = read(f->fd, inbuf, sizeof(inbuf));
347 if(res == -1) {
348 perror("reading fuse device");
349 continue;
350 }
351 insize = res;
352
353 if(insize < sizeof(struct fuse_in_header)) {
354 fprintf(stderr, "short read on fuse device\n");
355 continue;
356 }
357 printf("unique: %i, opcode: %i, ino: %li, insize: %i (%i)\n",
358 in->unique, in->opcode, in->ino, insize,
359 g_hash_table_size(f->nametab));
360
361 argsize = insize - sizeof(struct fuse_in_header);
362
363 switch(in->opcode) {
364 case FUSE_LOOKUP:
365 do_lookup(f, in, (char *) inarg);
366 break;
367
368 case FUSE_FORGET:
369 do_forget(f, (unsigned long *) inarg,
370 argsize / sizeof(unsigned long));
371 break;
372
373 case FUSE_GETATTR:
374 do_getattr(f, in);
375 break;
376
377 case FUSE_READLINK:
378 do_readlink(f, in);
379 break;
380
381 case FUSE_GETDIR:
382 do_getdir(f, in);
383 break;
384
385 case FUSE_MKNOD:
386 do_mknod(f, in, (struct fuse_mknod_in *) inarg);
387 break;
Miklos Szeredib483c932001-10-29 14:57:57 +0000388
389 case FUSE_MKDIR:
390 do_mkdir(f, in, (struct fuse_mkdir_in *) inarg);
391 break;
392
393 case FUSE_UNLINK:
394 case FUSE_RMDIR:
395 do_remove(f, in, (char *) inarg);
396 break;
397
398 case FUSE_SYMLINK:
399 do_symlink(f, in, (char *) inarg,
400 ((char *) inarg) + strlen((char *) inarg) + 1);
401 break;
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000402
403 default:
404 fprintf(stderr, "Operation %i not implemented\n", in->opcode);
405 /* No need to send reply to async requests */
406 if(in->unique != 0)
407 send_reply(f, in, -ENOSYS, NULL, 0);
408 }
409 }
410}
411
412struct fuse *fuse_new()
413{
414 struct fuse *f = g_new0(struct fuse, 1);
415
416 f->fd = -1;
417 f->dir = NULL;
418 f->nametab = g_hash_table_new((GHashFunc) name_hash,
419 (GCompareFunc) name_compare);
420
421 return f;
422}
423
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000424void fuse_set_operations(struct fuse *f, const struct fuse_operations *op)
425{
426 f->op = *op;
427}
428
429void fuse_destroy(struct fuse *f)
430{
431 fuse_unmount(f);
432 g_hash_table_foreach_remove(f->nametab, (GHRFunc) free_node, NULL);
433 g_hash_table_destroy(f->nametab);
434 g_free(f);
435}