more functionality for hello
authorTero Marttila <terom@fixme.fi>
Thu, 25 Sep 2008 21:28:08 +0300
changeset 2 11757d6b43a6
parent 1 b31db3248246
child 3 10b53719659c
more functionality for hello
src/hello.c
src/lib/common.c
src/lib/common.h
src/lib/math.h
--- a/src/hello.c	Thu Sep 25 15:03:09 2008 +0300
+++ b/src/hello.c	Thu Sep 25 21:28:08 2008 +0300
@@ -1,13 +1,22 @@
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
 #include <event2/event.h>
 #include <fuse/fuse_opt.h>
 
 #include "lib/common.h"
+#include "lib/math.h"
 #include "evfuse.h"
 
-struct hello {
+const char *file_name = "hello";
+const char *file_data = "Hello World\n";
+
+static struct hello {
     struct event_base *ev_base;
 
     struct evfuse *ev_fuse;
+
 } ctx;
 
 void hello_init (void *userdata, struct fuse_conn_info *conn) {
@@ -18,9 +27,205 @@
     INFO("[hello.destroy] userdata=%p", userdata);
 }
 
+void hello_lookup (fuse_req_t req, fuse_ino_t parent, const char *name) {
+    struct fuse_entry_param e;
+
+    INFO("[hello.lookup] (uid=%d, pid=%d) parent=%lu name=%s", fuse_req_ctx(req)->uid, fuse_req_ctx(req)->pid, parent, name);
+
+    // the world is flat
+    if (parent != 1 || strcmp(name, file_name)) {
+        fuse_reply_err(req, ENOENT);
+
+        return;
+    }
+    
+    // set up the entry
+    memset(&e, 0, sizeof(e));
+    e.ino = 2;
+    e.attr_timeout = 1.0;
+    e.entry_timeout = 1.0;
+    e.attr.st_mode = S_IFREG | 0444;
+    e.attr.st_nlink = 1;
+    e.attr.st_size = strlen(file_data);
+
+    // reply
+    fuse_reply_entry(req, &e);
+}
+
+void hello_getattr (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) {
+    struct stat stbuf;
+
+    INFO("[hello.getattr] (uid=%d, pid=%d) ino=%lu, fi=%p", fuse_req_ctx(req)->uid, fuse_req_ctx(req)->pid, ino, fi);
+
+    memset(&stbuf, 0, sizeof(stbuf));
+    
+    // the root dir, or the file?
+    if (ino == 1) {
+        stbuf.st_mode = S_IFDIR | 0555;
+        stbuf.st_nlink = 2;
+
+    } else if (ino == 2) {
+        stbuf.st_mode = S_IFREG | 0444;
+        stbuf.st_nlink = 1;
+        stbuf.st_size = strlen(file_data);
+
+    } else {
+        fuse_reply_err(req, ENOENT);
+        return;
+    }
+    
+    // reply
+    fuse_reply_attr(req, &stbuf, 1.0);
+}
+
+struct dirbuf {
+    char *buf;
+    size_t len;
+    size_t off;
+};
+
+#define DIRBUF_INITIAL_SIZE 1024
+
+static int dirbuf_init (struct dirbuf *buf) {
+    buf->len = DIRBUF_INITIAL_SIZE;
+    buf->off = 0;
+    
+    // allocate the mem
+    if ((buf->buf = malloc(buf->len)) == NULL)
+        ERROR("malloc");
+    
+    // ok
+    return 0;
+
+error:
+    return -1;
+}
+
+/*
+ * Ensure that `new` bytes fit into the buf. If they already fit, update offset and set *retry = 0. If they don't fit,
+ * grow buf and set *retry = 1.
+ *
+ * Returns 0 on success, -1 on failure (don't retry).
+ */
+static int dirbuf_update (struct dirbuf *buf, size_t new, int *retry) {
+    if (buf->off + new <= buf->len) {
+        INFO("\thello.dirbuf_update: update offset by %zu from %zu -> %zu", new, buf->off, buf->off + new);
+
+        // great, it fit, update offset and return
+        buf->off += new;
+
+        *retry = 0;
+
+    } else {
+        size_t old_len = buf->len;
+
+        // calc new size
+        do {
+            buf->len *= 2;
+
+        } while (buf->off + new > buf->len);
+
+        INFO("\thello.dirbuf_update: grow size for %zu from %zu -> %zu", new, old_len, buf->len);
+        
+        // realloc
+        if ((buf->buf = realloc(buf->buf, buf->len)) == NULL)
+            ERROR("realloc");
+
+        // done, just retry
+        *retry = 1;
+    }
+    
+    // success
+    return 0;
+
+error:
+    return -1;
+}
+
+static int dirbuf_add (fuse_req_t req, size_t req_size, off_t req_off, struct dirbuf *buf, off_t ent_off, off_t next_off, const char *ent_name, fuse_ino_t ent_ino) {
+    struct stat stbuf;
+    size_t ent_size;
+    int err, retry;
+
+    INFO("\thello.dirbuf_add: req_size=%zu, req_off=%zu, buf->len=%zu, buf->off=%zu, ent_off=%zu, next_off=%zu, ent_name=%s, ent_ino=%lu",
+        req_size, req_off, buf->len, buf->off, ent_off, next_off, ent_name, ent_ino);
+
+    // skip entries as needed
+    if (buf->len >= req_size || ent_off < req_off) 
+        return 0;
+
+    // set ino
+    stbuf.st_ino = ent_ino;
+
+    // add the dirent and update dirbuf until it fits
+    do {
+        ent_size = fuse_add_direntry(req, buf->buf + buf->off, buf->len - buf->off, ent_name, &stbuf, next_off);
+
+    } while (!(err = dirbuf_update(buf, ent_size, &retry)) && retry);
+
+    if (err)
+        return err;
+
+    // success
+    return 0;
+}
+
+static int dirbuf_done (fuse_req_t req, struct dirbuf *buf, size_t req_size) {
+    int err;
+    
+    // send the reply, return the error later
+    err = fuse_reply_buf(req, buf->buf, MIN(buf->off, req_size));
+
+    INFO("\thello.dirbuf_done: MIN(%zu, %zu)=%zu, err=%d", buf->off, req_size, MIN(buf->off, req_size), err);
+
+    // free the dirbuf
+    free(buf->buf);
+
+    // return the error code
+    return err;
+}
+
+void hello_readdir (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) {
+    int err = 0;
+    struct dirbuf buf;
+
+    INFO("[hello.readdir] ino=%lu, size=%zu, off=%zu, fi=%p", ino, size, off, fi);
+
+    // there exists only one dir
+    if (ino != 1) {
+        fuse_reply_err(req, ENOTDIR);
+        return;
+    }
+
+    // fill in the dirbuf
+    if (dirbuf_init(&buf))
+        ERROR("failed to init dirbuf");
+
+    if (    dirbuf_add(req, size, off, &buf, 0, 1,  ".",        1)
+        ||  dirbuf_add(req, size, off, &buf, 1, 2,  "..",       1)
+        ||  dirbuf_add(req, size, off, &buf, 2, 3,  file_name,  2)
+    ) ERROR("failed to add dirents to buf");
+    
+    // send it
+    if ((err = -dirbuf_done(req, &buf, size)))
+        EERROR(-err, "failed to send buf");
+
+    // success
+    return;
+
+error:
+    if ((err = fuse_reply_err(req, err ? err : EIO)))
+        EWARNING(err, "failed to send error reply");
+}
+
 struct fuse_lowlevel_ops hello_llops = {
     .init = &hello_init,
     .destroy = &hello_destroy,
+
+    .lookup = &hello_lookup,
+    .getattr = &hello_getattr,
+
+    .readdir = &hello_readdir,
 };
 
 
--- a/src/lib/common.c	Thu Sep 25 15:03:09 2008 +0300
+++ b/src/lib/common.c	Thu Sep 25 21:28:08 2008 +0300
@@ -15,7 +15,7 @@
     vfprintf(stream, fmt, va);
     
     if (perr)
-        fprintf(stream, ": %s\n", strerror(errno));
+        fprintf(stream, ": %s\n", strerror(perr > 0 ? errno : -perr));
 
     fprintf(stream, "\n");
 }
--- a/src/lib/common.h	Thu Sep 25 15:03:09 2008 +0300
+++ b/src/lib/common.h	Thu Sep 25 21:28:08 2008 +0300
@@ -32,14 +32,20 @@
 #define err_func_exit(func, ...)    _generic_err_exit(  1,  func,   0,  __VA_ARGS__ )
 #define perr_func(func, ...)        _generic_err(       1,  func,   1,  __VA_ARGS__ )
 #define perr_func_exit(func, ...)   _generic_err_exit(  1,  func,   1,  __VA_ARGS__ )
+#define eerr_func(func, err, ...)   _generic_err(       1,  func,   err,__VA_ARGS__ )
 
-// error(func + colon + msg, ...) + goto error
+/*
+ * error(func + colon + msg, ...) + goto error
+ * err = negative error code
+ */
 #define ERROR(...) do { err_func(__func__, __VA_ARGS__); goto error; } while (0)
 #define PERROR(...) do { perr_func(__func__, __VA_ARGS__); goto error; } while (0)
+#define EERROR(err, ...) do { eerr_func(__func__, (err), __VA_ARGS__); goto error; } while (0)
 #define FATAL(...) err_func_exit(__func__, __VA_ARGS__)
 #define PFATAL(...) perr_func_exit(__func__, __VA_ARGS__)
 #define WARNING(...) err_func(__func__, __VA_ARGS__)
 #define PWARNING(...) perr_func(__func__, __VA_ARGS__)
+#define EWARNING(err, ...) eerr_func(__func__, (err), __VA_ARGS__)
 
 #ifdef DEBUG_ENABLED
 #define DEBUG(...) err_func(__func__, __VA_ARGS__)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/math.h	Thu Sep 25 21:28:08 2008 +0300
@@ -0,0 +1,7 @@
+#ifndef LIB_MATH_H
+#define LIB_MATH_H
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+#endif /* LIB_MATH_H */