diff -r 40a3b13ffc9d -r 9dfc861273e5 src/dbfs/op_base.c --- a/src/dbfs/op_base.c Tue Nov 18 02:06:52 2008 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,299 +0,0 @@ -#include -#include - -#include "op_base.h" -#include "../lib/log.h" - -/* - * Free the op. - * - * The op must any oustanding request responded to first, must not be open, and must not have a transaction. - * - * The op will be free'd. - */ -static void dbfs_op_free (struct dbfs_op *op) { - assert(op); - assert(!op->open); - assert(!op->req); - assert(!op->trans); - - // free_fn - if (op->free_fn) - op->free_fn(op); - - // and then free the op - free(op); -} - -void dbfs_op_fail (struct dbfs_op *op, int err) { - assert(op->req); - - if (op->trans) { - // abort the trans - evsql_trans_abort(op->trans); - - op->trans = NULL; - } - - // send an error reply - if ((err = fuse_reply_err(op->req, err))) - // XXX: handle these failures /somehow/, or requests will hang and interrupts might handle invalid ops - EFATAL(err, "\tdbfs_op.fail %p:%p -> reply with fuse_reply_err", op, op->req); - - // drop the req - op->req = NULL; - - // is it open? - if (!op->open) { - // no, we can free it now and then forget about the whole thing - dbfs_op_free(op); - - } else { - // we need to wait for release - - } -} - -/* - * The op_open transaction is ready for use. - */ -static void dbfs_op_ready (struct evsql_trans *trans, void *arg) { - struct dbfs_op *op = arg; - - assert(trans == op->trans); - assert(op->req); - assert(!op->open); - - INFO("\tdbfs_op.ready %p:%p -> trans=%p", op, op->req, trans); - - // remember the transaction - op->trans = trans; - - // ready - op->open_fn(op); - - // good - return; -} - -/* - * The op trans was committed, i.e. release has completed - */ -static void dbfs_op_done (struct evsql_trans *trans, void *arg) { - struct dbfs_op *op = arg; - int err; - - assert(trans == op->trans); - assert(op->req); - assert(!op->open); // should not be considered as open anymore at this point, as errors should release - - INFO("\tdbfs_op.done %p:%p -> OK", op, op->req); - - // forget trans - op->trans = NULL; - - // just reply - if ((err = fuse_reply_err(op->req, 0))) - // XXX: handle these failures /somehow/, or requests will hang and interrupts might handle invalid ops - EFATAL(err, "dbfs_op.done %p:%p -> reply with fuse_reply_err", op, op->req); - - // req is done - op->req = NULL; - - // then we can just free op - dbfs_op_free(op); -} - -/* - * The op trans has failed, somehow, at some point, some where. - * - * This might happend during the open evsql_trans, during a read evsql_query, during the release - * evsql_trans_commit, or at any point in between. - * - * 1) loose the transaction - * 2) if op has a req, we handle failing it - */ -static void dbfs_op_error (struct evsql_trans *trans, void *arg) { - struct dbfs_op *op = arg; - - // unless we fail - assert(trans == op->trans); - - INFO("\tdbfs_op.error %p:%p -> evsql transaction error: %s", op, op->req, evsql_trans_error(trans)); - - // deassociate the trans - op->trans = NULL; - - // if we were answering a req, error it out, and if the op isn't open, free - // if we didn't have a req outstanding, the op must be open, so we wouldn't free it in any case, and must wait - // for the next read/release to detect this and return an error reply - if (op->req) - dbfs_op_fail(op, EIO); - else - assert(op->open); -} - -int dbfs_op_open (struct dbfs *ctx, struct dbfs_op *op, struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi, dbfs_op_free_cb free_fn, dbfs_op_open_cb open_fn) { - int err; - - assert(op && req && ino && fi); - assert(!(op->req || op->ino)); - - // initialize the op - op->req = req; - op->ino = ino; - // copy *fi since it's on the stack - op->fi = *fi; - op->fi.fh = (uint64_t) op; - op->free_fn = free_fn; - op->open_fn = open_fn; - - // start a new transaction - if ((op->trans = evsql_trans(ctx->db, EVSQL_TRANS_SERIALIZABLE, dbfs_op_error, dbfs_op_ready, dbfs_op_done, op)) == NULL) - SERROR(err = EIO); - - // XXX: handle interrupts - - // good - return 0; - -error: - // nothing of ours to cleanup - return err; -} - -int dbfs_op_open_reply (struct dbfs_op *op) { - int err; - - // detect earlier failures - if (!op->trans && (err = EIO)) - ERROR("op trans has failed"); - - // send the openddir reply - if ((err = fuse_reply_open(op->req, &op->fi))) - EERROR(err, "fuse_reply_open"); - - // req is done - op->req = NULL; - - // op is now open - op->open = 1; - - // good - return 0; - -error: - return err; -} - -struct dbfs_op *dbfs_op_req (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) { - struct dbfs_op *op = (struct dbfs_op *) fi->fh; - int err; - - // validate - assert(op); - assert(op->open); - assert(op->ino == ino); - - // detect concurrent requests - if (op->req) { - // must handle req ourself, shouldn't fail the other req - WARNING("op.%p: concurrent req: %p -> %p", op, op->req, req); - - // XXX: ignore error errors... - fuse_reply_err(req, EBUSY); - - return NULL; - - } else - // store the new req - op->req = req; - - // inodes change? - if (op->ino != ino) - XERROR(err = EBADF, "op.%p: wrong ino: %u -> %lu", op, op->ino, ino); - - // detect earlier failures - if (!op->trans && (err = EIO)) - ERROR("op trans has failed"); - - // good - return op; - -error: - dbfs_op_fail(op, err); - - return NULL; -} - -int dbfs_op_req_done (struct dbfs_op *op) { - // validate - assert(op); - assert(op->req); - assert(op->open); - - // unassign the req - op->req = NULL; - - // k - return 0; -} - -void dbfs_op_release (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) { - struct dbfs_op *op = (struct dbfs_op *) fi->fh; - int err; - - assert(op); - assert(!op->req); - assert(op->ino == ino); - - // update to this req - op->req = req; - - // fi is irrelevant, we don't touch the flags anyways - (void) fi; - - // handle failed trans - if (!op->trans && (err = EIO)) - ERROR("trans has failed"); - - // log - INFO("\tdbfs_op.release %p:%p : ino=%lu, fi=%p : trans=%p", op, req, ino, fi, op->trans); - - // we must commit the transaction. - // Note that this might cause dbfs_op_error to be called, we can tell if that happaned by looking at op->req - // or op->trans - this means that we need to keep the op open when calling trans_commit, so that op_error - // doesn't free it out from underneath us. - if (evsql_trans_commit(op->trans)) - SERROR(err = EIO); - - // fall-through to cleanup - err = 0; - -error: - // the op is not open anymore and can be free'd next, because we either: - // a) already caught an error - // b) we get+send an error later on - // c) we get+send the done/no-error later on - op->open = 0; - - // did the commit/pre-commit-checks fail? - if (err) { - // a) the trans failed earlier (read), so we have a req but no trans - // b) the trans commit failed, op_error got called -> no req and no trans - // c) the trans commit failed, op_error did not get called -> have req and trans - // we either have a req (may or may not have trans), or we don't have a trans either - // i.e. there is no situation where we don't have a req but do have a trans - - if (op->req) - dbfs_op_fail(op, err); - else - assert(!op->trans); - - } else { - // shouldn't slip by, op_done should not get called directly. Once it does, it will handle both. - assert(op->req); - assert(op->trans); - } -} -