src/dbfs.c
changeset 28 e944453ca924
parent 27 461be4cd34a3
child 30 d8fabd347a8e
equal deleted inserted replaced
27:461be4cd34a3 28:e944453ca924
     8 #include <errno.h>
     8 #include <errno.h>
     9 #include <assert.h>
     9 #include <assert.h>
    10 
    10 
    11 #include <event2/event.h>
    11 #include <event2/event.h>
    12 
    12 
       
    13 #include "dbfs.h"
    13 #include "evfuse.h"
    14 #include "evfuse.h"
    14 #include "evsql.h"
    15 #include "evsql.h"
    15 #include "dirbuf.h"
       
    16 #include "lib/log.h"
    16 #include "lib/log.h"
    17 #include "lib/signals.h"
    17 #include "lib/signals.h"
    18 #include "lib/misc.h"
    18 #include "lib/misc.h"
    19 
    19 
    20 #define SERROR(val) do { (val); goto error; } while(0)
       
    21 
       
    22 struct dbfs {
       
    23     struct event_base *ev_base;
       
    24     struct signals *signals;
       
    25     
       
    26     const char *db_conninfo;
       
    27     struct evsql *db;
       
    28 
       
    29     struct evfuse *ev_fuse;
       
    30 };
       
    31 
       
    32 #define CONNINFO_DEFAULT "dbname=test"
    20 #define CONNINFO_DEFAULT "dbname=test"
    33 
    21 
    34 // XXX: not sure how this should work
       
    35 #define CACHE_TIMEOUT 1.0
       
    36 
       
    37 mode_t _dbfs_mode (const char *type) {
       
    38     if (!strcmp(type, "DIR"))
       
    39         return S_IFDIR;
       
    40 
       
    41     if (!strcmp(type, "REG"))
       
    42         return S_IFREG;
       
    43 
       
    44     else {
       
    45         WARNING("[dbfs] weird mode-type: %s", type);
       
    46         return 0;
       
    47     }
       
    48 }
       
    49 
       
    50 void dbfs_init (void *userdata, struct fuse_conn_info *conn) {
       
    51     INFO("[dbfs.init] userdata=%p, conn=%p", userdata, conn);
       
    52 
       
    53 }
       
    54 
       
    55 void dbfs_destroy (void *arg) {
       
    56     struct dbfs *ctx = arg;
       
    57     INFO("[dbfs.destroy %p]", ctx);
       
    58 
       
    59     // exit libevent
       
    60     event_base_loopexit(ctx->ev_base, NULL);
       
    61 }
       
    62 
       
    63 /*
       
    64  * Check the result set.
       
    65  *
       
    66  * Returns;
       
    67  *  -1  if the query failed, the columns do not match, or there are too many/few rows (unless rows was zero)
       
    68  *  0   the results match
       
    69  *  1   there were no results
       
    70  */
       
    71 int _dbfs_check_res (const struct evsql_result_info *res, size_t rows, size_t cols) {
       
    72     int err = 0;
       
    73 
       
    74     // check if it failed
       
    75     if (res->error)
       
    76         NERROR(evsql_result_error(res));
       
    77         
       
    78     // not found?
       
    79     if (evsql_result_rows(res) == 0)
       
    80         SERROR(err = 1);
       
    81 
       
    82     // duplicate rows?
       
    83     if (rows && evsql_result_rows(res) != rows)
       
    84         ERROR("wrong number of rows returned");
       
    85     
       
    86     // correct number of columns
       
    87     if (evsql_result_cols(res) != cols)
       
    88         ERROR("wrong number of columns: %zu", evsql_result_cols(res));
       
    89 
       
    90     // good
       
    91     return 0;
       
    92 
       
    93 error:
       
    94     if (!err)
       
    95         err = -1;
       
    96 
       
    97     return err;
       
    98 }
       
    99 
       
   100 int _dbfs_stat_info (struct stat *st, const struct evsql_result_info *res, size_t row, size_t col_offset) {
       
   101     int err = 0;
       
   102     
       
   103     uint16_t mode;
       
   104     uint64_t size, nlink;
       
   105     const char *type;
       
   106     
       
   107     // extract the data
       
   108     if (0
       
   109         ||  evsql_result_string(res, row, col_offset + 0, &type,       0 ) // inodes.type
       
   110         ||  evsql_result_uint16(res, row, col_offset + 1, &mode,       0 ) // inodes.mode
       
   111         ||  evsql_result_uint64(res, row, col_offset + 2, &size,       0 ) // inodes.size
       
   112         ||  evsql_result_uint64(res, row, col_offset + 3, &nlink,      0 ) // count(*)
       
   113     )
       
   114         EERROR(err = EIO, "invalid db data");
       
   115 
       
   116     INFO("\tst_mode=S_IF%s | %ho, st_nlink=%llu, st_size=%llu", type, mode, (long long unsigned int) nlink, (long long unsigned int) size);
       
   117 
       
   118     // convert and store
       
   119     st->st_mode = _dbfs_mode(type) | mode;
       
   120     st->st_nlink = nlink;
       
   121     st->st_size = size;
       
   122     
       
   123     // good
       
   124     return 0;
       
   125 
       
   126 error:
       
   127     return -1;
       
   128 }
       
   129 
       
   130 void _dbfs_lookup_result (const struct evsql_result_info *res, void *arg) {
       
   131     struct fuse_req *req = arg;
       
   132     struct fuse_entry_param e; ZINIT(e);
       
   133     int err = 0;
       
   134     
       
   135     uint32_t ino;
       
   136     
       
   137     // check the results
       
   138     if ((err = _dbfs_check_res(res, 1, 5)))
       
   139         SERROR(err = (err ==  1 ? ENOENT : EIO));
       
   140     
       
   141     // get the data
       
   142     if (0
       
   143         ||  evsql_result_uint32(res, 0, 0, &ino,        0 ) // inodes.ino
       
   144     )
       
   145         EERROR(err = EIO, "invalid db data");
       
   146         
       
   147     INFO("[dbfs.lookup] -> ino=%u", ino);
       
   148     
       
   149     // stat attrs
       
   150     if (_dbfs_stat_info(&e.attr, res, 0, 1))
       
   151         goto error;
       
   152 
       
   153     // other attrs
       
   154     e.ino = e.attr.st_ino = ino;
       
   155     e.attr_timeout = CACHE_TIMEOUT;
       
   156     e.entry_timeout = CACHE_TIMEOUT;
       
   157         
       
   158     // reply
       
   159     if ((err = fuse_reply_entry(req, &e)))
       
   160         EERROR(err, "fuse_reply_entry");
       
   161 
       
   162 error:
       
   163     if (err && (err = fuse_reply_err(req, err)))
       
   164         EWARNING(err, "fuse_reply_err");
       
   165 
       
   166     // free
       
   167     evsql_result_free(res);
       
   168 }
       
   169 
       
   170 void dbfs_lookup (struct fuse_req *req, fuse_ino_t parent, const char *name) {
       
   171     struct dbfs *ctx = fuse_req_userdata(req);
       
   172     int err;
       
   173 
       
   174     INFO("[dbfs.lookup] parent=%lu name=%s", parent, name);
       
   175     
       
   176     // query and params
       
   177     const char *sql = 
       
   178         "SELECT"
       
   179         " inodes.ino, inodes.type, inodes.mode, inodes.size, count(*)"
       
   180         " FROM file_tree INNER JOIN inodes ON (file_tree.inode = inodes.ino)"
       
   181         " WHERE file_tree.parent = $1::int4 AND file_tree.name = $2::varchar"
       
   182         " GROUP BY inodes.ino, inodes.type, inodes.mode, inodes.size";
       
   183     
       
   184     static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) {
       
   185         EVSQL_PARAM ( UINT32 ),
       
   186         EVSQL_PARAM ( STRING ),
       
   187 
       
   188         EVSQL_PARAMS_END
       
   189     };
       
   190     
       
   191     // build params
       
   192     if (0
       
   193         ||  evsql_param_uint32(&params, 0, parent)
       
   194         ||  evsql_param_string(&params, 1, name)
       
   195     )
       
   196         EERROR(err = EIO, "evsql_param_*");
       
   197 
       
   198     // query
       
   199     if (evsql_query_params(ctx->db, NULL, sql, &params, _dbfs_lookup_result, req) == NULL)
       
   200         EERROR(err = EIO, "evsql_query_params");
       
   201 
       
   202     // XXX: handle interrupts
       
   203     
       
   204     // wait
       
   205     return;
       
   206 
       
   207 error:
       
   208     if ((err = fuse_reply_err(req, err)))
       
   209         EWARNING(err, "fuse_reply_err");
       
   210 }
       
   211 
       
   212 void _dbfs_getattr_result (const struct evsql_result_info *res, void *arg) {
       
   213     struct fuse_req *req = arg;
       
   214     struct stat st; ZINIT(st);
       
   215     int err = 0;
       
   216     
       
   217     // check the results
       
   218     if ((err = _dbfs_check_res(res, 1, 4)))
       
   219         SERROR(err = (err ==  1 ? ENOENT : EIO));
       
   220         
       
   221     INFO("[dbfs.getattr %p] -> (stat follows)", req);
       
   222     
       
   223     // stat attrs
       
   224     if (_dbfs_stat_info(&st, res, 0, 0))
       
   225         goto error;
       
   226 
       
   227     // XXX: we don't have the ino
       
   228     st.st_ino = 0;
       
   229 
       
   230     // reply
       
   231     if ((err = fuse_reply_attr(req, &st, CACHE_TIMEOUT)))
       
   232         EERROR(err, "fuse_reply_entry");
       
   233 
       
   234 error:
       
   235     if (err && (err = fuse_reply_err(req, err)))
       
   236         EWARNING(err, "fuse_reply_err");
       
   237 
       
   238     // free
       
   239     evsql_result_free(res);
       
   240 }
       
   241 
       
   242 static void dbfs_getattr (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) {
       
   243     struct dbfs *ctx = fuse_req_userdata(req);
       
   244     int err;
       
   245     
       
   246     (void) fi;
       
   247 
       
   248     INFO("[dbfs.getattr %p] ino=%lu", req, ino);
       
   249 
       
   250     const char *sql =
       
   251         "SELECT"
       
   252         " inodes.type, inodes.mode, inodes.size, count(*)"
       
   253         " FROM inodes"
       
   254         " WHERE inodes.ino = $1::int4"
       
   255         " GROUP BY inodes.type, inodes.mode, inodes.size";
       
   256 
       
   257     static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) {
       
   258         EVSQL_PARAM ( UINT32 ),
       
   259 
       
   260         EVSQL_PARAMS_END
       
   261     };
       
   262 
       
   263     // build params
       
   264     if (0
       
   265         ||  evsql_param_uint32(&params, 0, ino)
       
   266     )
       
   267         SERROR(err = EIO);
       
   268         
       
   269     // query
       
   270     if (evsql_query_params(ctx->db, NULL, sql, &params, _dbfs_getattr_result, req) == NULL)
       
   271         SERROR(err = EIO);
       
   272 
       
   273     // XXX: handle interrupts
       
   274     
       
   275     // wait
       
   276     return;
       
   277 
       
   278 error:
       
   279     if ((err = fuse_reply_err(req, err)))
       
   280         EWARNING(err, "fuse_reply_err");
       
   281 }
       
   282 
       
   283 struct dbfs_dirop {
       
   284     struct fuse_file_info fi;
       
   285     struct fuse_req *req;
       
   286 
       
   287     struct evsql_trans *trans;
       
   288     
       
   289     // dir/parent dir inodes
       
   290     uint32_t ino, parent;
       
   291     
       
   292     // opendir has returned and releasedir hasn't been called yet
       
   293     int open;
       
   294 
       
   295     // for readdir
       
   296     struct dirbuf dirbuf;
       
   297 };
       
   298 
       
   299 /*
       
   300  * Free the dirop, aborting any in-progress transaction.
       
   301  *
       
   302  * req must be NULL.
       
   303  */
       
   304 static void dbfs_dirop_free (struct dbfs_dirop *dirop) {
       
   305     assert(dirop);
       
   306     assert(!dirop->open);
       
   307     assert(!dirop->req);
       
   308 
       
   309     if (dirop->trans) {
       
   310         WARNING("aborting transaction");
       
   311         evsql_trans_abort(dirop->trans);
       
   312     }
       
   313 
       
   314     dirbuf_release(&dirop->dirbuf);
       
   315 
       
   316     free(dirop);
       
   317 }
       
   318 
       
   319 static void dbfs_opendir_info_res (const struct evsql_result_info *res, void *arg) {
       
   320     struct dbfs_dirop *dirop = arg;
       
   321     struct fuse_req *req = dirop->req; dirop->req = NULL;
       
   322     int err;
       
   323     
       
   324     assert(req != NULL);
       
   325    
       
   326     // check the results
       
   327     if ((err = _dbfs_check_res(res, 1, 2)))
       
   328         SERROR(err = (err ==  1 ? ENOENT : EIO));
       
   329 
       
   330     const char *type;
       
   331 
       
   332     // extract the data
       
   333     if (0
       
   334         ||  evsql_result_uint32(res, 0, 0, &dirop->parent,  1 ) // file_tree.parent
       
   335         ||  evsql_result_string(res, 0, 1, &type,           0 ) // inodes.type
       
   336     )
       
   337         SERROR(err = EIO);
       
   338 
       
   339     // is it a dir?
       
   340     if (_dbfs_mode(type) != S_IFDIR)
       
   341         EERROR(err = ENOTDIR, "wrong type: %s", type);
       
   342     
       
   343     INFO("[dbfs.opendir %p:%p] -> ino=%lu, parent=%lu, type=%s", dirop, req, (unsigned long int) dirop->ino, (unsigned long int) dirop->parent, type);
       
   344     
       
   345     // send the openddir reply
       
   346     if ((err = fuse_reply_open(req, &dirop->fi)))
       
   347         EERROR(err, "fuse_reply_open");
       
   348     
       
   349     // dirop is now open
       
   350     dirop->open = 1;
       
   351 
       
   352     // ok, wait for the opendir call
       
   353     return;
       
   354 
       
   355 error:
       
   356     if (err) {
       
   357         // abort the trans
       
   358         evsql_trans_abort(dirop->trans);
       
   359         
       
   360         dirop->trans = NULL;
       
   361 
       
   362         if ((err = fuse_reply_err(req, err)))
       
   363             EWARNING(err, "fuse_reply_err");
       
   364     }
       
   365     
       
   366     // free
       
   367     evsql_result_free(res);
       
   368 }
       
   369 
       
   370 /*
       
   371  * The opendir transaction is ready
       
   372  */
       
   373 static void dbfs_dirop_ready (struct evsql_trans *trans, void *arg) {
       
   374     struct dbfs_dirop *dirop = arg;
       
   375     struct fuse_req *req = dirop->req;
       
   376     struct dbfs *ctx = fuse_req_userdata(req);
       
   377     int err;
       
   378 
       
   379     assert(req != NULL);
       
   380 
       
   381     INFO("[dbfs.opendir %p:%p] -> trans=%p", dirop, req, trans);
       
   382 
       
   383     // remember the transaction
       
   384     dirop->trans = trans;
       
   385     
       
   386     // first fetch info about the dir itself
       
   387     const char *sql =
       
   388         "SELECT"
       
   389         " file_tree.parent, inodes.type"
       
   390         " FROM file_tree LEFT OUTER JOIN inodes ON (file_tree.inode = inodes.ino)"
       
   391         " WHERE file_tree.inode = $1::int4";
       
   392 
       
   393     static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) {
       
   394         EVSQL_PARAM ( UINT32 ),
       
   395 
       
   396         EVSQL_PARAMS_END
       
   397     };
       
   398 
       
   399     // build params
       
   400     if (0
       
   401         ||  evsql_param_uint32(&params, 0, dirop->ino)
       
   402     )
       
   403         SERROR(err = EIO);
       
   404         
       
   405     // query
       
   406     if (evsql_query_params(ctx->db, dirop->trans, sql, &params, dbfs_opendir_info_res, dirop) == NULL)
       
   407         SERROR(err = EIO);
       
   408 
       
   409     // ok, wait for the info results
       
   410     return;
       
   411 
       
   412 error:
       
   413     // we handle the req
       
   414     dirop->req = NULL;
       
   415     
       
   416     // free the dirop
       
   417     dbfs_dirop_free(dirop);
       
   418     
       
   419     if ((err = fuse_reply_err(req, err)))
       
   420         EWARNING(err, "fuse_reply_err");
       
   421 }
       
   422 
       
   423 static void dbfs_dirop_done (struct evsql_trans *trans, void *arg) {
       
   424     struct dbfs_dirop *dirop = arg;
       
   425     struct fuse_req *req = dirop->req; dirop->req = NULL;
       
   426     int err;
       
   427     
       
   428     assert(req != NULL);
       
   429 
       
   430     INFO("[dbfs.releasedir %p:%p] -> OK", dirop, req);
       
   431 
       
   432     // forget trans
       
   433     dirop->trans = NULL;
       
   434     
       
   435     // just reply
       
   436     if ((err = fuse_reply_err(req, 0)))
       
   437         EWARNING(err, "fuse_reply_err");
       
   438     
       
   439     // we can free dirop
       
   440     dbfs_dirop_free(dirop);
       
   441 }
       
   442 
       
   443 static void dbfs_dirop_error (struct evsql_trans *trans, void *arg) {
       
   444     struct dbfs_dirop *dirop = arg;
       
   445     int err;
       
   446 
       
   447     INFO("[dbfs:dirop %p:%p] evsql transaction error: %s", dirop, dirop->req, evsql_trans_error(trans));
       
   448     
       
   449     // deassociate the trans
       
   450     dirop->trans = NULL;
       
   451     
       
   452     // error out and pending req
       
   453     if (dirop->req) {
       
   454         if ((err = fuse_reply_err(dirop->req, EIO)))
       
   455             EWARNING(err, "fuse_erply_err");
       
   456 
       
   457         dirop->req = NULL;
       
   458 
       
   459         // only free the dirop if it isn't open
       
   460         if (!dirop->open)
       
   461             dbfs_dirop_free(dirop);
       
   462     }
       
   463 }
       
   464 
       
   465 static void dbfs_opendir (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) {
       
   466     struct dbfs *ctx = fuse_req_userdata(req);
       
   467     struct dbfs_dirop *dirop = NULL;
       
   468     int err;
       
   469     
       
   470     // allocate it
       
   471     if ((dirop = calloc(1, sizeof(*dirop))) == NULL && (err = EIO))
       
   472         ERROR("calloc");
       
   473 
       
   474     INFO("[dbfs.opendir %p:%p] ino=%lu, fi=%p", dirop, req, ino, fi);
       
   475     
       
   476     // store the dirop
       
   477     // copy *fi since it's on the stack
       
   478     dirop->fi = *fi;
       
   479     dirop->fi.fh = (uint64_t) dirop;
       
   480     dirop->req = req;
       
   481     dirop->ino = ino;
       
   482 
       
   483     // start a new transaction
       
   484     if ((dirop->trans = evsql_trans(ctx->db, EVSQL_TRANS_SERIALIZABLE, dbfs_dirop_error, dbfs_dirop_ready, dbfs_dirop_done, dirop)) == NULL)
       
   485         SERROR(err = EIO);
       
   486     
       
   487     // XXX: handle interrupts
       
   488     
       
   489     // wait
       
   490     return;
       
   491 
       
   492 error:
       
   493     // we handle the req
       
   494     dirop->req = NULL;
       
   495 
       
   496     dbfs_dirop_free(dirop);
       
   497 
       
   498     if ((err = fuse_reply_err(req, err)))
       
   499         EWARNING(err, "fuse_reply_err");
       
   500 }
       
   501 
       
   502 static void dbfs_readdir_files_res (const struct evsql_result_info *res, void *arg) {
       
   503     struct dbfs_dirop *dirop = arg;
       
   504     struct fuse_req *req = dirop->req; dirop->req = NULL;
       
   505     int err;
       
   506     size_t row;
       
   507     
       
   508     assert(req != NULL);
       
   509     
       
   510     // check the results
       
   511     if ((err = _dbfs_check_res(res, 0, 4)) < 0)
       
   512         SERROR(err = EIO);
       
   513         
       
   514     INFO("[dbfs.readdir %p:%p] -> files: res_rows=%zu", dirop, req, evsql_result_rows(res));
       
   515         
       
   516     // iterate over the rows
       
   517     for (row = 0; row < evsql_result_rows(res); row++) {
       
   518         uint32_t off, ino;
       
   519         const char *name, *type;
       
   520 
       
   521         // extract the data
       
   522         if (0
       
   523             ||  evsql_result_uint32(res, row, 0, &off,          0 ) // file_tree.offset
       
   524             ||  evsql_result_string(res, row, 1, &name,         0 ) // file_tree.name
       
   525             ||  evsql_result_uint32(res, row, 2, &ino,          0 ) // inodes.ino
       
   526             ||  evsql_result_string(res, row, 3, &type,         0 ) // inodes.type
       
   527         )
       
   528             SERROR(err = EIO);
       
   529         
       
   530         INFO("\t%zu: off=%lu+2, name=%s, ino=%lu, type=%s", row, (long unsigned int) off, name, (long unsigned int) ino, type);
       
   531 
       
   532         // add to the dirbuf
       
   533         // offsets are just offset + 2
       
   534         if ((err = dirbuf_add(req, &dirop->dirbuf, off + 2, off + 3, name, ino, _dbfs_mode(type))) < 0 && (err = EIO))
       
   535             ERROR("failed to add dirent for inode=%lu", (long unsigned int) ino);
       
   536         
       
   537         // stop if it's full
       
   538         if (err > 0)
       
   539             break;
       
   540     }
       
   541 
       
   542     // send it
       
   543     if ((err = dirbuf_done(req, &dirop->dirbuf)))
       
   544         EERROR(err, "failed to send buf");
       
   545     
       
   546     // good, fallthrough
       
   547     err = 0;
       
   548 
       
   549 error:
       
   550     if (err) {
       
   551         // abort the trans
       
   552         evsql_trans_abort(dirop->trans);
       
   553         
       
   554         dirop->trans = NULL;
       
   555 
       
   556         // we handle the req
       
   557         dirop->req = NULL;
       
   558     
       
   559         if ((err = fuse_reply_err(req, err)))
       
   560             EWARNING(err, "fuse_reply_err");
       
   561     }
       
   562     
       
   563     // free
       
   564     evsql_result_free(res);
       
   565 }
       
   566 
       
   567 static void dbfs_readdir (struct fuse_req *req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) {
       
   568     struct dbfs *ctx = fuse_req_userdata(req);
       
   569     struct dbfs_dirop *dirop = (struct dbfs_dirop *) fi->fh;
       
   570     int err;
       
   571 
       
   572     assert(!dirop->req);
       
   573     assert(dirop->trans);
       
   574     assert(dirop->ino == ino);
       
   575     
       
   576     INFO("[dbfs.readdir %p:%p] ino=%lu, size=%zu, off=%zu, fi=%p : trans=%p", dirop, req, ino, size, off, fi, dirop->trans);
       
   577 
       
   578     // update dirop
       
   579     dirop->req = req;
       
   580 
       
   581     // create the dirbuf
       
   582     if (dirbuf_init(&dirop->dirbuf, size, off))
       
   583         SERROR(err = EIO);
       
   584 
       
   585     // add . and ..
       
   586     // we set the next offset to 2, because all dirent offsets will be larger than that
       
   587     if ((err = (0
       
   588         ||  dirbuf_add(req, &dirop->dirbuf, 0, 1, ".",   dirop->ino,    S_IFDIR )
       
   589         ||  dirbuf_add(req, &dirop->dirbuf, 1, 2, "..",  
       
   590                         dirop->parent ? dirop->parent : dirop->ino,     S_IFDIR )
       
   591     )) && (err = EIO))
       
   592         ERROR("failed to add . and .. dirents");
       
   593 
       
   594     // select all relevant file entries
       
   595     const char *sql = 
       
   596         "SELECT"
       
   597         " file_tree.\"offset\", file_tree.name, inodes.ino, inodes.type"
       
   598         " FROM file_tree LEFT OUTER JOIN inodes ON (file_tree.inode = inodes.ino)"
       
   599         " WHERE file_tree.parent = $1::int4 AND file_tree.\"offset\" >= $2::int4"
       
   600         " LIMIT $3::int4";
       
   601 
       
   602     static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) {
       
   603         EVSQL_PARAM ( UINT32 ),
       
   604         EVSQL_PARAM ( UINT32 ),
       
   605         EVSQL_PARAM ( UINT32 ),
       
   606 
       
   607         EVSQL_PARAMS_END
       
   608     };
       
   609 
       
   610     // adjust offset to take . and .. into account
       
   611     if (off > 2)
       
   612         off -= 2;
       
   613     
       
   614     // build params
       
   615     if (0
       
   616         ||  evsql_param_uint32(&params, 0, dirop->ino)
       
   617         ||  evsql_param_uint32(&params, 1, off)
       
   618         ||  evsql_param_uint32(&params, 2, dirbuf_estimate(&dirop->dirbuf, 0))
       
   619     )
       
   620         SERROR(err = EIO);
       
   621 
       
   622     // query
       
   623     if (evsql_query_params(ctx->db, dirop->trans, sql, &params, dbfs_readdir_files_res, dirop) == NULL)
       
   624         SERROR(err = EIO);
       
   625 
       
   626     // good, wait
       
   627     return;
       
   628 
       
   629 error:
       
   630     // we handle the req
       
   631     dirop->req = NULL;
       
   632 
       
   633     // abort the trans
       
   634     evsql_trans_abort(dirop->trans); dirop->trans = NULL;
       
   635 
       
   636     if ((err = fuse_reply_err(req, err)))
       
   637         EWARNING(err, "fuse_reply_err");
       
   638 
       
   639 }
       
   640 
       
   641 static void dbfs_releasedir (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) {
       
   642     struct dbfs *ctx = fuse_req_userdata(req);
       
   643     struct dbfs_dirop *dirop = (struct dbfs_dirop *) fi->fh;
       
   644     int err;
       
   645 
       
   646     (void) ctx;
       
   647     
       
   648     assert(!dirop->req);
       
   649     assert(dirop->ino == ino);
       
   650 
       
   651     INFO("[dbfs.releasedir %p:%p] ino=%lu, fi=%p : trans=%p", dirop, req, ino, fi, dirop->trans);
       
   652 
       
   653     // update dirop. Must keep it open so that dbfs_dirop_error won't free it
       
   654     // copy *fi since it's on the stack
       
   655     dirop->fi = *fi;
       
   656     dirop->fi.fh = (uint64_t) dirop;
       
   657     dirop->req = req;
       
   658     
       
   659     if (dirop->trans) {
       
   660         // we can commit the transaction, although we didn't make any changes
       
   661         // if this fails the transaction, then dbfs_dirop_error will take care of sending the error, and dirop->req will be
       
   662         // NULL
       
   663         if (evsql_trans_commit(dirop->trans))
       
   664             SERROR(err = EIO);
       
   665 
       
   666     } else {
       
   667         // trans failed earlier, so have releasedir just succeed
       
   668         if ((err = fuse_reply_err(req, 0)))
       
   669             EERROR(err, "fuse_reply_err");
       
   670 
       
   671         // req is done
       
   672         dirop->req = NULL;
       
   673     }
       
   674 
       
   675     // fall-through to cleanup
       
   676     err = 0;
       
   677 
       
   678 error:
       
   679     // the dirop is not open anymore and can be freed once done with
       
   680     dirop->open = 0;
       
   681 
       
   682     // if trans_commit triggered an error but didn't call dbfs_dirop_error, we need to take care of it
       
   683     if (err && dirop->req) {
       
   684         int err2;
       
   685 
       
   686         // we handle the req
       
   687         dirop->req = NULL;
       
   688 
       
   689         if ((err2 = fuse_reply_err(req, err)))
       
   690             EWARNING(err2, "fuse_reply_err");
       
   691     } 
       
   692     
       
   693     // same for trans, we need to abort it if trans_commit failed and fs_dirop_error didn't get called
       
   694     if (err && dirop->trans) {
       
   695         dbfs_dirop_free(dirop);
       
   696     
       
   697     } else
       
   698       // alternatively, if the trans error'd itself away (now or earlier), we don't need to keep the dirop around
       
   699       // anymore now that we've checkd its state
       
   700       if (!dirop->trans) {
       
   701         dbfs_dirop_free(dirop);
       
   702     }
       
   703 }
       
   704 
       
   705 struct fuse_lowlevel_ops dbfs_llops = {
       
   706 
       
   707     .init           = dbfs_init,
       
   708     .destroy        = dbfs_destroy,
       
   709     
       
   710     .lookup         = dbfs_lookup,
       
   711 
       
   712     .getattr        = dbfs_getattr,
       
   713 
       
   714     .opendir        = dbfs_opendir,
       
   715     .readdir        = dbfs_readdir,
       
   716     .releasedir     = dbfs_releasedir,
       
   717 };
       
   718 
       
   719 void dbfs_sql_error (struct evsql *evsql, void *arg) {
       
   720     struct dbfs *ctx = arg;
       
   721 
       
   722     // AAAAAAAAAA.... panic
       
   723     WARNING("[dbfs] SQL error: BREAKING MAIN LOOP LIEK NAO");
       
   724 
       
   725     event_base_loopbreak(ctx->ev_base);
       
   726 }
       
   727 
       
   728 int main (int argc, char **argv) {
    22 int main (int argc, char **argv) {
       
    23     struct event_base *ev_base = NULL;
       
    24     struct signals *signals = NULL;
       
    25     struct dbfs *ctx = NULL;
       
    26     const char *db_conninfo;
   729     struct fuse_args fuse_args = FUSE_ARGS_INIT(argc, argv);
    27     struct fuse_args fuse_args = FUSE_ARGS_INIT(argc, argv);
   730     struct dbfs ctx; ZINIT(ctx);
       
   731     
    28     
   732     // parse args, XXX: fuse_args
    29     // parse args, XXX: fuse_args
   733     ctx.db_conninfo = CONNINFO_DEFAULT;
    30     db_conninfo = CONNINFO_DEFAULT;
   734     
    31     
   735     // init libevent
    32     // init libevent
   736     if ((ctx.ev_base = event_base_new()) == NULL)
    33     if ((ev_base = event_base_new()) == NULL)
   737         ERROR("event_base_new");
    34         ERROR("event_base_new");
   738     
    35     
   739     // setup signals
    36     // setup signals
   740     if ((ctx.signals = signals_default(ctx.ev_base)) == NULL)
    37     if ((signals = signals_default(ev_base)) == NULL)
   741         ERROR("signals_default");
    38         ERROR("signals_default");
   742 
    39 
   743     // open sql
    40     // setup dbfs
   744     if ((ctx.db = evsql_new_pq(ctx.ev_base, ctx.db_conninfo, dbfs_sql_error, &ctx)) == NULL)
    41     if ((ctx = dbfs_open(ev_base, &fuse_args, db_conninfo)) == NULL)
   745         ERROR("evsql_new_pq");
    42         ERROR("dbfs_open");
   746 
       
   747     // open fuse
       
   748     if ((ctx.ev_fuse = evfuse_new(ctx.ev_base, &fuse_args, &dbfs_llops, &ctx)) == NULL)
       
   749         ERROR("evfuse_new");
       
   750 
    43 
   751     // run libevent
    44     // run libevent
   752     INFO("running libevent loop");
    45     INFO("running libevent loop");
   753 
    46 
   754     if (event_base_dispatch(ctx.ev_base))
    47     if (event_base_dispatch(ev_base))
   755         PERROR("event_base_dispatch");
    48         PERROR("event_base_dispatch");
   756     
    49     
   757     // clean shutdown
    50     // clean shutdown
   758 
    51 
   759 error :
    52 error :
   760     // cleanup
    53     if (ctx)
   761     if (ctx.ev_fuse)
    54         dbfs_release(ctx);
   762         evfuse_free(ctx.ev_fuse);
    55     
       
    56     if (signals)
       
    57         signals_free(signals);
   763 
    58 
   764     // XXX: ctx.db
    59     if (ev_base)
   765     
    60         event_base_free(ev_base);
   766     if (ctx.signals)
       
   767         signals_free(ctx.signals);
       
   768 
       
   769     if (ctx.ev_base)
       
   770         event_base_free(ctx.ev_base);
       
   771     
    61     
   772     fuse_opt_free_args(&fuse_args);
    62     fuse_opt_free_args(&fuse_args);
   773 }
    63 }
   774 
    64