--- a/doc/fuse_db.sql Sun Oct 12 21:59:52 2008 +0300
+++ b/doc/fuse_db.sql Mon Oct 13 02:27:59 2008 +0300
@@ -5,3 +5,7 @@
INSERT INTO inodes VALUES (2, 'REG', 292, 0);
INSERT INTO file_tree (name, parent, inode) VALUES ('foo', 1, 2);
+ALTER TABLE file_tree ALTER COLUMN name DROP NOT NULL;
+ALTER TABLE file_tree ALTER COLUMN parent DROP NOT NULL;
+INSERT INTO file_tree (name, parent, inode) VALUES (NULL, NULL, 1);
+
--- 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
--- a/src/dirbuf.c Sun Oct 12 21:59:52 2008 +0300
+++ b/src/dirbuf.c Mon Oct 13 02:27:59 2008 +0300
@@ -5,11 +5,11 @@
#include "lib/log.h"
#include "lib/math.h"
-int dirbuf_init (struct dirbuf *buf, size_t req_size) {
+int dirbuf_init (struct dirbuf *buf, size_t req_size, off_t req_off) {
buf->len = req_size;
- buf->off = 0;
+ buf->req_off = req_off;
- INFO("\tdirbuf.init: req_size=%zu", req_size);
+ DEBUG("\tdirbuf.init: req_size=%zu", req_size);
// allocate the mem
if ((buf->buf = malloc(buf->len)) == NULL)
@@ -22,7 +22,7 @@
return -1;
}
-size_t difbuf_estimate (size_t req_size, size_t min_namelen) {
+size_t dirbuf_estimate (struct dirbuf *buf, size_t min_namelen) {
char namebuf[DIRBUF_NAME_MAX];
int i;
@@ -32,18 +32,18 @@
namebuf[i] = '\0';
- return req_size / (fuse_add_direntry(NULL, NULL, 0, namebuf, NULL, 0));
+ return buf->len / (fuse_add_direntry(NULL, NULL, 0, namebuf, NULL, 0));
}
-int dirbuf_add (fuse_req_t req, off_t req_off, struct dirbuf *buf, off_t ent_off, off_t next_off, const char *ent_name, fuse_ino_t ent_ino, mode_t ent_mode) {
+int dirbuf_add (fuse_req_t req, struct dirbuf *buf, off_t ent_off, off_t next_off, const char *ent_name, fuse_ino_t ent_ino, mode_t ent_mode) {
struct stat stbuf;
size_t ent_size;
- INFO("\tdirbuf.add: req_off=%zu, buf->len=%zu, buf->off=%zu, ent_off=%zu, next_off=%zu, ent_name=`%s`, ent_ino=%lu, ent_mode=%07o",
+ DEBUG("\tdirbuf.add: req_off=%zu, buf->len=%zu, buf->off=%zu, ent_off=%zu, next_off=%zu, ent_name=`%s`, ent_ino=%lu, ent_mode=%07o",
req_off, buf->len, buf->off, ent_off, next_off, ent_name, ent_ino, ent_mode);
// skip entries as needed
- if (ent_off < req_off)
+ if (ent_off < buf->req_off)
return 0;
// set ino
@@ -70,12 +70,16 @@
// send the reply, return the error later
err = fuse_reply_buf(req, buf->buf, buf->off);
- INFO("\tdirbuf.done: size=%zu/%zu, err=%d", buf->off, buf->len, err);
+ DEBUG("\tdirbuf.done: size=%zu/%zu, err=%d", buf->off, buf->len, err);
// free the dirbuf
- free(buf->buf);
+ dirbuf_release(buf);
// return the error code
return err;
}
+void dirbuf_release (struct dirbuf *buf) {
+ free(buf->buf); buf->buf = NULL;
+}
+
--- a/src/dirbuf.h Sun Oct 12 21:59:52 2008 +0300
+++ b/src/dirbuf.h Mon Oct 13 02:27:59 2008 +0300
@@ -13,27 +13,29 @@
struct dirbuf {
char *buf;
size_t len;
- size_t off;
+ off_t off, req_off;
};
// maximum length for a dirbuf name, including NUL byte
#define DIRBUF_NAME_MAX 256
/*
+ * Initialize a dirbuf for a request. The dirbuf will be filled with at most req_size bytes of dir entries.
+ *
+ * req_size - how many bytes of dirbuf data we want, at most
+ * req_off - the offset of the first dirent to include
+ */
+int dirbuf_init (struct dirbuf *buf, size_t req_size, off_t req_off);
+
+/*
* Estimate how many dir entries will, at most, fit into a difbuf of the given size, based on a minimum filename size.
*/
-size_t difbuf_estimate (size_t req_size, size_t min_namelen);
-
-/*
- * Initialize a dirbuf for a request. The dirbuf will be filled with at most req_size bytes of dir entries.
- */
-int dirbuf_init (struct dirbuf *buf, size_t req_size);
+size_t dirbuf_estimate (struct dirbuf *buf, size_t min_namelen);
/*
* Add an dir entry to the dirbuf. The dirbuf should not be full.
*
* Offsets are followed:
- * req_off - the offset of the first dirent to include
* ent_off - the offset of this dirent
* next_off - the offset of the next dirent
*
@@ -41,11 +43,18 @@
*
* Returns 0 if the ent was added or skipped, -1 on error, 1 if the dirbuf is full (no more ents should be added).
*/
-int dirbuf_add (fuse_req_t req, off_t req_off, struct dirbuf *buf, off_t ent_off, off_t next_off, const char *ent_name, fuse_ino_t ent_ino, mode_t ent_mode);
+int dirbuf_add (fuse_req_t req, struct dirbuf *buf, off_t ent_off, off_t next_off, const char *ent_name, fuse_ino_t ent_ino, mode_t ent_mode);
/*
* Attempt to send the readdir reply, free the buf, and return the error code from fuse_reply_buf
*/
int dirbuf_done (fuse_req_t req, struct dirbuf *buf);
+/*
+ * Release the dirop without sending any reply back.
+ *
+ * This is safe to be called multiple times.
+ */
+void dirbuf_release (struct dirbuf *buf);
+
#endif /* DIRBUF_H */
--- a/src/evfuse.c Sun Oct 12 21:59:52 2008 +0300
+++ b/src/evfuse.c Mon Oct 13 02:27:59 2008 +0300
@@ -26,6 +26,9 @@
char *recv_buf;
};
+// prototypes
+void evfuse_close (struct evfuse *ctx);
+
static void _evfuse_ev_read (evutil_socket_t fd, short what, void *arg) {
struct evfuse *ctx = arg;
struct fuse_chan *ch = ctx->chan;
@@ -58,7 +61,7 @@
return;
error:
- // fail
+ // close, but don't free
evfuse_close(ctx);
}
@@ -104,28 +107,47 @@
return ctx;
error:
- free(ctx);
+ evfuse_free(ctx);
return NULL;
}
void evfuse_close (struct evfuse *ctx) {
- // remove our event
- if (event_del(ctx->ev))
- PWARNING("event_del");
+ if (ctx->ev) {
+ // remove our event
+ if (event_del(ctx->ev))
+ PWARNING("event_del");
- // remove the chan
- fuse_session_remove_chan(ctx->chan);
-
- // destroy the session
- fuse_session_destroy(ctx->session);
+ ctx->ev = NULL;
+ }
- // unmount
- fuse_unmount(ctx->mountpoint, ctx->chan);
-
+ if (ctx->session) {
+ // remove the chan
+ fuse_session_remove_chan(ctx->chan);
+
+ // destroy the session
+ fuse_session_destroy(ctx->session);
+
+ ctx->session = NULL;
+ }
+
+ if (ctx->chan) {
+ // unmount
+ fuse_unmount(ctx->mountpoint, ctx->chan);
+
+ ctx->chan = NULL;
+ }
+
// free
- free(ctx->recv_buf);
- free(ctx->mountpoint);
- free(ctx);
+ free(ctx->recv_buf); ctx->recv_buf = NULL;
+ free(ctx->mountpoint); ctx->mountpoint = NULL;
}
+void evfuse_free (struct evfuse *ctx) {
+ if (ctx) {
+ evfuse_close(ctx);
+
+ free(ctx);
+ }
+}
+
--- a/src/evfuse.h Sun Oct 12 21:59:52 2008 +0300
+++ b/src/evfuse.h Mon Oct 13 02:27:59 2008 +0300
@@ -17,9 +17,11 @@
struct evfuse *evfuse_new (struct event_base *evbase, struct fuse_args *args, struct fuse_lowlevel_ops *llops, void *cb_data);
/*
- * Close a evfuse context.
+ * Close and free evfuse context.
+ *
+ * Safe to call after errors/llops.destroy
*/
-void evfuse_close (struct evfuse *ctx);
+void evfuse_free (struct evfuse *ctx);
#endif /* EVFUSE_H */
--- a/src/evsql.c Sun Oct 12 21:59:52 2008 +0300
+++ b/src/evsql.c Mon Oct 13 02:27:59 2008 +0300
@@ -69,6 +69,9 @@
* The command should already be taken care of (NULL).
*/
static void _evsql_query_free (struct evsql_query *query) {
+ if (!query)
+ return;
+
assert(query->command == NULL);
// free params if present
@@ -83,12 +86,18 @@
/*
* Execute the callback if res is given, and free the query.
+ *
+ * The query has been aborted, it will simply be freed
*/
static void _evsql_query_done (struct evsql_query *query, const struct evsql_result_info *res) {
- if (res)
- // call the callback
- query->cb_fn(res, query->cb_arg);
-
+ if (res) {
+ if (query->cb_fn)
+ // call the callback
+ query->cb_fn(res, query->cb_arg);
+ else
+ WARNING("supressing cb_fn because query was aborted");
+ }
+
// free
_evsql_query_free(query);
}
@@ -171,12 +180,15 @@
/*
* Fail a single query, this will trigger the callback and free it.
+ *
+ * NOTE: Only for *TRANSACTIONLESS* queries.
*/
static void _evsql_query_fail (struct evsql* evsql, struct evsql_query *query) {
struct evsql_result_info res; ZINIT(res);
// set up the result_info
res.evsql = evsql;
+ res.trans = NULL;
res.error = 1;
// finish off the query
@@ -187,16 +199,21 @@
* Fail a transaction, this will silently drop any query, trigger the error callback, two-way-deassociate/release the
* conn, and then free the trans.
*/
-static void _evsql_trans_fail (struct evsql_trans *trans, int silent) {
+static void _evsql_trans_fail (struct evsql_trans *trans) {
if (trans->query) {
// free the query silently
_evsql_query_free(trans->query); trans->query = NULL;
+
+ // also deassociate it from the conn!
+ trans->conn->query = NULL;
}
// tell the user
// XXX: trans is in a bad state during this call
- if (!silent)
+ if (trans->error_fn)
trans->error_fn(trans, trans->cb_arg);
+ else
+ WARNING("supressing error because error_fn was NULL");
// deassociate and release the conn
trans->conn->trans = NULL; _evsql_conn_release(trans->conn); trans->conn = NULL;
@@ -215,7 +232,7 @@
static void _evsql_conn_fail (struct evsql_conn *conn) {
if (conn->trans) {
// let transactions handle their connection failures
- _evsql_trans_fail(conn->trans, 0);
+ _evsql_trans_fail(conn->trans);
} else {
if (conn->query) {
@@ -302,13 +319,15 @@
return;
error:
- _evsql_trans_fail(res->trans, 0);
+ _evsql_trans_fail(res->trans);
}
/*
* The transaction's connection is ready, send the 'BEGIN' query.
+ *
+ * If anything fails, calls _evsql_trans_fail and returns nonzero, zero on success
*/
-static void _evsql_trans_conn_ready (struct evsql *evsql, struct evsql_trans *trans) {
+static int _evsql_trans_conn_ready (struct evsql *evsql, struct evsql_trans *trans) {
char trans_sql[EVSQL_QUERY_BEGIN_BUF];
const char *isolation_level;
int ret;
@@ -345,15 +364,17 @@
ERROR("trans_sql overflow: %d >= %d", ret, EVSQL_QUERY_BEGIN_BUF);
// execute the query
- if (evsql_query(evsql, trans, trans_sql, _evsql_trans_ready, NULL))
+ if (evsql_query(evsql, trans, trans_sql, _evsql_trans_ready, NULL) == NULL)
ERROR("evsql_query");
// success
- return;
+ return 0;
error:
// fail the transaction
- _evsql_trans_fail(trans, 0);
+ _evsql_trans_fail(trans);
+
+ return -1;
}
/*
@@ -364,7 +385,8 @@
if (conn->trans)
// notify the transaction
- _evsql_trans_conn_ready(conn->evsql, conn->trans);
+ // don't care about errors
+ (void) _evsql_trans_conn_ready(conn->evsql, conn->trans);
else
// pump any waiting transactionless queries
@@ -403,6 +425,7 @@
// set up the result_info
res.evsql = conn->evsql;
+ res.trans = conn->trans;
if (query->result.evpq == NULL) {
// if a query didn't return any results (bug?), warn and fail the query
@@ -429,6 +452,11 @@
// we can deassign the trans's query
conn->trans->query = NULL;
+ // was an abort?
+ if (!query->cb_fn)
+ // notify the user that the transaction query has been aborted
+ conn->trans->ready_fn(conn->trans, conn->trans->cb_arg);
+
// then hand the query to the user
_evsql_query_done(query, &res);
@@ -616,10 +644,15 @@
* request should be dropped.
*/
static int _evsql_conn_get (struct evsql *evsql, struct evsql_conn **conn_ptr, int may_queue) {
+ int have_nontrans = 0;
*conn_ptr = NULL;
// find a connection that isn't busy and is ready (unless the query queue is empty).
LIST_FOREACH(*conn_ptr, &evsql->conn_list, entry) {
+ // we can only have a query enqueue itself if there is a non-trans conn it can later use
+ if (!(*conn_ptr)->trans)
+ have_nontrans = 1;
+
// skip busy conns always
if (_evsql_conn_busy(*conn_ptr))
continue;
@@ -637,8 +670,8 @@
if (*conn_ptr)
return 0;
- // return NULL if may_queue and the conn list is not empty
- if (may_queue && !LIST_EMPTY(&evsql->conn_list))
+ // return NULL if may_queue and we have a non-trans conn that we can, at some point, use
+ if (may_queue && have_nontrans)
return 0;
// we need to open a new connection
@@ -660,7 +693,6 @@
// store
trans->evsql = evsql;
- trans->error_fn = error_fn;
trans->ready_fn = ready_fn;
trans->done_fn = done_fn;
trans->cb_arg = cb_arg;
@@ -675,13 +707,19 @@
// is it already ready?
if (_evsql_conn_ready(trans->conn) > 0) {
- // call _evsql_trans_conn_ready directly
- _evsql_trans_conn_ready(evsql, trans);
+ // call _evsql_trans_conn_ready directly, it will handle cleanup (silently, !error_fn)
+ if (_evsql_trans_conn_ready(evsql, trans)) {
+ // return NULL directly
+ return NULL;
+ }
} else {
// otherwise, wait for the conn to be ready
}
+
+ // and let it pass errors to the user
+ trans->error_fn = error_fn;
// ok
return trans;
@@ -734,8 +772,8 @@
// execute directly
if (_evsql_query_exec(trans->conn, query, command)) {
- // ack, fail the connection
- _evsql_conn_fail(trans->conn);
+ // ack, fail the transaction
+ _evsql_trans_fail(trans);
// caller frees query
goto error;
@@ -863,6 +901,18 @@
return NULL;
}
+void evsql_query_abort (struct evsql_trans *trans, struct evsql_query *query) {
+ assert(query);
+
+ if (trans) {
+ // must be the right query
+ assert(trans->query == query);
+ }
+
+ // just strip the callback and wait for it to complete as normal
+ query->cb_fn = NULL;
+}
+
void _evsql_trans_commit_res (const struct evsql_result_info *res, void *arg) {
(void) arg;
@@ -882,14 +932,27 @@
return;
error:
- _evsql_trans_fail(res->trans, 0);
+ _evsql_trans_fail(res->trans);
}
int evsql_trans_commit (struct evsql_trans *trans) {
static const char *sql = "COMMIT TRANSACTION";
+
+ if (trans->query)
+ ERROR("cannot COMMIT because transaction is still busy");
// query
- return (evsql_query(trans->evsql, trans, sql, _evsql_trans_commit_res, NULL) != NULL);
+ if (evsql_query(trans->evsql, trans, sql, _evsql_trans_commit_res, NULL) == NULL)
+ goto error;
+
+ // mark it as commited in case someone wants to abort it
+ trans->has_commit = 1;
+
+ // success
+ return 0;
+
+error:
+ return -1;
}
void _evsql_trans_rollback_res (const struct evsql_result_info *res, void *arg) {
@@ -908,14 +971,49 @@
return;
error:
- // but do it silently
- _evsql_trans_fail(res->trans, 1);
+ // fail the connection too, errors are supressed
+ _evsql_trans_fail(res->trans);
}
-int evsql_trans_abort (struct evsql_trans *trans) {
+/*
+ * Used as the ready_fn callback in case of abort, otherwise directly
+ */
+void _evsql_trans_rollback (struct evsql_trans *trans, void *unused) {
static const char *sql = "ROLLBACK TRANSACTION";
+ (void) unused;
+
// query
- return (evsql_query(trans->evsql, trans, sql, _evsql_trans_rollback_res, NULL) != NULL);
+ if (evsql_query(trans->evsql, trans, sql, _evsql_trans_rollback_res, NULL) == NULL) {
+ // fail the transaction/connection
+ _evsql_trans_fail(trans);
+ }
+
}
+void evsql_trans_abort (struct evsql_trans *trans) {
+ // supress errors
+ trans->error_fn = NULL;
+
+ if (trans->has_commit) {
+ // abort after commit doesn't make sense
+ FATAL("transaction was already commited");
+ }
+
+ if (trans->query) {
+ // gah, some query is running
+ WARNING("aborting pending query");
+
+ // prepare to rollback once complete
+ trans->ready_fn = _evsql_trans_rollback;
+
+ // abort
+ evsql_query_abort(trans, trans->query);
+
+ } else {
+ // just rollback directly
+ _evsql_trans_rollback(trans, NULL);
+
+ }
+}
+
--- a/src/evsql.h Sun Oct 12 21:59:52 2008 +0300
+++ b/src/evsql.h Mon Oct 13 02:27:59 2008 +0300
@@ -175,15 +175,30 @@
struct evsql_query *evsql_query_params (struct evsql *evsql, struct evsql_trans *trans, const char *command, const struct evsql_query_params *params, evsql_query_cb query_fn, void *cb_arg);
/*
+ * Abort a query, the query callback will not be called, the query and any possible results will be discarded.
+ *
+ * This does not garuntee that the query will not execute, simply that you won't get the results.
+ *
+ * If the query is part of a transaction, then trans must be given, and the query must be the query that is currently
+ * executing on that trans. The transaction's ready_fn will be called once the query has been aborted.
+ */
+void evsql_query_abort (struct evsql_trans *trans, struct evsql_query *query);
+
+/*
* Commit a transaction, calling done_fn if it was succesfull (error_fn otherwise).
+ *
+ * trans must be idle, just like for evsql_query.
+ *
+ * You cannot abort a COMMIT, calling trans_abort on trans after a succesful trans_commit is a FATAL error.
*/
int evsql_trans_commit (struct evsql_trans *trans);
/*
- * Abort a transaction, rolling it back. No callbacks will be called, unless this function returns nonzero, in which
- * case error_fn might be called.
+ * Abort a transaction, rolling it back. No callbacks will be called.
+ *
+ * You cannot abort a COMMIT, calling trans_abort on trans after a succesful trans_commit is a FATAL error.
*/
-int evsql_trans_abort (struct evsql_trans *trans);
+void evsql_trans_abort (struct evsql_trans *trans);
/*
* Transaction-handling functions
--- a/src/evsql_internal.h Sun Oct 12 21:59:52 2008 +0300
+++ b/src/evsql_internal.h Mon Oct 13 02:27:59 2008 +0300
@@ -81,6 +81,9 @@
// the transaction type
enum evsql_trans_type type;
+ // has evsql_trans_commit be called?
+ int has_commit : 1;
+
// our current query
struct evsql_query *query;
--- a/src/evsql_util.c Sun Oct 12 21:59:52 2008 +0300
+++ b/src/evsql_util.c Mon Oct 13 02:27:59 2008 +0300
@@ -106,6 +106,9 @@
if (evsql_result_binary(res, row, col, &data, sizeof(*uval), nullok))
goto error;
+
+ if (!data)
+ return 0;
sval = ntohs(*((int16_t *) data));
@@ -126,6 +129,9 @@
if (evsql_result_binary(res, row, col, &data, sizeof(*uval), nullok))
goto error;
+
+ if (!data)
+ return 0;
sval = ntohl(*(int32_t *) data);
@@ -146,6 +152,9 @@
if (evsql_result_binary(res, row, col, &data, sizeof(*uval), nullok))
goto error;
+
+ if (!data)
+ return 0;
sval = ntohq(*(int64_t *) data);
--- a/src/hello.c Sun Oct 12 21:59:52 2008 +0300
+++ b/src/hello.c Mon Oct 13 02:27:59 2008 +0300
@@ -96,12 +96,12 @@
}
// fill in the dirbuf
- if (dirbuf_init(&buf, size))
+ if (dirbuf_init(&buf, size, off))
ERROR("failed to init dirbuf");
- err = dirbuf_add(req, off, &buf, 0, 1, ".", 1, S_IFDIR )
- || dirbuf_add(req, off, &buf, 1, 2, "..", 1, S_IFDIR )
- || dirbuf_add(req, off, &buf, 2, 3, file_name, 2, S_IFREG );
+ err = dirbuf_add(req, &buf, 0, 1, ".", 1, S_IFDIR )
+ || dirbuf_add(req, &buf, 1, 2, "..", 1, S_IFDIR )
+ || dirbuf_add(req, &buf, 2, 3, file_name, 2, S_IFREG );
if (err < 0)
ERROR("failed to add dirents to buf");
@@ -234,7 +234,7 @@
error :
// cleanup
if (ctx.ev_fuse)
- evfuse_close(ctx.ev_fuse);
+ evfuse_free(ctx.ev_fuse);
if (ctx.signals)
signals_free(ctx.signals);
--- a/src/lib/log.c Sun Oct 12 21:59:52 2008 +0300
+++ b/src/lib/log.c Mon Oct 13 02:27:59 2008 +0300
@@ -9,6 +9,9 @@
static void _generic_err_vargs (int flags, const char *func, int err, const char *fmt, va_list va) {
FILE *stream = flags & LOG_DISPLAY_STDERR ? stderr : stdout;
+ if (flags & LOG_DISPLAY_FATAL)
+ fprintf(stream, "FATAL: ");
+
if (func)
fprintf(stream, "%s: ", func);
@@ -33,7 +36,7 @@
va_list va;
va_start(va, fmt);
- _generic_err_vargs(flags, func, err, fmt, va);
+ _generic_err_vargs(flags | LOG_DISPLAY_FATAL, func, err, fmt, va);
va_end(va);
exit(EXIT_FAILURE);
--- a/src/lib/log.h Sun Oct 12 21:59:52 2008 +0300
+++ b/src/lib/log.h Mon Oct 13 02:27:59 2008 +0300
@@ -12,6 +12,8 @@
LOG_DISPLAY_PERR = 0x02,
LOG_DISPLAY_NONL = 0x04,
+
+ LOG_DISPLAY_FATAL = 0x08,
};
@@ -53,6 +55,7 @@
#include "error.h"
#define WARNING(...) err_func(__func__, __VA_ARGS__)
+#define NWARNING(...) err_func_nonl(__func__, __VA_ARGS__)
#define PWARNING(...) perr_func(__func__, __VA_ARGS__)
#define EWARNING(err, ...) eerr_func(__func__, (err), __VA_ARGS__)
--- a/src/simple.c Sun Oct 12 21:59:52 2008 +0300
+++ b/src/simple.c Mon Oct 13 02:27:59 2008 +0300
@@ -157,13 +157,13 @@
EERROR(err = ENOTDIR, "bad mode");
// fill in the dirbuf
- if (dirbuf_init(&buf, size))
+ if (dirbuf_init(&buf, size, off))
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 );
+ err = dirbuf_add(req, &buf, 0, 1, ".", dir_node->inode, S_IFDIR )
+ || dirbuf_add(req, &buf, 1, 2, "..", dir_node->inode, S_IFDIR );
if (err != 0)
EERROR(err, "failed to add . and .. dirents");
@@ -175,7 +175,7 @@
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)
+ if ((err = dirbuf_add(req, &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
--- a/src/simple_hello.c Sun Oct 12 21:59:52 2008 +0300
+++ b/src/simple_hello.c Mon Oct 13 02:27:59 2008 +0300
@@ -59,7 +59,7 @@
error :
// cleanup
if (ctx.ev_fuse)
- evfuse_close(ctx.ev_fuse);
+ evfuse_free(ctx.ev_fuse);
/*
if (ctx.fs)