src/dbfs/op_base.c
changeset 29 5de62ca9a5aa
child 40 03017f5f0087
equal deleted inserted replaced
28:e944453ca924 29:5de62ca9a5aa
       
     1 #include <stdlib.h>
       
     2 #include <assert.h>
       
     3 
       
     4 #include "op_base.h"
       
     5 #include "../lib/log.h"
       
     6 
       
     7 /*
       
     8  * Free the op.
       
     9  *
       
    10  * The op must any oustanding request responded to first, must not be open, and must not have a transaction.
       
    11  *
       
    12  * The op will be free'd.
       
    13  */
       
    14 static void dbfs_op_free (struct dbfs_op *op) {
       
    15     assert(op);
       
    16     assert(!op->open);
       
    17     assert(!op->req);
       
    18     assert(!op->trans);
       
    19 
       
    20     // free_fn
       
    21     if (op->free_fn)
       
    22         op->free_fn(op);
       
    23     
       
    24     // and then free the op
       
    25     free(op);
       
    26 }
       
    27 
       
    28 void dbfs_op_fail (struct dbfs_op *op, int err) {
       
    29     assert(op->req);
       
    30     
       
    31     if (op->trans) {
       
    32         // abort the trans
       
    33         evsql_trans_abort(op->trans);
       
    34         
       
    35         op->trans = NULL;
       
    36     }
       
    37 
       
    38     // send an error reply
       
    39     if ((err = fuse_reply_err(op->req, err)))
       
    40         // XXX: handle these failures /somehow/, or requests will hang and interrupts might handle invalid ops
       
    41         EFATAL(err, "\tdbfs_op.fail %p:%p -> reply with fuse_reply_err", op, op->req);
       
    42    
       
    43     // drop the req
       
    44     op->req = NULL;
       
    45 
       
    46     // is it open?
       
    47     if (!op->open) {
       
    48         // no, we can free it now and then forget about the whole thing
       
    49         dbfs_op_free(op);
       
    50 
       
    51     } else {
       
    52         // we need to wait for release
       
    53 
       
    54     }
       
    55 }
       
    56 
       
    57 /*
       
    58  * The op_open transaction is ready for use.
       
    59  */
       
    60 static void dbfs_op_ready (struct evsql_trans *trans, void *arg) {
       
    61     struct dbfs_op *op = arg;
       
    62     
       
    63     assert(trans == op->trans);
       
    64     assert(op->req);
       
    65     assert(!op->open);
       
    66 
       
    67     INFO("\tdbfs_op.ready %p:%p -> trans=%p", op, op->req, trans);
       
    68 
       
    69     // remember the transaction
       
    70     op->trans = trans;
       
    71 
       
    72     // ready
       
    73     op->open_fn(op);
       
    74     
       
    75     // good
       
    76     return;
       
    77 }
       
    78 
       
    79 /*
       
    80  * The op trans was committed, i.e. release has completed
       
    81  */
       
    82 static void dbfs_op_done (struct evsql_trans *trans, void *arg) {
       
    83     struct dbfs_op *op = arg;
       
    84     int err;
       
    85     
       
    86     assert(trans == op->trans);
       
    87     assert(op->req);
       
    88     assert(!op->open);   // should not be considered as open anymore at this point, as errors should release
       
    89 
       
    90     INFO("\tdbfs_op.done %p:%p -> OK", op, op->req);
       
    91 
       
    92     // forget trans
       
    93     op->trans = NULL;
       
    94     
       
    95     // just reply
       
    96     if ((err = fuse_reply_err(op->req, 0)))
       
    97         // XXX: handle these failures /somehow/, or requests will hang and interrupts might handle invalid ops
       
    98         EFATAL(err, "dbfs_op.done %p:%p -> reply with fuse_reply_err", op, op->req);
       
    99     
       
   100     // req is done
       
   101     op->req = NULL;
       
   102 
       
   103     // then we can just free op
       
   104     dbfs_op_free(op);
       
   105 }
       
   106 
       
   107 /*
       
   108  * The op trans has failed, somehow, at some point, some where.
       
   109  *
       
   110  * This might happend during the open evsql_trans, during a read evsql_query, during the release
       
   111  * evsql_trans_commit, or at any point in between.
       
   112  *
       
   113  * 1) loose the transaction
       
   114  * 2) if op has a req, we handle failing it
       
   115  */
       
   116 static void dbfs_op_error (struct evsql_trans *trans, void *arg) {
       
   117     struct dbfs_op *op = arg;
       
   118     
       
   119     // unless we fail 
       
   120     assert(trans == op->trans);
       
   121 
       
   122     INFO("\tdbfs_op.error %p:%p -> evsql transaction error: %s", op, op->req, evsql_trans_error(trans));
       
   123     
       
   124     // deassociate the trans
       
   125     op->trans = NULL;
       
   126     
       
   127     // if we were answering a req, error it out, and if the op isn't open, free
       
   128     // 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
       
   129     // for the next read/release to detect this and return an error reply
       
   130     if (op->req)
       
   131         dbfs_op_fail(op, EIO);
       
   132     else
       
   133         assert(op->open);
       
   134 }
       
   135 
       
   136 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) {
       
   137     int err;
       
   138 
       
   139     assert(op && req && ino && fi);
       
   140     assert(!(op->req || op->ino));
       
   141 
       
   142     // initialize the op
       
   143     op->req = req;
       
   144     op->ino = ino;
       
   145     // copy *fi since it's on the stack
       
   146     op->fi = *fi;
       
   147     op->fi.fh = (uint64_t) op;
       
   148     op->free_fn = free_fn;
       
   149     op->open_fn = open_fn;
       
   150 
       
   151     // start a new transaction
       
   152     if ((op->trans = evsql_trans(ctx->db, EVSQL_TRANS_SERIALIZABLE, dbfs_op_error, dbfs_op_ready, dbfs_op_done, op)) == NULL)
       
   153         SERROR(err = EIO);
       
   154     
       
   155     // XXX: handle interrupts
       
   156     
       
   157     // good
       
   158     return 0;
       
   159 
       
   160 error:
       
   161     // nothing of ours to cleanup
       
   162     return err;
       
   163 }
       
   164 
       
   165 int dbfs_op_open_reply (struct dbfs_op *op) {
       
   166     int err;
       
   167     
       
   168     // detect earlier failures
       
   169     if (!op->trans && (err = EIO))
       
   170         ERROR("op trans has failed");
       
   171 
       
   172     // send the openddir reply
       
   173     if ((err = fuse_reply_open(op->req, &op->fi)))
       
   174         EERROR(err, "fuse_reply_open");
       
   175     
       
   176     // req is done
       
   177     op->req = NULL;
       
   178 
       
   179     // op is now open
       
   180     op->open = 1;
       
   181  
       
   182     // good
       
   183     return 0;
       
   184 
       
   185 error:
       
   186     return err;
       
   187 }
       
   188 
       
   189 struct dbfs_op *dbfs_op_req (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) {
       
   190     struct dbfs_op *op = (struct dbfs_op *) fi->fh;
       
   191     int err;
       
   192     
       
   193     // validate
       
   194     assert(op);
       
   195     assert(!op->req);
       
   196     assert(op->open);
       
   197     assert(op->ino == ino);
       
   198 
       
   199     // store the new req
       
   200     op->req = req;
       
   201     
       
   202     // detect earlier failures
       
   203     if (!op->trans && (err = EIO))
       
   204         ERROR("op trans has failed");
       
   205 
       
   206     // good
       
   207     return op;
       
   208 
       
   209 error:
       
   210     dbfs_op_fail(op, err);
       
   211     
       
   212     return NULL;
       
   213 }
       
   214 
       
   215 int dbfs_op_req_done (struct dbfs_op *op) {
       
   216     // validate
       
   217     assert(op);
       
   218     assert(op->req);
       
   219     assert(op->open);
       
   220 
       
   221     // unassign the req
       
   222     op->req = NULL;
       
   223 
       
   224     // k
       
   225     return 0;
       
   226 }
       
   227 
       
   228 void dbfs_op_release (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) {
       
   229     struct dbfs_op *op = (struct dbfs_op *) fi->fh;
       
   230     int err;
       
   231     
       
   232     assert(op);
       
   233     assert(!op->req);
       
   234     assert(op->ino == ino);
       
   235     
       
   236     // update to this req
       
   237     op->req = req;
       
   238 
       
   239     // fi is irrelevant, we don't touch the flags anyways
       
   240     (void) fi;
       
   241 
       
   242     // handle failed trans
       
   243     if (!op->trans && (err = EIO))
       
   244         ERROR("trans has failed");
       
   245     
       
   246     // log
       
   247     INFO("\tdbfs_op.release %p:%p : ino=%lu, fi=%p : trans=%p", op, req, ino, fi, op->trans);
       
   248     
       
   249     // we must commit the transaction.
       
   250     // Note that this might cause dbfs_op_error to be called, we can tell if that happaned by looking at op->req
       
   251     // or op->trans - this means that we need to keep the op open when calling trans_commit, so that op_error
       
   252     // doesn't free it out from underneath us.
       
   253     if (evsql_trans_commit(op->trans))
       
   254         SERROR(err = EIO);
       
   255 
       
   256     // fall-through to cleanup
       
   257     err = 0;
       
   258 
       
   259 error:
       
   260     // the op is not open anymore and can be free'd next, because we either:
       
   261     // a) already caught an error
       
   262     // b) we get+send an error later on
       
   263     // c) we get+send the done/no-error later on
       
   264     op->open = 0;
       
   265 
       
   266     // did the commit/pre-commit-checks fail?
       
   267     if (err) {
       
   268         // a) the trans failed earlier (read), so we have a req but no trans
       
   269         // b) the trans commit failed, op_error got called -> no req and no trans
       
   270         // c) the trans commit failed, op_error did not get called -> have req and trans
       
   271         // we either have a req (may or may not have trans), or we don't have a trans either
       
   272         // i.e. there is no situation where we don't have a req but do have a trans
       
   273 
       
   274         if (op->req)
       
   275             dbfs_op_fail(op, err);
       
   276         else
       
   277             assert(!op->trans);
       
   278 
       
   279     } else {
       
   280         // shouldn't slip by, op_done should not get called directly. Once it does, it will handle both.
       
   281         assert(op->req);
       
   282         assert(op->trans);
       
   283     }
       
   284 }
       
   285