fusefs 0.7.0

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
@@ -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
+ }