src/dbfs/dirop.c
changeset 29 5de62ca9a5aa
parent 28 e944453ca924
child 30 d8fabd347a8e
equal deleted inserted replaced
28:e944453ca924 29:5de62ca9a5aa
     1 
     1 
     2 #include <stdlib.h>
     2 #include <stdlib.h>
     3 #include <assert.h>
     3 #include <assert.h>
     4 
     4 
     5 #include "common.h"
     5 #include "dbfs.h"
     6 #include "ops.h"
     6 #include "op_base.h"
     7 #include "../dirbuf.h"
     7 #include "../dirbuf.h"
       
     8 #include "../lib/log.h"
     8 
     9 
     9 /*
    10 /*
    10  * Directory related functionality like opendir, readdir, releasedir
    11  * Directory related functionality like opendir, readdir, releasedir
    11  */
    12  */
    12 
       
    13 struct dbfs_dirop {
    13 struct dbfs_dirop {
    14     struct fuse_file_info fi;
    14     struct dbfs_op base;
    15     struct fuse_req *req;
    15 
    16 
    16     // parent dir inodes
    17     struct evsql_trans *trans;
    17     uint32_t parent;
    18     
    18     
    19     // dir/parent dir inodes
       
    20     uint32_t ino, parent;
       
    21     
       
    22     // opendir has returned and releasedir hasn't been called yet
       
    23     int open;
       
    24 
       
    25     // for readdir
    19     // for readdir
    26     struct dirbuf dirbuf;
    20     struct dirbuf dirbuf;
    27 };
    21 };
    28 
    22 
    29 /*
    23 /*
    30  * Free the dirop, aborting any in-progress transaction.
    24  * Release the dirbuf.
    31  *
    25  */
    32  * The dirop must any oustanding request responded to first, must not be open, and must not have a transaction.
    26 static void _dbfs_dirop_free (struct dbfs_op *op_base) {
    33  *
    27     struct dbfs_dirop *dirop = (struct dbfs_dirop *) op_base;
    34  * The dirbuf will be released, and the dirop free'd.
    28 
    35  */
       
    36 static void _dbfs_dirop_free (struct dbfs_dirop *dirop) {
       
    37     assert(dirop);
       
    38     assert(!dirop->open);
       
    39     assert(!dirop->req);
       
    40     assert(!dirop->trans);
       
    41     
       
    42     // just release the dirbuf
    29     // just release the dirbuf
    43     dirbuf_release(&dirop->dirbuf);
    30     dirbuf_release(&dirop->dirbuf);
    44     
       
    45     // and then free the dirop
       
    46     free(dirop);
       
    47 }
       
    48 
       
    49 /*
       
    50  * This will handle backend failures during requests.
       
    51  *
       
    52  * 1) if we have a trans, abort it
       
    53  * 2) fail the req (mandatory)
       
    54  *
       
    55  * If the dirop is open, then we don't release it, but if it's not open, then the dirop will be free'd completely.
       
    56  *
       
    57  */
       
    58 static void _dbfs_dirop_fail (struct dbfs_dirop *dirop) {
       
    59     int err;
       
    60 
       
    61     assert(dirop->req);
       
    62     
       
    63     if (dirop->trans) {
       
    64         // abort the trans
       
    65         evsql_trans_abort(dirop->trans);
       
    66         
       
    67         dirop->trans = NULL;
       
    68     }
       
    69 
       
    70     // send an error reply
       
    71     if ((err = fuse_reply_err(dirop->req, err)))
       
    72         // XXX: handle these failures /somehow/, or requests will hang and interrupts might handle invalid dirops
       
    73         EFATAL(err, "dbfs.fail %p:%p dirop_fail: reply with fuse_reply_err", dirop, dirop->req);
       
    74    
       
    75     // drop the req
       
    76     dirop->req = NULL;
       
    77 
       
    78     // is it open?
       
    79     if (!dirop->open) {
       
    80         // no, we can free it now and then forget about the whole thing
       
    81         _dbfs_dirop_free(dirop);
       
    82 
       
    83     } else {
       
    84         // we need to wait for releasedir
       
    85 
       
    86     }
       
    87 }
    31 }
    88 
    32 
    89 /*
    33 /*
    90  * Handle the results for the initial attribute lookup for the dir itself during opendir ops.
    34  * Handle the results for the initial attribute lookup for the dir itself during opendir ops.
    91  */
    35  */
    92 static void dbfs_opendir_info_res (const struct evsql_result_info *res, void *arg) {
    36 static void dbfs_opendir_res (const struct evsql_result_info *res, void *arg) {
    93     struct dbfs_dirop *dirop = arg;
    37     struct dbfs_dirop *dirop = arg;
    94     int err;
    38     int err;
    95     
    39     
    96     assert(dirop->trans);
    40     assert(dirop->base.req);
    97     assert(dirop->req);
    41     assert(dirop->base.trans); // query callbacks don't get called if the trans fails
    98     assert(!dirop->open);
    42     assert(!dirop->base.open);
    99    
    43    
   100     // check the results
    44     // check the results
   101     if ((err = _dbfs_check_res(res, 1, 2)))
    45     if ((err = _dbfs_check_res(res, 1, 2)))
   102         SERROR(err = (err ==  1 ? ENOENT : EIO));
    46         SERROR(err = (err ==  1 ? ENOENT : EIO));
   103 
    47 
   112 
    56 
   113     // is it a dir?
    57     // is it a dir?
   114     if (_dbfs_mode(type) != S_IFDIR)
    58     if (_dbfs_mode(type) != S_IFDIR)
   115         EERROR(err = ENOTDIR, "wrong type: %s", type);
    59         EERROR(err = ENOTDIR, "wrong type: %s", type);
   116     
    60     
   117     INFO("[dbfs.opendir %p:%p] -> ino=%lu, parent=%lu, type=%s", dirop, dirop->req, (unsigned long int) dirop->ino, (unsigned long int) dirop->parent, type);
    61     INFO("[dbfs.opendir %p:%p] -> ino=%lu, parent=%lu, type=%s", dirop, dirop->base.req, (unsigned long int) dirop->base.ino, (unsigned long int) dirop->parent, type);
   118     
    62     
   119     // send the openddir reply
    63     // open_fn done, do the open_reply
   120     if ((err = fuse_reply_open(dirop->req, &dirop->fi)))
    64     if ((err = dbfs_op_open_reply(&dirop->base)))
   121         EERROR(err, "fuse_reply_open");
    65         goto error;
   122     
       
   123     // req is done
       
   124     dirop->req = NULL;
       
   125 
       
   126     // dirop is now open
       
   127     dirop->open = 1;
       
   128 
    66 
   129     // success, fallthrough for evsql_result_free
    67     // success, fallthrough for evsql_result_free
   130     err = 0;
    68     err = 0;
   131 
    69 
   132 error:
    70 error:
   133     if (err)
    71     if (err)
   134         // fail it
    72         // fail it
   135         _dbfs_dirop_fail(dirop);
    73         dbfs_op_fail(&dirop->base, err);
   136     
    74     
   137     // free
    75     // free
   138     evsql_result_free(res);
    76     evsql_result_free(res);
   139 }
    77 }
   140 
    78 
   141 /*
    79 /*
   142  * The opendir transaction is ready for use. Query for the given dir's info
    80  * The opendir transaction is ready for use. Query for the given dir's info
   143  */
    81  */
   144 static void dbfs_dirop_ready (struct evsql_trans *trans, void *arg) {
    82 static void dbfs_dirop_open (struct dbfs_op *op_base) {
   145     struct dbfs_dirop *dirop = arg;
    83     struct dbfs_dirop *dirop = (struct dbfs_dirop *) op_base;
   146     struct dbfs *ctx = fuse_req_userdata(dirop->req);
    84     struct dbfs *ctx = fuse_req_userdata(dirop->base.req);
   147     int err;
    85     int err;
   148     
    86     
   149     // XXX: unless we abort queries
    87     assert(dirop->base.trans); 
   150     assert(trans == dirop->trans);
    88     assert(dirop->base.req);
   151     assert(dirop->req);
    89     assert(!dirop->base.open);
   152     assert(!dirop->open);
    90 
   153 
    91     INFO("[dbfs.opendir %p:%p] -> trans=%p", dirop, dirop->base.req, dirop->base.trans);
   154     INFO("[dbfs.opendir %p:%p] -> trans=%p", dirop, dirop->req, trans);
       
   155 
       
   156     // remember the transaction
       
   157     dirop->trans = trans;
       
   158     
    92     
   159     // first fetch info about the dir itself
    93     // first fetch info about the dir itself
   160     const char *sql =
    94     const char *sql =
   161         "SELECT"
    95         "SELECT"
   162         " file_tree.parent, inodes.type"
    96         " file_tree.parent, inodes.type"
   169         EVSQL_PARAMS_END
   103         EVSQL_PARAMS_END
   170     };
   104     };
   171 
   105 
   172     // build params
   106     // build params
   173     if (0
   107     if (0
   174         ||  evsql_param_uint32(&params, 0, dirop->ino)
   108         ||  evsql_param_uint32(&params, 0, dirop->base.ino)
   175     )
   109     )
   176         SERROR(err = EIO);
   110         SERROR(err = EIO);
   177         
   111         
   178     // query
   112     // query
   179     if (evsql_query_params(ctx->db, dirop->trans, sql, &params, dbfs_opendir_info_res, dirop) == NULL)
   113     if (evsql_query_params(ctx->db, dirop->base.trans, sql, &params, dbfs_opendir_res, dirop) == NULL)
   180         SERROR(err = EIO);
   114         SERROR(err = EIO);
   181 
   115 
   182     // ok, wait for the info results
   116     // ok, wait for the info results
   183     return;
   117     return;
   184 
   118 
   185 error:
   119 error:
   186     // fail it
   120     // fail it
   187     _dbfs_dirop_fail(dirop);
   121     dbfs_op_fail(&dirop->base, err);
   188 }
   122 }
   189 
   123 
   190 /*
   124 /*
   191  * The dirop trans was committed, i.e. releasedir has completed
   125  * Handle opendir(), this means starting a new op.
   192  */
       
   193 static void dbfs_dirop_done (struct evsql_trans *trans, void *arg) {
       
   194     struct dbfs_dirop *dirop = arg;
       
   195     int err;
       
   196     
       
   197     assert(dirop->trans);
       
   198     assert(dirop->req);
       
   199     assert(!dirop->open);   // should not be considered as open anymore at this point, as errors should release
       
   200 
       
   201     INFO("[dbfs.releasedir %p:%p] -> OK", dirop, dirop->req);
       
   202 
       
   203     // forget trans
       
   204     dirop->trans = NULL;
       
   205     
       
   206     // just reply
       
   207     if ((err = fuse_reply_err(dirop->req, 0)))
       
   208         // XXX: handle these failures /somehow/, or requests will hang and interrupts might handle invalid dirops
       
   209         EFATAL(err, "[dbfs.releasedir %p:%p] dirop_done: reply with fuse_reply_err", dirop, dirop->req);
       
   210     
       
   211     // req is done
       
   212     dirop->req = NULL;
       
   213 
       
   214     // then we can just free dirop
       
   215     _dbfs_dirop_free(dirop);
       
   216 }
       
   217 
       
   218 /*
       
   219  * The dirop trans has failed, somehow, at some point, some where.
       
   220  *
       
   221  * This might happend during the opendir evsql_trans, during a readdir evsql_query, during the releasedir
       
   222  * evsql_trans_commit, or at any point in between.
       
   223  *
       
   224  * 1) loose the transaction
       
   225  * 2) if dirop has a req, we handle failing it
       
   226  */
       
   227 static void dbfs_dirop_error (struct evsql_trans *trans, void *arg) {
       
   228     struct dbfs_dirop *dirop = arg;
       
   229 
       
   230     INFO("[dbfs:dirop %p:%p] evsql transaction error: %s", dirop, dirop->req, evsql_trans_error(trans));
       
   231     
       
   232     // deassociate the trans
       
   233     dirop->trans = NULL;
       
   234     
       
   235     // if we were answering a req, error it out, and if the dirop isn't open, release it
       
   236     // if we didn't have a req outstanding, the dirop must be open, so we wouldn't free it in any case, and must wait
       
   237     // for the next readdir/releasedir to detect this and return an error reply
       
   238     if (dirop->req)
       
   239         _dbfs_dirop_fail(dirop);
       
   240     else
       
   241         assert(dirop->open);
       
   242 }
       
   243 
       
   244 /*
       
   245  * Handle opendir(), this means starting a new transaction, dbfs_dirop_ready/error will continue on from there.
       
   246  *
       
   247  * The contents of fi will be copied into the dirop, and will be used as the basis for the fuse_reply_open reply.
       
   248  */
   126  */
   249 void dbfs_opendir (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) {
   127 void dbfs_opendir (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) {
   250     struct dbfs *ctx = fuse_req_userdata(req);
   128     struct dbfs *ctx = fuse_req_userdata(req);
   251     struct dbfs_dirop *dirop = NULL;
   129     struct dbfs_dirop *dirop = NULL;
   252     int err;
   130     int err;
   253     
   131     
   254     // allocate it
   132     // allocate it
   255     if ((dirop = calloc(1, sizeof(*dirop))) == NULL && (err = EIO))
   133     if ((dirop = calloc(1, sizeof(*dirop))) == NULL && (err = EIO))
   256         ERROR("calloc");
   134         ERROR("calloc");
   257 
   135 
       
   136     // do the op_open
       
   137     if ((err = dbfs_op_open(ctx, &dirop->base, req, ino, fi, _dbfs_dirop_free, dbfs_dirop_open)))
       
   138         ERROR("dbfs_op_open");
       
   139 
   258     INFO("[dbfs.opendir %p:%p] ino=%lu, fi=%p", dirop, req, ino, fi);
   140     INFO("[dbfs.opendir %p:%p] ino=%lu, fi=%p", dirop, req, ino, fi);
   259     
       
   260     // store the dirop
       
   261     // copy *fi since it's on the stack
       
   262     dirop->fi = *fi;
       
   263     dirop->fi.fh = (uint64_t) dirop;
       
   264     dirop->req = req;
       
   265     dirop->ino = ino;
       
   266 
       
   267     // start a new transaction
       
   268     if ((dirop->trans = evsql_trans(ctx->db, EVSQL_TRANS_SERIALIZABLE, dbfs_dirop_error, dbfs_dirop_ready, dbfs_dirop_done, dirop)) == NULL)
       
   269         SERROR(err = EIO);
       
   270     
       
   271     // XXX: handle interrupts
       
   272     
   141     
   273     // wait
   142     // wait
   274     return;
   143     return;
   275 
   144 
   276 error:
   145 error:
   277     if (dirop) {
   146     if (dirop) {
   278         // we can fail normally
   147         // we can fail normally
   279         _dbfs_dirop_fail(dirop);
   148         dbfs_op_fail(&dirop->base, err);
   280 
   149 
   281     } else {
   150     } else {
   282         // must error out manually as we couldn't alloc the context
   151         // must error out manually as we couldn't alloc the context
   283         if ((err = fuse_reply_err(req, err)))
   152         if ((err = fuse_reply_err(req, err)))
   284             EWARNING(err, "fuse_reply_err");
   153             EWARNING(err, "fuse_reply_err");
   289  * Got the list of files for our readdir() request.
   158  * Got the list of files for our readdir() request.
   290  *
   159  *
   291  * Fill up the dirbuf, and then send the reply.
   160  * Fill up the dirbuf, and then send the reply.
   292  *
   161  *
   293  */
   162  */
   294 static void dbfs_readdir_files_res (const struct evsql_result_info *res, void *arg) {
   163 static void dbfs_readdir_res (const struct evsql_result_info *res, void *arg) {
   295     struct dbfs_dirop *dirop = arg;
   164     struct dbfs_dirop *dirop = arg;
   296     int err;
   165     int err;
   297     size_t row;
   166     size_t row;
   298     
   167     
   299     assert(dirop->req);
   168     assert(dirop->base.req);
   300     assert(dirop->trans);
   169     assert(dirop->base.trans); // query callbacks don't get called if the trans fails
   301     assert(dirop->open);
   170     assert(dirop->base.open);
   302     
   171     
   303     // check the results
   172     // check the results
   304     if ((err = _dbfs_check_res(res, 0, 4)) < 0)
   173     if ((err = _dbfs_check_res(res, 0, 4)) < 0)
   305         SERROR(err = EIO);
   174         SERROR(err = EIO);
   306         
   175         
   307     INFO("[dbfs.readdir %p:%p] -> files: res_rows=%zu", dirop, dirop->req, evsql_result_rows(res));
   176     INFO("[dbfs.readdir %p:%p] -> files: res_rows=%zu", dirop, dirop->base.req, evsql_result_rows(res));
   308         
   177         
   309     // iterate over the rows
   178     // iterate over the rows
   310     for (row = 0; row < evsql_result_rows(res); row++) {
   179     for (row = 0; row < evsql_result_rows(res); row++) {
   311         uint32_t off, ino;
   180         uint32_t off, ino;
   312         const char *name, *type;
   181         const char *name, *type;
   322         
   191         
   323         INFO("\t%zu: off=%lu+2, name=%s, ino=%lu, type=%s", row, (long unsigned int) off, name, (long unsigned int) ino, type);
   192         INFO("\t%zu: off=%lu+2, name=%s, ino=%lu, type=%s", row, (long unsigned int) off, name, (long unsigned int) ino, type);
   324 
   193 
   325         // add to the dirbuf
   194         // add to the dirbuf
   326         // offsets are just offset + 2
   195         // offsets are just offset + 2
   327         if ((err = dirbuf_add(dirop->req, &dirop->dirbuf, off + 2, off + 3, name, ino, _dbfs_mode(type))) < 0 && (err = EIO))
   196         if ((err = dirbuf_add(dirop->base.req, &dirop->dirbuf, off + 2, off + 3, name, ino, _dbfs_mode(type))) < 0 && (err = EIO))
   328             ERROR("failed to add dirent for inode=%lu", (long unsigned int) ino);
   197             ERROR("failed to add dirent for inode=%lu", (long unsigned int) ino);
   329         
   198         
   330         // stop if it's full
   199         // stop if it's full
   331         if (err > 0)
   200         if (err > 0)
   332             break;
   201             break;
   333     }
   202     }
   334 
   203 
   335     // send it
   204     // send it
   336     if ((err = dirbuf_done(dirop->req, &dirop->dirbuf)))
   205     if ((err = dirbuf_done(dirop->base.req, &dirop->dirbuf)))
   337         EERROR(err, "failed to send buf");
   206         EERROR(err, "failed to send buf");
   338 
   207     
   339     // req is done
   208     // handled the req
   340     dirop->req = NULL;
   209     if ((err = dbfs_op_req_done(&dirop->base)))
   341     
   210         goto error;
       
   211 
   342     // good, fallthrough
   212     // good, fallthrough
   343     err = 0;
   213     err = 0;
   344 
   214 
   345 error:
   215 error:
   346     if (err)
   216     if (err)
   347         _dbfs_dirop_fail(dirop);
   217         dbfs_op_fail(&dirop->base, err);
   348 
   218 
   349     // free
   219     // free
   350     evsql_result_free(res);
   220     evsql_result_free(res);
   351 }
   221 }
   352 
   222 
   353 /*
   223 /*
   354  * Handle a readdir request. This will execute a SQL query inside the transaction to get the files at the given offset,
   224  * Handle a readdir request. This will execute a SQL query inside the transaction to get the files at the given offset,
   355  * and _dbfs_readdir_res will handle the results.
   225  * and dbfs_readdir_res will handle the results.
   356  *
   226  *
   357  * If trans failed earlier, detect that and return an error.
   227  * If trans failed earlier, detect that and return an error.
   358  */
   228  */
   359 void dbfs_readdir (struct fuse_req *req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) {
   229 void dbfs_readdir (struct fuse_req *req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) {
   360     struct dbfs *ctx = fuse_req_userdata(req);
   230     struct dbfs *ctx = fuse_req_userdata(req);
   361     struct dbfs_dirop *dirop = (struct dbfs_dirop *) fi->fh;
   231     struct dbfs_dirop *dirop = (struct dbfs_dirop *) fi->fh;
   362     int err;
   232     int err;
   363     
   233 
   364     assert(dirop);
   234     // get the op
   365     assert(!dirop->req);
   235     if ((dirop = (struct dbfs_dirop *) dbfs_op_req(req, ino, fi)) == NULL)
   366     assert(dirop->open);
   236         SERROR(err = EIO);
   367     assert(dirop->ino == ino);
   237     
   368     
   238     INFO("[dbfs.readdir %p:%p] ino=%lu, size=%zu, off=%zu, fi=%p : trans=%p", dirop, req, ino, size, off, fi, dirop->base.trans);
   369     // store the new req
       
   370     dirop->req = req;
       
   371 
       
   372     // detect earlier failures
       
   373     if (!dirop->trans && (err = EIO))
       
   374         ERROR("dirop trans has failed");
       
   375     
       
   376     INFO("[dbfs.readdir %p:%p] ino=%lu, size=%zu, off=%zu, fi=%p : trans=%p", dirop, req, ino, size, off, fi, dirop->trans);
       
   377 
   239 
   378     // create the dirbuf
   240     // create the dirbuf
   379     if (dirbuf_init(&dirop->dirbuf, size, off))
   241     if (dirbuf_init(&dirop->dirbuf, size, off))
   380         SERROR(err = EIO);
   242         SERROR(err = EIO);
   381 
   243 
   382     // add . and ..
   244     // add . and ..
   383     // we set the next offset to 2, because all dirent offsets will be larger than that
   245     // we set the next offset to 2, because all dirent offsets will be larger than that
   384     // assume that these two should *always* fit
   246     // assume that these two should *always* fit
   385     if ((err = (0
   247     if ((err = (0
   386         ||  dirbuf_add(req, &dirop->dirbuf, 0, 1, ".",   dirop->ino,    S_IFDIR )
   248         ||  dirbuf_add(req, &dirop->dirbuf, 0, 1, ".",   dirop->base.ino,    S_IFDIR )
   387         ||  dirbuf_add(req, &dirop->dirbuf, 1, 2, "..",  
   249         ||  dirbuf_add(req, &dirop->dirbuf, 1, 2, "..",  
   388                         dirop->parent ? dirop->parent : dirop->ino,     S_IFDIR )
   250                         dirop->parent ? dirop->parent : dirop->base.ino,     S_IFDIR )
   389     )) && (err = EIO))
   251     )) && (err = EIO))
   390         ERROR("failed to add . and .. dirents");
   252         ERROR("failed to add . and .. dirents");
   391 
   253 
   392     // select all relevant file entries
   254     // select all relevant file entries
   393     const char *sql = 
   255     const char *sql = 
   409     if (off > 2)
   271     if (off > 2)
   410         off -= 2;
   272         off -= 2;
   411     
   273     
   412     // build params
   274     // build params
   413     if (0
   275     if (0
   414         ||  evsql_param_uint32(&params, 0, dirop->ino)
   276         ||  evsql_param_uint32(&params, 0, ino)
   415         ||  evsql_param_uint32(&params, 1, off)
   277         ||  evsql_param_uint32(&params, 1, off)
   416         ||  evsql_param_uint32(&params, 2, dirbuf_estimate(&dirop->dirbuf, 0))
   278         ||  evsql_param_uint32(&params, 2, dirbuf_estimate(&dirop->dirbuf, 0))
   417     )
   279     )
   418         SERROR(err = EIO);
   280         SERROR(err = EIO);
   419 
   281 
   420     // query
   282     // query
   421     if (evsql_query_params(ctx->db, dirop->trans, sql, &params, dbfs_readdir_files_res, dirop) == NULL)
   283     if (evsql_query_params(ctx->db, dirop->base.trans, sql, &params, dbfs_readdir_res, dirop) == NULL)
   422         SERROR(err = EIO);
   284         SERROR(err = EIO);
   423 
   285 
   424     // good, wait
   286     // good, wait
   425     return;
   287     return;
   426 
   288 
   427 error:
   289 error:
   428     _dbfs_dirop_fail(dirop);
   290     dbfs_op_fail(&dirop->base, err);
   429 }
   291 }
   430 
   292 
   431 /*
   293 /*
   432  * "For every [succesfull] opendir call there will be exactly one releasedir call."
   294  * "For every [succesfull] opendir call there will be exactly one releasedir call."
   433  *
   295  *
   434  * The dirop may be in a failed state.
   296  * The dirop may be in a failed state.
   435  */
   297  */
   436 void dbfs_releasedir (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) {
   298 void dbfs_releasedir (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) {
   437     struct dbfs *ctx = fuse_req_userdata(req);
   299     // just passthrough to dbfs_op
   438     struct dbfs_dirop *dirop = (struct dbfs_dirop *) fi->fh;
   300     dbfs_op_release(req, ino, fi);
   439     int err;
   301 }
   440 
   302 
   441     (void) ctx;
       
   442     
       
   443     assert(dirop);
       
   444     assert(!dirop->req);
       
   445     assert(dirop->ino == ino);
       
   446     
       
   447     // update to this req
       
   448     dirop->req = req;
       
   449 
       
   450     // fi is irrelevant, we don't touch the flags anyways
       
   451     (void) fi;
       
   452 
       
   453     // handle failed trans
       
   454     if (!dirop->trans)
       
   455         ERROR("trans has failed");
       
   456     
       
   457     // log
       
   458     INFO("[dbfs.releasedir %p:%p] ino=%lu, fi=%p : trans=%p", dirop, req, ino, fi, dirop->trans);
       
   459     
       
   460     // we must commit the transaction (although it was jut SELECTs, no changes).
       
   461     // Note that this might cause dbfs_dirop_error to be called, we can tell if that happaned by looking at dirop->req
       
   462     // or dirop->trans this means that we need to keep the dirop open when calling trans_commit, so that dirop_error
       
   463     // doesn't free it out from underneath us.
       
   464     if (evsql_trans_commit(dirop->trans))
       
   465         SERROR(err = EIO);
       
   466 
       
   467     // fall-through to cleanup
       
   468     err = 0;
       
   469 
       
   470 error:
       
   471     // the dirop is not open anymore and can be free'd:
       
   472     // a) if we already caught an error
       
   473     // b) if we get+send an error later on
       
   474     // c) if we get+send the done/no-error later on
       
   475     dirop->open = 0;
       
   476 
       
   477     // did the commit/pre-commit-checks fail?
       
   478     if (err) {
       
   479         // a) the trans failed earlier (readdir), so we have a req but no trans
       
   480         // b) the trans commit failed, dirop_error got called -> no req and no trans
       
   481         // c) the trans commit failed, dirop_error did not get called -> have req and trans
       
   482         // we either have a req (may or may not have trans), or we don't have a trans either
       
   483         // i.e. there is no situation where we don't have a req but do have a trans
       
   484 
       
   485         if (dirop->req)
       
   486             _dbfs_dirop_fail(dirop);
       
   487         else
       
   488             assert(!dirop->trans);
       
   489 
       
   490     } else {
       
   491         // shouldn't slip by, dirop_done should not get called directly. Once it does, it will handle both.
       
   492         assert(dirop->req);
       
   493         assert(dirop->trans);
       
   494     }
       
   495 }
       
   496