hysios-fusefs 0.0.1

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