fusefs 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.gitignore +11 -0
- data/API.txt +279 -0
- data/Changes.txt +63 -0
- data/LICENSE +20 -0
- data/README +46 -0
- data/Rakefile +56 -0
- data/TODO +11 -0
- data/VERSION +1 -0
- data/ext/MANIFEST +4 -0
- data/ext/extconf.rb +7 -0
- data/ext/fusefs_fuse.c +149 -0
- data/ext/fusefs_fuse.h +19 -0
- data/ext/fusefs_lib.c +1514 -0
- data/fusefs.gemspec +69 -0
- data/hello.sh +10 -0
- data/lib/fusefs.rb +244 -0
- data/sample/demo.rb +100 -0
- data/sample/dictfs.rb +84 -0
- data/sample/hello.rb +27 -0
- data/sample/openurifs.rb +53 -0
- data/sample/railsfs.rb +77 -0
- data/sample/sqlfs.rb +134 -0
- data/sample/yamlfs.rb +168 -0
- data/test/fusefs_test.rb +7 -0
- data/test/test_helper.rb +10 -0
- metadata +83 -0
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
data/ext/extconf.rb
ADDED
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,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
|
data/ext/fusefs_lib.c
ADDED
@@ -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
|
+
}
|