fusefs 0.7.0

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