PageRenderTime 35ms CodeModel.GetById 11ms app.highlight 20ms RepoModel.GetById 1ms app.codeStats 0ms

/filesystems/unixfs/common/unixfs/unixfs.c

http://macfuse.googlecode.com/
C | 358 lines | 276 code | 72 blank | 10 comment | 59 complexity | 87c473f2d88e0a87d502b25ed838386c MD5 | raw file
  1/*
  2 * UnixFS
  3 *
  4 * A general-purpose file system layer for writing/reimplementing/porting
  5 * Unix file systems through MacFUSE.
  6
  7 * Copyright (c) 2008 Amit Singh. All Rights Reserved.
  8 * http://osxbook.com
  9 */
 10
 11#include "unixfs.h"
 12
 13#include <errno.h>
 14#include <stddef.h>
 15#include <stdio.h>
 16#include <stdlib.h>
 17#include <string.h>
 18#include <unistd.h>
 19#include <ctype.h>
 20#include <dlfcn.h>
 21
 22#include <fuse/fuse_opt.h>
 23#include <fuse/fuse_lowlevel.h>
 24
 25#define UNIXFS_META_TIMEOUT 60.0 /* timeout for nodes and their attributes */
 26
 27static struct unixfs* unixfs = (struct unixfs*)0;
 28
 29static void
 30unixfs_ll_statfs(fuse_req_t req, fuse_ino_t ino)
 31{
 32    struct statvfs sv;
 33    unixfs->ops->statvfs(&sv);
 34    fuse_reply_statfs(req, &sv);
 35}
 36
 37/* no unixfs_ll_init() since we do initialization before mounting */
 38
 39static void
 40unixfs_ll_destroy(void* data)
 41{
 42    unixfs->ops->fini(unixfs->filsys);
 43}
 44
 45static void
 46unixfs_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char* name)
 47{
 48    struct fuse_entry_param e;
 49    memset(&e, 0, sizeof(e));
 50
 51    int error = unixfs->ops->namei(parent, name, &(e.attr));
 52    if (error) {
 53        fuse_reply_err(req, error);
 54        return;
 55    }
 56
 57    e.ino = e.attr.st_ino;
 58    e.attr_timeout = e.entry_timeout = UNIXFS_META_TIMEOUT;
 59
 60    fuse_reply_entry(req, &e);
 61}
 62
 63static
 64void unixfs_ll_getattr(fuse_req_t req, fuse_ino_t ino,
 65                       struct fuse_file_info* fi)
 66{
 67    struct stat stbuf;
 68    int error = unixfs->ops->igetattr(ino, &stbuf);
 69    if (!error)
 70        fuse_reply_attr(req, &stbuf, UNIXFS_META_TIMEOUT);
 71    else
 72        fuse_reply_err(req, error);
 73}
 74
 75static void
 76unixfs_ll_readlink(fuse_req_t req, fuse_ino_t ino)
 77{
 78    int ret = ENOSYS;
 79
 80    char path[UNIXFS_MAXPATHLEN];
 81
 82    if ((ret = unixfs->ops->readlink(ino, path)) != 0)
 83        fuse_reply_err(req, ret);
 84
 85    fuse_reply_readlink(req, path);
 86}
 87
 88static void
 89unixfs_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
 90                  struct fuse_file_info* fi)
 91{
 92    (void)fi;
 93
 94    struct inode* dp = unixfs->ops->iget(ino);
 95    if (!dp) {
 96        fuse_reply_err(req, ENOENT);
 97        return;
 98    }
 99
100    struct stat stbuf;
101    unixfs->ops->istat(dp, &stbuf);
102
103    if (!S_ISDIR(stbuf.st_mode)) {
104        unixfs->ops->iput(dp);
105        fuse_reply_err(req, ENOTDIR);
106        return;
107    }
108
109    off_t offset = 0;
110    struct unixfs_direntry dent;
111
112    struct replybuf {
113        char*  p;
114        size_t size;
115    } b;
116
117    memset(&b, 0, sizeof(b));
118
119    struct unixfs_dirbuf dirbuf;
120
121    while (unixfs->ops->nextdirentry(dp, &dirbuf, &offset, &dent) == 0) {
122
123        if (dent.ino == 0)
124            continue;
125
126        if (unixfs->ops->igetattr(dent.ino, &stbuf) != 0)
127            continue;
128
129        size_t oldsize = b.size;
130        b.size += fuse_add_direntry(req, NULL, 0, dent.name, NULL, 0);
131        char* newp = (char *)realloc(b.p, b.size);
132        if (!newp) {
133            fprintf(stderr, "*** fatal error: cannot allocate memory\n");
134            abort();
135        }
136        b.p = newp;
137        fuse_add_direntry(req, b.p + oldsize, b.size - oldsize, dent.name,
138                          &stbuf, b.size);
139    }
140
141    unixfs->ops->iput(dp);
142
143    if (off < b.size)
144        fuse_reply_buf(req, b.p + off, min(b.size - off, size));
145    else
146        fuse_reply_buf(req, NULL, 0);
147
148    free(b.p);
149}
150
151static void
152unixfs_ll_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi)
153{
154    struct inode* ip = unixfs->ops->iget(ino);
155    if (!ip)
156        fuse_reply_err(req, ENOENT);
157
158    struct stat stbuf;
159    unixfs->ops->istat(ip, &stbuf);
160
161    if (!S_ISREG(stbuf.st_mode)) {
162        if (S_ISDIR(stbuf.st_mode))
163            fuse_reply_err(req, EISDIR);
164        else if (S_ISBLK(stbuf.st_mode) || S_ISCHR(stbuf.st_mode))
165            fuse_reply_err(req, ENXIO);
166        else
167            fuse_reply_err(req, EACCES);
168        unixfs->ops->iput(ip);
169    } else {
170        fi->fh = (uint64_t)(long)ip;
171        fuse_reply_open(req, fi);
172    }
173}
174
175static void
176unixfs_ll_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi)
177{
178    if (fi->fh)
179        unixfs->ops->iput((struct inode *)(long)(fi->fh));
180
181    fi->fh = 0;
182
183    fuse_reply_err(req, 0);
184}
185
186static void
187unixfs_ll_read(fuse_req_t req, fuse_ino_t ino, size_t count, off_t offset,
188               struct fuse_file_info* fi)
189{
190    struct inode* ip = (struct inode*)(long)(fi->fh);
191    if (!ip) {
192        fuse_reply_err(req, EBADF);
193        return;
194    }
195
196    struct stat stbuf;
197    unixfs->ops->istat(ip, &stbuf);
198    off_t size = stbuf.st_size;
199
200    if ((count == 0) || (offset > size)) {
201        fuse_reply_buf(req, NULL, 0);
202        return;
203    }
204
205    if ((offset + count) > size)
206        count = size - offset;
207
208    char *buf = calloc(count, 1);
209    if (!buf) {
210        fuse_reply_err(req, ENOMEM);
211        return;
212    }
213
214    int error = 0;
215    char* bp = buf;
216    size_t nbytes = 0;
217
218    do {
219        ssize_t ret = unixfs->ops->pbread(ip, bp, count, offset, &error);
220        if (ret < 0)
221            goto out; 
222        count -= ret;
223        offset += ret;
224        nbytes += ret;
225        bp += ret;
226    } while (!error && count);
227
228out:
229    fuse_reply_buf(req, buf, nbytes);
230
231    free(buf);
232}
233
234static struct fuse_lowlevel_ops unixfs_ll_oper = {
235    .statfs     = unixfs_ll_statfs,
236    .destroy    = unixfs_ll_destroy,
237    .lookup     = unixfs_ll_lookup,
238    .getattr    = unixfs_ll_getattr,
239    .readlink   = unixfs_ll_readlink,
240    .readdir    = unixfs_ll_readdir,
241    .open       = unixfs_ll_open,
242    .release    = unixfs_ll_release,
243    .read       = unixfs_ll_read,
244};
245
246struct options {
247    char* dmg;
248    int   force;
249    char* fsendian;
250    char* type;
251} options;
252
253#define UNIXFS_OPT_KEY(t, p, v) { t, offsetof(struct options, p), v }
254
255static struct fuse_opt unixfs_opts[] = {
256
257    UNIXFS_OPT_KEY("--dmg %s", dmg, 0),
258    UNIXFS_OPT_KEY("--force", force, 1),
259    UNIXFS_OPT_KEY("--fsendian %s", fsendian, 0),
260    UNIXFS_OPT_KEY("--type %s", type, 0),
261
262    FUSE_OPT_END
263};
264
265int
266main(int argc, char* argv[])
267{
268    struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
269
270    memset(&options, 0, sizeof(struct options));
271
272    if ((fuse_opt_parse(&args, &options, unixfs_opts, NULL) == -1) ||
273        !options.dmg) {
274        unixfs_usage();
275        return -1;
276    }
277
278    char* mountpoint;
279    int   multithreaded;
280    int   foregrounded;
281
282    if (fuse_parse_cmdline(&args, &mountpoint,
283                           &multithreaded, &foregrounded) == -1) {
284       unixfs_usage();
285       return -1;
286    }
287
288    if (!(unixfs = unixfs_preflight(options.dmg, &(options.type), &unixfs))) {
289        if (options.type)
290            fprintf(stderr, "invalid file system type %s\n", options.type);
291        else
292            fprintf(stderr, "missing file system type\n");
293        return -1;
294    }
295
296    if (options.force)
297        unixfs->flags |= UNIXFS_FORCE;
298
299    unixfs->fsname = options.type; /* XXX quick fix */
300
301    unixfs->fsendian = UNIXFS_FS_INVALID;
302
303    if (options.fsendian) {
304        if (strcasecmp(options.fsendian, "pdp") == 0) {
305            unixfs->fsendian = UNIXFS_FS_PDP;
306        } else if (strcasecmp(options.fsendian, "big") == 0) {
307            unixfs->fsendian = UNIXFS_FS_BIG;
308        } else if (strcasecmp(options.fsendian, "little") == 0) {
309            unixfs->fsendian = UNIXFS_FS_LITTLE;
310        } else {
311            fprintf(stderr, "invalid endian type %s\n", options.fsendian);
312            return -1;
313        }
314    }
315
316    if ((unixfs->filsys =
317        unixfs->ops->init(options.dmg, unixfs->flags, unixfs->fsendian,
318                          &unixfs->fsname, &unixfs->volname)) == NULL) {
319        fprintf(stderr, "failed to initialize file system\n");
320        return -1;
321    }
322
323    char extra_args[UNIXFS_ARGLEN] = { 0 };
324    unixfs_postflight(unixfs->fsname, unixfs->volname, extra_args);
325
326    fuse_opt_add_arg(&args, extra_args);
327
328    int err = -1;
329    struct fuse_chan *ch;
330
331    if ((ch = fuse_mount(mountpoint, &args)) != NULL) {
332
333        struct fuse_session* se;
334
335        se = fuse_lowlevel_new(&args, &unixfs_ll_oper, sizeof(unixfs_ll_oper),
336                               (void*)&unixfs);
337        if (se != NULL) {
338            if ((err = fuse_daemonize(foregrounded)) == -1)
339                goto bailout;
340            if (fuse_set_signal_handlers(se) != -1) {
341                fuse_session_add_chan(se, ch);
342                if (multithreaded)
343                    err = fuse_session_loop_mt(se);
344                else
345                    err = fuse_session_loop(se);
346                fuse_remove_signal_handlers(se);
347                fuse_session_remove_chan(ch);
348            }
349bailout:
350            fuse_session_destroy(se);
351        }
352        fuse_unmount(mountpoint, ch);
353    }
354
355    fuse_opt_free_args(&args);
356
357    return err ? 1 : 0;
358}