terom@24: terom@24: /* terom@24: * A simple PostgreSQL-based filesystem. terom@24: */ terom@24: terom@26: #include terom@24: #include terom@24: #include terom@26: #include terom@24: terom@24: #include terom@24: terom@24: #include "evfuse.h" terom@24: #include "evsql.h" terom@24: #include "dirbuf.h" terom@24: #include "lib/log.h" terom@24: #include "lib/signals.h" terom@24: #include "lib/misc.h" terom@24: terom@24: #define SERROR(val) do { (val); goto error; } while(0) terom@24: terom@24: struct dbfs { terom@24: struct event_base *ev_base; terom@24: struct signals *signals; terom@24: terom@24: const char *db_conninfo; terom@24: struct evsql *db; terom@24: terom@24: struct evfuse *ev_fuse; terom@24: }; terom@24: terom@24: #define CONNINFO_DEFAULT "dbname=test" terom@24: terom@24: // XXX: not sure how this should work terom@24: #define CACHE_TIMEOUT 1.0 terom@24: terom@24: mode_t _dbfs_mode (const char *type) { terom@24: if (!strcmp(type, "DIR")) terom@24: return S_IFDIR; terom@24: terom@24: if (!strcmp(type, "REG")) terom@24: return S_IFREG; terom@24: terom@24: else { terom@24: WARNING("[dbfs] weird mode-type: %s", type); terom@24: return 0; terom@24: } terom@24: } terom@24: terom@24: void dbfs_init (void *userdata, struct fuse_conn_info *conn) { terom@24: INFO("[dbfs.init] userdata=%p, conn=%p", userdata, conn); terom@24: terom@24: } terom@24: terom@27: void dbfs_destroy (void *arg) { terom@27: struct dbfs *ctx = arg; terom@27: INFO("[dbfs.destroy %p]", ctx); terom@24: terom@27: // exit libevent terom@27: event_base_loopexit(ctx->ev_base, NULL); terom@24: } terom@24: terom@25: /* terom@25: * Check the result set. terom@25: * terom@25: * Returns; terom@27: * -1 if the query failed, the columns do not match, or there are too many/few rows (unless rows was zero) terom@25: * 0 the results match terom@25: * 1 there were no results terom@25: */ terom@25: int _dbfs_check_res (const struct evsql_result_info *res, size_t rows, size_t cols) { terom@25: int err = 0; terom@25: terom@25: // check if it failed terom@25: if (res->error) terom@25: NERROR(evsql_result_error(res)); terom@25: terom@25: // not found? terom@25: if (evsql_result_rows(res) == 0) terom@25: SERROR(err = 1); terom@25: terom@25: // duplicate rows? terom@27: if (rows && evsql_result_rows(res) != rows) terom@27: ERROR("wrong number of rows returned"); terom@25: terom@25: // correct number of columns terom@27: if (evsql_result_cols(res) != cols) terom@25: ERROR("wrong number of columns: %zu", evsql_result_cols(res)); terom@25: terom@25: // good terom@25: return 0; terom@25: terom@25: error: terom@25: if (!err) terom@25: err = -1; terom@25: terom@25: return err; terom@25: } terom@25: terom@25: int _dbfs_stat_info (struct stat *st, const struct evsql_result_info *res, size_t row, size_t col_offset) { terom@25: int err = 0; terom@25: terom@25: uint16_t mode; terom@25: uint64_t size, nlink; terom@25: const char *type; terom@25: terom@25: // extract the data terom@25: if (0 terom@25: || evsql_result_string(res, row, col_offset + 0, &type, 0 ) // inodes.type terom@25: || evsql_result_uint16(res, row, col_offset + 1, &mode, 0 ) // inodes.mode terom@25: || evsql_result_uint64(res, row, col_offset + 2, &size, 0 ) // inodes.size terom@25: || evsql_result_uint64(res, row, col_offset + 3, &nlink, 0 ) // count(*) terom@25: ) terom@25: EERROR(err = EIO, "invalid db data"); terom@25: terom@25: 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); terom@25: terom@25: // convert and store terom@25: st->st_mode = _dbfs_mode(type) | mode; terom@25: st->st_nlink = nlink; terom@25: st->st_size = size; terom@25: terom@25: // good terom@25: return 0; terom@25: terom@25: error: terom@25: return -1; terom@25: } terom@25: terom@24: void _dbfs_lookup_result (const struct evsql_result_info *res, void *arg) { terom@24: struct fuse_req *req = arg; terom@24: struct fuse_entry_param e; ZINIT(e); terom@24: int err = 0; terom@24: terom@24: uint32_t ino; terom@24: terom@25: // check the results terom@25: if ((err = _dbfs_check_res(res, 1, 5))) terom@25: SERROR(err = (err == 1 ? ENOENT : EIO)); terom@24: terom@24: // get the data terom@24: if (0 terom@24: || evsql_result_uint32(res, 0, 0, &ino, 0 ) // inodes.ino terom@24: ) terom@24: EERROR(err = EIO, "invalid db data"); terom@25: terom@27: INFO("[dbfs.lookup] -> ino=%u", ino); terom@24: terom@25: // stat attrs terom@25: if (_dbfs_stat_info(&e.attr, res, 0, 1)) terom@25: goto error; terom@24: terom@25: // other attrs terom@24: e.ino = e.attr.st_ino = ino; terom@24: e.attr_timeout = CACHE_TIMEOUT; terom@24: e.entry_timeout = CACHE_TIMEOUT; terom@24: terom@24: // reply terom@24: if ((err = fuse_reply_entry(req, &e))) terom@24: EERROR(err, "fuse_reply_entry"); terom@24: terom@24: error: terom@24: if (err && (err = fuse_reply_err(req, err))) terom@24: EWARNING(err, "fuse_reply_err"); terom@24: terom@24: // free terom@24: evsql_result_free(res); terom@24: } terom@24: terom@24: void dbfs_lookup (struct fuse_req *req, fuse_ino_t parent, const char *name) { terom@24: struct dbfs *ctx = fuse_req_userdata(req); terom@24: int err; terom@24: terom@24: INFO("[dbfs.lookup] parent=%lu name=%s", parent, name); terom@24: terom@24: // query and params terom@24: const char *sql = terom@24: "SELECT" terom@24: " inodes.ino, inodes.type, inodes.mode, inodes.size, count(*)" terom@24: " FROM file_tree INNER JOIN inodes ON (file_tree.inode = inodes.ino)" terom@25: " WHERE file_tree.parent = $1::int4 AND file_tree.name = $2::varchar" terom@24: " GROUP BY inodes.ino, inodes.type, inodes.mode, inodes.size"; terom@24: terom@24: static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) { terom@24: EVSQL_PARAM ( UINT32 ), terom@24: EVSQL_PARAM ( STRING ), terom@24: terom@24: EVSQL_PARAMS_END terom@24: }; terom@24: terom@24: // build params terom@24: if (0 terom@24: || evsql_param_uint32(¶ms, 0, parent) terom@24: || evsql_param_string(¶ms, 1, name) terom@24: ) terom@24: EERROR(err = EIO, "evsql_param_*"); terom@24: terom@24: // query terom@25: if (evsql_query_params(ctx->db, NULL, sql, ¶ms, _dbfs_lookup_result, req) == NULL) terom@24: EERROR(err = EIO, "evsql_query_params"); terom@24: terom@24: // XXX: handle interrupts terom@24: terom@24: // wait terom@24: return; terom@24: terom@24: error: terom@24: if ((err = fuse_reply_err(req, err))) terom@24: EWARNING(err, "fuse_reply_err"); terom@24: } terom@24: terom@25: void _dbfs_getattr_result (const struct evsql_result_info *res, void *arg) { terom@25: struct fuse_req *req = arg; terom@25: struct stat st; ZINIT(st); terom@25: int err = 0; terom@25: terom@25: // check the results terom@25: if ((err = _dbfs_check_res(res, 1, 4))) terom@25: SERROR(err = (err == 1 ? ENOENT : EIO)); terom@25: terom@25: INFO("[dbfs.getattr %p] -> (stat follows)", req); terom@25: terom@25: // stat attrs terom@25: if (_dbfs_stat_info(&st, res, 0, 0)) terom@25: goto error; terom@25: terom@25: // XXX: we don't have the ino terom@25: st.st_ino = 0; terom@25: terom@25: // reply terom@25: if ((err = fuse_reply_attr(req, &st, CACHE_TIMEOUT))) terom@25: EERROR(err, "fuse_reply_entry"); terom@25: terom@25: error: terom@25: if (err && (err = fuse_reply_err(req, err))) terom@25: EWARNING(err, "fuse_reply_err"); terom@25: terom@25: // free terom@25: evsql_result_free(res); terom@25: } terom@25: terom@25: static void dbfs_getattr (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) { terom@25: struct dbfs *ctx = fuse_req_userdata(req); terom@25: int err; terom@25: terom@25: (void) fi; terom@25: terom@25: INFO("[dbfs.getattr %p] ino=%lu", req, ino); terom@25: terom@25: const char *sql = terom@25: "SELECT" terom@25: " inodes.type, inodes.mode, inodes.size, count(*)" terom@25: " FROM inodes" terom@27: " WHERE inodes.ino = $1::int4" terom@25: " GROUP BY inodes.type, inodes.mode, inodes.size"; terom@25: terom@25: static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) { terom@25: EVSQL_PARAM ( UINT32 ), terom@25: terom@25: EVSQL_PARAMS_END terom@25: }; terom@25: terom@25: // build params terom@25: if (0 terom@25: || evsql_param_uint32(¶ms, 0, ino) terom@25: ) terom@25: SERROR(err = EIO); terom@25: terom@25: // query terom@25: if (evsql_query_params(ctx->db, NULL, sql, ¶ms, _dbfs_getattr_result, req) == NULL) terom@25: SERROR(err = EIO); terom@25: terom@25: // XXX: handle interrupts terom@25: terom@25: // wait terom@25: return; terom@25: terom@25: error: terom@25: if ((err = fuse_reply_err(req, err))) terom@25: EWARNING(err, "fuse_reply_err"); terom@25: } terom@25: terom@26: struct dbfs_dirop { terom@27: struct fuse_file_info fi; terom@26: struct fuse_req *req; terom@26: terom@26: struct evsql_trans *trans; terom@26: terom@27: // dir/parent dir inodes terom@27: uint32_t ino, parent; terom@27: terom@26: // opendir has returned and releasedir hasn't been called yet terom@26: int open; terom@27: terom@27: // for readdir terom@27: struct dirbuf dirbuf; terom@26: }; terom@26: terom@26: /* terom@26: * Free the dirop, aborting any in-progress transaction. terom@26: * terom@26: * req must be NULL. terom@26: */ terom@26: static void dbfs_dirop_free (struct dbfs_dirop *dirop) { terom@27: assert(dirop); terom@27: assert(!dirop->open); terom@27: assert(!dirop->req); terom@26: terom@27: if (dirop->trans) { terom@27: WARNING("aborting transaction"); terom@26: evsql_trans_abort(dirop->trans); terom@27: } terom@27: terom@27: dirbuf_release(&dirop->dirbuf); terom@26: terom@26: free(dirop); terom@26: } terom@26: terom@27: static void dbfs_opendir_info_res (const struct evsql_result_info *res, void *arg) { terom@27: struct dbfs_dirop *dirop = arg; terom@27: struct fuse_req *req = dirop->req; dirop->req = NULL; terom@27: int err; terom@27: terom@27: assert(req != NULL); terom@27: terom@27: // check the results terom@27: if ((err = _dbfs_check_res(res, 1, 2))) terom@27: SERROR(err = (err == 1 ? ENOENT : EIO)); terom@27: terom@27: const char *type; terom@27: terom@27: // extract the data terom@27: if (0 terom@27: || evsql_result_uint32(res, 0, 0, &dirop->parent, 1 ) // file_tree.parent terom@27: || evsql_result_string(res, 0, 1, &type, 0 ) // inodes.type terom@27: ) terom@27: SERROR(err = EIO); terom@27: terom@27: // is it a dir? terom@27: if (_dbfs_mode(type) != S_IFDIR) terom@27: EERROR(err = ENOTDIR, "wrong type: %s", type); terom@27: terom@27: INFO("[dbfs.opendir %p:%p] -> ino=%lu, parent=%lu, type=%s", dirop, req, (unsigned long int) dirop->ino, (unsigned long int) dirop->parent, type); terom@27: terom@27: // send the openddir reply terom@27: if ((err = fuse_reply_open(req, &dirop->fi))) terom@27: EERROR(err, "fuse_reply_open"); terom@27: terom@27: // dirop is now open terom@27: dirop->open = 1; terom@27: terom@27: // ok, wait for the opendir call terom@27: return; terom@27: terom@27: error: terom@27: if (err) { terom@27: // abort the trans terom@27: evsql_trans_abort(dirop->trans); terom@27: terom@27: dirop->trans = NULL; terom@27: terom@27: if ((err = fuse_reply_err(req, err))) terom@27: EWARNING(err, "fuse_reply_err"); terom@27: } terom@27: terom@27: // free terom@27: evsql_result_free(res); terom@27: } terom@27: terom@26: /* terom@26: * The opendir transaction is ready terom@26: */ terom@26: static void dbfs_dirop_ready (struct evsql_trans *trans, void *arg) { terom@26: struct dbfs_dirop *dirop = arg; terom@27: struct fuse_req *req = dirop->req; terom@27: struct dbfs *ctx = fuse_req_userdata(req); terom@26: int err; terom@26: terom@27: assert(req != NULL); terom@27: terom@27: INFO("[dbfs.opendir %p:%p] -> trans=%p", dirop, req, trans); terom@26: terom@26: // remember the transaction terom@26: dirop->trans = trans; terom@27: terom@27: // first fetch info about the dir itself terom@27: const char *sql = terom@27: "SELECT" terom@27: " file_tree.parent, inodes.type" terom@27: " FROM file_tree LEFT OUTER JOIN inodes ON (file_tree.inode = inodes.ino)" terom@27: " WHERE file_tree.inode = $1::int4"; terom@26: terom@27: static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) { terom@27: EVSQL_PARAM ( UINT32 ), terom@26: terom@27: EVSQL_PARAMS_END terom@27: }; terom@27: terom@27: // build params terom@27: if (0 terom@27: || evsql_param_uint32(¶ms, 0, dirop->ino) terom@27: ) terom@27: SERROR(err = EIO); terom@27: terom@27: // query terom@27: if (evsql_query_params(ctx->db, dirop->trans, sql, ¶ms, dbfs_opendir_info_res, dirop) == NULL) terom@27: SERROR(err = EIO); terom@27: terom@27: // ok, wait for the info results terom@26: return; terom@26: terom@26: error: terom@27: // we handle the req terom@27: dirop->req = NULL; terom@27: terom@27: // free the dirop terom@26: dbfs_dirop_free(dirop); terom@27: terom@26: if ((err = fuse_reply_err(req, err))) terom@26: EWARNING(err, "fuse_reply_err"); terom@26: } terom@26: terom@26: static void dbfs_dirop_done (struct evsql_trans *trans, void *arg) { terom@26: struct dbfs_dirop *dirop = arg; terom@27: struct fuse_req *req = dirop->req; dirop->req = NULL; terom@26: int err; terom@26: terom@27: assert(req != NULL); terom@26: terom@27: INFO("[dbfs.releasedir %p:%p] -> OK", dirop, req); terom@27: terom@27: // forget trans terom@27: dirop->trans = NULL; terom@27: terom@27: // just reply terom@27: if ((err = fuse_reply_err(req, 0))) terom@27: EWARNING(err, "fuse_reply_err"); terom@27: terom@27: // we can free dirop terom@27: dbfs_dirop_free(dirop); terom@26: } terom@26: terom@26: static void dbfs_dirop_error (struct evsql_trans *trans, void *arg) { terom@26: struct dbfs_dirop *dirop = arg; terom@26: int err; terom@26: terom@26: INFO("[dbfs:dirop %p:%p] evsql transaction error: %s", dirop, dirop->req, evsql_trans_error(trans)); terom@26: terom@26: // deassociate the trans terom@26: dirop->trans = NULL; terom@26: terom@26: // error out and pending req terom@26: if (dirop->req) { terom@26: if ((err = fuse_reply_err(dirop->req, EIO))) terom@26: EWARNING(err, "fuse_erply_err"); terom@26: terom@26: dirop->req = NULL; terom@26: terom@26: // only free the dirop if it isn't open terom@26: if (!dirop->open) terom@26: dbfs_dirop_free(dirop); terom@26: } terom@26: } terom@26: terom@26: static void dbfs_opendir (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) { terom@26: struct dbfs *ctx = fuse_req_userdata(req); terom@26: struct dbfs_dirop *dirop = NULL; terom@26: int err; terom@26: terom@26: // allocate it terom@26: if ((dirop = calloc(1, sizeof(*dirop))) == NULL && (err = EIO)) terom@26: ERROR("calloc"); terom@26: terom@26: INFO("[dbfs.opendir %p:%p] ino=%lu, fi=%p", dirop, req, ino, fi); terom@27: terom@26: // store the dirop terom@27: // copy *fi since it's on the stack terom@27: dirop->fi = *fi; terom@27: dirop->fi.fh = (uint64_t) dirop; terom@26: dirop->req = req; terom@27: dirop->ino = ino; terom@26: terom@26: // start a new transaction terom@27: if ((dirop->trans = evsql_trans(ctx->db, EVSQL_TRANS_SERIALIZABLE, dbfs_dirop_error, dbfs_dirop_ready, dbfs_dirop_done, dirop)) == NULL) terom@26: SERROR(err = EIO); terom@26: terom@26: // XXX: handle interrupts terom@26: terom@26: // wait terom@26: return; terom@26: terom@26: error: terom@26: // we handle the req terom@26: dirop->req = NULL; terom@26: terom@26: dbfs_dirop_free(dirop); terom@26: terom@26: if ((err = fuse_reply_err(req, err))) terom@26: EWARNING(err, "fuse_reply_err"); terom@26: } terom@26: terom@27: static void dbfs_readdir_files_res (const struct evsql_result_info *res, void *arg) { terom@27: struct dbfs_dirop *dirop = arg; terom@27: struct fuse_req *req = dirop->req; dirop->req = NULL; terom@27: int err; terom@27: size_t row; terom@27: terom@27: assert(req != NULL); terom@27: terom@27: // check the results terom@27: if ((err = _dbfs_check_res(res, 0, 4)) < 0) terom@27: SERROR(err = EIO); terom@27: terom@27: INFO("[dbfs.readdir %p:%p] -> files: res_rows=%zu", dirop, req, evsql_result_rows(res)); terom@27: terom@27: // iterate over the rows terom@27: for (row = 0; row < evsql_result_rows(res); row++) { terom@27: uint32_t off, ino; terom@27: const char *name, *type; terom@27: terom@27: // extract the data terom@27: if (0 terom@27: || evsql_result_uint32(res, row, 0, &off, 0 ) // file_tree.offset terom@27: || evsql_result_string(res, row, 1, &name, 0 ) // file_tree.name terom@27: || evsql_result_uint32(res, row, 2, &ino, 0 ) // inodes.ino terom@27: || evsql_result_string(res, row, 3, &type, 0 ) // inodes.type terom@27: ) terom@27: SERROR(err = EIO); terom@27: terom@27: INFO("\t%zu: off=%lu+2, name=%s, ino=%lu, type=%s", row, (long unsigned int) off, name, (long unsigned int) ino, type); terom@27: terom@27: // add to the dirbuf terom@27: // offsets are just offset + 2 terom@27: if ((err = dirbuf_add(req, &dirop->dirbuf, off + 2, off + 3, name, ino, _dbfs_mode(type))) < 0 && (err = EIO)) terom@27: ERROR("failed to add dirent for inode=%lu", (long unsigned int) ino); terom@27: terom@27: // stop if it's full terom@27: if (err > 0) terom@27: break; terom@27: } terom@27: terom@27: // send it terom@27: if ((err = dirbuf_done(req, &dirop->dirbuf))) terom@27: EERROR(err, "failed to send buf"); terom@27: terom@27: // good, fallthrough terom@27: err = 0; terom@27: terom@27: error: terom@27: if (err) { terom@27: // abort the trans terom@27: evsql_trans_abort(dirop->trans); terom@27: terom@27: dirop->trans = NULL; terom@27: terom@27: // we handle the req terom@27: dirop->req = NULL; terom@27: terom@27: if ((err = fuse_reply_err(req, err))) terom@27: EWARNING(err, "fuse_reply_err"); terom@27: } terom@27: terom@27: // free terom@27: evsql_result_free(res); terom@27: } terom@27: terom@26: static void dbfs_readdir (struct fuse_req *req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { terom@26: struct dbfs *ctx = fuse_req_userdata(req); terom@26: struct dbfs_dirop *dirop = (struct dbfs_dirop *) fi->fh; terom@26: int err; terom@26: terom@27: assert(!dirop->req); terom@27: assert(dirop->trans); terom@27: assert(dirop->ino == ino); terom@27: terom@26: INFO("[dbfs.readdir %p:%p] ino=%lu, size=%zu, off=%zu, fi=%p : trans=%p", dirop, req, ino, size, off, fi, dirop->trans); terom@26: terom@26: // update dirop terom@26: dirop->req = req; terom@27: terom@27: // create the dirbuf terom@27: if (dirbuf_init(&dirop->dirbuf, size, off)) terom@27: SERROR(err = EIO); terom@27: terom@27: // add . and .. terom@27: // we set the next offset to 2, because all dirent offsets will be larger than that terom@27: if ((err = (0 terom@27: || dirbuf_add(req, &dirop->dirbuf, 0, 1, ".", dirop->ino, S_IFDIR ) terom@27: || dirbuf_add(req, &dirop->dirbuf, 1, 2, "..", terom@27: dirop->parent ? dirop->parent : dirop->ino, S_IFDIR ) terom@27: )) && (err = EIO)) terom@27: ERROR("failed to add . and .. dirents"); terom@26: terom@26: // select all relevant file entries terom@26: const char *sql = terom@26: "SELECT" terom@27: " file_tree.\"offset\", file_tree.name, inodes.ino, inodes.type" terom@26: " FROM file_tree LEFT OUTER JOIN inodes ON (file_tree.inode = inodes.ino)" terom@27: " WHERE file_tree.parent = $1::int4 AND file_tree.\"offset\" >= $2::int4" terom@26: " LIMIT $3::int4"; terom@26: terom@26: static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) { terom@26: EVSQL_PARAM ( UINT32 ), terom@26: EVSQL_PARAM ( UINT32 ), terom@26: EVSQL_PARAM ( UINT32 ), terom@26: terom@26: EVSQL_PARAMS_END terom@26: }; terom@26: terom@27: // adjust offset to take . and .. into account terom@27: if (off > 2) terom@27: off -= 2; terom@27: terom@27: // build params terom@27: if (0 terom@27: || evsql_param_uint32(¶ms, 0, dirop->ino) terom@27: || evsql_param_uint32(¶ms, 1, off) terom@27: || evsql_param_uint32(¶ms, 2, dirbuf_estimate(&dirop->dirbuf, 0)) terom@27: ) terom@27: SERROR(err = EIO); terom@27: terom@27: // query terom@27: if (evsql_query_params(ctx->db, dirop->trans, sql, ¶ms, dbfs_readdir_files_res, dirop) == NULL) terom@27: SERROR(err = EIO); terom@27: terom@27: // good, wait terom@27: return; terom@27: terom@27: error: terom@27: // we handle the req terom@27: dirop->req = NULL; terom@27: terom@27: // abort the trans terom@27: evsql_trans_abort(dirop->trans); dirop->trans = NULL; terom@27: terom@27: if ((err = fuse_reply_err(req, err))) terom@27: EWARNING(err, "fuse_reply_err"); terom@27: terom@26: } terom@26: terom@26: static void dbfs_releasedir (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) { terom@26: struct dbfs *ctx = fuse_req_userdata(req); terom@26: struct dbfs_dirop *dirop = (struct dbfs_dirop *) fi->fh; terom@26: int err; terom@26: terom@26: (void) ctx; terom@27: terom@27: assert(!dirop->req); terom@27: assert(dirop->ino == ino); terom@26: terom@26: INFO("[dbfs.releasedir %p:%p] ino=%lu, fi=%p : trans=%p", dirop, req, ino, fi, dirop->trans); terom@26: terom@26: // update dirop. Must keep it open so that dbfs_dirop_error won't free it terom@27: // copy *fi since it's on the stack terom@27: dirop->fi = *fi; terom@27: dirop->fi.fh = (uint64_t) dirop; terom@26: dirop->req = req; terom@27: terom@27: if (dirop->trans) { terom@27: // we can commit the transaction, although we didn't make any changes terom@27: // if this fails the transaction, then dbfs_dirop_error will take care of sending the error, and dirop->req will be terom@27: // NULL terom@27: if (evsql_trans_commit(dirop->trans)) terom@27: SERROR(err = EIO); terom@26: terom@27: } else { terom@27: // trans failed earlier, so have releasedir just succeed terom@27: if ((err = fuse_reply_err(req, 0))) terom@27: EERROR(err, "fuse_reply_err"); terom@26: terom@27: // req is done terom@27: dirop->req = NULL; terom@27: } terom@27: terom@27: // fall-through to cleanup terom@27: err = 0; terom@27: terom@27: error: terom@27: // the dirop is not open anymore and can be freed once done with terom@26: dirop->open = 0; terom@26: terom@27: // if trans_commit triggered an error but didn't call dbfs_dirop_error, we need to take care of it terom@27: if (err && dirop->req) { terom@27: int err2; terom@26: terom@26: // we handle the req terom@26: dirop->req = NULL; terom@26: terom@27: if ((err2 = fuse_reply_err(req, err))) terom@27: EWARNING(err2, "fuse_reply_err"); terom@27: } terom@27: terom@27: // same for trans, we need to abort it if trans_commit failed and fs_dirop_error didn't get called terom@27: if (err && dirop->trans) { terom@26: dbfs_dirop_free(dirop); terom@27: terom@27: } else terom@27: // alternatively, if the trans error'd itself away (now or earlier), we don't need to keep the dirop around terom@27: // anymore now that we've checkd its state terom@27: if (!dirop->trans) { terom@27: dbfs_dirop_free(dirop); terom@26: } terom@26: } terom@26: terom@24: struct fuse_lowlevel_ops dbfs_llops = { terom@25: terom@24: .init = dbfs_init, terom@24: .destroy = dbfs_destroy, terom@24: terom@24: .lookup = dbfs_lookup, terom@25: terom@25: .getattr = dbfs_getattr, terom@26: terom@26: .opendir = dbfs_opendir, terom@27: .readdir = dbfs_readdir, terom@26: .releasedir = dbfs_releasedir, terom@24: }; terom@24: terom@24: void dbfs_sql_error (struct evsql *evsql, void *arg) { terom@24: struct dbfs *ctx = arg; terom@24: terom@24: // AAAAAAAAAA.... panic terom@24: WARNING("[dbfs] SQL error: BREAKING MAIN LOOP LIEK NAO"); terom@24: terom@24: event_base_loopbreak(ctx->ev_base); terom@24: } terom@24: terom@24: int main (int argc, char **argv) { terom@24: struct fuse_args fuse_args = FUSE_ARGS_INIT(argc, argv); terom@24: struct dbfs ctx; ZINIT(ctx); terom@24: terom@24: // parse args, XXX: fuse_args terom@24: ctx.db_conninfo = CONNINFO_DEFAULT; terom@24: terom@24: // init libevent terom@24: if ((ctx.ev_base = event_base_new()) == NULL) terom@24: ERROR("event_base_new"); terom@24: terom@24: // setup signals terom@24: if ((ctx.signals = signals_default(ctx.ev_base)) == NULL) terom@24: ERROR("signals_default"); terom@24: terom@24: // open sql terom@24: if ((ctx.db = evsql_new_pq(ctx.ev_base, ctx.db_conninfo, dbfs_sql_error, &ctx)) == NULL) terom@24: ERROR("evsql_new_pq"); terom@24: terom@24: // open fuse terom@24: if ((ctx.ev_fuse = evfuse_new(ctx.ev_base, &fuse_args, &dbfs_llops, &ctx)) == NULL) terom@24: ERROR("evfuse_new"); terom@24: terom@24: // run libevent terom@24: INFO("running libevent loop"); terom@24: terom@24: if (event_base_dispatch(ctx.ev_base)) terom@24: PERROR("event_base_dispatch"); terom@24: terom@24: // clean shutdown terom@24: terom@24: error : terom@24: // cleanup terom@24: if (ctx.ev_fuse) terom@27: evfuse_free(ctx.ev_fuse); terom@24: terom@24: // XXX: ctx.db terom@24: terom@24: if (ctx.signals) terom@24: signals_free(ctx.signals); terom@24: terom@24: if (ctx.ev_base) terom@24: event_base_free(ctx.ev_base); terom@24: terom@24: fuse_opt_free_args(&fuse_args); terom@24: } terom@24: