src/simple.c
changeset 56 9dfc861273e5
parent 42 40a3b13ffc9d
child 57 527d23bf6441
equal deleted inserted replaced
42:40a3b13ffc9d 56:9dfc861273e5
     1 #include <string.h>
       
     2 #include <errno.h>
       
     3 #include <stdlib.h>
       
     4 #include <assert.h>
       
     5 
       
     6 #include "simple.h"
       
     7 #include "dirbuf.h"
       
     8 #include "lib/log.h"
       
     9 #include "lib/math.h"
       
    10 #include "lib/misc.h"
       
    11 
       
    12 struct simple_fs {
       
    13     const struct simple_node *inode_table;
       
    14 
       
    15     size_t inode_count;
       
    16 };
       
    17 
       
    18 /*
       
    19  * Used for stat/entry timeouts... not sure how this should really be set.
       
    20  */
       
    21 #define CACHE_TIMEOUT 1.0
       
    22 
       
    23 static void _simple_stat (struct stat *stat, const struct simple_node *node) {
       
    24     stat->st_ino = node->inode;
       
    25     stat->st_mode = node->mode_type | node->mode_perm;
       
    26     stat->st_nlink = 1;
       
    27     stat->st_size = node->data ? strlen(node->data) : 0;
       
    28 }
       
    29 
       
    30 /*
       
    31  * Fetch the simple_node for the given inode.
       
    32  *
       
    33  * Returns NULL for invalid inodes.
       
    34  */
       
    35 static const struct simple_node *_simple_get_ino (struct simple_fs *fs, fuse_ino_t ino) {
       
    36     // make sure it's a valid inode
       
    37     if (ino < 1 || ino > fs->inode_count) {
       
    38         WARNING("invalid inode=%zu", ino);
       
    39         return NULL;
       
    40     }
       
    41     
       
    42     // return the node
       
    43     return fs->inode_table + (ino - 1);
       
    44 }
       
    45 
       
    46 static void simple_lookup (fuse_req_t req, fuse_ino_t parent, const char *name) {
       
    47     struct simple_fs *fs = fuse_req_userdata(req);
       
    48     const struct simple_node *node;
       
    49     struct fuse_entry_param e; ZINIT(e);
       
    50     int err;
       
    51     
       
    52     INFO("[simple.lookup %p] parent=%lu, name=`%s'", fs, parent, name);
       
    53 
       
    54     // find the matching node
       
    55     for (node = fs->inode_table; node->inode > 0; node++) {
       
    56         if (node->parent == parent && strcmp(node->name, name) == 0)
       
    57             break;
       
    58 
       
    59     }
       
    60 
       
    61     // did we find it?
       
    62     if (node->inode) {
       
    63         // set up the entry
       
    64         e.ino = node->inode;
       
    65         e.generation = 0x01;
       
    66         _simple_stat(&e.attr, node);
       
    67         e.attr_timeout = CACHE_TIMEOUT;
       
    68         e.entry_timeout = CACHE_TIMEOUT;
       
    69 
       
    70         // reply
       
    71         if ((err = fuse_reply_entry(req, &e)))
       
    72             EERROR(err, "fuse_reply_entry");
       
    73 
       
    74     } else {
       
    75         // not found
       
    76         err = ENOENT;
       
    77         goto error;
       
    78     }
       
    79 
       
    80     // success
       
    81     return;
       
    82 
       
    83 error:
       
    84     if ((err = fuse_reply_err(req, err)))
       
    85         EWARNING(err, "fuse_reply_err");
       
    86 }
       
    87 
       
    88 static void simple_getattr (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) {
       
    89     struct simple_fs *fs = fuse_req_userdata(req);
       
    90     const struct simple_node *node;
       
    91     struct stat stbuf; ZINIT(stbuf);
       
    92     int err;
       
    93 
       
    94     INFO("[simple.getattr %p] ino=%lu", fs, ino);
       
    95     
       
    96     // look up the node 
       
    97     if ((node = _simple_get_ino(fs, ino)) == NULL)
       
    98         EERROR(err = EINVAL, "bad inode");
       
    99     
       
   100     // set up the stbuf
       
   101     _simple_stat(&stbuf, node);
       
   102     
       
   103     // reply
       
   104     if ((err = fuse_reply_attr(req, &stbuf, CACHE_TIMEOUT)))
       
   105         EERROR(err, "fuse_reply_attr");
       
   106     
       
   107     // suceccss
       
   108     return;
       
   109 
       
   110 error:
       
   111     if ((err = fuse_reply_err(req, err)))
       
   112         EWARNING(err, "fuse_reply_err");
       
   113 }
       
   114 
       
   115 static void simple_readlink (fuse_req_t req, fuse_ino_t ino) {
       
   116     struct simple_fs *fs = fuse_req_userdata(req);
       
   117     const struct simple_node *node;
       
   118     int err;
       
   119 
       
   120     INFO("[simple.readlink %p] ino=%lu", fs, ino);
       
   121     
       
   122     // look up the node 
       
   123     if ((node = _simple_get_ino(fs, ino)) == NULL)
       
   124         EERROR(err = EINVAL, "bad inode");
       
   125 
       
   126     // check that it's a symlink
       
   127     if (node->mode_type != S_IFLNK)
       
   128         EERROR(err = EINVAL, "bad mode");
       
   129 
       
   130     // return the contents
       
   131     if ((err = fuse_reply_readlink(req, node->data)))
       
   132         EERROR(err, "fuse_reply_readlink");
       
   133 
       
   134     // suceccss
       
   135     return;
       
   136 
       
   137 error:
       
   138     if ((err = fuse_reply_err(req, err)))
       
   139         EWARNING(err, "fuse_reply_err");
       
   140 
       
   141 }
       
   142 
       
   143 static void simple_readdir (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) {
       
   144     struct simple_fs *fs = fuse_req_userdata(req);
       
   145     const struct simple_node *dir_node, *node;
       
   146     struct dirbuf buf;
       
   147     int err;
       
   148 
       
   149     INFO("[simple.readdir] ino=%lu, size=%zu, off=%zu, fi=%p", ino, size, off, fi);
       
   150     
       
   151     // look up the inode
       
   152     if ((dir_node = _simple_get_ino(fs, ino)) == NULL)
       
   153         EERROR(err = EINVAL, "bad inode");
       
   154     
       
   155     // check that it's a dir
       
   156     if (dir_node->mode_type != S_IFDIR)
       
   157         EERROR(err = ENOTDIR, "bad mode");
       
   158 
       
   159     // fill in the dirbuf
       
   160     if (dirbuf_init(&buf, size, off))
       
   161         ERROR("failed to init dirbuf");
       
   162     
       
   163     // add . and ..
       
   164     // we set the next offset to 2, because all dirent offsets will be larger than that
       
   165     err =   dirbuf_add(req, &buf, 0, 1, ".",   dir_node->inode,    S_IFDIR )
       
   166         ||  dirbuf_add(req, &buf, 1, 2, "..",  dir_node->inode,    S_IFDIR );
       
   167     
       
   168     if (err != 0)
       
   169         EERROR(err, "failed to add . and .. dirents");
       
   170 
       
   171     // look up all child nodes
       
   172     for (node = fs->inode_table; node->inode; node++) {
       
   173         // skip non-children
       
   174         if (node->parent != dir_node->inode)
       
   175             continue;
       
   176         
       
   177         // child node offsets are just inode + 2
       
   178         if ((err = dirbuf_add(req, &buf, node->inode + 2, node->inode + 3, node->name, node->inode, node->mode_type)) < 0)
       
   179             EERROR(err, "failed to add dirent for inode=%lu", node->inode);
       
   180         
       
   181         // stop if it's full
       
   182         if (err > 0)
       
   183             break;
       
   184     }
       
   185 
       
   186     // send it
       
   187     if ((err = -dirbuf_done(req, &buf)))
       
   188         EERROR(err, "failed to send buf");
       
   189 
       
   190     // success
       
   191     return;
       
   192 
       
   193 error:
       
   194     if ((err = fuse_reply_err(req, err)))
       
   195         EWARNING(err, "fuse_reply_err");
       
   196 }
       
   197 
       
   198 static void simple_read (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) {
       
   199     struct simple_fs *fs = fuse_req_userdata(req);
       
   200     const struct simple_node *node;
       
   201     int err ;
       
   202 
       
   203     // fi is unused
       
   204     (void) fi;
       
   205 
       
   206     INFO("[simple.read] ino=%lu, size=%zu, off=%zu, fi=%p", ino, size, off, fi);
       
   207     
       
   208     // look up the inode
       
   209     if ((node = _simple_get_ino(fs, ino)) == NULL)
       
   210         EERROR(err = EINVAL, "bad inode");
       
   211     
       
   212     // check that it's a dir
       
   213     if (node->mode_type != S_IFREG)
       
   214         EERROR(err = (node->mode_type == S_IFDIR ? EISDIR : EINVAL), "bad mode");
       
   215     
       
   216     // seek past EOF?
       
   217     if (off >= strlen(node->data)) {
       
   218         // offset is out-of-file, so return EOF
       
   219         if ((err = fuse_reply_buf(req, NULL, 0)))
       
   220             EERROR(err, "fuse_reply_buf size=0");
       
   221 
       
   222     } else {
       
   223         // reply with the requested file data
       
   224         if ((err = fuse_reply_buf(req, node->data + off, MIN(strlen(node->data) - off, size))))
       
   225             EERROR(err, "fuse_reply_buf buf=%p + %zu, size=MIN(%zu, %zu)", node->data, off, strlen(node->data) - off, size);
       
   226     }
       
   227 
       
   228     // success
       
   229     return;
       
   230 
       
   231 error:
       
   232     if ((err = fuse_reply_err(req, err)))
       
   233         EWARNING(err, "fuse_reply_err");
       
   234 }
       
   235 
       
   236 
       
   237 /*
       
   238  * Define our fuse_lowlevel_ops struct.
       
   239  */
       
   240 static struct fuse_lowlevel_ops simple_ops = {
       
   241     .lookup = simple_lookup,
       
   242 
       
   243     .getattr = simple_getattr,
       
   244 
       
   245     .readlink = simple_readlink,
       
   246 
       
   247     .readdir = simple_readdir,
       
   248 
       
   249     .read = simple_read,
       
   250 };
       
   251 
       
   252 struct fuse_lowlevel_ops *simple_init () {
       
   253     return &simple_ops;
       
   254 }
       
   255 
       
   256 struct simple_fs *simple_new (const struct simple_node *node_list) {
       
   257     struct simple_fs *fs = NULL;
       
   258     const struct simple_node *node;
       
   259     
       
   260     // generate
       
   261     if ((fs = calloc(1, sizeof(*fs))) == NULL)
       
   262         ERROR("calloc");
       
   263 
       
   264     // remember node_list
       
   265     fs->inode_count = 0;
       
   266     fs->inode_table = node_list;
       
   267     
       
   268     // validate it
       
   269     for (node = fs->inode_table; node->inode; node++) {
       
   270         // update inode_count
       
   271         fs->inode_count++;
       
   272 
       
   273         // check that parent is valid
       
   274         assert(node->inode == fs->inode_count);
       
   275         assert(node->parent < node->inode);
       
   276     }
       
   277     
       
   278     // success
       
   279     return fs;
       
   280 
       
   281 error:
       
   282     return NULL;
       
   283 }