src/simple.c
changeset 8 21bb5cdca4db
parent 6 d2036d7799fd
child 9 fbd239431dbe
--- a/src/simple.c	Fri Sep 26 20:39:51 2008 +0300
+++ b/src/simple.c	Fri Sep 26 21:13:35 2008 +0300
@@ -4,6 +4,7 @@
 #include <assert.h>
 
 #include "simple.h"
+#include "dirbuf.h"
 #include "lib/log.h"
 #include "lib/misc.h"
 
@@ -25,8 +26,20 @@
     stat->st_size = node->data ? strlen(node->data) : 0;
 }
 
-static int _simple_check_ino (struct simple_fs *fs, fuse_ino_t ino) {
-    return (ino < 1 || ino > fs->inode_count) ? EIO : 0;
+/*
+ * Fetch the simple_node for the given inode.
+ *
+ * Returns NULL for invalid inodes.
+ */
+static const struct simple_node *_simple_get_ino (struct simple_fs *fs, fuse_ino_t ino) {
+    // make sure it's a valid inode
+    if (ino < 1 || ino > fs->inode_count) {
+        WARNING("invalid inode=%zu", ino);
+        return NULL;
+    }
+    
+    // return the node
+    return fs->inode_table + (ino - 1);
 }
 
 static void simple_lookup (fuse_req_t req, fuse_ino_t parent, const char *name) {
@@ -79,13 +92,10 @@
 
     INFO("[simple.getattr %p] ino=%lu", fs, ino);
     
-    // make sure ino is valid
-    if ((err = _simple_check_ino(fs, ino)))
-        ERROR("invalid inode");
-
-    // look up the node
-    node = fs->inode_table + (ino - 1);
-
+    // look up the node 
+    if ((node = _simple_get_ino(fs, ino)) == NULL)
+        EERROR(err = EINVAL, "bad inode");
+    
     // set up the stbuf
     _simple_stat(&stbuf, node);
     
@@ -101,6 +111,60 @@
         EWARNING(err, "fuse_reply_err");
 }
 
+void simple_readdir (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) {
+    struct simple_fs *fs = fuse_req_userdata(req);
+    const struct simple_node *dir_node, *node;
+    struct dirbuf buf;
+    int err;
+
+    INFO("[simple.readdir] ino=%lu, size=%zu, off=%zu, fi=%p", ino, size, off, fi);
+    
+    // look up the inode
+    if ((dir_node = _simple_get_ino(fs, ino)) == NULL)
+        EERROR(err = EINVAL, "bad inode");
+    
+    // check that it's a dir
+    if (dir_node->mode_type != S_IFDIR)
+        EERROR(err = ENOTDIR, "bad mode");
+
+    // fill in the dirbuf
+    if (dirbuf_init(&buf, size))
+        ERROR("failed to init dirbuf");
+    
+    // add . and ..
+    // we set the next offset to 2, because all dirent offsets will be larger than that
+    err =   dirbuf_add(req, off, &buf, 0, 1, ".",   dir_node->inode,    S_IFDIR )
+        ||  dirbuf_add(req, off, &buf, 1, 2, "..",  dir_node->inode,    S_IFDIR );
+    
+    if (err != 0)
+        EERROR(err, "failed to add . and .. dirents");
+
+    // look up all child nodes
+    for (node = fs->inode_table; node->inode; node++) {
+        // skip non-children
+        if (node->parent != dir_node->inode)
+            continue;
+        
+        // child node offsets are just inode + 2
+        if ((err = dirbuf_add(req, off, &buf, node->inode + 2, node->inode + 3, node->name, node->inode, node->mode_type)) < 0)
+            EERROR(err, "failed to add dirent for inode=%lu", node->inode);
+        
+        // stop if it's full
+        if (err > 0)
+            break;
+    }
+
+    // send it
+    if ((err = -dirbuf_done(req, &buf)))
+        EERROR(err, "failed to send buf");
+
+    // success
+    return;
+
+error:
+    if ((err = fuse_reply_err(req, err)))
+        EWARNING(err, "fuse_reply_err");
+}
 
 /*
  * Define our fuse_lowlevel_ops struct.
@@ -109,6 +173,8 @@
     .lookup = simple_lookup,
 
     .getattr = simple_getattr,
+
+    .readdir = simple_readdir,
 };
 
 struct fuse_lowlevel_ops *simple_init () {