pfuse 0.7.5

Sign up to get free protection for your applications and to get access to all the features.
data/ext/fusefs_lib.c ADDED
@@ -0,0 +1,560 @@
1
+ // ruby-fuse
2
+ // A Ruby module to interact with the FUSE userland filesystem in
3
+ // a clear way.
4
+
5
+ // TODO
6
+ // * return values are inconsistent
7
+ // * fill in the rest of the fuse api
8
+ // * turn half of these into macros
9
+ // * sample FS for testing
10
+
11
+ #include "fusefs_lib.h"
12
+
13
+ // rf_getattr - FuseRoot.getattr should return a FuseFS::Stat object
14
+ static int
15
+ rf_getattr(const char *path, struct stat *stbuf)
16
+ {
17
+ VALUE cur_entry;
18
+
19
+ debug("rf_getattr(%s)\n", path);
20
+
21
+ memset(stbuf, 0, sizeof(struct stat));
22
+ cur_entry = rf_call("getattr", path, Qnil);
23
+
24
+ if (RTEST(cur_entry)) {
25
+ VSTAT2STAT(cur_entry, stbuf);
26
+ return 0;
27
+ } else {
28
+ return -ENOENT;
29
+ }
30
+ }
31
+
32
+ // rf_readlink - resolve symlink then read bufsize into buf
33
+
34
+ // rf_mknod - make a special file node
35
+ static int
36
+ rf_mknod(const char *path, mode_t umode, dev_t rdev)
37
+ {
38
+ debug("rf_mknod(%s, 0%o)\n", path, umode);
39
+
40
+ // We ONLY permit regular files. No blocks, characters, fifos, etc.
41
+ if (!S_ISREG(umode)) {
42
+ return -EACCES;
43
+ }
44
+
45
+ // FuseFS.mknod only takes a path, since we don't handle non-files as above
46
+ if (!RTEST(rf_call("mknod", path, Qnil)))
47
+ return -EACCES;
48
+
49
+ return 0;
50
+ }
51
+
52
+ // rf_mkdir - make a directory file
53
+ static int
54
+ rf_mkdir(const char *path, mode_t mode)
55
+ {
56
+ VALUE ret;
57
+
58
+ debug("rf_mkdir(%s, 0%o)\n", path, mode);
59
+
60
+ ret = rf_call("mkdir", path, UINT2NUM(mode));
61
+
62
+ if (RTEST(ret))
63
+ return NUM2INT(ret);
64
+
65
+ return -ENOENT;
66
+ }
67
+
68
+ // rf_unlink - remove directory entry
69
+ static int
70
+ rf_unlink(const char *path)
71
+ {
72
+ debug("rf_unlink(%s)\n", path);
73
+
74
+ if (!RTEST(rf_call("unlink", path, Qnil)))
75
+ return -ENOENT;
76
+
77
+ return 0;
78
+ }
79
+
80
+ // rf_rmdir - remove a directory file
81
+ static int
82
+ rf_rmdir(const char *path)
83
+ {
84
+ VALUE ret;
85
+
86
+ debug("rf_mkdir(%s)\n", path);
87
+
88
+ ret = rf_call("rmdir", path, Qnil);
89
+
90
+ if (RTEST(ret))
91
+ return NUM2INT(ret);
92
+
93
+ return -ENOENT;
94
+ }
95
+
96
+ // rf_symlink - make a symbolic link to a file
97
+ static int
98
+ rf_symlink(const char *path, const char *dest)
99
+ {
100
+ VALUE args;
101
+ VALUE ret;
102
+
103
+ debug("rf_symlink(%s, %s)\n", path, dest);
104
+
105
+ args = rb_ary_new();
106
+ rb_ary_push(args, rb_str_new2(dest));
107
+
108
+ ret = rf_call("symlink", path, args);
109
+
110
+ if (RTEST(ret))
111
+ return NUM2INT(ret);
112
+
113
+ return -ENOENT;
114
+ }
115
+
116
+ // rf_rename - change the name of a file
117
+ static int
118
+ rf_rename(const char *path, const char *dest)
119
+ {
120
+ VALUE ret;
121
+ VALUE args;
122
+
123
+ debug("rf_rename(%s, %s)\n", path, dest);
124
+
125
+ args = rb_ary_new();
126
+ rb_ary_push(args, rb_str_new2(dest));
127
+
128
+ ret = rf_call("rename", path, args);
129
+
130
+ if (RTEST(ret))
131
+ return NUM2INT(ret);
132
+
133
+ return 0;
134
+ }
135
+
136
+ // rf_link - make a hard file link
137
+ static int
138
+ rf_link(const char *path, const char *dest)
139
+ {
140
+ VALUE args;
141
+ VALUE ret;
142
+
143
+ debug("rf_link(%s, %s)\n", path, dest);
144
+
145
+ args = rb_ary_new();
146
+ rb_ary_push(args, rb_str_new2(dest));
147
+
148
+ ret = rf_call("link", path, args);
149
+ if (RTEST(ret))
150
+ return NUM2INT(ret);
151
+
152
+ return 0;
153
+ }
154
+
155
+ // rf_chmod - change mode of file
156
+ static int
157
+ rf_chmod(const char *path, mode_t mode)
158
+ {
159
+ VALUE ret;
160
+
161
+ debug("rf_chmod(%s, %o)\n", path, mode);
162
+
163
+ ret = rf_call("chmod", path, INT2NUM(mode));
164
+
165
+ if (RTEST(ret))
166
+ return NUM2INT(ret);
167
+
168
+ return -ENOENT;
169
+ }
170
+
171
+ // rf_chown - change owner and group of a file
172
+
173
+ // rf_truncate - truncate or extend a file to a specified length
174
+ static int
175
+ rf_truncate(const char *path, off_t offset)
176
+ {
177
+ VALUE ret;
178
+
179
+ debug("rf_truncate(%s, %d)\n", path, offset);
180
+
181
+ ret = rf_call("truncate", path, ULONG2NUM(offset));
182
+
183
+ if (RTEST(ret))
184
+ return NUM2INT(ret);
185
+
186
+ return -ENOENT;
187
+ }
188
+
189
+ // rf_utime - set file times
190
+ static int
191
+ rf_utime(const char *path, struct utimbuf *ubuf)
192
+ {
193
+ VALUE ret;
194
+ VALUE args;
195
+
196
+ args = rb_ary_new();
197
+ rb_ary_push(args, LONG2NUM(ubuf->actime));
198
+ rb_ary_push(args, LONG2NUM(ubuf->modtime));
199
+
200
+ ret = rf_call("utime", path, args);
201
+
202
+ if (RTEST(ret))
203
+ return ret;
204
+
205
+ // default to non-error because I don't see myself using this much
206
+ return 0;
207
+ }
208
+
209
+ // rf_open - open or create a file for reading or writing
210
+ // opening the file will probably be a noop for most backends
211
+ static int
212
+ rf_open(const char *path, struct fuse_file_info *fi)
213
+ {
214
+ char open_opts[4], *optr;
215
+
216
+ debug("rf_open(%s)\n", path);
217
+
218
+ optr = open_opts;
219
+ switch (fi->flags & 3) {
220
+ case 0:
221
+ *(optr++) = 'r';
222
+ break;
223
+ case 1:
224
+ *(optr++) = 'w';
225
+ break;
226
+ case 2:
227
+ *(optr++) = 'w';
228
+ *(optr++) = 'r';
229
+ break;
230
+ default:
231
+ debug("Opening a file with something other than rd, wr, or rdwr?");
232
+ }
233
+
234
+ if (fi->flags & O_APPEND)
235
+ *(optr++) = 'a';
236
+ *(optr) = '\0';
237
+
238
+ if (!RTEST(rf_call("open", path, rb_str_new2(open_opts)))) {
239
+ return -ENOENT;
240
+ }
241
+
242
+ return 0;
243
+ }
244
+
245
+ // rf_read - read input
246
+ static int
247
+ rf_read(
248
+ const char *path,
249
+ char *buf,
250
+ size_t size,
251
+ off_t offset,
252
+ struct fuse_file_info *fi)
253
+ {
254
+ VALUE ret;
255
+ VALUE args;
256
+
257
+ debug("rf_read(%s)\n", path);
258
+
259
+ args = rb_ary_new();
260
+ rb_ary_push(args, LONG2NUM(offset));
261
+ rb_ary_push(args, LONG2NUM(size));
262
+
263
+ ret = rf_call("read", path, args);
264
+
265
+ if (!RTEST(ret))
266
+ return 0;
267
+
268
+ if (TYPE(ret) != T_STRING)
269
+ return 0;
270
+
271
+ memcpy(buf, RSTRING(ret)->ptr, RSTRING(ret)->len);
272
+ return RSTRING(ret)->len;
273
+ }
274
+
275
+ // rf_write - write output
276
+ static int
277
+ rf_write(
278
+ const char *path,
279
+ const char *buf,
280
+ size_t size,
281
+ off_t offset,
282
+ struct fuse_file_info *fi)
283
+ {
284
+ VALUE ret;
285
+ VALUE args;
286
+
287
+ debug("rf_write(%s)\n", path);
288
+
289
+ args = rb_ary_new();
290
+ rb_ary_push(args, ULONG2NUM(offset));
291
+ rb_ary_push(args, ULONG2NUM(size));
292
+ rb_ary_push(args, rb_str_new(buf, size));
293
+
294
+ ret = rf_call("write", path, args);
295
+
296
+ if (RTEST(ret))
297
+ return NUM2INT(ret);
298
+
299
+ return 0UL;
300
+ }
301
+
302
+ // rf_statfs - get filesystem statistics
303
+ static int
304
+ rf_statfs(const char *path, struct statvfs *stbuf)
305
+ {
306
+ return 0;
307
+ // VALUE cur_entry;
308
+
309
+ // debug("rf_statfs(%s)\n", path);
310
+
311
+ // cur_entry = rf_call("statfs", path, Qnil);
312
+
313
+ // if (RTEST(cur_entry)) {
314
+ // VSTATVFS2STATVFS(cur_entry, stbuf);
315
+ // return 0;
316
+ // } else {
317
+ // return -ENOENT;
318
+ // }
319
+ }
320
+
321
+ // rf_flush - close file (after system calls close())
322
+
323
+ // rf_release - close file (the final flush)
324
+ static int
325
+ rf_release(const char *path, struct fuse_file_info *fi)
326
+ {
327
+ debug("rf_release(%s)\n", path);
328
+
329
+ if (!RTEST(rf_call("release", path, Qnil)))
330
+ return -ENOENT;
331
+
332
+ return 0;
333
+ }
334
+
335
+ // rf_fsync - synchronise a file's in-core state with that on disk
336
+
337
+ // rf_setxattr - set an extended attribute value
338
+
339
+ // rf_getxattr - get an extended attribute value
340
+
341
+ // rf_listxattr - list extended attribute names
342
+
343
+ // rf_removexattr - remove an extended attribute value
344
+
345
+ // rf_opendir - check if opendir is permitted for this directory
346
+
347
+ // rf_readdir - an array of FuseFS::Stat objects sent to fill_dir
348
+ static int
349
+ rf_readdir(
350
+ const char *path,
351
+ void *buf,
352
+ fuse_fill_dir_t filler,
353
+ off_t offset,
354
+ struct fuse_file_info *fi)
355
+ {
356
+ VALUE cur_entry;
357
+ VALUE retval;
358
+ struct stat *stbuf;
359
+
360
+ debug("rf_readdir(%s)\n", path);
361
+
362
+ stbuf = malloc(sizeof(struct stat));
363
+
364
+ // TODO maybe fi is interesting
365
+ // This is what fuse does to turn off 'unused' warnings.
366
+ (void) offset;
367
+ (void) fi;
368
+
369
+ // filler takes (void *dh_, const char *name, const struct stat *statp, off_t off)
370
+ // you can pass in NULL statp for defaults
371
+ // you can pass in 0 off for magic
372
+
373
+ // These two are always in a directory
374
+ filler(buf, ".", NULL, 0);
375
+ filler(buf, "..", NULL, 0);
376
+
377
+ retval = rf_call("readdir", path, Qnil);
378
+ if (!RTEST(retval) || (TYPE(retval) != T_ARRAY)) {
379
+ return 0;
380
+ }
381
+
382
+ while ((cur_entry = rb_ary_shift(retval)) != Qnil) {
383
+ VSTAT2STAT(cur_entry, stbuf);
384
+ filler(buf, STR2CSTR(rb_funcall(cur_entry, rb_intern("name"), 0)), stbuf, 0);
385
+ }
386
+
387
+ return 0;
388
+ }
389
+
390
+ // rf_releasedir - release dir (file returned from opendir is passed in)
391
+
392
+ // rf_fsyncdir - sync everything in the dir
393
+ static int
394
+ rf_fsyncdir(const char * path, int p, struct fuse_file_info *fi)
395
+ {
396
+ VALUE ret;
397
+ VALUE args;
398
+
399
+ // non-0 p means user data only, not metadata
400
+ args = rb_ary_new();
401
+ rb_ary_push(args, INT2NUM(p));
402
+
403
+ ret = rf_call("fsyncdir", path, args);
404
+
405
+ if (RTEST(ret))
406
+ return NUM2INT(ret);
407
+
408
+ // default to non-error because I don't see myself using this much
409
+ return 0;
410
+ }
411
+
412
+ // rf_access - check access permissions of a file or pathname
413
+ static int
414
+ rf_access(const char *path, int mask)
415
+ {
416
+ debug("rf_access(%s, %d)\n", path, mask);
417
+ return 0;
418
+ }
419
+
420
+ // rf_create - create and open a file
421
+
422
+ // rf_ftruncate - truncate() but called from system ftruncate()
423
+
424
+ // rf_fgetattr - getattr() but called if rf_create() is implemented
425
+
426
+ // rf_lock - perform POSIX file locking operation
427
+
428
+ // rf_utimens - utime with nanosecond resolution
429
+
430
+ // rf_bmap - map block index within file to block index within device
431
+
432
+ // rf_flag_nullpath_ok - accept NULL path
433
+
434
+
435
+ // rf_oper
436
+ static struct fuse_operations rf_oper = {
437
+ .getattr = rf_getattr,
438
+ .readdir = rf_readdir,
439
+ .mknod = rf_mknod,
440
+ .mkdir = rf_mkdir,
441
+ .unlink = rf_unlink,
442
+ .rmdir = rf_rmdir,
443
+ .rename = rf_rename,
444
+ .truncate = rf_truncate,
445
+ .utime = rf_utime,
446
+ .open = rf_open,
447
+ .read = rf_read,
448
+ .write = rf_write,
449
+ .statfs = rf_statfs,
450
+ .release = rf_release,
451
+ .fsyncdir = rf_fsyncdir,
452
+ };
453
+
454
+ static struct fuse_operations rf_unused_oper = {
455
+ .symlink = rf_symlink,
456
+ .access = rf_access,
457
+ .link = rf_link,
458
+ .chmod = rf_chmod,
459
+ };
460
+
461
+ // rf_set_root
462
+ VALUE
463
+ rf_set_root(VALUE self, VALUE rootval)
464
+ {
465
+ if (self != cFuseFS) {
466
+ rb_raise(cFSException, "Error: 'set_root' called outside of FuseFS?!");
467
+ return Qnil;
468
+ }
469
+
470
+ rb_iv_set(cFuseFS, "@root", rootval);
471
+ FuseRoot = rootval;
472
+
473
+ return Qtrue;
474
+ }
475
+
476
+ // rf_mount_to
477
+ VALUE
478
+ rf_mount_to(int argc, VALUE *argv, VALUE self)
479
+ {
480
+ struct fuse_args *opts;
481
+ VALUE mountpoint;
482
+ int i;
483
+ char *cur;
484
+
485
+ if (self != cFuseFS) {
486
+ rb_raise(cFSException, "Error: 'mount_to' called outside of FuseFS?!");
487
+ return Qnil;
488
+ }
489
+
490
+ if (argc == 0) {
491
+ rb_raise(rb_eArgError, "mount_to requires at least 1 argument!");
492
+ return Qnil;
493
+ }
494
+
495
+ mountpoint = argv[0];
496
+
497
+ Check_Type(mountpoint, T_STRING);
498
+ opts = ALLOC(struct fuse_args);
499
+ opts->argc = argc;
500
+ opts->argv = ALLOC_N(char *, opts->argc);
501
+ opts->allocated = 1;
502
+
503
+ // TODO why
504
+ opts->argv[0] = strdup("-odirect_io");
505
+
506
+ for (i = 1; i < argc; i++) {
507
+ cur = StringValuePtr(argv[i]);
508
+ opts->argv[i] = ALLOC_N(char, RSTRING(argv[i])->len + 2);
509
+ sprintf(opts->argv[i], "-o%s", cur);
510
+ }
511
+
512
+ // TODO debug(rf_unused_oper);
513
+ (void) rf_unused_oper;
514
+
515
+ rb_iv_set(cFuseFS, "@mountpoint", mountpoint);
516
+ fusefs_setup(STR2CSTR(mountpoint), &rf_oper, opts);
517
+
518
+ return Qtrue;
519
+ }
520
+
521
+ // rf_fd
522
+ VALUE
523
+ rf_fd(VALUE self)
524
+ {
525
+ int fd = fusefs_fd();
526
+ if (fd < 0)
527
+ return Qnil;
528
+ return INT2NUM(fd);
529
+ }
530
+
531
+ // rf_process
532
+ VALUE
533
+ rf_process(VALUE self)
534
+ {
535
+ fusefs_process();
536
+ return Qnil;
537
+ }
538
+
539
+ // Init_fusefs_lib()
540
+ void
541
+ Init_fusefs_lib() {
542
+ init_time = time(NULL);
543
+
544
+ /* module FuseFS */
545
+ cFuseFS = rb_define_module("FuseFS");
546
+
547
+ /* Our exception */
548
+ cFSException = rb_define_class_under(cFuseFS, "FuseFSException",
549
+ rb_eStandardError);
550
+
551
+ /* def Fuse.run */
552
+ rb_define_singleton_method(cFuseFS, "fuse_fd", rf_fd, 0);
553
+ rb_define_singleton_method(cFuseFS, "process", rf_process, 0);
554
+ rb_define_singleton_method(cFuseFS, "mount_to", rf_mount_to, -1);
555
+ rb_define_singleton_method(cFuseFS, "mount_under", rf_mount_to, -1);
556
+ rb_define_singleton_method(cFuseFS, "mountpoint", rf_mount_to, -1);
557
+ rb_define_singleton_method(cFuseFS, "set_root", rf_set_root, 1);
558
+ rb_define_singleton_method(cFuseFS, "root=", rf_set_root, 1);
559
+ }
560
+
data/ext/fusefs_lib.h ADDED
@@ -0,0 +1,119 @@
1
+ #define FUSE_USE_VERSION 26
2
+ #define _FILE_OFFSET_BITS 64
3
+
4
+ #include <fuse.h>
5
+ #include <stdio.h>
6
+ #include <string.h>
7
+ #include <errno.h>
8
+ #include <sys/types.h>
9
+ #include <sys/stat.h>
10
+ #include <fcntl.h>
11
+ #include <unistd.h>
12
+ #include <ruby.h>
13
+ #include <stdarg.h>
14
+
15
+ #include "fusefs_fuse.h"
16
+
17
+ #define VSTAT2STAT(cur_entry, stbuf) do {\
18
+ stbuf->st_mode = NUM2INT (rb_funcall(cur_entry, rb_intern("st_mode"), 0)); \
19
+ stbuf->st_nlink = NUM2INT (rb_funcall(cur_entry, rb_intern("st_nlink"), 0)); \
20
+ stbuf->st_size = NUM2LONG (rb_funcall(cur_entry, rb_intern("st_size"), 0)); \
21
+ stbuf->st_uid = NUM2INT (rb_funcall(cur_entry, rb_intern("st_uid"), 0)); \
22
+ stbuf->st_gid = NUM2INT (rb_funcall(cur_entry, rb_intern("st_gid"), 0)); \
23
+ stbuf->st_mtime = NUM2ULONG(rb_funcall(cur_entry, rb_intern("st_mtime"), 0)); \
24
+ stbuf->st_atime = NUM2ULONG(rb_funcall(cur_entry, rb_intern("st_atime"), 0)); \
25
+ stbuf->st_ctime = NUM2ULONG(rb_funcall(cur_entry, rb_intern("st_ctime"), 0)); \
26
+ } while (0)
27
+
28
+ #define VSTATVFS2STATVFS(cur_entry, stbuf) do {\
29
+ memset(stbuf, 0, sizeof(struct statvfs)); \
30
+ stbuf->f_bsize = NUM2INT(rb_funcall(cur_entry, rb_intern("f_bsize"), 0)); \
31
+ stbuf->f_frsize = NUM2INT(rb_funcall(cur_entry, rb_intern("f_frsize"), 0)); \
32
+ stbuf->f_blocks = NUM2INT(rb_funcall(cur_entry, rb_intern("f_blocks"), 0)); \
33
+ stbuf->f_bfree = NUM2INT(rb_funcall(cur_entry, rb_intern("f_bfree"), 0)); \
34
+ stbuf->f_bavail = NUM2INT(rb_funcall(cur_entry, rb_intern("f_bavail"), 0)); \
35
+ stbuf->f_files = NUM2INT(rb_funcall(cur_entry, rb_intern("f_files"), 0)); \
36
+ stbuf->f_ffree = NUM2INT(rb_funcall(cur_entry, rb_intern("f_ffree"), 0)); \
37
+ stbuf->f_favail = NUM2INT(rb_funcall(cur_entry, rb_intern("f_favail"), 0)); \
38
+ stbuf->f_fsid = NUM2INT(rb_funcall(cur_entry, rb_intern("f_fsid"), 0)); \
39
+ stbuf->f_flag = NUM2INT(rb_funcall(cur_entry, rb_intern("f_flag"), 0)); \
40
+ stbuf->f_namemax = NUM2INT(rb_funcall(cur_entry, rb_intern("f_namemax"), 0)); \
41
+ } while (0)
42
+
43
+ // Ruby Constants constants
44
+ VALUE cFuseFS = Qnil; /* FuseFS class */
45
+ VALUE cFSException = Qnil; /* Our Exception. */
46
+ VALUE FuseRoot = Qnil; /* The root object we call */
47
+
48
+ typedef unsigned long int (*rbfunc)();
49
+
50
+ // file created, modified timestamp
51
+ // TODO no
52
+ time_t init_time;
53
+
54
+ // debug()
55
+ static void
56
+ debug(char *msg,...)
57
+ {
58
+ va_list ap;
59
+ va_start(ap,msg);
60
+ vfprintf(stdout,msg,ap);
61
+ }
62
+
63
+ // rf_protected and rf_call
64
+ //
65
+ // Used for: protection.
66
+ //
67
+ // This is called by rb_protect, and will make a call using
68
+ // the above rb_path and to_call ID to call the method safely
69
+ // on FuseRoot.
70
+ //
71
+ // We call rf_call(path,method_id), and rf_call will use rb_protect
72
+ // to call rf_protected, which makes the call on FuseRoot and returns
73
+ // whatever the call returns.
74
+ static VALUE
75
+ rf_protected(VALUE args)
76
+ {
77
+ ID to_call = SYM2ID(rb_ary_shift(args));
78
+ return rb_apply(FuseRoot,to_call,args);
79
+ }
80
+
81
+ static VALUE
82
+ rf_call(char *methname, const char *path, VALUE arg)
83
+ {
84
+ int error;
85
+ VALUE result;
86
+ VALUE methargs;
87
+ ID method;
88
+
89
+ method = rb_intern(methname);
90
+
91
+ if (arg == Qnil) {
92
+ debug(" root.%s(%s)\n", methname, path);
93
+ } else {
94
+ debug(" root.%s(%s,...)\n", methname, path);
95
+ }
96
+
97
+ if (TYPE(arg) == T_ARRAY) {
98
+ methargs = arg;
99
+ } else if (arg != Qnil) {
100
+ methargs = rb_ary_new();
101
+ rb_ary_push(methargs,arg);
102
+ } else {
103
+ methargs = rb_ary_new();
104
+ }
105
+
106
+ rb_ary_unshift(methargs,rb_str_new2(path));
107
+ rb_ary_unshift(methargs,ID2SYM(method));
108
+
109
+ /* Set up the call and make it. */
110
+ result = rb_protect(rf_protected, methargs, &error);
111
+
112
+ /* Did it error? */
113
+ if (error) {
114
+ return Qnil;
115
+ }
116
+
117
+ return result;
118
+ }
119
+
data/hello.sh ADDED
@@ -0,0 +1,10 @@
1
+ #!/bin/sh
2
+ mkdir -p hello
3
+ ruby -rsample/hello -e "puts 'starting...'" hello &
4
+ echo $? > pid
5
+ sleep 1
6
+ cat hello/hello.txt
7
+ # umount hello/
8
+ sleep 1
9
+ cat pid | xargs kill
10
+ rmdir hello