--- a/src/dbfs.c Sun Oct 12 21:59:52 2008 +0300
+++ b/src/dbfs.c Mon Oct 13 02:27:59 2008 +0300
@@ -52,17 +52,19 @@
}
-void dbfs_destroy (void *userdata) {
- INFO("[dbfs.destroy] userdata=%p", userdata);
+void dbfs_destroy (void *arg) {
+ struct dbfs *ctx = arg;
+ INFO("[dbfs.destroy %p]", ctx);
-
+ // exit libevent
+ event_base_loopexit(ctx->ev_base, NULL);
}
/*
* Check the result set.
*
* Returns;
- * -1 if the query failed, the columns do not match, or there are too many/few rows
+ * -1 if the query failed, the columns do not match, or there are too many/few rows (unless rows was zero)
* 0 the results match
* 1 there were no results
*/
@@ -78,11 +80,11 @@
SERROR(err = 1);
// duplicate rows?
- if (evsql_result_rows(res) != rows)
- ERROR("multiple rows returned");
+ if (rows && evsql_result_rows(res) != rows)
+ ERROR("wrong number of rows returned");
// correct number of columns
- if (evsql_result_cols(res) != 5)
+ if (evsql_result_cols(res) != cols)
ERROR("wrong number of columns: %zu", evsql_result_cols(res));
// good
@@ -142,7 +144,7 @@
)
EERROR(err = EIO, "invalid db data");
- INFO("[dbfs.lookup] -> ion=%u", ino);
+ INFO("[dbfs.lookup] -> ino=%u", ino);
// stat attrs
if (_dbfs_stat_info(&e.attr, res, 0, 1))
@@ -249,7 +251,7 @@
"SELECT"
" inodes.type, inodes.mode, inodes.size, count(*)"
" FROM inodes"
- " WHERE inodes.ino = ‰1::int4"
+ " WHERE inodes.ino = $1::int4"
" GROUP BY inodes.type, inodes.mode, inodes.size";
static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) {
@@ -279,13 +281,19 @@
}
struct dbfs_dirop {
- struct fuse_file_info *fi;
+ struct fuse_file_info fi;
struct fuse_req *req;
struct evsql_trans *trans;
+ // dir/parent dir inodes
+ uint32_t ino, parent;
+
// opendir has returned and releasedir hasn't been called yet
int open;
+
+ // for readdir
+ struct dirbuf dirbuf;
};
/*
@@ -294,49 +302,142 @@
* req must be NULL.
*/
static void dbfs_dirop_free (struct dbfs_dirop *dirop) {
- assert(dirop->req == NULL);
+ assert(dirop);
+ assert(!dirop->open);
+ assert(!dirop->req);
- if (dirop->trans)
+ if (dirop->trans) {
+ WARNING("aborting transaction");
evsql_trans_abort(dirop->trans);
+ }
+
+ dirbuf_release(&dirop->dirbuf);
free(dirop);
}
+static void dbfs_opendir_info_res (const struct evsql_result_info *res, void *arg) {
+ struct dbfs_dirop *dirop = arg;
+ struct fuse_req *req = dirop->req; dirop->req = NULL;
+ int err;
+
+ assert(req != NULL);
+
+ // check the results
+ if ((err = _dbfs_check_res(res, 1, 2)))
+ SERROR(err = (err == 1 ? ENOENT : EIO));
+
+ const char *type;
+
+ // extract the data
+ if (0
+ || evsql_result_uint32(res, 0, 0, &dirop->parent, 1 ) // file_tree.parent
+ || evsql_result_string(res, 0, 1, &type, 0 ) // inodes.type
+ )
+ SERROR(err = EIO);
+
+ // is it a dir?
+ if (_dbfs_mode(type) != S_IFDIR)
+ EERROR(err = ENOTDIR, "wrong type: %s", type);
+
+ INFO("[dbfs.opendir %p:%p] -> ino=%lu, parent=%lu, type=%s", dirop, req, (unsigned long int) dirop->ino, (unsigned long int) dirop->parent, type);
+
+ // send the openddir reply
+ if ((err = fuse_reply_open(req, &dirop->fi)))
+ EERROR(err, "fuse_reply_open");
+
+ // dirop is now open
+ dirop->open = 1;
+
+ // ok, wait for the opendir call
+ return;
+
+error:
+ if (err) {
+ // abort the trans
+ evsql_trans_abort(dirop->trans);
+
+ dirop->trans = NULL;
+
+ if ((err = fuse_reply_err(req, err)))
+ EWARNING(err, "fuse_reply_err");
+ }
+
+ // free
+ evsql_result_free(res);
+}
+
/*
* 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;
+ struct fuse_req *req = dirop->req;
+ struct dbfs *ctx = fuse_req_userdata(req);
int err;
- INFO("[dbfs.openddir %p:%p] -> trans=%p", dirop, req, trans);
+ assert(req != NULL);
+
+ INFO("[dbfs.opendir %p:%p] -> trans=%p", dirop, req, trans);
// remember the transaction
dirop->trans = trans;
+
+ // first fetch info about the dir itself
+ const char *sql =
+ "SELECT"
+ " file_tree.parent, inodes.type"
+ " FROM file_tree LEFT OUTER JOIN inodes ON (file_tree.inode = inodes.ino)"
+ " WHERE file_tree.inode = $1::int4";
- // 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;
+ static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) {
+ EVSQL_PARAM ( UINT32 ),
- // ok, wait for the next fs req
+ EVSQL_PARAMS_END
+ };
+
+ // build params
+ if (0
+ || evsql_param_uint32(¶ms, 0, dirop->ino)
+ )
+ SERROR(err = EIO);
+
+ // query
+ if (evsql_query_params(ctx->db, dirop->trans, sql, ¶ms, dbfs_opendir_info_res, dirop) == NULL)
+ SERROR(err = EIO);
+
+ // ok, wait for the info results
return;
error:
+ // we handle the req
+ dirop->req = NULL;
+
+ // free the dirop
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;
+ struct fuse_req *req = dirop->req; dirop->req = NULL;
int err;
+ assert(req != NULL);
+ INFO("[dbfs.releasedir %p:%p] -> OK", dirop, req);
+
+ // forget trans
+ dirop->trans = NULL;
+
+ // just reply
+ if ((err = fuse_reply_err(req, 0)))
+ EWARNING(err, "fuse_reply_err");
+
+ // we can free dirop
+ dbfs_dirop_free(dirop);
}
static void dbfs_dirop_error (struct evsql_trans *trans, void *arg) {
@@ -371,14 +472,16 @@
ERROR("calloc");
INFO("[dbfs.opendir %p:%p] ino=%lu, fi=%p", dirop, req, ino, fi);
-
+
// store the dirop
- fi->fh = (uint64_t) dirop;
+ // copy *fi since it's on the stack
+ dirop->fi = *fi;
+ dirop->fi.fh = (uint64_t) dirop;
dirop->req = req;
- dirop->fi = fi;
+ dirop->ino = ino;
// start a new transaction
- if (evsql_trans(ctx->db, EVSQL_TRANS_SERIALIZABLE, dbfs_dirop_error, dbfs_dirop_ready, dbfs_dirop_done, dirop))
+ if ((dirop->trans = evsql_trans(ctx->db, EVSQL_TRANS_SERIALIZABLE, dbfs_dirop_error, dbfs_dirop_ready, dbfs_dirop_done, dirop)) == NULL)
SERROR(err = EIO);
// XXX: handle interrupts
@@ -396,23 +499,104 @@
EWARNING(err, "fuse_reply_err");
}
+static void dbfs_readdir_files_res (const struct evsql_result_info *res, void *arg) {
+ struct dbfs_dirop *dirop = arg;
+ struct fuse_req *req = dirop->req; dirop->req = NULL;
+ int err;
+ size_t row;
+
+ assert(req != NULL);
+
+ // check the results
+ if ((err = _dbfs_check_res(res, 0, 4)) < 0)
+ SERROR(err = EIO);
+
+ INFO("[dbfs.readdir %p:%p] -> files: res_rows=%zu", dirop, req, evsql_result_rows(res));
+
+ // iterate over the rows
+ for (row = 0; row < evsql_result_rows(res); row++) {
+ uint32_t off, ino;
+ const char *name, *type;
+
+ // extract the data
+ if (0
+ || evsql_result_uint32(res, row, 0, &off, 0 ) // file_tree.offset
+ || evsql_result_string(res, row, 1, &name, 0 ) // file_tree.name
+ || evsql_result_uint32(res, row, 2, &ino, 0 ) // inodes.ino
+ || evsql_result_string(res, row, 3, &type, 0 ) // inodes.type
+ )
+ SERROR(err = EIO);
+
+ INFO("\t%zu: off=%lu+2, name=%s, ino=%lu, type=%s", row, (long unsigned int) off, name, (long unsigned int) ino, type);
+
+ // add to the dirbuf
+ // offsets are just offset + 2
+ if ((err = dirbuf_add(req, &dirop->dirbuf, off + 2, off + 3, name, ino, _dbfs_mode(type))) < 0 && (err = EIO))
+ ERROR("failed to add dirent for inode=%lu", (long unsigned int) ino);
+
+ // stop if it's full
+ if (err > 0)
+ break;
+ }
+
+ // send it
+ if ((err = dirbuf_done(req, &dirop->dirbuf)))
+ EERROR(err, "failed to send buf");
+
+ // good, fallthrough
+ err = 0;
+
+error:
+ if (err) {
+ // abort the trans
+ evsql_trans_abort(dirop->trans);
+
+ dirop->trans = NULL;
+
+ // we handle the req
+ dirop->req = NULL;
+
+ if ((err = fuse_reply_err(req, err)))
+ EWARNING(err, "fuse_reply_err");
+ }
+
+ // free
+ evsql_result_free(res);
+}
+
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;
+ assert(!dirop->req);
+ assert(dirop->trans);
+ assert(dirop->ino == ino);
+
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);
+
+ // create the dirbuf
+ if (dirbuf_init(&dirop->dirbuf, size, off))
+ SERROR(err = EIO);
+
+ // add . and ..
+ // we set the next offset to 2, because all dirent offsets will be larger than that
+ if ((err = (0
+ || dirbuf_add(req, &dirop->dirbuf, 0, 1, ".", dirop->ino, S_IFDIR )
+ || dirbuf_add(req, &dirop->dirbuf, 1, 2, "..",
+ dirop->parent ? dirop->parent : dirop->ino, S_IFDIR )
+ )) && (err = EIO))
+ ERROR("failed to add . and .. dirents");
// select all relevant file entries
const char *sql =
"SELECT"
- " \"file_tree.offset\", file_tree.name, inodes.ino, inodes.type"
+ " 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"
+ " 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) {
@@ -423,7 +607,35 @@
EVSQL_PARAMS_END
};
- // XXX: incomplete
+ // adjust offset to take . and .. into account
+ if (off > 2)
+ off -= 2;
+
+ // build params
+ if (0
+ || evsql_param_uint32(¶ms, 0, dirop->ino)
+ || evsql_param_uint32(¶ms, 1, off)
+ || evsql_param_uint32(¶ms, 2, dirbuf_estimate(&dirop->dirbuf, 0))
+ )
+ SERROR(err = EIO);
+
+ // query
+ if (evsql_query_params(ctx->db, dirop->trans, sql, ¶ms, dbfs_readdir_files_res, dirop) == NULL)
+ SERROR(err = EIO);
+
+ // good, wait
+ return;
+
+error:
+ // we handle the req
+ dirop->req = NULL;
+
+ // abort the trans
+ evsql_trans_abort(dirop->trans); dirop->trans = NULL;
+
+ if ((err = fuse_reply_err(req, err)))
+ EWARNING(err, "fuse_reply_err");
+
}
static void dbfs_releasedir (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) {
@@ -432,36 +644,61 @@
int err;
(void) ctx;
+
+ assert(!dirop->req);
+ assert(dirop->ino == ino);
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
+ // copy *fi since it's on the stack
+ dirop->fi = *fi;
+ dirop->fi.fh = (uint64_t) dirop;
dirop->req = req;
- assert(dirop->fi == fi);
+
+ if (dirop->trans) {
+ // 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);
- // 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);
+ } else {
+ // trans failed earlier, so have releasedir just succeed
+ if ((err = fuse_reply_err(req, 0)))
+ EERROR(err, "fuse_reply_err");
- // not open anymore
+ // req is done
+ dirop->req = NULL;
+ }
+
+ // fall-through to cleanup
+ err = 0;
+
+error:
+ // the dirop is not open anymore and can be freed once done with
dirop->open = 0;
- // XXX: handle interrupts
-
- // wait
- return;
+ // if trans_commit triggered an error but didn't call dbfs_dirop_error, we need to take care of it
+ if (err && dirop->req) {
+ int err2;
-error:
- if (dirop->req) {
// we handle the req
dirop->req = NULL;
+ if ((err2 = fuse_reply_err(req, err)))
+ EWARNING(err2, "fuse_reply_err");
+ }
+
+ // same for trans, we need to abort it if trans_commit failed and fs_dirop_error didn't get called
+ if (err && dirop->trans) {
dbfs_dirop_free(dirop);
-
- if ((err = fuse_reply_err(req, err)))
- EWARNING(err, "fuse_reply_err");
+
+ } else
+ // alternatively, if the trans error'd itself away (now or earlier), we don't need to keep the dirop around
+ // anymore now that we've checkd its state
+ if (!dirop->trans) {
+ dbfs_dirop_free(dirop);
}
}
@@ -475,6 +712,7 @@
.getattr = dbfs_getattr,
.opendir = dbfs_opendir,
+ .readdir = dbfs_readdir,
.releasedir = dbfs_releasedir,
};
@@ -521,7 +759,7 @@
error :
// cleanup
if (ctx.ev_fuse)
- evfuse_close(ctx.ev_fuse);
+ evfuse_free(ctx.ev_fuse);
// XXX: ctx.db