hysios-fusefs 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/TODO ADDED
@@ -0,0 +1,11 @@
1
+ TODO for FuseFS
2
+ ===============
3
+
4
+ - Problem with editors: vim attempts to create and then delete a numbered file.
5
+ This can be an issue if the filename (all digits) is reported as not valid.
6
+
7
+ I am looking for a solution for this, but it's gonna be a pain.
8
+
9
+ - Tests, more tests, and unit tests!
10
+
11
+ - Consider FIFOs? We can use FIFOs to mimic TCP internet, if we do this right.
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.7.0
data/ext/MANIFEST ADDED
@@ -0,0 +1,4 @@
1
+ fusefs_fuse.c
2
+ fusefs_fuse.h
3
+ fusefs_lib.c
4
+ 
data/ext/extconf.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'mkmf'
2
+ dir_config('fusefs_lib.so')
3
+ if have_library('fuse_ino64') || have_library('fuse')
4
+ create_makefile('fusefs_lib')
5
+ else
6
+ puts "No FUSE install available"
7
+ end
data/ext/fusefs_fuse.c ADDED
@@ -0,0 +1,149 @@
1
+ /* fusefs_fuse.c */
2
+
3
+ /* This is rewriting most of the things that occur
4
+ * in fuse_main up through fuse_loop */
5
+
6
+ #define FUSE_USE_VERSION 26
7
+ #define _FILE_OFFSET_BITS 64
8
+
9
+ #include <fuse.h>
10
+ #include <stdio.h>
11
+ #include <string.h>
12
+ #include <stdlib.h>
13
+ #include <unistd.h>
14
+ #include <limits.h>
15
+ #include <errno.h>
16
+ #include <assert.h>
17
+ #include <stdint.h>
18
+ #include <sys/param.h>
19
+ #include <sys/uio.h>
20
+ #include <signal.h>
21
+
22
+ struct fuse *fuse_instance = NULL;
23
+ struct fuse_chan *fusech = NULL;
24
+ static char *mounted_at = NULL;
25
+
26
+ static int set_one_signal_handler(int signal, void (*handler)(int));
27
+
28
+ int fusefs_fd() {
29
+ if(fusech == NULL)
30
+ return -1;
31
+ return fuse_chan_fd(fusech);
32
+ }
33
+
34
+ int
35
+ fusefs_unmount() {
36
+ char buf[128];
37
+
38
+ if (mounted_at && fusech) {
39
+ fuse_unmount(mounted_at, fusech);
40
+ sprintf(buf, "/sbin/umount %s", mounted_at);
41
+ system(buf);
42
+ }
43
+ if (fuse_instance)
44
+ fuse_destroy(fuse_instance);
45
+ fuse_instance = NULL;
46
+ free(mounted_at);
47
+ fusech = NULL;
48
+ }
49
+
50
+ static void
51
+ fusefs_ehandler() {
52
+ if (fuse_instance != NULL) {
53
+ fusefs_unmount();
54
+ }
55
+ }
56
+
57
+ int
58
+ fusefs_setup(char *mountpoint, const struct fuse_operations *op, struct fuse_args *opts) {
59
+ fusech = NULL;
60
+ if (fuse_instance != NULL) {
61
+ return 0;
62
+ }
63
+ if (mounted_at != NULL) {
64
+ return 0;
65
+ }
66
+
67
+ /* First, mount us */
68
+ fusech = fuse_mount(mountpoint, opts);
69
+ if (fusech == NULL) return 0;
70
+
71
+ fuse_instance = fuse_new(fusech, opts, op, sizeof(*op), NULL);
72
+ if (fuse_instance == NULL)
73
+ goto err_unmount;
74
+
75
+ /* Set signal handlers */
76
+ if (set_one_signal_handler(SIGHUP, fusefs_ehandler) == -1 ||
77
+ set_one_signal_handler(SIGINT, fusefs_ehandler) == -1 ||
78
+ set_one_signal_handler(SIGTERM, fusefs_ehandler) == -1 ||
79
+ set_one_signal_handler(SIGPIPE, SIG_IGN) == -1)
80
+ return 0;
81
+
82
+ atexit(fusefs_ehandler);
83
+
84
+ /* We've initialized it! */
85
+ mounted_at = strdup(mountpoint);
86
+ return 1;
87
+ err_destroy:
88
+ fuse_destroy(fuse_instance);
89
+ err_unmount:
90
+ fuse_unmount(mountpoint, fusech);
91
+ return 0;
92
+ }
93
+
94
+ int
95
+ fusefs_uid() {
96
+ struct fuse_context *context = fuse_get_context();
97
+ if (context) return context->uid;
98
+ return -1;
99
+ }
100
+
101
+ int
102
+ fusefs_gid() {
103
+ struct fuse_context *context = fuse_get_context();
104
+ if (context) return context->gid;
105
+ return -1;
106
+ }
107
+
108
+ int
109
+ fusefs_process() {
110
+ /* This gets exactly 1 command out of fuse fd. */
111
+ /* Ideally, this is triggered after a select() returns */
112
+ if (fuse_instance != NULL) {
113
+ struct fuse_cmd *cmd;
114
+
115
+ if (fuse_exited(fuse_instance))
116
+ return 0;
117
+
118
+ cmd = fuse_read_cmd(fuse_instance);
119
+ if (cmd == NULL)
120
+ return 1;
121
+
122
+ fuse_process_cmd(fuse_instance, cmd);
123
+ }
124
+ return 1;
125
+ }
126
+
127
+
128
+ static int set_one_signal_handler(int signal, void (*handler)(int))
129
+ {
130
+ struct sigaction sa;
131
+ struct sigaction old_sa;
132
+
133
+ memset(&sa, 0, sizeof(struct sigaction));
134
+ sa.sa_handler = handler;
135
+ sigemptyset(&(sa.sa_mask));
136
+ sa.sa_flags = 0;
137
+
138
+ if (sigaction(signal, NULL, &old_sa) == -1) {
139
+ perror("FUSE: cannot get old signal handler");
140
+ return -1;
141
+ }
142
+
143
+ if (old_sa.sa_handler == SIG_DFL &&
144
+ sigaction(signal, &sa, NULL) == -1) {
145
+ perror("Cannot set signal handler");
146
+ return -1;
147
+ }
148
+ return 0;
149
+ }
data/ext/fusefs_fuse.h ADDED
@@ -0,0 +1,22 @@
1
+ /* fusefs_fuse.h */
2
+
3
+ /* This is rewriting most of the things that occur
4
+ * in fuse_main up through fuse_loop */
5
+
6
+ #ifndef __FUSEFS_FUSE_H_
7
+ #define __FUSEFS_FUSE_H_
8
+
9
+ struct fuse_args;
10
+
11
+ int fusefs_fd();
12
+ int fusefs_unmount();
13
+ int fusefs_ehandler();
14
+ int fusefs_setup(char *mountpoint, const struct fuse_operations *op, struct fuse_args *opts);
15
+ int fusefs_process();
16
+ int fusefs_uid();
17
+ int fusefs_gid();
18
+
19
+ char*
20
+ rb_str2cstr(VALUE str, long *len);
21
+
22
+ #endif
data/ext/fusefs_lib.c ADDED
@@ -0,0 +1,1525 @@
1
+ /* ruby-fuse
2
+ *
3
+ * A Ruby module to interact with the FUSE userland filesystem in
4
+ * a Rubyish way.
5
+ */
6
+
7
+ /* #define DEBUG /* */
8
+
9
+ #define FUSE_USE_VERSION 26
10
+ #define _FILE_OFFSET_BITS 64
11
+
12
+ #include <fuse.h>
13
+ #include <stdio.h>
14
+ #include <string.h>
15
+ #include <errno.h>
16
+ #include <sys/types.h>
17
+ #include <sys/stat.h>
18
+ #include <fcntl.h>
19
+ #include <ruby.h>
20
+
21
+ #ifdef DEBUG
22
+ #include <stdarg.h>
23
+ #endif
24
+
25
+ #include "fusefs_fuse.h"
26
+
27
+ char*
28
+ rb_str2cstr(VALUE str, long *len)
29
+ {
30
+ StringValue(str);
31
+ if (len) *len = RSTRING_LEN(str);
32
+ else if (RTEST(ruby_verbose) && RSTRING_LEN(str) != strlen(RSTRING_PTR(str))) {
33
+ rb_warn("string contains \\0 character");
34
+ }
35
+ return RSTRING_PTR(str);
36
+ }
37
+
38
+ /* init_time
39
+ *
40
+ * All files will have a modified time equal to this. */
41
+ time_t init_time;
42
+
43
+ /* opened_file
44
+ *
45
+ * FuseFS uses the opened_file list to keep files that are written to in
46
+ * memory until they are closed before passing it to FuseRoot.write_to,
47
+ * and file contents returned by FuseRoot.read_file until FUSE informs
48
+ * us it is safe to close.
49
+ */
50
+ typedef struct __opened_file_ {
51
+ char *path;
52
+ char *value;
53
+ int modified;
54
+ long writesize;
55
+ long size;
56
+ long zero_offset;
57
+ int raw;
58
+ struct __opened_file_ *next;
59
+ } opened_file;
60
+
61
+ typedef opened_file editor_file;
62
+
63
+ static opened_file *opened_head = NULL;
64
+ static editor_file *editor_head = NULL;
65
+
66
+ static int
67
+ file_openedP(const char *path) {
68
+ opened_file *ptr;
69
+ for (ptr = opened_head;ptr; ptr = ptr->next)
70
+ if (!strcmp(path,ptr->path)) return 1;
71
+ return 0;
72
+ }
73
+
74
+ /* When a file is being written to, its value starts with this much
75
+ * allocated and grows by this much when necessary. */
76
+ #define FILE_GROW_SIZE 1024
77
+
78
+ /* When a file is created, the OS will first mknod it, then attempt to
79
+ * fstat it immediately. We get around this by using a static path name
80
+ * for the most recently mknodd'd path. */
81
+ static char *created_file = NULL;
82
+
83
+ /* Ruby Constants constants */
84
+ VALUE cFuseFS = Qnil; /* FuseFS class */
85
+ VALUE cFSException = Qnil; /* Our Exception. */
86
+ VALUE FuseRoot = Qnil; /* The root object we call */
87
+
88
+ /* IDs for calling methods on objects. */
89
+
90
+ #define RMETHOD(name,cstr) \
91
+ char *c_ ## name = cstr; \
92
+ ID name;
93
+
94
+ RMETHOD(id_dir_contents,"contents");
95
+ RMETHOD(id_read_file,"read_file");
96
+ RMETHOD(id_write_to,"write_to");
97
+ RMETHOD(id_delete,"delete");
98
+ RMETHOD(id_mkdir,"mkdir");
99
+ RMETHOD(id_rmdir,"rmdir");
100
+ RMETHOD(id_touch,"touch");
101
+ RMETHOD(id_size,"size");
102
+
103
+ RMETHOD(is_directory,"directory?");
104
+ RMETHOD(is_file,"file?");
105
+ RMETHOD(is_executable,"executable?");
106
+ RMETHOD(can_write,"can_write?");
107
+ RMETHOD(can_delete,"can_delete?");
108
+ RMETHOD(can_mkdir,"can_mkdir?");
109
+ RMETHOD(can_rmdir,"can_rmdir?");
110
+
111
+ RMETHOD(id_raw_open,"raw_open");
112
+ RMETHOD(id_raw_close,"raw_close");
113
+ RMETHOD(id_raw_read,"raw_read");
114
+ RMETHOD(id_raw_write,"raw_write");
115
+
116
+ RMETHOD(id_dup,"dup");
117
+
118
+ typedef unsigned long int (*rbfunc)();
119
+
120
+ /* debug()
121
+ *
122
+ * If #define DEBUG is enabled, then this acts as a printf to stderr
123
+ */
124
+ #ifdef DEBUG
125
+ static void
126
+ debug(char *msg,...) {
127
+ va_list ap;
128
+ va_start(ap,msg);
129
+ vfprintf(stderr,msg,ap);
130
+ }
131
+ #else
132
+ // Make debug just comment out what's after it.
133
+ #define debug // debug
134
+ #endif
135
+
136
+ /* catch_editor_files
137
+ *
138
+ * If this is a true value, then FuseFS will attempt to capture
139
+ * editor swap files and handle them itself, so the ruby filesystem
140
+ * is not passed swap files it doesn't care about.
141
+ */
142
+
143
+ int handle_editor = 1;
144
+ int which_editor = 0;
145
+ #define EDITOR_VIM 1
146
+ #define EDITOR_EMACS 2
147
+
148
+ /* editor_fileP
149
+ *
150
+ * Passed a path, editor_fileP will return if it is likely to be a file
151
+ * belonging to an editor.
152
+ *
153
+ * vim: /path/to/.somename.ext.sw*
154
+ * emacs: /path/to/#somename.ext#
155
+ */
156
+ static int
157
+ editor_fileP(const char *path) {
158
+ char *filename;
159
+ editor_file *ptr;
160
+
161
+ if (!handle_editor)
162
+ return 0;
163
+
164
+ /* Already created one */
165
+ for (ptr = editor_head ; ptr ; ptr = ptr->next) {
166
+ if (strcasecmp(ptr->path,path) == 0) {
167
+ return 2;
168
+ }
169
+ }
170
+
171
+ /* Basic checks */
172
+ filename = strrchr(path,'/');
173
+ if (!filename) return 0; // No /.
174
+ filename++;
175
+ if (!*filename) return 0; // / is the last.
176
+
177
+ /* vim */
178
+ do {
179
+ // vim uses: .filename.sw?
180
+ char *ptr = filename;
181
+ int len;
182
+ if (*ptr != '.') break;
183
+
184
+ // ends with .sw?
185
+ ptr = strrchr(ptr,'.');
186
+ len = strlen(ptr);
187
+ // .swp or .swpx
188
+ if (len != 4 && len != 5) break;
189
+ if (strncmp(ptr,".sw",3) == 0) {
190
+ debug(" (%s is a vim file).\n", path);
191
+ which_editor = EDITOR_VIM;
192
+ return 1; // It's a vim file.
193
+ }
194
+ } while (0);
195
+
196
+ /* emacs */
197
+ do {
198
+ char *ptr = filename;
199
+ // Begins with a #
200
+ if (*ptr != '#') break;
201
+
202
+ // Ends with a #
203
+ ptr = strrchr(ptr,'#');
204
+ if (!ptr) break;
205
+ // the # must be the end of the filename.
206
+ ptr++;
207
+ if (*ptr) break;
208
+ debug(" (%s is an emacs file).\n", path);
209
+ which_editor = EDITOR_EMACS;
210
+ return 1;
211
+ } while (0);
212
+ return 0;
213
+ }
214
+
215
+ /* rf_protected and rf_call
216
+ *
217
+ * Used for: protection.
218
+ *
219
+ * This is called by rb_protect, and will make a call using
220
+ * the above rb_path and to_call ID to call the method safely
221
+ * on FuseRoot.
222
+ *
223
+ * We call rf_call(path,method_id), and rf_call will use rb_protect
224
+ * to call rf_protected, which makes the call on FuseRoot and returns
225
+ * whatever the call returns.
226
+ */
227
+ static VALUE
228
+ rf_protected(VALUE args) {
229
+ ID to_call = SYM2ID(rb_ary_shift(args));
230
+ return rb_apply(FuseRoot,to_call,args);
231
+ }
232
+
233
+ #define rf_call(p,m,a) \
234
+ rf_mcall(p,m, c_ ## m, a)
235
+
236
+ static VALUE
237
+ rf_mcall(const char *path, ID method, char *methname, VALUE arg) {
238
+ int error;
239
+ VALUE result;
240
+ VALUE methargs;
241
+
242
+ if (!rb_respond_to(FuseRoot,method)) {
243
+ return Qnil;
244
+ }
245
+
246
+ if (arg == Qnil) {
247
+ debug(" root.%s(%s)\n", methname, path );
248
+ } else {
249
+ debug(" root.%s(%s,...)\n", methname, path );
250
+ }
251
+
252
+ if (TYPE(arg) == T_ARRAY) {
253
+ methargs = arg;
254
+ } else if (arg != Qnil) {
255
+ methargs = rb_ary_new();
256
+ rb_ary_push(methargs,arg);
257
+ } else {
258
+ methargs = rb_ary_new();
259
+ }
260
+
261
+ rb_ary_unshift(methargs,rb_str_new2(path));
262
+ rb_ary_unshift(methargs,ID2SYM(method));
263
+
264
+ /* Set up the call and make it. */
265
+ result = rb_protect(rf_protected, methargs, &error);
266
+
267
+ /* Did it error? */
268
+ if (error) return Qnil;
269
+
270
+ return result;
271
+ }
272
+ /* rf_getattr
273
+ *
274
+ * Used when: 'ls', and before opening a file.
275
+ *
276
+ * FuseFS will call: directory? and file? on FuseRoot
277
+ * to determine if the path in question is pointing
278
+ * at a directory or file. The permissions attributes
279
+ * will be 777 (dirs) and 666 (files) xor'd with FuseFS.umask
280
+ */
281
+ static int
282
+ rf_getattr(const char *path, struct stat *stbuf) {
283
+ /* If it doesn't exist, it doesn't exist. Simple as that. */
284
+ VALUE retval;
285
+ char *value;
286
+ size_t len;
287
+
288
+ debug("rf_getattr(%s)\n", path );
289
+ /* Zero out the stat buffer */
290
+ memset(stbuf, 0, sizeof(struct stat));
291
+
292
+ /* "/" is automatically a dir. */
293
+ if (strcmp(path,"/") == 0) {
294
+ stbuf->st_mode = S_IFDIR | 0755;
295
+ stbuf->st_nlink = 3;
296
+ stbuf->st_uid = getuid();
297
+ stbuf->st_gid = getgid();
298
+ stbuf->st_mtime = init_time;
299
+ stbuf->st_atime = init_time;
300
+ stbuf->st_ctime = init_time;
301
+ return 0;
302
+ }
303
+
304
+ /* If we created it with mknod, then it "exists" */
305
+ debug(" Checking for created file ...");
306
+ if (created_file && (strcmp(created_file,path) == 0)) {
307
+ /* It's created */
308
+ debug(" created.\n");
309
+ stbuf->st_mode = S_IFREG | 0666;
310
+ stbuf->st_nlink = 1 + file_openedP(path);
311
+ stbuf->st_size = 0;
312
+ stbuf->st_uid = getuid();
313
+ stbuf->st_gid = getgid();
314
+ stbuf->st_mtime = init_time;
315
+ stbuf->st_atime = init_time;
316
+ stbuf->st_ctime = init_time;
317
+ return 0;
318
+ }
319
+ debug(" no.\n");
320
+
321
+ /* debug(" Checking file_opened ...");
322
+ if (file_openedP(path)) {
323
+ debug(" opened.\n");
324
+ stbuf->st_mode = S_IFREG | 0666;
325
+ stbuf->st_nlink = 1 + file_openedP(path);
326
+ stbuf->st_size = 0;
327
+ stbuf->st_uid = getuid();
328
+ stbuf->st_gid = getgid();
329
+ stbuf->st_mtime = init_time;
330
+ stbuf->st_atime = init_time;
331
+ stbuf->st_ctime = init_time;
332
+ return 0;
333
+ }
334
+ debug(" no.\n");
335
+ */
336
+
337
+ debug(" Checking if editor file...");
338
+ switch (editor_fileP(path)) {
339
+ case 2:
340
+ debug(" Yes, and does exist.\n");
341
+ stbuf->st_mode = S_IFREG | 0444;
342
+ stbuf->st_nlink = 1;
343
+ stbuf->st_size = 0;
344
+ stbuf->st_uid = getuid();
345
+ stbuf->st_gid = getgid();
346
+ stbuf->st_mtime = init_time;
347
+ stbuf->st_atime = init_time;
348
+ stbuf->st_ctime = init_time;
349
+ return 0;
350
+ case 1:
351
+ debug(" Yes, but doesn't exist.\n");
352
+ return -ENOENT;
353
+ default:
354
+ debug("No.\n");
355
+ }
356
+
357
+ /* If FuseRoot says the path is a directory, we set it 0555.
358
+ * If FuseRoot says the path is a file, it's 0444.
359
+ *
360
+ * Otherwise, -ENOENT */
361
+ debug("Checking filetype ...");
362
+ if (RTEST(rf_call(path, is_directory,Qnil))) {
363
+ debug(" directory.\n");
364
+ stbuf->st_mode = S_IFDIR | 0555;
365
+ stbuf->st_nlink = 1;
366
+ stbuf->st_size = 4096;
367
+ stbuf->st_uid = getuid();
368
+ stbuf->st_gid = getgid();
369
+ stbuf->st_mtime = init_time;
370
+ stbuf->st_atime = init_time;
371
+ stbuf->st_ctime = init_time;
372
+ return 0;
373
+ } else if (RTEST(rf_call(path, is_file,Qnil))) {
374
+ VALUE rsize;
375
+ debug(" file.\n");
376
+ stbuf->st_mode = S_IFREG | 0444;
377
+ if (RTEST(rf_call(path,can_write,Qnil))) {
378
+ stbuf->st_mode |= 0666;
379
+ }
380
+ if (RTEST(rf_call(path,is_executable,Qnil))) {
381
+ stbuf->st_mode |= 0111;
382
+ }
383
+ stbuf->st_nlink = 1 + file_openedP(path);
384
+ if (RTEST(rsize = rf_call(path,id_size,Qnil)) && FIXNUM_P(rsize)) {
385
+ stbuf->st_size = FIX2LONG(rsize);
386
+ } else {
387
+ stbuf->st_size = 0;
388
+ }
389
+ stbuf->st_uid = getuid();
390
+ stbuf->st_gid = getgid();
391
+ stbuf->st_mtime = init_time;
392
+ stbuf->st_atime = init_time;
393
+ stbuf->st_ctime = init_time;
394
+ return 0;
395
+ }
396
+ debug(" nonexistant.\n");
397
+ return -ENOENT;
398
+ }
399
+
400
+ /* rf_readdir
401
+ *
402
+ * Used when: 'ls'
403
+ *
404
+ * FuseFS will call: 'directory?' on FuseRoot with the given path
405
+ * as an argument. If the return value is true, then it will in turn
406
+ * call 'contents' and expects to receive an array of file contents.
407
+ *
408
+ * '.' and '..' are automatically added, so the programmer does not
409
+ * need to worry about those.
410
+ */
411
+ static int
412
+ rf_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
413
+ off_t offset, struct fuse_file_info *fi) {
414
+ VALUE contents;
415
+ VALUE cur_entry;
416
+ VALUE retval;
417
+
418
+ debug("rf_readdir(%s)\n", path );
419
+
420
+ /* This is what fuse does to turn off 'unused' warnings. */
421
+ (void) offset;
422
+ (void) fi;
423
+
424
+ /* FuseRoot must exist */
425
+ if (FuseRoot == Qnil) {
426
+ if (!strcmp(path,"/")) {
427
+ filler(buf,".", NULL, 0);
428
+ filler(buf,"..", NULL, 0);
429
+ return 0;
430
+ }
431
+ return -ENOENT;
432
+ }
433
+
434
+ if (strcmp(path,"/") != 0) {
435
+ debug(" Checking is_directory? ...");
436
+ retval = rf_call(path, is_directory,Qnil);
437
+
438
+ if (!RTEST(retval)) {
439
+ debug(" no.\n");
440
+ return -ENOENT;
441
+ }
442
+ debug(" yes.\n");
443
+ }
444
+
445
+ /* These two are Always in a directory */
446
+ filler(buf,".", NULL, 0);
447
+ filler(buf,"..", NULL, 0);
448
+
449
+ retval = rf_call(path, id_dir_contents,Qnil);
450
+ if (!RTEST(retval)) {
451
+ return 0;
452
+ }
453
+ if (TYPE(retval) != T_ARRAY) {
454
+ return 0;
455
+ }
456
+
457
+ /* Duplicate the array, just in case. */
458
+ /* TODO: Do this better! */
459
+ retval = rb_funcall(retval,id_dup,0);
460
+
461
+ while ((cur_entry = rb_ary_shift(retval)) != Qnil) {
462
+
463
+ if (TYPE(cur_entry) != T_STRING)
464
+ continue;
465
+
466
+ filler(buf,StringValuePtr(cur_entry),NULL,0);
467
+ }
468
+ return 0;
469
+ }
470
+
471
+ /* rf_mknod
472
+ *
473
+ * Used when: This is called when a file is created.
474
+ *
475
+ * Note that this is actually almost useless to FuseFS, so all we do is check
476
+ * if a path is writable? and if so, return true. The open() will do the
477
+ * actual work of creating the file.
478
+ */
479
+ static int
480
+ rf_mknod(const char *path, mode_t umode, dev_t rdev) {
481
+ opened_file *ptr;
482
+
483
+ debug("rf_mknod(%s)\n", path);
484
+ /* Make sure it's not already open. */
485
+
486
+ debug(" Checking if it's opened ...");
487
+ if (file_openedP(path)) {
488
+ debug(" yes.\n");
489
+ return -EACCES;
490
+ }
491
+ debug(" no.\n");
492
+
493
+ /* We ONLY permit regular files. No blocks, characters, fifos, etc. */
494
+ debug(" Checking if an IFREG is requested ...");
495
+ if (!S_ISREG(umode)) {
496
+ debug(" no.\n");
497
+ return -EACCES;
498
+ }
499
+ debug(" yes.\n");
500
+
501
+ debug(" Checking if it's an editor file ...");
502
+ switch (editor_fileP(path)) {
503
+ case 2:
504
+ debug(" yes, and it exists.\n");
505
+ return -EEXIST;
506
+ case 1:
507
+ debug(" yes, and it doesn't exist.\n");
508
+ editor_file *eptr;
509
+ eptr = ALLOC(editor_file);
510
+ eptr->writesize = FILE_GROW_SIZE;
511
+ eptr->value = ALLOC_N(char,eptr->writesize);
512
+ eptr->path = strdup(path);
513
+ eptr->size = 0;
514
+ eptr->raw = 0;
515
+ eptr->zero_offset = 0;
516
+ eptr->modified = 0;
517
+ *(eptr->value) = '\0';
518
+ eptr->next = editor_head;
519
+ editor_head = eptr;
520
+ return 0;
521
+ default:
522
+ debug("no.\n");
523
+ }
524
+
525
+ debug(" Checking if it's a file ..." );
526
+ if (RTEST(rf_call(path, is_file,Qnil))) {
527
+ debug(" yes.\n");
528
+ return -EEXIST;
529
+ }
530
+ debug(" no.\n");
531
+
532
+ /* Is this writable to */
533
+ debug(" Checking if it's writable to ...");
534
+ if (!RTEST(rf_call(path,can_write,Qnil))) {
535
+ debug(" no.\n");
536
+ debug(" Checking if it looks like an editor tempfile...");
537
+ if (editor_head && (which_editor == EDITOR_VIM)) {
538
+ char *ptr = strrchr(path,'/');
539
+ while (ptr && isdigit(*ptr)) ptr++;
540
+ if (ptr && (*ptr == '\0')) {
541
+ debug(" yes.\n");
542
+ editor_file *eptr;
543
+ eptr = ALLOC(editor_file);
544
+ eptr->writesize = FILE_GROW_SIZE;
545
+ eptr->value = ALLOC_N(char,eptr->writesize);
546
+ eptr->path = strdup(path);
547
+ eptr->raw = 0;
548
+ eptr->size = 0;
549
+ eptr->zero_offset = 0;
550
+ eptr->modified = 0;
551
+ *(eptr->value) = '\0';
552
+ eptr->next = editor_head;
553
+ editor_head = eptr;
554
+ return 0;
555
+ }
556
+ }
557
+ debug(" no.\n");
558
+ return -EACCES;
559
+ }
560
+ debug(" yes.\n");
561
+
562
+ if (created_file)
563
+ free(created_file);
564
+
565
+ created_file = strdup(path);
566
+
567
+ return 0;
568
+ }
569
+
570
+ /* rf_open
571
+ *
572
+ * Used when: A file is opened for read or write.
573
+ *
574
+ * If called to open a file for reading, then FuseFS will call "read_file" on
575
+ * FuseRoot, and store the results into the linked list of "opened_file"
576
+ * structures, so as to provide the same file for mmap, all excutes of
577
+ * read(), and preventing more than one call to FuseRoot.
578
+ *
579
+ * If called on a file opened for writing, FuseFS will first double check
580
+ * if the file is writable to by calling "writable?" on FuseRoot, passing
581
+ * the path. If the return value is a truth value, it will create an entry
582
+ * into the opened_file list, flagged as for writing.
583
+ *
584
+ * If called with any other set of flags, this will return -ENOPERM, since
585
+ * FuseFS does not (currently) need to support anything other than direct
586
+ * read and write.
587
+ */
588
+ static int
589
+ rf_open(const char *path, struct fuse_file_info *fi) {
590
+ VALUE body;
591
+ char *value;
592
+ size_t len;
593
+ char open_opts[4], *optr;
594
+ opened_file *newfile;
595
+
596
+ debug("rf_open(%s)\n", path);
597
+
598
+ /* Make sure it's not already open. */
599
+ debug(" Checking if it's already open ...");
600
+ if (file_openedP(path)) {
601
+ debug(" yes.\n");
602
+ return -EACCES;
603
+ }
604
+ debug(" no.\n");
605
+
606
+ debug("Checking if an editor file is requested...");
607
+ switch (editor_fileP(path)) {
608
+ case 2:
609
+ debug(" yes, and it was created.\n");
610
+ return 0;
611
+ case 1:
612
+ debug(" yes, but it was not created.\n");
613
+ return -ENOENT;
614
+ default:
615
+ debug(" no.\n");
616
+ }
617
+
618
+ optr = open_opts;
619
+ switch (fi->flags & 3) {
620
+ case 0:
621
+ *(optr++) = 'r';
622
+ break;
623
+ case 1:
624
+ *(optr++) = 'w';
625
+ break;
626
+ case 2:
627
+ *(optr++) = 'w';
628
+ *(optr++) = 'r';
629
+ break;
630
+ default:
631
+ debug("Opening a file with something other than rd, wr, or rdwr?");
632
+ }
633
+ if (fi->flags & O_APPEND)
634
+ *(optr++) = 'a';
635
+ *(optr) = '\0';
636
+
637
+ debug(" Checking for a raw_opened file... ");
638
+ if (RTEST(rf_call(path,id_raw_open,rb_str_new2(open_opts)))) {
639
+ debug(" yes.\n");
640
+ newfile = ALLOC(opened_file);
641
+ newfile->size = 0;
642
+ newfile->value = NULL;
643
+ newfile->writesize = 0;
644
+ newfile->zero_offset = 0;
645
+ newfile->modified = 0;
646
+ newfile->path = strdup(path);
647
+ newfile->raw = 1;
648
+
649
+ newfile->next = opened_head;
650
+ opened_head = newfile;
651
+ return 0;
652
+ }
653
+ debug(" no.\n");
654
+
655
+ debug(" Checking open type ...");
656
+ if ((fi->flags & 3) == O_RDONLY) {
657
+ debug(" RDONLY.\n");
658
+ /* Open for read. */
659
+ /* Make sure it exists. */
660
+ if (!RTEST(rf_call(path,is_file,Qnil))) {
661
+ return -ENOENT;
662
+ }
663
+
664
+ body = rf_call(path, id_read_file,Qnil);
665
+
666
+ /* I don't wanna deal with non-strings :D. */
667
+ if (TYPE(body) != T_STRING) {
668
+ return -ENOENT;
669
+ }
670
+
671
+ /* We have the body, now save it the entire contents to our
672
+ * opened_file lists. */
673
+ newfile = ALLOC(opened_file);
674
+ value = rb_str2cstr(body, &(newfile->size));
675
+ newfile->value = ALLOC_N(char,(newfile->size)+1);
676
+ memcpy(newfile->value,value,newfile->size);
677
+ newfile->value[newfile->size] = '\0';
678
+ newfile->writesize = 0;
679
+ newfile->zero_offset = 0;
680
+ newfile->modified = 0;
681
+ newfile->path = strdup(path);
682
+ newfile->raw = 0;
683
+
684
+ newfile->next = opened_head;
685
+ opened_head = newfile;
686
+ return 0;
687
+
688
+ } else if (((fi->flags & 3) == O_RDWR) ||
689
+ (((fi->flags & 3) == O_WRONLY) && (fi->flags & O_APPEND))) {
690
+ /* Can we write to it? */
691
+ debug(" RDWR or Append.\n");
692
+ debug(" Checking if created file ...");
693
+ if (created_file && (strcmp(created_file,path) == 0)) {
694
+ debug(" yes.\n");
695
+ newfile = ALLOC(opened_file);
696
+ newfile->writesize = FILE_GROW_SIZE;
697
+ newfile->value = ALLOC_N(char,newfile->writesize);
698
+ newfile->path = strdup(path);
699
+ newfile->size = 0;
700
+ newfile->raw = 0;
701
+ newfile->zero_offset = 0;
702
+ *(newfile->value) = '\0';
703
+ newfile->modified = 0;
704
+ newfile->next = opened_head;
705
+ opened_head = newfile;
706
+ return 0;
707
+ }
708
+ debug(" no\n");
709
+
710
+ debug(" Checking if we can write to it...");
711
+ if (!RTEST(rf_call(path,can_write,Qnil))) {
712
+ debug(" yes.\n");
713
+ return -EACCES;
714
+ }
715
+ debug(" no\n");
716
+
717
+ /* Make sure it exists. */
718
+ if (RTEST(rf_call(path,is_file,Qnil))) {
719
+ body = rf_call(path, id_read_file,Qnil);
720
+
721
+ /* I don't wanna deal with non-strings :D. */
722
+ if (TYPE(body) != T_STRING) {
723
+ return -ENOENT;
724
+ }
725
+
726
+ /* We have the body, now save it the entire contents to our
727
+ * opened_file lists. */
728
+ newfile = ALLOC(opened_file);
729
+ value = rb_str2cstr(body,&newfile->size);
730
+ newfile->value = ALLOC_N(char,(newfile->size)+1);
731
+ memcpy(newfile->value,value,newfile->size);
732
+ newfile->writesize = newfile->size+1;
733
+ newfile->path = strdup(path);
734
+ newfile->raw = 0;
735
+ newfile->zero_offset = 0;
736
+ } else {
737
+ newfile = ALLOC(opened_file);
738
+ newfile->writesize = FILE_GROW_SIZE;
739
+ newfile->value = ALLOC_N(char,newfile->writesize);
740
+ newfile->path = strdup(path);
741
+ newfile->size = 0;
742
+ newfile->raw = 0;
743
+ newfile->zero_offset = 0;
744
+ *(newfile->value) = '\0';
745
+ }
746
+ newfile->modified = 0;
747
+
748
+ if (fi->flags & O_APPEND) {
749
+ newfile->zero_offset = newfile->size;
750
+ }
751
+
752
+ newfile->next = opened_head;
753
+ opened_head = newfile;
754
+ return 0;
755
+ } else if ((fi->flags & 3) == O_WRONLY) {
756
+ debug(" WRONLY.\n");
757
+ #ifdef DEBUG
758
+ if (fi->flags & O_APPEND)
759
+ debug(" It's opened for O_APPEND\n");
760
+ if (fi->flags & O_ASYNC)
761
+ debug(" It's opened for O_ASYNC\n");
762
+ if (fi->flags & O_CREAT)
763
+ debug(" It's opened for O_CREAT\n");
764
+ if (fi->flags & O_EXCL)
765
+ debug(" It's opened for O_EXCL\n");
766
+ if (fi->flags & O_NOCTTY)
767
+ debug(" It's opened for O_NOCTTY\n");
768
+ if (fi->flags & O_NONBLOCK)
769
+ debug(" It's opened for O_NONBLOCK\n");
770
+ if (fi->flags & O_SYNC)
771
+ debug(" It's opened for O_SYNC\n");
772
+ if (fi->flags & O_TRUNC)
773
+ debug(" It's opened for O_TRUNC\n");
774
+ #endif
775
+
776
+ /* Open for write. */
777
+ /* Can we write to it? */
778
+ debug(" Checking if we can write to it ... ");
779
+ if (!((created_file && (strcmp(created_file,path) == 0)) ||
780
+ RTEST(rf_call(path,can_write,Qnil)))) {
781
+ debug(" no.\n");
782
+ return -EACCES;
783
+ }
784
+ debug(" yes.\n");
785
+
786
+ /* We can write to it. Create an opened_write_file entry and initialize
787
+ * it to a small size. */
788
+ newfile = ALLOC(opened_file);
789
+ newfile->writesize = FILE_GROW_SIZE;
790
+ newfile->value = ALLOC_N(char,newfile->writesize);
791
+ newfile->path = strdup(path);
792
+ newfile->size = 0;
793
+ newfile->zero_offset = 0;
794
+ newfile->modified = 0;
795
+ newfile->raw = 0;
796
+ *(newfile->value) = '\0';
797
+
798
+ newfile->next = opened_head;
799
+ opened_head = newfile;
800
+
801
+ if (created_file && (strcasecmp(created_file,path) == 0)) {
802
+ free(created_file);
803
+ created_file = NULL;
804
+ }
805
+ return 0;
806
+ } else {
807
+ debug(" Unknown...\n");
808
+ return -ENOENT;
809
+ }
810
+ }
811
+
812
+ /* rf_release
813
+ *
814
+ * Used when: A file is no longer being read or written to.
815
+ *
816
+ * If release is called on a written file, FuseFS will call 'write_to' on
817
+ * FuseRoot, passing the path and contents of the file. It will then
818
+ * clear the file information from the in-memory file storage that
819
+ * FuseFS uses to prevent FuseRoot from receiving incomplete files.
820
+ *
821
+ * If called on a file opened for reading, FuseFS will just clear the
822
+ * in-memory copy of the return value from rf_open.
823
+ */
824
+ static int
825
+ rf_release(const char *path, struct fuse_file_info *fi) {
826
+
827
+ opened_file *ptr,*prev;
828
+ int is_editor = 0;
829
+
830
+ debug("rf_release(%s)\n", path);
831
+
832
+ debug(" Checking for opened file ...");
833
+ /* Find the opened file. */
834
+ for (ptr = opened_head, prev=NULL;ptr;prev = ptr,ptr = ptr->next)
835
+ if (strcmp(ptr->path,path) == 0) break;
836
+
837
+ /* If we don't have this open, it doesn't exist. */
838
+ if (ptr == NULL) {
839
+ debug(" no.\n");
840
+ debug(" Checking for opened editor file ...");
841
+ for (ptr = opened_head, prev=NULL;ptr;prev = ptr,ptr = ptr->next)
842
+ if (strcmp(ptr->path,path) == 0) {
843
+ is_editor = 1;
844
+ break;
845
+ }
846
+ }
847
+ if (ptr == NULL) {
848
+ debug(" no.\n");
849
+ return -ENOENT;
850
+ }
851
+ debug(" yes.\n");
852
+
853
+ /* If it's opened for raw read/write, call raw_close */
854
+ debug(" Checking if it's opened for raw write...");
855
+ if (ptr->raw) {
856
+ /* raw read */
857
+ debug(" yes.\n");
858
+ rf_call(path,id_raw_close,Qnil);
859
+ } else {
860
+ debug(" no.\n");
861
+
862
+ /* Is this a file that was open for write?
863
+ *
864
+ * If so, call write_to. */
865
+ debug(" Checking if it's for write ...\n");
866
+ if ((!ptr->raw) && (ptr->writesize != 0) && !editor_fileP(path)) {
867
+ debug(" yes ...");
868
+ if (ptr->modified) {
869
+ debug(" and modified.\n");
870
+ rf_call(path,id_write_to,rb_str_new(ptr->value,ptr->size));
871
+ } else {
872
+ debug(" and not modified.\n");
873
+ if (!handle_editor) {
874
+ debug(" ... But calling write anyawy.");
875
+ rf_call(path,id_write_to,rb_str_new(ptr->value,ptr->size));
876
+ }
877
+ }
878
+ }
879
+ }
880
+
881
+ /* Free the file contents. */
882
+ if (!is_editor) {
883
+ if (prev == NULL) {
884
+ opened_head = ptr->next;
885
+ } else {
886
+ prev->next = ptr->next;
887
+ }
888
+ if (ptr->value)
889
+ free(ptr->value);
890
+ free(ptr->path);
891
+ free(ptr);
892
+ }
893
+
894
+ return 0;
895
+ }
896
+
897
+ /* rf_touch
898
+ *
899
+ * Used when: A program tries to modify the file's times.
900
+ *
901
+ * We use this for a neat side-effect thingy. When a file is touched, we
902
+ * call the "touch" method. i.e: "touch button" would call
903
+ * "FuseRoot.touch('/button')" and something *can* happen. =).
904
+ */
905
+ static int
906
+ rf_touch(const char *path, struct utimbuf *ignore) {
907
+ debug("rf_touch(%s)\n", path);
908
+ rf_call(path,id_touch,Qnil);
909
+ return 0;
910
+ }
911
+
912
+ /* rf_rename
913
+ *
914
+ * Used when: a file is renamed.
915
+ *
916
+ * When FuseFS receives a rename command, it really just removes the old file
917
+ * and creates the new file with the same contents.
918
+ */
919
+ static int
920
+ rf_rename(const char *path, const char *dest) {
921
+ /* Does it exist to be edited? */
922
+ int iseditor = 0;
923
+ if (editor_fileP(path) == 2) {
924
+ iseditor = 1;
925
+ } else {
926
+ debug("rf_rename(%s,%s)\n", path,dest);
927
+ debug(" Checking if %s is file ...", path);
928
+ if (!RTEST(rf_call(path,is_file,Qnil))) {
929
+ debug(" no.\n");
930
+ return -ENOENT;
931
+ }
932
+ debug(" yes.\n");
933
+
934
+ /* Can we remove the old one? */
935
+ debug(" Checking if we can delete %s ...", path);
936
+ if (!RTEST(rf_call(path,can_delete,Qnil))) {
937
+ debug(" no.\n");
938
+ return -EACCES;
939
+ }
940
+ debug(" yes.\n");
941
+ }
942
+
943
+ /* Can we create the new one? */
944
+ debug(" Checking if we can write to %s ...", dest);
945
+ if (!RTEST(rf_call(dest,can_write,Qnil))) {
946
+ debug(" no.\n");
947
+ return -EACCES;
948
+ }
949
+ debug(" yes.\n");
950
+
951
+ /* Copy it over and then remove. */
952
+ debug(" Copying.\n");
953
+ if (iseditor) {
954
+ editor_file *eptr,*prev;
955
+ for (eptr=editor_head,prev=NULL;eptr;prev = eptr,eptr = eptr->next) {
956
+ if (strcmp(path,eptr->path) == 0) {
957
+ if (prev == NULL) {
958
+ editor_head = eptr->next;
959
+ } else {
960
+ prev->next = eptr->next;
961
+ }
962
+ VALUE body = rb_str_new(eptr->value,eptr->size);
963
+ rf_call(dest,id_write_to,body);
964
+ free(eptr->value);
965
+ free(eptr->path);
966
+ free(eptr);
967
+ break;
968
+ }
969
+ }
970
+ } else {
971
+ VALUE body = rf_call(path,id_read_file,Qnil);
972
+ if (TYPE(body) != T_STRING) {
973
+ /* We just write a null file, then. Ah well. */
974
+ VALUE newstr = rb_str_new2("");
975
+ rf_call(path,id_delete,Qnil);
976
+ rf_call(dest,id_write_to,newstr);
977
+ } else {
978
+ rf_call(path,id_delete,Qnil);
979
+ rf_call(dest,id_write_to,body);
980
+ }
981
+ }
982
+ return 0;
983
+ }
984
+
985
+ /* rf_unlink
986
+ *
987
+ * Used when: a file is removed.
988
+ *
989
+ * This calls can_remove? and remove() on FuseRoot.
990
+ */
991
+ static int
992
+ rf_unlink(const char *path) {
993
+ editor_file *eptr,*prev;
994
+ debug("rf_unlink(%s)\n",path);
995
+
996
+ debug(" Checking if it's an editor file ...");
997
+ switch (editor_fileP(path)) {
998
+ case 2:
999
+ debug(" yes. Removing.\n");
1000
+ for (eptr=editor_head,prev=NULL;eptr;prev = eptr,eptr = eptr->next) {
1001
+ if (strcmp(path,eptr->path) == 0) {
1002
+ if (prev == NULL) {
1003
+ editor_head = eptr->next;
1004
+ } else {
1005
+ prev->next = eptr->next;
1006
+ }
1007
+ free(eptr->value);
1008
+ free(eptr->path);
1009
+ free(eptr);
1010
+ return 0;
1011
+ }
1012
+ }
1013
+ return -ENOENT;
1014
+ case 1:
1015
+ debug(" yes, but it wasn't created.\n");
1016
+ return -ENOENT;
1017
+ }
1018
+ debug(" no.\n");
1019
+
1020
+ /* Does it exist to be removed? */
1021
+ debug(" Checking if it exists...");
1022
+ if (!RTEST(rf_call(path,is_file,Qnil))) {
1023
+ debug(" no.\n");
1024
+ return -ENOENT;
1025
+ }
1026
+ debug(" yes.\n");
1027
+
1028
+ /* Can we remove it? */
1029
+ debug(" Checking if we can remove it...");
1030
+ if (!RTEST(rf_call(path,can_delete,Qnil))) {
1031
+ debug(" yes.\n");
1032
+ return -EACCES;
1033
+ }
1034
+ debug(" no.\n");
1035
+
1036
+ /* Ok, remove it! */
1037
+ debug(" Removing it.\n");
1038
+ rf_call(path,id_delete,Qnil);
1039
+ return 0;
1040
+ }
1041
+
1042
+ /* rf_truncate
1043
+ *
1044
+ * Used when: a file is truncated.
1045
+ *
1046
+ * If this is an existing file?, that is writable? to, then FuseFS will
1047
+ * read the file, truncate it, and call write_to with the new value.
1048
+ */
1049
+ static int
1050
+ rf_truncate(const char *path, off_t offset) {
1051
+ debug( "rf_truncate(%s,%d)\n", path, offset );
1052
+
1053
+ debug("Checking if it's an editor file ... ");
1054
+ if (editor_fileP(path)) {
1055
+ debug(" Yes.\n");
1056
+ opened_file *ptr;
1057
+ for (ptr = opened_head;ptr;ptr = ptr->next) {
1058
+ if (!strcmp(ptr->path,path)) {
1059
+ ptr->size = offset;
1060
+ return 0;
1061
+ }
1062
+ }
1063
+ return 0;
1064
+ }
1065
+
1066
+ /* Does it exist to be truncated? */
1067
+ if (!RTEST(rf_call(path,is_file,Qnil))) {
1068
+ return -ENOENT;
1069
+ }
1070
+
1071
+ /* Can we write to it? */
1072
+ if (!RTEST(rf_call(path,can_delete,Qnil))) {
1073
+ return -EACCES;
1074
+ }
1075
+
1076
+ /* If offset is 0, then we just overwrite it with an empty file. */
1077
+ if (offset > 0) {
1078
+ VALUE newstr = rb_str_new2("");
1079
+ rf_call(path,id_write_to,newstr);
1080
+ } else {
1081
+ VALUE body = rf_call(path,id_read_file,Qnil);
1082
+ if (TYPE(body) != T_STRING) {
1083
+ /* We just write a null file, then. Ah well. */
1084
+ VALUE newstr = rb_str_new2("");
1085
+ rf_call(path,id_write_to,newstr);
1086
+ } else {
1087
+ long size;
1088
+ char *str = rb_str2cstr(body,&size);
1089
+
1090
+ /* Just in case offset is bigger than the file. */
1091
+ if (offset >= size) return 0;
1092
+
1093
+ str[offset] = '\0';
1094
+ rf_call(path,id_write_to,rb_str_new2(str));
1095
+ }
1096
+ }
1097
+ return 0;
1098
+ }
1099
+
1100
+ /* rf_mkdir
1101
+ *
1102
+ * Used when: A user calls 'mkdir'
1103
+ *
1104
+ * This calls can_mkdir? and mkdir() on FuseRoot.
1105
+ */
1106
+ static int
1107
+ rf_mkdir(const char *path, mode_t mode) {
1108
+ debug("rf_mkdir(%s)",path);
1109
+ /* Does it exist? */
1110
+ if (RTEST(rf_call(path,is_directory,Qnil)))
1111
+ return -EEXIST;
1112
+
1113
+ if (RTEST(rf_call(path,is_file,Qnil)))
1114
+ return -EEXIST;
1115
+
1116
+ /* Can we mkdir it? */
1117
+ if (!RTEST(rf_call(path,can_mkdir,Qnil)))
1118
+ return -EACCES;
1119
+
1120
+ /* Ok, mkdir it! */
1121
+ rf_call(path,id_mkdir,Qnil);
1122
+ return 0;
1123
+ }
1124
+
1125
+ /* rf_rmdir
1126
+ *
1127
+ * Used when: A user calls 'rmdir'
1128
+ *
1129
+ * This calls can_rmdir? and rmdir() on FuseRoot.
1130
+ */
1131
+ static int
1132
+ rf_rmdir(const char *path) {
1133
+ debug("rf_rmdir(%s)",path);
1134
+ /* Does it exist? */
1135
+ if (!RTEST(rf_call(path,is_directory,Qnil))) {
1136
+ if (RTEST(rf_call(path,is_file,Qnil))) {
1137
+ return -ENOTDIR;
1138
+ } else {
1139
+ return -ENOENT;
1140
+ }
1141
+ }
1142
+
1143
+ /* Can we rmdir it? */
1144
+ if (!RTEST(rf_call(path,can_rmdir,Qnil)))
1145
+ return -EACCES;
1146
+
1147
+ /* Ok, rmdir it! */
1148
+ rf_call(path,id_rmdir,Qnil);
1149
+ return 0;
1150
+ }
1151
+
1152
+ /* rf_write
1153
+ *
1154
+ * Used when: a file is written to by the user.
1155
+ *
1156
+ * This does not access FuseRoot at all. Instead, it appends the written
1157
+ * data to the opened_file entry, growing its memory usage if necessary.
1158
+ */
1159
+ static int
1160
+ rf_write(const char *path, const char *buf, size_t size, off_t offset,
1161
+ struct fuse_file_info *fi) {
1162
+ debug("rf_write(%s)",path);
1163
+
1164
+ opened_file *ptr;
1165
+
1166
+ debug( " Offset is %d\n", offset );
1167
+
1168
+ debug(" Checking if file is open... ");
1169
+ /* Find the opened file. */
1170
+ for (ptr = opened_head;ptr;ptr = ptr->next)
1171
+ if (strcmp(ptr->path,path) == 0) break;
1172
+
1173
+ /* If we don't have this open, we can't write to it. */
1174
+ if (ptr == NULL) {
1175
+ for (ptr = editor_head;ptr;ptr = ptr->next)
1176
+ if (strcmp(ptr->path,path) == 0) break;
1177
+ }
1178
+
1179
+ if (ptr == NULL) {
1180
+ debug(" no.\n");
1181
+ return 0;
1182
+ }
1183
+ debug(" yes.\n");
1184
+
1185
+ /* Make sure it's open for write ... */
1186
+ /* If it's opened for raw read/write, call raw_write */
1187
+ debug(" Checking if it's opened for raw write...");
1188
+ if (ptr->raw) {
1189
+ /* raw read */
1190
+ VALUE args = rb_ary_new();
1191
+ debug(" yes.\n");
1192
+ rb_ary_push(args,INT2NUM(offset));
1193
+ rb_ary_push(args,INT2NUM(size));
1194
+ rb_ary_push(args,rb_str_new(buf,size));
1195
+ rf_call(path,id_raw_write,args);
1196
+ return size;
1197
+ }
1198
+ debug(" no.\n");
1199
+ debug(" Checking if it's open for write ...");
1200
+ if (ptr->writesize == 0) {
1201
+ debug(" no.\n");
1202
+ return 0;
1203
+ }
1204
+ debug(" yes.\n");
1205
+
1206
+ /* Mark it modified. */
1207
+ ptr->modified = 1;
1208
+
1209
+ /* We have it, so now we need to write to it. */
1210
+ offset += ptr->zero_offset;
1211
+
1212
+ /* Grow memory if necessary. */
1213
+ if ((offset + size + 1) > ptr->writesize) {
1214
+ size_t newsize;
1215
+ newsize = offset + size + 1 + FILE_GROW_SIZE;
1216
+ newsize -= newsize % FILE_GROW_SIZE;
1217
+ ptr->writesize = newsize;
1218
+ ptr->value = REALLOC_N(ptr->value, char, newsize);
1219
+ }
1220
+
1221
+ memcpy(ptr->value + offset, buf, size);
1222
+
1223
+ /* I really don't know if a null bit is required, but this
1224
+ * also functions as a size bit I can pass to rb_string_new2
1225
+ * to allow binary data */
1226
+ if (offset+size > ptr->size)
1227
+ ptr->size = offset+size;
1228
+ ptr->value[ptr->size] = '\0';
1229
+
1230
+ return size;
1231
+ }
1232
+
1233
+ /* rf_read
1234
+ *
1235
+ * Used when: A file opened by rf_open is read.
1236
+ *
1237
+ * In most cases, this does not access FuseRoot at all. It merely reads from
1238
+ * the already-read 'file' that is saved in the opened_file list.
1239
+ *
1240
+ * For files opened with raw_open, it calls raw_read
1241
+ */
1242
+ static int
1243
+ rf_read(const char *path, char *buf, size_t size, off_t offset,
1244
+ struct fuse_file_info *fi) {
1245
+ opened_file *ptr;
1246
+
1247
+ debug( "rf_read(%s)\n", path );
1248
+ /* Find the opened file. */
1249
+ for (ptr = opened_head;ptr;ptr = ptr->next)
1250
+ if (strcmp(ptr->path,path) == 0) break;
1251
+
1252
+ /* If we don't have this open, it doesn't exist. */
1253
+ if (ptr == NULL)
1254
+ return -ENOENT;
1255
+
1256
+ /* If it's opened for raw read/write, call raw_read */
1257
+ if (ptr->raw) {
1258
+ /* raw read */
1259
+ VALUE args = rb_ary_new();
1260
+ rb_ary_push(args,INT2NUM(offset));
1261
+ rb_ary_push(args,INT2NUM(size));
1262
+ VALUE ret = rf_call(path,id_raw_read,args);
1263
+ if (!RTEST(ret))
1264
+ return 0;
1265
+ if (TYPE(ret) != T_STRING)
1266
+ return 0;
1267
+ memcpy(buf, RSTRING_LEN(ret), RSTRING_LEN(ret));
1268
+ return RSTRING_LEN(ret);
1269
+ }
1270
+
1271
+ /* Is there anything left to read? */
1272
+ if (offset < ptr->size) {
1273
+ if (offset + size > ptr->size)
1274
+ size = ptr->size - offset;
1275
+ memcpy(buf, ptr->value + offset, size);
1276
+ return size;
1277
+ }
1278
+
1279
+ return 0;
1280
+ }
1281
+
1282
+ static int
1283
+ rf_fsyncdir(const char * path, int p, struct fuse_file_info *fi)
1284
+ {
1285
+ return 0;
1286
+ }
1287
+
1288
+ static int
1289
+ rf_utime(const char *path, struct utimbuf *ubuf)
1290
+ {
1291
+ return 0;
1292
+ }
1293
+
1294
+ static int
1295
+ rf_statfs(const char *path, struct statvfs *buf)
1296
+ {
1297
+ return 0;
1298
+ }
1299
+
1300
+ /* rf_oper
1301
+ *
1302
+ * Used for: FUSE utilizes this to call operations at the appropriate time.
1303
+ *
1304
+ * This is utilized by rf_mount
1305
+ */
1306
+ static struct fuse_operations rf_oper = {
1307
+ .getattr = rf_getattr,
1308
+ .readdir = rf_readdir,
1309
+ .mknod = rf_mknod,
1310
+ .unlink = rf_unlink,
1311
+ .mkdir = rf_mkdir,
1312
+ .rmdir = rf_rmdir,
1313
+ .truncate = rf_truncate,
1314
+ .rename = rf_rename,
1315
+ .open = rf_open,
1316
+ .release = rf_release,
1317
+ .utime = rf_touch,
1318
+ .read = rf_read,
1319
+ .write = rf_write,
1320
+ .fsyncdir = rf_fsyncdir,
1321
+ .utime = rf_utime,
1322
+ .statfs = rf_statfs,
1323
+ };
1324
+
1325
+ /* rf_set_root
1326
+ *
1327
+ * Used by: FuseFS.set_root
1328
+ *
1329
+ * This defines FuseRoot, which is the crux of FuseFS. It is required to
1330
+ * have the methods "directory?" "file?" "contents" "writable?" "read_file"
1331
+ * and "write_to"
1332
+ */
1333
+ VALUE
1334
+ rf_set_root(VALUE self, VALUE rootval) {
1335
+ if (self != cFuseFS) {
1336
+ rb_raise(cFSException,"Error: 'set_root' called outside of FuseFS?!");
1337
+ return Qnil;
1338
+ }
1339
+
1340
+ rb_iv_set(cFuseFS,"@root",rootval);
1341
+ FuseRoot = rootval;
1342
+ return Qtrue;
1343
+ }
1344
+
1345
+ /* rf_handle_editor
1346
+ *
1347
+ * Used by: FuseFS.handle_editor <value>
1348
+ *
1349
+ * If passed a false value, then FuseFS will not attempt to handle editor
1350
+ * swap files on its own, instead passing them to the filesystem as
1351
+ * normal files.
1352
+ */
1353
+ VALUE
1354
+ rf_handle_editor(VALUE self, VALUE troo) {
1355
+ if (self != cFuseFS) {
1356
+ rb_raise(cFSException,"Error: 'set_root' called outside of FuseFS?!");
1357
+ return Qnil;
1358
+ }
1359
+
1360
+ handle_editor = RTEST(troo);
1361
+ return Qtrue;
1362
+ }
1363
+
1364
+ /* rf_mount_to
1365
+ *
1366
+ * Used by: FuseFS.mount_to(dir)
1367
+ *
1368
+ * FuseFS.mount_to(dir) calls FUSE to mount FuseFS under the given directory.
1369
+ */
1370
+ VALUE
1371
+ rf_mount_to(int argc, VALUE *argv, VALUE self) {
1372
+ struct fuse_args *opts;
1373
+ VALUE mountpoint;
1374
+ int i;
1375
+ char *cur;
1376
+
1377
+ if (self != cFuseFS) {
1378
+ rb_raise(cFSException,"Error: 'mount_to' called outside of FuseFS?!");
1379
+ return Qnil;
1380
+ }
1381
+
1382
+ if (argc == 0) {
1383
+ rb_raise(rb_eArgError,"mount_to requires at least 1 argument!");
1384
+ return Qnil;
1385
+ }
1386
+
1387
+ mountpoint = argv[0];
1388
+
1389
+ Check_Type(mountpoint, T_STRING);
1390
+ opts = ALLOC(struct fuse_args);
1391
+ opts->argc = argc;
1392
+ opts->argv = ALLOC_N(char *, opts->argc);
1393
+ opts->allocated = 1;
1394
+
1395
+ opts->argv[0] = strdup("-odirect_io");
1396
+
1397
+ for (i = 1; i < argc; i++) {
1398
+ cur = StringValuePtr(argv[i]);
1399
+ opts->argv[i] = ALLOC_N(char, RSTRING_LEN(argv[i]) + 2);
1400
+ sprintf(opts->argv[i], "-o%s", cur);
1401
+ }
1402
+
1403
+ rb_iv_set(cFuseFS,"@mountpoint",mountpoint);
1404
+ fusefs_setup(StringValuePtr(mountpoint), &rf_oper, opts);
1405
+ return Qtrue;
1406
+ }
1407
+
1408
+ /* rf_fd
1409
+ *
1410
+ * Used by: FuseFS.fuse_fd(dir)
1411
+ *
1412
+ * FuseFS.fuse_fd returns the file descriptor of the open handle on the
1413
+ * /dev/fuse object that is utilized by FUSE. This is crucial for letting
1414
+ * ruby keep control of the script, as it can now use IO.select, rather
1415
+ * than turning control over to fuse_main.
1416
+ */
1417
+ VALUE
1418
+ rf_fd(VALUE self) {
1419
+ int fd = fusefs_fd();
1420
+ if (fd < 0)
1421
+ return Qnil;
1422
+ return INT2NUM(fd);
1423
+ }
1424
+
1425
+ /* rf_process
1426
+ *
1427
+ * Used for: FuseFS.process
1428
+ *
1429
+ * rf_process, which calls fusefs_process, is the other crucial portion to
1430
+ * keeping ruby in control of the script. fusefs_process will read and
1431
+ * process exactly one command from the fuse_fd. If this is called when
1432
+ * there is no incoming data waiting, it *will* hang until it receives a
1433
+ * command on the fuse_fd
1434
+ */
1435
+ VALUE
1436
+ rf_process(VALUE self) {
1437
+ fusefs_process();
1438
+ }
1439
+
1440
+
1441
+ /* rf_uid and rf_gid
1442
+ *
1443
+ * Used by: FuseFS.reader_uid and FuseFS.reader_gid
1444
+ *
1445
+ * These return the UID and GID of the processes that are causing the
1446
+ * separate Fuse methods to be called. This can be used for permissions
1447
+ * checking, returning a different file for different users, etc.
1448
+ */
1449
+ VALUE
1450
+ rf_uid(VALUE self) {
1451
+ int fd = fusefs_uid();
1452
+ if (fd < 0)
1453
+ return Qnil;
1454
+ return INT2NUM(fd);
1455
+ }
1456
+
1457
+ VALUE
1458
+ rf_gid(VALUE self) {
1459
+ int fd = fusefs_gid();
1460
+ if (fd < 0)
1461
+ return Qnil;
1462
+ return INT2NUM(fd);
1463
+ }
1464
+
1465
+ /* Init_fusefs_lib()
1466
+ *
1467
+ * Used by: Ruby, to initialize FuseFS.
1468
+ *
1469
+ * This is just stuff to set up and establish the Ruby module FuseFS and
1470
+ * its methods.
1471
+ */
1472
+ void
1473
+ Init_fusefs_lib() {
1474
+ opened_head = NULL;
1475
+ init_time = time(NULL);
1476
+
1477
+ /* module FuseFS */
1478
+ cFuseFS = rb_define_module("FuseFS");
1479
+
1480
+ /* Our exception */
1481
+ cFSException = rb_define_class_under(cFuseFS,"FuseFSException",rb_eStandardError);
1482
+
1483
+ /* def Fuse.run */
1484
+ rb_define_singleton_method(cFuseFS,"fuse_fd", (rbfunc) rf_fd, 0);
1485
+ rb_define_singleton_method(cFuseFS,"reader_uid", (rbfunc) rf_uid, 0);
1486
+ rb_define_singleton_method(cFuseFS,"uid", (rbfunc) rf_uid, 0);
1487
+ rb_define_singleton_method(cFuseFS,"reader_gid", (rbfunc) rf_gid, 0);
1488
+ rb_define_singleton_method(cFuseFS,"gid", (rbfunc) rf_gid, 0);
1489
+ rb_define_singleton_method(cFuseFS,"process", (rbfunc) rf_process, 0);
1490
+ rb_define_singleton_method(cFuseFS,"mount_to", (rbfunc) rf_mount_to, -1);
1491
+ rb_define_singleton_method(cFuseFS,"mount_under", (rbfunc) rf_mount_to, -1);
1492
+ rb_define_singleton_method(cFuseFS,"mountpoint", (rbfunc) rf_mount_to, -1);
1493
+ rb_define_singleton_method(cFuseFS,"set_root", (rbfunc) rf_set_root, 1);
1494
+ rb_define_singleton_method(cFuseFS,"root=", (rbfunc) rf_set_root, 1);
1495
+ rb_define_singleton_method(cFuseFS,"handle_editor", (rbfunc) rf_handle_editor, 1);
1496
+ rb_define_singleton_method(cFuseFS,"handle_editor=", (rbfunc) rf_handle_editor, 1);
1497
+
1498
+ #undef RMETHOD
1499
+ #define RMETHOD(name,cstr) \
1500
+ name = rb_intern(cstr);
1501
+
1502
+ RMETHOD(id_dir_contents,"contents");
1503
+ RMETHOD(id_read_file,"read_file");
1504
+ RMETHOD(id_write_to,"write_to");
1505
+ RMETHOD(id_delete,"delete");
1506
+ RMETHOD(id_mkdir,"mkdir");
1507
+ RMETHOD(id_rmdir,"rmdir");
1508
+ RMETHOD(id_touch,"touch");
1509
+ RMETHOD(id_size,"size");
1510
+
1511
+ RMETHOD(is_directory,"directory?");
1512
+ RMETHOD(is_file,"file?");
1513
+ RMETHOD(is_executable,"executable?");
1514
+ RMETHOD(can_write,"can_write?");
1515
+ RMETHOD(can_delete,"can_delete?");
1516
+ RMETHOD(can_mkdir,"can_mkdir?");
1517
+ RMETHOD(can_rmdir,"can_rmdir?");
1518
+
1519
+ RMETHOD(id_raw_open,"raw_open");
1520
+ RMETHOD(id_raw_close,"raw_close");
1521
+ RMETHOD(id_raw_read,"raw_read");
1522
+ RMETHOD(id_raw_write,"raw_write");
1523
+
1524
+ RMETHOD(id_dup,"dup");
1525
+ }