src/dbfs.c
changeset 26 61668c57f4bb
parent 25 99a41f48e29b
child 27 461be4cd34a3
--- a/src/dbfs.c	Sun Oct 12 20:10:47 2008 +0300
+++ b/src/dbfs.c	Sun Oct 12 21:59:52 2008 +0300
@@ -3,8 +3,10 @@
  * A simple PostgreSQL-based filesystem.
  */
 
+#include <stdlib.h>
 #include <string.h>
 #include <errno.h>
+#include <assert.h>
 
 #include <event2/event.h>
 
@@ -276,6 +278,193 @@
         EWARNING(err, "fuse_reply_err");
 }
 
+struct dbfs_dirop {
+    struct fuse_file_info *fi;
+    struct fuse_req *req;
+
+    struct evsql_trans *trans;
+    
+    // opendir has returned and releasedir hasn't been called yet
+    int open;
+};
+
+/*
+ * Free the dirop, aborting any in-progress transaction.
+ *
+ * req must be NULL.
+ */
+static void dbfs_dirop_free (struct dbfs_dirop *dirop) {
+    assert(dirop->req == NULL);
+
+    if (dirop->trans)
+        evsql_trans_abort(dirop->trans);
+
+    free(dirop);
+}
+
+/*
+ * The opendir transaction is ready
+ */
+static void dbfs_dirop_ready (struct evsql_trans *trans, void *arg) {
+    struct dbfs_dirop *dirop = arg;
+    struct fuse_req *req = dirop->req; dirop->req = NULL;
+    int err;
+
+    INFO("[dbfs.openddir %p:%p] -> trans=%p", dirop, req, trans);
+
+    // remember the transaction
+    dirop->trans = trans;
+
+    // send the openddir reply
+    if ((err = fuse_reply_open(dirop->req, dirop->fi)))
+        EERROR(err, "fuse_reply_open");
+    
+    // dirop is now open
+    dirop->open = 1;
+
+    // ok, wait for the next fs req
+    return;
+
+error:
+    dbfs_dirop_free(dirop);
+
+    if ((err = fuse_reply_err(req, err)))
+        EWARNING(err, "fuse_reply_err");
+}
+
+static void dbfs_dirop_done (struct evsql_trans *trans, void *arg) {
+    struct dbfs_dirop *dirop = arg;
+    int err;
+    
+
+}
+
+static void dbfs_dirop_error (struct evsql_trans *trans, void *arg) {
+    struct dbfs_dirop *dirop = arg;
+    int err;
+
+    INFO("[dbfs:dirop %p:%p] evsql transaction error: %s", dirop, dirop->req, evsql_trans_error(trans));
+    
+    // deassociate the trans
+    dirop->trans = NULL;
+    
+    // error out and pending req
+    if (dirop->req) {
+        if ((err = fuse_reply_err(dirop->req, EIO)))
+            EWARNING(err, "fuse_erply_err");
+
+        dirop->req = NULL;
+
+        // only free the dirop if it isn't open
+        if (!dirop->open)
+            dbfs_dirop_free(dirop);
+    }
+}
+
+static void dbfs_opendir (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) {
+    struct dbfs *ctx = fuse_req_userdata(req);
+    struct dbfs_dirop *dirop = NULL;
+    int err;
+    
+    // allocate it
+    if ((dirop = calloc(1, sizeof(*dirop))) == NULL && (err = EIO))
+        ERROR("calloc");
+
+    INFO("[dbfs.opendir %p:%p] ino=%lu, fi=%p", dirop, req, ino, fi);
+        
+    // store the dirop
+    fi->fh = (uint64_t) dirop;
+    dirop->req = req;
+    dirop->fi = fi;
+
+    // start a new transaction
+    if (evsql_trans(ctx->db, EVSQL_TRANS_SERIALIZABLE, dbfs_dirop_error, dbfs_dirop_ready, dbfs_dirop_done, dirop))
+        SERROR(err = EIO);
+    
+    // XXX: handle interrupts
+    
+    // wait
+    return;
+
+error:
+    // we handle the req
+    dirop->req = NULL;
+
+    dbfs_dirop_free(dirop);
+
+    if ((err = fuse_reply_err(req, err)))
+        EWARNING(err, "fuse_reply_err");
+}
+
+static void dbfs_readdir (struct fuse_req *req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) {
+    struct dbfs *ctx = fuse_req_userdata(req);
+    struct dbfs_dirop *dirop = (struct dbfs_dirop *) fi->fh;
+    int err;
+
+    INFO("[dbfs.readdir %p:%p] ino=%lu, size=%zu, off=%zu, fi=%p : trans=%p", dirop, req, ino, size, off, fi, dirop->trans);
+
+    // update dirop
+    dirop->req = req;
+    assert(dirop->fi == fi);
+
+    // select all relevant file entries
+    const char *sql = 
+        "SELECT"
+        " \"file_tree.offset\", file_tree.name, inodes.ino, inodes.type"
+        " FROM file_tree LEFT OUTER JOIN inodes ON (file_tree.inode = inodes.ino)"
+        " WHERE file_tree.parent = $1::int4 AND \"file_tree.offset\" >= $2::int4"
+        " LIMIT $3::int4";
+
+    static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) {
+        EVSQL_PARAM ( UINT32 ),
+        EVSQL_PARAM ( UINT32 ),
+        EVSQL_PARAM ( UINT32 ),
+
+        EVSQL_PARAMS_END
+    };
+
+    // XXX: incomplete
+}
+
+static void dbfs_releasedir (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) {
+    struct dbfs *ctx = fuse_req_userdata(req);
+    struct dbfs_dirop *dirop = (struct dbfs_dirop *) fi->fh;
+    int err;
+
+    (void) ctx;
+
+    INFO("[dbfs.releasedir %p:%p] ino=%lu, fi=%p : trans=%p", dirop, req, ino, fi, dirop->trans);
+
+    // update dirop. Must keep it open so that dbfs_dirop_error won't free it
+    dirop->req = req;
+    assert(dirop->fi == fi);
+
+    // we can commit the transaction, although we didn't make any changes
+    // if this fails the transaction, then dbfs_dirop_error will take care of sending the error, and dirop->req will be
+    // NULL
+    if (evsql_trans_commit(dirop->trans))
+        SERROR(err = EIO);
+
+    // not open anymore
+    dirop->open = 0;
+
+    // XXX: handle interrupts
+    
+    // wait
+    return;
+
+error:
+    if (dirop->req) {
+        // we handle the req
+        dirop->req = NULL;
+
+        dbfs_dirop_free(dirop);
+
+        if ((err = fuse_reply_err(req, err)))
+            EWARNING(err, "fuse_reply_err");
+    }
+}
+
 struct fuse_lowlevel_ops dbfs_llops = {
 
     .init           = dbfs_init,
@@ -284,6 +473,9 @@
     .lookup         = dbfs_lookup,
 
     .getattr        = dbfs_getattr,
+
+    .opendir        = dbfs_opendir,
+    .releasedir     = dbfs_releasedir,
 };
 
 void dbfs_sql_error (struct evsql *evsql, void *arg) {