terom@6: #include terom@6: #include terom@6: #include terom@6: #include terom@6: terom@6: #include "simple.h" terom@8: #include "dirbuf.h" terom@6: #include "lib/log.h" terom@9: #include "lib/math.h" terom@6: #include "lib/misc.h" terom@6: terom@6: struct simple_fs { terom@6: const struct simple_node *inode_table; terom@6: terom@6: size_t inode_count; terom@6: }; terom@6: terom@6: /* terom@6: * Used for stat/entry timeouts... not sure how this should really be set. terom@6: */ terom@6: #define CACHE_TIMEOUT 1.0 terom@6: terom@6: static void _simple_stat (struct stat *stat, const struct simple_node *node) { terom@6: stat->st_ino = node->inode; terom@6: stat->st_mode = node->mode_type | node->mode_perm; terom@6: stat->st_nlink = 1; terom@6: stat->st_size = node->data ? strlen(node->data) : 0; terom@6: } terom@6: terom@8: /* terom@8: * Fetch the simple_node for the given inode. terom@8: * terom@8: * Returns NULL for invalid inodes. terom@8: */ terom@8: static const struct simple_node *_simple_get_ino (struct simple_fs *fs, fuse_ino_t ino) { terom@8: // make sure it's a valid inode terom@8: if (ino < 1 || ino > fs->inode_count) { terom@8: WARNING("invalid inode=%zu", ino); terom@8: return NULL; terom@8: } terom@8: terom@8: // return the node terom@8: return fs->inode_table + (ino - 1); terom@6: } terom@6: terom@6: static void simple_lookup (fuse_req_t req, fuse_ino_t parent, const char *name) { terom@6: struct simple_fs *fs = fuse_req_userdata(req); terom@6: const struct simple_node *node; terom@6: struct fuse_entry_param e; ZINIT(e); terom@6: int err; terom@6: terom@6: INFO("[simple.lookup %p] parent=%lu, name=`%s'", fs, parent, name); terom@6: terom@6: // find the matching node terom@6: for (node = fs->inode_table; node->inode > 0; node++) { terom@6: if (node->parent == parent && strcmp(node->name, name) == 0) terom@6: break; terom@6: terom@6: } terom@6: terom@6: // did we find it? terom@6: if (node->inode) { terom@6: // set up the entry terom@6: e.ino = node->inode; terom@6: e.generation = 0x01; terom@6: _simple_stat(&e.attr, node); terom@6: e.attr_timeout = CACHE_TIMEOUT; terom@6: e.entry_timeout = CACHE_TIMEOUT; terom@6: terom@6: // reply terom@6: if ((err = fuse_reply_entry(req, &e))) terom@6: EERROR(err, "fuse_reply_entry"); terom@6: terom@6: } else { terom@6: // not found terom@6: err = ENOENT; terom@6: goto error; terom@6: } terom@6: terom@6: // success terom@6: return; terom@6: terom@6: error: terom@6: if ((err = fuse_reply_err(req, err))) terom@6: EWARNING(err, "fuse_reply_err"); terom@6: } terom@6: terom@6: static void simple_getattr (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { terom@6: struct simple_fs *fs = fuse_req_userdata(req); terom@6: const struct simple_node *node; terom@6: struct stat stbuf; ZINIT(stbuf); terom@6: int err; terom@6: terom@6: INFO("[simple.getattr %p] ino=%lu", fs, ino); terom@6: terom@8: // look up the node terom@8: if ((node = _simple_get_ino(fs, ino)) == NULL) terom@8: EERROR(err = EINVAL, "bad inode"); terom@8: terom@6: // set up the stbuf terom@6: _simple_stat(&stbuf, node); terom@6: terom@6: // reply terom@6: if ((err = fuse_reply_attr(req, &stbuf, CACHE_TIMEOUT))) terom@6: EERROR(err, "fuse_reply_attr"); terom@6: terom@6: // suceccss terom@6: return; terom@6: terom@6: error: terom@6: if ((err = fuse_reply_err(req, err))) terom@6: EWARNING(err, "fuse_reply_err"); terom@6: } terom@6: terom@10: static void simple_readlink (fuse_req_t req, fuse_ino_t ino) { terom@10: struct simple_fs *fs = fuse_req_userdata(req); terom@10: const struct simple_node *node; terom@10: int err; terom@10: terom@10: INFO("[simple.readlink %p] ino=%lu", fs, ino); terom@10: terom@10: // look up the node terom@10: if ((node = _simple_get_ino(fs, ino)) == NULL) terom@10: EERROR(err = EINVAL, "bad inode"); terom@10: terom@10: // check that it's a symlink terom@10: if (node->mode_type != S_IFLNK) terom@10: EERROR(err = EINVAL, "bad mode"); terom@10: terom@10: // return the contents terom@10: if ((err = fuse_reply_readlink(req, node->data))) terom@10: EERROR(err, "fuse_reply_readlink"); terom@10: terom@10: // suceccss terom@10: return; terom@10: terom@10: error: terom@10: if ((err = fuse_reply_err(req, err))) terom@10: EWARNING(err, "fuse_reply_err"); terom@10: terom@10: } terom@10: terom@9: static void simple_readdir (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { terom@8: struct simple_fs *fs = fuse_req_userdata(req); terom@8: const struct simple_node *dir_node, *node; terom@8: struct dirbuf buf; terom@8: int err; terom@8: terom@8: INFO("[simple.readdir] ino=%lu, size=%zu, off=%zu, fi=%p", ino, size, off, fi); terom@8: terom@8: // look up the inode terom@8: if ((dir_node = _simple_get_ino(fs, ino)) == NULL) terom@8: EERROR(err = EINVAL, "bad inode"); terom@8: terom@8: // check that it's a dir terom@8: if (dir_node->mode_type != S_IFDIR) terom@8: EERROR(err = ENOTDIR, "bad mode"); terom@8: terom@8: // fill in the dirbuf terom@8: if (dirbuf_init(&buf, size)) terom@8: ERROR("failed to init dirbuf"); terom@8: terom@8: // add . and .. terom@8: // we set the next offset to 2, because all dirent offsets will be larger than that terom@8: err = dirbuf_add(req, off, &buf, 0, 1, ".", dir_node->inode, S_IFDIR ) terom@8: || dirbuf_add(req, off, &buf, 1, 2, "..", dir_node->inode, S_IFDIR ); terom@8: terom@8: if (err != 0) terom@8: EERROR(err, "failed to add . and .. dirents"); terom@8: terom@8: // look up all child nodes terom@8: for (node = fs->inode_table; node->inode; node++) { terom@8: // skip non-children terom@8: if (node->parent != dir_node->inode) terom@8: continue; terom@8: terom@8: // child node offsets are just inode + 2 terom@8: if ((err = dirbuf_add(req, off, &buf, node->inode + 2, node->inode + 3, node->name, node->inode, node->mode_type)) < 0) terom@8: EERROR(err, "failed to add dirent for inode=%lu", node->inode); terom@8: terom@8: // stop if it's full terom@8: if (err > 0) terom@8: break; terom@8: } terom@8: terom@8: // send it terom@8: if ((err = -dirbuf_done(req, &buf))) terom@8: EERROR(err, "failed to send buf"); terom@8: terom@8: // success terom@8: return; terom@8: terom@8: error: terom@8: if ((err = fuse_reply_err(req, err))) terom@8: EWARNING(err, "fuse_reply_err"); terom@8: } terom@6: terom@9: static void simple_read (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { terom@9: struct simple_fs *fs = fuse_req_userdata(req); terom@9: const struct simple_node *node; terom@9: int err ; terom@9: terom@9: // fi is unused terom@9: (void) fi; terom@9: terom@9: INFO("[simple.read] ino=%lu, size=%zu, off=%zu, fi=%p", ino, size, off, fi); terom@9: terom@9: // look up the inode terom@9: if ((node = _simple_get_ino(fs, ino)) == NULL) terom@9: EERROR(err = EINVAL, "bad inode"); terom@9: terom@9: // check that it's a dir terom@9: if (node->mode_type != S_IFREG) terom@9: EERROR(err = (node->mode_type == S_IFDIR ? EISDIR : EINVAL), "bad mode"); terom@9: terom@9: // seek past EOF? terom@9: if (off >= strlen(node->data)) { terom@9: // offset is out-of-file, so return EOF terom@9: if ((err = fuse_reply_buf(req, NULL, 0))) terom@9: EERROR(err, "fuse_reply_buf size=0"); terom@9: terom@9: } else { terom@9: // reply with the requested file data terom@9: if ((err = fuse_reply_buf(req, node->data + off, MIN(strlen(node->data) - off, size)))) terom@9: EERROR(err, "fuse_reply_buf buf=%p + %zu, size=MIN(%zu, %zu)", node->data, off, strlen(node->data) - off, size); terom@9: } terom@9: terom@9: // success terom@9: return; terom@9: terom@9: error: terom@9: if ((err = fuse_reply_err(req, err))) terom@9: EWARNING(err, "fuse_reply_err"); terom@9: } terom@9: terom@9: terom@6: /* terom@6: * Define our fuse_lowlevel_ops struct. terom@6: */ terom@6: static struct fuse_lowlevel_ops simple_ops = { terom@6: .lookup = simple_lookup, terom@6: terom@6: .getattr = simple_getattr, terom@8: terom@10: .readlink = simple_readlink, terom@10: terom@8: .readdir = simple_readdir, terom@9: terom@9: .read = simple_read, terom@6: }; terom@6: terom@6: struct fuse_lowlevel_ops *simple_init () { terom@6: return &simple_ops; terom@6: } terom@6: terom@6: struct simple_fs *simple_new (const struct simple_node *node_list) { terom@6: struct simple_fs *fs = NULL; terom@6: const struct simple_node *node; terom@6: terom@6: // generate terom@6: if ((fs = calloc(1, sizeof(*fs))) == NULL) terom@6: ERROR("calloc"); terom@6: terom@6: // remember node_list terom@6: fs->inode_count = 0; terom@6: fs->inode_table = node_list; terom@6: terom@6: // validate it terom@6: for (node = fs->inode_table; node->inode; node++) { terom@6: // update inode_count terom@6: fs->inode_count++; terom@6: terom@6: // check that parent is valid terom@6: assert(node->inode == fs->inode_count); terom@6: assert(node->parent < node->inode); terom@6: } terom@6: terom@6: // success terom@6: return fs; terom@6: terom@6: error: terom@6: return NULL; terom@6: }