terom@35: #include terom@35: #include terom@35: #include terom@35: terom@35: #include "trans.h" terom@35: #include "../lib/log.h" terom@35: terom@35: struct dbfs_mk_ctx { terom@35: struct dbfs_trans base; terom@35: terom@35: const char *type, *data_expr; terom@35: char *link, *name; terom@35: uint16_t mode; terom@35: uint32_t ino, parent; terom@38: terom@38: unsigned char is_dir : 1; terom@35: }; terom@35: terom@35: // default mode for symlinks terom@35: #define DBFS_SYMLINK_MODE 0777 terom@35: terom@35: // max. size for an dbfs_mk INSERT query terom@35: #define DBFS_MK_SQL_MAX 512 terom@35: terom@35: void dbfs_mk_free (struct dbfs_trans *ctx_base) { terom@35: struct dbfs_mk_ctx *ctx = (struct dbfs_mk_ctx *) ctx_base; terom@35: terom@35: free(ctx->link); terom@35: free(ctx->name); terom@35: } terom@35: terom@35: void dbfs_mk_commit (struct dbfs_trans *ctx_base) { terom@35: struct dbfs_mk_ctx *ctx = (struct dbfs_mk_ctx *) ctx_base; terom@35: struct fuse_entry_param e; terom@35: int err; terom@35: terom@35: // build entry terom@35: e.ino = e.attr.st_ino = ctx->ino; terom@35: e.attr.st_mode = _dbfs_mode(ctx->type) | ctx->mode; terom@35: e.attr.st_size = ctx->link ? strlen(ctx->link) : 0; terom@35: e.attr.st_nlink = 1; terom@35: e.attr_timeout = e.entry_timeout = CACHE_TIMEOUT; terom@35: terom@35: // reply terom@35: if ((err = fuse_reply_entry(ctx_base->req, &e))) terom@35: goto error; terom@35: terom@35: // req good terom@35: ctx_base->req = NULL; terom@35: terom@35: // free terom@35: dbfs_trans_free(ctx_base); terom@35: terom@35: // return terom@35: return; terom@35: terom@35: error: terom@35: dbfs_trans_fail(ctx_base, err); terom@35: } terom@35: terom@48: void dbfs_mk_filetree (struct evsql_result *res, void *arg) { terom@35: struct dbfs_mk_ctx *ctx = arg; terom@35: int err = EIO; terom@35: terom@35: // check results terom@35: if (_dbfs_check_res(res, 0, 0) < 0) terom@35: goto error; terom@35: terom@35: // commit terom@35: dbfs_trans_commit(&ctx->base); terom@35: terom@35: // fallthrough for result_free terom@35: err = 0; terom@35: terom@35: error: terom@35: if (err) terom@35: dbfs_trans_fail(&ctx->base, err); terom@35: terom@35: evsql_result_free(res); terom@35: } terom@35: terom@48: void dbfs_mk_inode (struct evsql_result *res, void *arg) { terom@35: struct dbfs_mk_ctx *ctx = arg; terom@35: struct dbfs *dbfs_ctx = fuse_req_userdata(ctx->base.req); terom@35: int err = EIO; terom@35: terom@35: // check result terom@35: if ((err = _dbfs_check_res(res, 1, 1))) terom@35: SERROR(err = err > 0 ? ENOENT : EIO); terom@35: terom@35: // get ino terom@35: if (evsql_result_uint32(res, 0, 0, &ctx->ino, 0)) terom@35: goto error; terom@35: terom@35: // insert file_tree entry terom@35: const char *sql = terom@35: "INSERT" terom@38: " INTO file_tree (name, parent, ino, ino_dir)" terom@38: " VALUES ($1::varchar, $2::int4, $3::int4, $4::int4)"; terom@35: terom@35: static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) { terom@35: EVSQL_PARAM ( STRING ), terom@35: EVSQL_PARAM ( UINT32 ), terom@35: EVSQL_PARAM ( UINT32 ), terom@38: EVSQL_PARAM ( UINT32 ), terom@35: terom@35: EVSQL_PARAMS_END terom@35: }; terom@35: terom@35: if (0 terom@35: || evsql_param_string(¶ms, 0, ctx->name) terom@35: || evsql_param_uint32(¶ms, 1, ctx->parent) terom@35: || evsql_param_uint32(¶ms, 2, ctx->ino) terom@38: || ctx->is_dir ? evsql_param_uint32(¶ms, 3, ctx->ino) : evsql_param_null(¶ms, 3) terom@35: ) terom@35: goto error; terom@35: terom@35: // query terom@35: if (evsql_query_params(dbfs_ctx->db, ctx->base.trans, sql, ¶ms, dbfs_mk_filetree, ctx)) terom@35: goto error; terom@35: terom@35: // fallthrough for result_free terom@35: err = 0; terom@35: terom@35: error: terom@35: if (err) terom@35: dbfs_trans_fail(&ctx->base, err); terom@35: terom@35: evsql_result_free(res); terom@35: } terom@35: terom@35: void dbfs_mk_begin (struct dbfs_trans *ctx_base) { terom@35: struct dbfs_mk_ctx *ctx = (struct dbfs_mk_ctx *) ctx_base; terom@35: struct dbfs *dbfs_ctx = fuse_req_userdata(ctx_base->req); terom@35: int ret; terom@35: terom@35: // insert inode terom@35: char sql_buf[DBFS_MK_SQL_MAX]; terom@35: terom@35: if ((ret = snprintf(sql_buf, DBFS_MK_SQL_MAX, terom@35: "INSERT" terom@35: " INTO inodes (type, mode, data, link_path)" terom@35: " VALUES ($1::char(3), $2::int2, %s, $3::varchar)" terom@35: " RETURNING inodes.ino", ctx->data_expr ? ctx->data_expr : "NULL" terom@35: )) >= DBFS_MK_SQL_MAX) terom@35: ERROR("sql_buf is too small: %d", ret); terom@35: terom@35: static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) { terom@35: EVSQL_PARAM ( STRING ), terom@35: EVSQL_PARAM ( UINT16 ), terom@35: EVSQL_PARAM ( STRING ), terom@35: terom@35: EVSQL_PARAMS_END terom@35: }; terom@35: terom@35: if (0 terom@35: || evsql_param_string(¶ms, 0, ctx->type) terom@35: || evsql_param_uint16(¶ms, 1, ctx->mode) terom@35: || evsql_param_string(¶ms, 2, ctx->link) terom@35: ) terom@35: goto error; terom@35: terom@35: if (evsql_query_params(dbfs_ctx->db, ctx_base->trans, sql_buf, ¶ms, dbfs_mk_inode, ctx) == NULL) terom@35: goto error; terom@35: terom@35: return; terom@35: terom@35: error: terom@35: dbfs_trans_fail(ctx_base, EIO); terom@35: } terom@35: terom@35: /* terom@35: * It is assumed that name and link_path must be copied, but type remains useable terom@35: */ terom@38: void dbfs_mk (struct fuse_req *req, fuse_ino_t parent, const char *name, const char *type, uint16_t mode, const char *data_expr, const char *link, unsigned char is_dir) { terom@35: struct dbfs_mk_ctx *ctx = NULL; terom@35: terom@35: // alloc terom@35: if ((ctx = calloc(1, sizeof(*ctx))) == NULL) terom@35: ERROR("calloc"); terom@35: terom@35: // start trans terom@35: if (dbfs_trans_init(&ctx->base, req)) terom@35: goto error; terom@35: terom@35: // callbacks terom@35: ctx->base.free_fn = dbfs_mk_free; terom@35: ctx->base.begin_fn = dbfs_mk_begin; terom@35: ctx->base.commit_fn = dbfs_mk_commit; terom@35: terom@35: // state terom@35: ctx->ino = 0; terom@35: ctx->parent = parent; terom@35: ctx->type = type; terom@35: ctx->data_expr = data_expr; terom@35: ctx->mode = mode; terom@38: ctx->is_dir = is_dir; terom@35: terom@35: // copy volatile strings terom@35: if ( terom@35: (link && (ctx->link = strdup(link)) == NULL) terom@35: || (name && (ctx->name = strdup(name)) == NULL) terom@35: ) terom@35: ERROR("strdup"); terom@35: terom@35: // log terom@38: INFO("[dbfs.mk %p:%p] parent=%lu, name=%s, type=%s, mode=%#04o data_expr=%s link=%s is_dir=%hhd", ctx, req, parent, name, type, mode, data_expr, link, is_dir); terom@35: terom@35: // wait terom@35: return; terom@35: terom@35: error: terom@35: if (ctx) terom@35: dbfs_trans_fail(&ctx->base, EIO); terom@35: } terom@35: terom@35: /* terom@35: * These are all just aliases to dbfs_mk terom@35: */ terom@35: void dbfs_mknod (struct fuse_req *req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev) { terom@35: int err; terom@35: terom@35: if ((mode & S_IFMT) != S_IFREG) terom@35: EERROR(err = EINVAL, "mode is not REG: %#08o", mode); terom@35: terom@38: dbfs_mk(req, parent, name, "REG", mode & 07777, "lo_create(0)", NULL, 0); terom@35: terom@35: return; terom@35: terom@35: error: terom@35: if ((err = fuse_reply_err(req, err))) terom@35: EWARNING(err, "fuse_reply_error"); terom@35: } terom@35: terom@35: void dbfs_mkdir (struct fuse_req *req, fuse_ino_t parent, const char *name, mode_t mode) { terom@38: dbfs_mk(req, parent, name, "DIR", mode, NULL, NULL, 1); terom@35: } terom@35: terom@35: terom@35: void dbfs_symlink (struct fuse_req *req, const char *link, fuse_ino_t parent, const char *name) { terom@38: dbfs_mk(req, parent, name, "LNK", DBFS_SYMLINK_MODE, NULL, link, 0); terom@35: } terom@35: