Added README, LICENSE, Makefile for future PDP-11 Unix FUSE fs driver project.
[pdp11-unix-fusefs] / unixfs.c
CommitLineData
845dfc67
AT
1#include "fs.h"
2#include <errno.h>
3#include <fcntl.h>
4#include <unistd.h>
5#include <fuse_lowlevel.h>
6
7#include "args.h"
8
9uint8 *fsdata;
10int fslen;
11
12void
13panic(char *fmt, ...)
14{
15 va_list ap;
16 va_start(ap, fmt);
17 vfprintf(stderr, fmt, ap);
18 fprintf(stderr, "\n");
19 va_end(ap);
20 exit(1);
21}
22
23void*
24emalloc(int size)
25{
26 void *p;
27 p = malloc(size);
28 if(p == nil)
29 panic("error: no memory");
30 return p;
31}
32
33FILE*
34mustopen(const char *name, const char *mode)
35{
36 FILE *f;
37 if(f = fopen(name, mode), f == nil)
38 panic("couldn't open file: %s", name);
39 return f;
40}
41
42
43typedef struct Options Options;
44struct Options
45{
46 int singlethread;
47 int foreground;
48 int debug;
49 int nodefault_subtype;
50 char *device;
51 char *mountpoint;
52 int show_version;
53 int show_help;
54 int clone_fd;
55 unsigned int max_idle_threads;
56};
57static Options options;
58
59#define offsetof(type, field) ((long)&((type*)0)->field)
60#define OPTION(t, p) \
61 { t, offsetof(Options, p), 1 }
62
63static const struct fuse_opt myopts[] = {
64 OPTION("-h", show_help),
65 OPTION("--help", show_help),
66 OPTION("-V", show_version),
67 OPTION("--version", show_version),
68 OPTION("-d", debug),
69 OPTION("debug", debug),
70 OPTION("-d", foreground),
71 OPTION("debug", foreground),
72 FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP),
73 FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP),
74 OPTION("-f", foreground),
75 OPTION("-s", singlethread),
76 OPTION("clone_fd", clone_fd),
77 OPTION("max_idle_threads=%u", max_idle_threads),
78 FUSE_OPT_END
79};
80
81static int
82opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs)
83{
84 (void)data; (void)outargs;
85 switch(key){
86 case FUSE_OPT_KEY_NONOPT:
87 if(options.device == nil){
88 options.device = strdup(arg);
89 return 0;
90 }
91 if(options.mountpoint == nil){
92 options.mountpoint = strdup(arg);
93 return 0;
94 }
95 return -1;
96 default:
97 panic("invalid option");
98 }
99 return -1;
100}
101
102void
103usage(void)
104{
105 fprintf(stderr,
106 " usage: asdf [options] filesystem mountpoint\n"
107 " -h --help print help\n"
108 " -V --version print version\n"
109 " -d -o debug enable debug output (implies -f)\n"
110 " -f foreground operation\n"
111 " -s disable multi-threaded operation\n"
112 " -o clone_fd use separate fuse device fd for each thread\n"
113 " (may improve performance)\n"
114 " -o max_idle_threads the maximum number of idle worker threads\n"
115 " allowed (default: 10)\n");
116 exit(1);
117}
118
119
120
121
122
123void
124dirbuf_add(fuse_req_t req, Dirbuf *b, const char *name, fuse_ino_t ino)
125{
126 struct stat stbuf;
127 size_t oldsize = b->size;
128 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
129 b->p = (char *) realloc(b->p, b->size);
130 memset(&stbuf, 0, sizeof(stbuf));
131 stbuf.st_ino = ino;
132 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
133 b->size);
134}
135
136#define min(x, y) ((x) < (y) ? (x) : (y))
137
138int
139reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
140 off_t off, size_t maxsize)
141{
142 if(off < bufsize)
143 return fuse_reply_buf(req, buf + off,
144 min(bufsize - off, maxsize));
145 else
146 return fuse_reply_buf(req, NULL, 0);
147}
148
149static void
150vfs_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
151{
152 struct stat stbuf;
153 USED(fi);
154
155 memset(&stbuf, 0, sizeof(stbuf));
156 if(fs_stat(ino, &stbuf) == -1)
157 fuse_reply_err(req, ENOENT);
158 else
159 fuse_reply_attr(req, &stbuf, 1.0);
160}
161
162static void
163vfs_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, struct fuse_file_info *fi)
164{
165 int ret = 0;
166 USED(fi);
167
168 if(to_set & FUSE_SET_ATTR_ATIME)
169 ret = fs_atime(ino, attr->st_atime);
170 if(to_set & FUSE_SET_ATTR_MTIME)
171 ret = fs_mtime(ino, attr->st_mtime);
172 if(to_set & FUSE_SET_ATTR_GID)
173 ret = fs_gid(ino, attr->st_gid);
174 if(to_set & FUSE_SET_ATTR_UID)
175 ret = fs_uid(ino, attr->st_uid);
176
177 if(ret)
178 fuse_reply_err(req, ret);
179 else
180 vfs_getattr(req, ino, fi);
181}
182
183void
184vfs_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
185{
186 int e;
187
188 e = fs_open(ino, fi->flags);
189 if(e)
190 fuse_reply_err(req, e);
191 else
192 fuse_reply_open(req, fi);
193}
194
195void
196vfs_read(fuse_req_t req, fuse_ino_t ino, size_t size,
197 off_t off, struct fuse_file_info *fi)
198{
199 USED(fi);
200 char *buf;
201 int n;
202
203 buf = malloc(size);
204 n = fs_read(ino, buf, off, size);
205 reply_buf_limited(req, buf, n, off, size);
206 free(buf);
207}
208
209void
210vfs_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
211 size_t size, off_t off, struct fuse_file_info *fi)
212{
213 int n;
214 USED(fi);
215 n = fs_write(ino, (void*)buf, off, size);
216 fuse_reply_write(req, n);
217}
218
219void
220vfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
221 off_t off, struct fuse_file_info *fi)
222{
223 struct dirent *dirs, *dp;
224 Dirbuf b;
225
226 USED(fi);
227 dirs = fs_readdir(ino);
228 if(dirs == nil){
229 fuse_reply_err(req, ENOTDIR);
230 return;
231 }
232 memset(&b, 0, sizeof(b));
233 for(dp = dirs; dp->d_ino; dp++)
234 dirbuf_add(req, &b, dp->d_name, dp->d_ino);
235 free(dirs);
236 reply_buf_limited(req, b.p, b.size, off, size);
237 free(b.p);
238}
239
240/* returns static pointer! */
241struct dirent*
242lookup(uint ino, const char *name)
243{
244 struct dirent *dirs, *dp;
245 static struct dirent de;
246
247 dirs = fs_readdir(ino);
248 if(dirs == nil)
249 return nil;
250 for(dp = dirs; dp->d_ino; dp++)
251 if(strcmp(dp->d_name, name) == 0){
252 de = *dp;
253 free(dirs);
254 return &de;
255 }
256 free(dirs);
257 return nil;
258}
259
260void
261vfs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
262{
263 struct fuse_entry_param e;
264 struct dirent *de;
265
266 de = lookup(parent, name);
267 if(de == nil){
268 fuse_reply_err(req, ENOENT);
269 return;
270 }
271 memset(&e, 0, sizeof(e));
272 e.ino = de->d_ino;
273 e.attr_timeout = 1.0;
274 e.entry_timeout = 1.0;
275 fs_stat(e.ino, &e.attr);
276
277 /* increment ref count */
278 fs_iget(e.ino);
279 fuse_reply_entry(req, &e);
280}
281
282void
283vfs_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
284{
285 Inode *ip;
286
287 printf("forgetting %d\n", nlookup);
288 ip = fs_iget(ino);
289 fs_iput(ip);
290 while(nlookup--)
291 fs_iput(ip);
292 fuse_reply_none(req);
293}
294
295void
296vfs_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev)
297{
298 int ret;
299
300 ret = fs_mknod(parent, name, mode, rdev, nil);
301 if(ret)
302 fuse_reply_err(req, ret);
303 else
304 vfs_lookup(req, parent, name);
305}
306
307void
308vfs_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode)
309{
310 int ret;
311
312 ret = fs_mkdir(parent, name, mode);
313 if(ret)
314 fuse_reply_err(req, ret);
315 else
316 vfs_lookup(req, parent, name);
317}
318
319void
320vfs_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
321{
322 int ret;
323
324 ret = fs_unlink(parent, name);
325 fuse_reply_err(req, ret);
326}
327
328int
329isdirempty(struct dirent *de)
330{
331 for(; de->d_ino; de++)
332 if(strcmp(de->d_name, ".") != 0 &&
333 strcmp(de->d_name, "..") != 0)
334 return 0;
335 return 1;
336}
337
338void
339vfs_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
340{
341 struct dirent *dir, *de;
342 int ret;
343 int dino;
344
345 ret = 0;
346 de = lookup(parent, name);
347 /* this shouldn't happen */
348 if(de == nil)
349 return;
350 dino = de->d_ino;
351 dir = fs_readdir(dino);
352 assert(dir);
353 if(!isdirempty(dir)){
354 free(dir);
355 fuse_reply_err(req, ENOTEMPTY);
356 return;
357 }
358printf("dir to delete: %d %s\n", dino, name);
359 for(de = dir; de->d_ino; de++)
360 fs_unlink(dino, de->d_name);
361 free(dir);
362 fs_unlink(parent, name);
363
364 fuse_reply_err(req, ret);
365}
366
367void
368vfs_link(fuse_req_t req, fuse_ino_t ino,
369 fuse_ino_t newparent, const char *newname)
370{
371 int ret;
372
373 ret = fs_link(ino, newparent, newname);
374 if(ret)
375 fuse_reply_err(req, ret);
376 else
377 vfs_lookup(req, newparent, newname);
378}
379
380static struct fuse_lowlevel_ops hello_ll_oper = {
381 .lookup = vfs_lookup,
382 .getattr = vfs_getattr,
383 .setattr = vfs_setattr,
384 .readdir = vfs_readdir,
385 .open = vfs_open,
386 .read = vfs_read,
387 .write = vfs_write,
388 .mknod = vfs_mknod,
389 .mkdir = vfs_mkdir,
390 .unlink = vfs_unlink,
391 .rmdir = vfs_rmdir,
392 .link = vfs_link,
393 .forget = vfs_forget,
394 // TODO
395 // rename
396};
397
398
399void
400fsload(const char *filename)
401{
402 FILE *f = mustopen(filename, "rb");
403 fseek(f, 0, SEEK_END);
404 fslen = ftell(f);
405 fseek(f, 0, SEEK_SET);
406 fsdata = emalloc(fslen);
407 fread(fsdata, 1, fslen, f);
408 fclose(f);
409}
410
411int
412main(int argc, char *argv[])
413{
414 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
415 struct fuse_session *se;
416 char *m;
417 int ret = -1;
418
419 if(fuse_opt_parse(&args, &options, myopts, opt_proc) == -1 ||
420 options.device == nil || options.mountpoint == nil)
421 usage();
422
423 m = options.mountpoint;
424 options.mountpoint = realpath(m, nil);
425 if(options.mountpoint == nil)
426 panic("bad mount point %s", m);
427
428 printf("device: %s\n", options.device);
429 printf("mountpoint: %s\n", options.mountpoint);
430
431 if(options.show_help){
432 usage();
433 return 0;
434 }
435 if(options.show_version){
436 printf("FUSE library version %s\n", fuse_pkgversion());
437 fuse_lowlevel_version();
438 return 0;
439 }
440
441 fsload(options.device);
442 fs_init();
443 dcheck();
444
445 se = fuse_session_new(&args, &hello_ll_oper,
446 sizeof(hello_ll_oper), NULL);
447 if(se == NULL)
448 return 1;
449
450 if(fuse_set_signal_handlers(se) != 0)
451 goto err_out2;
452
453 if(fuse_session_mount(se, options.mountpoint) != 0)
454 goto err_out3;
455
456 fuse_daemonize(options.foreground);
457
458 /* Block until ctrl+c or fusermount -u */
459 if(options.singlethread)
460 ret = fuse_session_loop(se);
461 else
462 ret = fuse_session_loop_mt(se, options.clone_fd);
463
464 fuse_session_unmount(se);
465err_out3:
466 fuse_remove_signal_handlers(se);
467err_out2:
468 fuse_session_destroy(se);
469 free(options.mountpoint);
470 fuse_opt_free_args(&args);
471
472 return ret ? 1 : 0;
473}