--- 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.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__)