void fcgi_delete_file(void) { const char *rev = get_arg("rev"); const sx_hashfs_volume_t *vol; rc_ty s; s = sx_hashfs_volume_by_name(hashfs, volume, &vol); if(s != OK) quit_errmsg(rc2http(s), msg_get_reason()); if(!sx_hashfs_is_or_was_my_volume(hashfs, vol, 0)) quit_errnum(404); if(has_priv(PRIV_CLUSTER)) { /* Request comes in from the cluster: apply locally */ s = sx_hashfs_file_delete(hashfs, vol, path, rev); if(s != OK) quit_errmsg(rc2http(s), msg_get_reason()); CGI_PUTS("\r\n"); } else { /* Request comes in from the user: create job */ job_t job; s = sx_hashfs_filedelete_job(hashfs, uid, vol, path, rev, &job); if(s != OK) quit_errmsg(rc2http(s), msg_get_reason()); send_job_info(job); } }
void fcgi_send_file_revisions(void) { const sx_hashfs_volume_t *vol; const sx_hashfs_file_t *file; int comma = 0; rc_ty s; s = sx_hashfs_volume_by_name(hashfs, volume, &vol); if(s != OK) quit_errmsg(rc2http(s), msg_get_reason()); s = sx_hashfs_revision_first(hashfs, vol, path, &file, 0); if(s != OK) quit_errmsg(rc2http(s), msg_get_reason()); CGI_PUTS("Content-type: application/json\r\n\r\n{\"fileRevisions\":{"); do { CGI_PRINTF("%s\"%s\":{\"blockSize\":%d,\"fileSize\":", comma ? "," : "", file->revision, file->block_size); CGI_PUTLL(file->file_size); CGI_PRINTF(",\"createdAt\":%u}", file->created_at); comma = 1; s = sx_hashfs_revision_next(hashfs, 0); } while(s == OK); if(s == ITER_NO_MORE) CGI_PUTS("}}"); }
void fcgi_upgrade_2_1_4(void) { rc_ty s; const sx_hashfs_volume_t *vol = NULL; const char *startname, *startrev = NULL; struct rplfiles ctx; if(!has_arg("maxrev")) quit_errmsg(400, "Parameter maxrev is required"); startname = strchr(path, '/'); if(!startname) { s = sx_hashfs_volume_by_name(hashfs, path, &vol); } else { unsigned int vnamelen = startname - path; char *vname = malloc(vnamelen + 1); if(!vname) quit_errmsg(503, "Out of memory"); memcpy(vname, path, vnamelen); vname[vnamelen] = '\0'; s = sx_hashfs_volume_by_name(hashfs, vname, &vol); free(vname); startname++; if(strlen(startname)) startrev = get_arg("startrev"); else startname = NULL; } if(s != OK) quit_errmsg(rc2http(s), msg_get_reason()); if(!sx_hashfs_is_or_was_my_volume(hashfs, vol, 0)) quit_errnum(404); ctx.bytes_sent = 0; ctx.b = sx_blob_new(); if(!ctx.b) quit_errmsg(503, "Out of memory"); s = sx_hashfs_file_find(hashfs, vol, startname, startrev, get_arg("maxrev"), upgrade_2_1_4_cb, &ctx); if(s == FAIL_ETOOMANY || s == ITER_NO_MORE) { sx_blob_reset(ctx.b); if(!sx_blob_add_string(ctx.b, "$FILE$") && !sx_blob_add_string(ctx.b, ctx.lastfile.name) && !sx_blob_add_string(ctx.b, ctx.lastfile.revision)) send_rplfiles_header(&ctx); } if(s == ITER_NO_MORE) { sx_blob_reset(ctx.b); if(!sx_blob_add_string(ctx.b, "$THEEND$")) send_rplfiles_header(&ctx); sx_blob_free(ctx.b); return; } sx_blob_free(ctx.b); if(s != FAIL_ETOOMANY && !ctx.bytes_sent) quit_errmsg(rc2http(s), msg_get_reason()); }
void fcgi_create_tempfile(void) { const sx_hashfs_volume_t *vol; rc_ty s = sx_hashfs_putfile_begin(hashfs, uid, volume, path, &vol); if(s != OK) quit_errmsg(rc2http(s), msg_get_reason()); create_or_extend_tempfile(vol, path, 0); }
static void cb_newfile_delmeta(jparse_t *J, void *ctx) { const char *metakey = sxi_jpath_mapkey(sxi_jpath_down(sxi_jparse_whereami(J))); struct cb_newfile_ctx *c = ctx; c->rc = sx_hashfs_putfile_putmeta(hashfs, metakey, NULL, 0); if(c->rc != OK) { const char *reason = msg_get_reason(); sxi_jparse_cancel(J, "'%s'", reason ? reason : "Invalid file metadata"); return; } }
void fcgi_flush_tempfile(void) { job_t job; rc_ty s; auth_complete(); quit_unless_authed(); s = sx_hashfs_putfile_commitjob(hashfs, user, uid, path, &job); if(s != OK) quit_errmsg(rc2http(s), msg_get_reason()); send_job_info(job); return; }
static void cb_newfile_block(jparse_t *J, void *ctx, const char *string, unsigned int length) { struct cb_newfile_ctx *c = (struct cb_newfile_ctx *)ctx; sx_hash_t hash; if(length != SXI_SHA1_TEXT_LEN || hex2bin(string, SXI_SHA1_TEXT_LEN, hash.b, sizeof(hash.b))) { c->rc = EINVAL; sxi_jparse_cancel(J, "Invalid block '%.*s'", length, string); return; } c->rc = sx_hashfs_putfile_putblock(hashfs, &hash); if(c->rc != OK) { const char *reason = msg_get_reason(); sxi_jparse_cancel(J, "Failed to add block '%.*s': %s", length, string, reason ? reason : " (reason unknown)"); return; } }
static void cb_newfile_addmeta(jparse_t *J, void *ctx, const char *string, unsigned int length) { const char *metakey = sxi_jpath_mapkey(sxi_jpath_down(sxi_jparse_whereami(J))); uint8_t metavalue[SXLIMIT_META_MAX_VALUE_LEN]; struct cb_newfile_ctx *c = ctx; if(hex2bin(string, length, metavalue, sizeof(metavalue))) { c->rc = EINVAL; sxi_jparse_cancel(J, "Invalid volume metadata value for key '%s'", metakey); return; } c->rc = sx_hashfs_putfile_putmeta(hashfs, metakey, metavalue, length / 2); if(c->rc) { const char *reason = msg_get_reason(); sxi_jparse_cancel(J, "'%s'", reason ? reason : "Invalid file metadata"); return; } }
static void blockmgr_del_xfer(struct blockmgr_data_t *q, int64_t xfer_id) { sx_uuid_t target; sx_hash_t block; int64_t flowid = FLOW_DEFAULT_UID; rc_ty s; if(verbose_rebalance) { sqlite3_stmt *qinfo = NULL; const void *tptr, *bptr; if(!qprep(sx_hashfs_xferdb(q->hashfs), &qinfo, "SELECT flow, block, node FROM topush WHERE id = :id") && !qbind_int64(qinfo, ":id", xfer_id) && qstep(qinfo) == SQLITE_ROW && (flowid = sqlite3_column_int64(qinfo, 0)) == FLOW_BULK_UID && (bptr = sqlite3_column_blob(qinfo, 1)) && sqlite3_column_bytes(qinfo, 1) == sizeof(block) && (tptr = sqlite3_column_blob(qinfo, 2)) && sqlite3_column_bytes(qinfo, 2) == sizeof(target.binary)) { uuid_from_binary(&target, tptr); memcpy(&block, bptr, sizeof(block)); } else flowid = FLOW_DEFAULT_UID; sqlite3_finalize(qinfo); } s = sx_hashfs_blkrb_release(q->hashfs, xfer_id); if(s != OK && s != ENOENT) { WARN("Failed to release block %lld", (long long)xfer_id); if(flowid == FLOW_BULK_UID) rbl_log(&block, "blkrb_release", 0, "Error %d (%s)", s, msg_get_reason()); } else { if(flowid == FLOW_BULK_UID) rbl_log(&block, "blkrb_release", 1, s == OK ? "Block released" : "Block not locked"); sqlite3_reset(q->qdel); if(qbind_int64(q->qdel, ":id", xfer_id) || qstep_noret(q->qdel)) WARN("Failed to delete transfer %lld", (long long)xfer_id); sqlite3_reset(q->qdel); } }
void handle_request(worker_type_t wtype) { const char *param, *p_method, *p_uri; char *argp; unsigned int plen; int cluster_readonly = 0, s2sreq = 0; if(sx_hashfs_cluster_get_mode(hashfs, &cluster_readonly)) { CRIT("Failed to get cluster operating mode"); quit_errmsg(500, "Internal error: failed to check cluster operating mode"); } if(sx_hashfs_distcheck(hashfs) < 0) { CRIT("Failed to reload distribution"); quit_errmsg(503, "Internal error: failed to load distribution"); } if(sx_hashfs_is_orphan(hashfs)) quit_errmsg(410, "This node is no longer a cluster member"); msg_new_id(); verb = VERB_UNSUP; p_method = FCGX_GetParam("REQUEST_METHOD", envp); if(p_method) { plen = strlen(p_method); switch(plen) { case 3: if(!memcmp(p_method, "GET", 4)) verb = VERB_GET; else if(!memcmp(p_method, "PUT", 4)) verb = VERB_PUT; break; case 4: if(!memcmp(p_method, "HEAD", 5)) verb = VERB_HEAD; else if(!memcmp(p_method, "POST", 5)) verb = VERB_POST; break; case 6: if(!memcmp(p_method, "DELETE", 7)) verb = VERB_DELETE; break; case 7: if(!memcmp(p_method, "OPTIONS", 8)) { CGI_PUTS("Allow: GET,HEAD,OPTIONS,PUT,DELETE\r\nContent-Length: 0\r\n\r\n"); return; } break; } } if(verb == VERB_UNSUP) quit_errmsg(405, "Method Not Allowed"); if(content_len()<0 || (verb != VERB_PUT && content_len())) quit_errmsg(400, "Invalid Content-Length: must be positive and method must be PUT"); p_uri = param = FCGX_GetParam("REQUEST_URI", envp); if(!p_uri) quit_errmsg(400, "No URI provided"); plen = strlen(p_uri); if(*p_uri != '/') quit_errmsg(400, "URI must start with /"); if(plen > sizeof(reqbuf) - 1) quit_errmsg(414, "URL too long: request line must be <8k"); do { param++; plen--; } while(*param == '/'); if(!strncmp(param, ".s2s/", lenof(".s2s/"))) { param += lenof(".s2s/"); plen -= lenof(".s2s/"); while(*param == '/') { param++; plen--; } s2sreq = 1; } if(wtype == WORKER_S2S && !s2sreq) WARN("Misconfiguration detected. Please make sure your restricted-socket config option is properly set."); /* FIXME: we could detect the opposite kind of mismatch * at the cost of extra complications in the wtype definition * I prefer to privilege simplicity at this point */ memcpy(reqbuf, param, plen+1); argp = memchr(reqbuf, '?', plen); nargs = 0; if(argp) { unsigned int argslen = plen - (argp - reqbuf); plen = argp - reqbuf; do { *argp = '\0'; argp++; argslen--; } while(*argp == '?'); if(!argslen) argp = NULL; else { do { char *nextarg; if(nargs >= MAX_ARGS) quit_errmsg(414, "Too many parameters"); nextarg = memchr(argp, '&', argslen); if(nextarg) { do { *nextarg = '\0'; nextarg++; } while(*nextarg == '&'); } if(*argp) { if(!(args[nargs] = inplace_urldecode(argp, 0, 0, NULL, 1))) quit_errmsg(400, "Invalid URL encoding"); if(sxi_utf8_validate_len(args[nargs]) < 0) quit_errmsg(400, "Parameters with invalid utf-8 encoding"); nargs++; } argslen -= nextarg - argp; argp = nextarg; } while (argp); } } while(plen && reqbuf[plen-1] == '/') { plen--; reqbuf[plen] = '\0'; } path = memchr(reqbuf, '/', plen); if(path) { do { *path = '\0'; path ++; } while(*path == '/'); if(!*path) path = NULL; } volume = *reqbuf ? reqbuf : NULL; int forbidden = 0; if((volume && !inplace_urldecode(volume, '/', 0, &forbidden, 0)) || (path && !inplace_urldecode(path, '/', '/', &forbidden, 0))) { if (forbidden) quit_errmsg(400, "Volume or path with forbidden %2f or %00"); else quit_errmsg(400, "Invalid URL encoding"); } int vlen = volume ? sxi_utf8_validate_len(volume) : 0; int flen = path ? strlen(path) : 0; if (vlen < 0 || flen < 0) quit_errmsg(400, "URL with invalid utf-8 encoding"); if (is_reserved()) { /* No UTF8/url-encoding used on reserved volumes, allow higher limit. * Otherwise we hit the 1024 limit with batch requests already */ if (path && strlen(path) > SXLIMIT_MAX_FILENAME_LEN * 3) { msg_set_reason("Path too long: filename must be <%d bytes (%ld)", SXLIMIT_MAX_FILENAME_LEN*3+ 1, strlen(path)); quit_errmsg(414, msg_get_reason()); } } else { if (flen > SXLIMIT_MAX_FILENAME_LEN) { msg_set_reason("Path too long: filename must be <%d bytes (%d)", SXLIMIT_MAX_FILENAME_LEN + 1, flen); quit_errmsg(414, msg_get_reason()); } } if (volume && strlen(volume) > SXLIMIT_MAX_VOLNAME_LEN) { msg_set_reason("Volume name too long: must be <= %d bytes", SXLIMIT_MAX_VOLNAME_LEN); quit_errmsg(414, msg_get_reason()); } body_ctx = sxi_md_init(); if (!body_ctx || !sxi_sha1_init(body_ctx)) quit_errmsg(500, "Failed to initialize crypto engine"); hmac_ctx = sxi_hmac_sha1_init(); if (!hmac_ctx) quit_errmsg(503, "Cannot initialize crypto library"); authed = AUTH_NOTAUTH; role = PRIV_NONE; /* Begin auth check */ uint8_t buf[AUTHTOK_BIN_LEN], key[AUTH_KEY_LEN]; unsigned int blen = sizeof(buf); time_t reqdate, now; param = FCGX_GetParam("HTTP_AUTHORIZATION", envp); if(!param || strlen(param) != lenof("SKY ") + AUTHTOK_ASCII_LEN || strncmp(param, "SKY ", 4)) { if(volume) { send_authreq(); return; } quit_home(); } if(sxi_b64_dec_core(param+4, buf, &blen) || blen != sizeof(buf)) { send_authreq(); return; } memcpy(user, buf, sizeof(user)); memcpy(rhmac, buf+20, sizeof(rhmac)); if(sx_hashfs_get_user_info(hashfs, user, &uid, key, &role, NULL, &user_quota) != OK) /* no such user */ { DEBUG("No such user: %s", param+4); send_authreq(); return; } DEBUG("Request from uid %lld", (long long)uid); if(cluster_readonly && (verb == VERB_PUT || verb == VERB_DELETE) && !has_priv(PRIV_CLUSTER) && !has_priv(PRIV_ADMIN)) quit_errmsg(503, "Cluster is in read-only mode"); if(s2sreq && !has_priv(PRIV_CLUSTER)) { send_authreq(); return; } if(!sxi_hmac_sha1_init_ex(hmac_ctx, key, sizeof(key))) { WARN("hmac_init failed"); quit_errmsg(500, "Failed to initialize crypto engine"); } if(!sxi_hmac_sha1_update_str(hmac_ctx, p_method)) quit_errmsg(500, "Crypto error authenticating the request"); if(!sxi_hmac_sha1_update_str(hmac_ctx, p_uri+1)) quit_errmsg(500, "Crypto error authenticating the request"); param = FCGX_GetParam("HTTP_DATE", envp); if(!param) quit_errmsg(400, "Missing Date: header"); if(httpdate_to_time_t(param, &reqdate)) quit_errmsg(400, "Date header in wrong format"); now = time(NULL); if(reqdate < now - MAX_CLOCK_DRIFT * 60 || reqdate > now + MAX_CLOCK_DRIFT * 60) { CGI_PUTS("WWW-Authenticate: SKY realm=\"SXCLOCK\"\r\n"); quit_errmsg(401, "Client clock drifted more than "STRIFY(MAX_CLOCK_DRIFT)" minutes"); } if(!sxi_hmac_sha1_update_str(hmac_ctx, param)) quit_errmsg(500, "Crypto error authenticating the request"); if(!content_len()) { /* If no body is present, complete authentication now */ uint8_t chmac[20]; unsigned int chmac_len = 20; if(!sxi_hmac_sha1_update_str(hmac_ctx, "da39a3ee5e6b4b0d3255bfef95601890afd80709")) quit_errmsg(500, "Crypto error authenticating the request"); if(!sxi_hmac_sha1_final(hmac_ctx, chmac, &chmac_len)) quit_errmsg(500, "Crypto error authenticating the request"); if(!hmac_compare(chmac, rhmac, sizeof(rhmac))) { authed = AUTH_OK; } else { /* WARN("auth mismatch"); */ send_authreq(); return; } } else /* Otherwise set it as pending */ authed = AUTH_BODYCHECK; if(has_priv(PRIV_CLUSTER) && sx_hashfs_uses_secure_proto(hashfs) != is_https() && !sx_storage_is_bare(hashfs)) { /* programmed nodes: must obey cluster SSL mode * unprogrammed nodes: can use SSL instead of non-SSL, * it is the cluster's responsibility to initiate programming via SSL, * as the unprogrammed node would accept both * * */ WARN("hashfs use-ssl: %d, https: %d, is_bare: %d", sx_hashfs_uses_secure_proto(hashfs), is_https(), sx_storage_is_bare(hashfs)); quit_errmsg(403, sx_hashfs_uses_secure_proto(hashfs) ? "Cluster operations require SECURE mode" : "Cluster operations require INSECURE mode"); } if(!volume) cluster_ops(); else if(!path) volume_ops(); else file_ops(); if(authed == AUTH_BODYCHECKING) DEBUG("Bad request signature"); sxi_hmac_sha1_cleanup(&hmac_ctx); sxi_md_cleanup(&body_ctx); }
void send_partial_error(const char *message, rc_ty rc) { char *reason = *msg_get_reason() ? strdup(msg_get_reason()) : NULL; msg_set_reason("%s: %s", message, reason ? reason : rc2str(rc)); free(reason); send_error_helper(',', 500, msg_get_reason()); }
void handle_request(void) { const char *param; char *argp; unsigned int plen; msg_new_id(); verb = VERB_UNSUP; param = FCGX_GetParam("REQUEST_METHOD", envp); if(param) { plen = strlen(param); switch(plen) { case 3: if(!memcmp(param, "GET", 4)) verb = VERB_GET; else if(!memcmp(param, "PUT", 4)) verb = VERB_PUT; break; case 4: if(!memcmp(param, "HEAD", 5)) verb = VERB_HEAD; else if(!memcmp(param, "POST", 5)) verb = VERB_POST; break; case 6: if(!memcmp(param, "DELETE", 7)) verb = VERB_DELETE; break; case 7: if(!memcmp(param, "OPTIONS", 8)) { CGI_PUTS("Allow: GET,HEAD,OPTIONS,PUT,DELETE\r\nContent-Length: 0\r\n\r\n"); return; } break; } } if(verb == VERB_UNSUP) quit_errmsg(405, "Method Not Allowed"); if(content_len()<0 || (verb != VERB_PUT && content_len())) quit_errmsg(400, "Invalid Content-Length: must be positive and method must be PUT"); param = FCGX_GetParam("REQUEST_URI", envp); if(!param) quit_errmsg(400, "No URI provided"); plen = strlen(param); if(*param != '/') quit_errmsg(400, "URI must start with /"); if(plen > sizeof(reqbuf) - 1) quit_errmsg(414, "URL too long: request line must be <8k"); do { param++; plen--; } while(*param == '/'); memcpy(reqbuf, param, plen+1); argp = memchr(reqbuf, '?', plen); nargs = 0; if(argp) { unsigned int argslen = plen - (argp - reqbuf); plen = argp - reqbuf; do { *argp = '\0'; argp++; argslen--; } while(*argp == '?'); if(!argslen) argp = NULL; else { do { char *nextarg; if(nargs >= MAX_ARGS) quit_errmsg(414, "Too many parameters"); nextarg = memchr(argp, '&', argslen); if(nextarg) { do { *nextarg = '\0'; nextarg++; } while(*nextarg == '&'); } if(*argp) { if(!(args[nargs] = inplace_urldecode(argp, 0, 0, NULL))) quit_errmsg(400, "Invalid URL encoding"); if(utf8_validate_len(args[nargs]) < 0) quit_errmsg(400, "Parameters with invalid utf-8 encoding"); nargs++; } argslen -= nextarg - argp; argp = nextarg; } while (argp); } } while(plen && reqbuf[plen-1] == '/') { plen--; reqbuf[plen] = '\0'; } path = memchr(reqbuf, '/', plen); if(path) { do { *path = '\0'; path ++; } while(*path == '/'); if(!*path) path = NULL; } volume = *reqbuf ? reqbuf : NULL; int forbidden = 0; if((volume && !inplace_urldecode(volume, '/', 0, &forbidden)) || (path && !inplace_urldecode(path, '/', '/', &forbidden))) { if (forbidden) quit_errmsg(400, "Volume or path with forbidden %2f or %00"); else quit_errmsg(400, "Invalid URL encoding"); } int vlen = volume ? utf8_validate_len(volume) : 0; int flen = path ? utf8_validate_len(path) : 0; if (vlen < 0 || flen < 0) quit_errmsg(400, "URL with invalid utf-8 encoding"); if (is_reserved()) { /* No UTF8 used on reserved volumes, allow higher limit. * Otherwise we hit the 512 limit with batch requests already */ if (path && strlen(path) > SXLIMIT_MAX_FILENAME_LEN * 12) { msg_set_reason("Path too long: filename must be <%d characters (%ld)", SXLIMIT_MAX_FILENAME_LEN*12+ 1, strlen(path)); quit_errmsg(414, msg_get_reason()); } } else { if (flen > SXLIMIT_MAX_FILENAME_LEN) { msg_set_reason("Path too long: filename must be <%d UTF8 characters (%d)", SXLIMIT_MAX_FILENAME_LEN + 1, flen); quit_errmsg(414, msg_get_reason()); } } if (volume && strlen(volume) > SXLIMIT_MAX_VOLNAME_LEN) { msg_set_reason("Volume name too long: must be <= %d bytes", SXLIMIT_MAX_VOLNAME_LEN); quit_errmsg(414, msg_get_reason()); } if(!EVP_DigestInit(&body_ctx, EVP_sha1())) quit_errmsg(500, "Failed to initialize crypto engine"); HMAC_CTX_init(&hmac_ctx); authed = AUTH_NOTAUTH; role = PRIV_NONE; auth_begin(); if(has_priv(PRIV_CLUSTER) && sx_hashfs_uses_secure_proto(hashfs) != is_https() && !sx_storage_is_bare(hashfs)) { /* programmed nodes: must obey cluster SSL mode * unprogrammed nodes: can use SSL instead of non-SSL, * it is the cluster's responsibility to initiate programming via SSL, * as the unprogrammed node would accept both * * */ WARN("hashfs use-ssl: %d, https: %d, is_bare: %d", sx_hashfs_uses_secure_proto(hashfs), is_https(), sx_storage_is_bare(hashfs)); quit_errmsg(403, sx_hashfs_uses_secure_proto(hashfs) ? "Cluster operations require SECURE mode" : "Cluster operations require INSECURE mode"); } int dc = sx_hashfs_distcheck(hashfs); if(dc < 0) { CRIT("Failed to reload distribution"); /* MODHDIST: should die here */ } if(!volume) cluster_ops(); else if(!path) volume_ops(); else file_ops(); if(authed == AUTH_BODYCHECKING) WARN("FIXME: Security fail"); HMAC_CTX_cleanup(&hmac_ctx); EVP_MD_CTX_cleanup(&body_ctx); }
static int unbump_revs(struct revunbump_data_t *unb) { int64_t revs[MAX_UNBUMPS]; sqlite3_stmt *q; char *qry = NULL; const void *tgt; unsigned int i, qlen = 0, qat = 0; int64_t unbid; const sx_node_t *target, *me; int r, err = 0, remote; q = unb->quget_hi; sqlite3_reset(q); if(qbind_blob(q, ":oldtarget", (const void *)&unb->last_target, sizeof(unb->last_target))) return -1; /* Error */ r = qstep(q); if(r == SQLITE_DONE) { q = unb->quget_lo; sqlite3_reset(q); if(qbind_blob(q, ":oldtarget", (const void *)&unb->last_target, sizeof(unb->last_target))) return -1; /* Error */ r = qstep(q); if(r == SQLITE_DONE) return 0; /* Work complete */ } if(r != SQLITE_ROW) return -1; /* Error */ unbid = sqlite3_column_int64(q, 0); tgt = sqlite3_column_blob(q, 3); if(!tgt || sqlite3_column_bytes(q, 3) != sizeof(unb->last_target.binary)) { WARN("Removing unbid %lld with bogus target", (long long)unbid); sqlite3_reset(q); return unbump_unq(unb, unbid); } uuid_from_binary(&unb->last_target, tgt); target = sx_nodelist_lookup(sx_hashfs_all_nodes(unb->hashfs, NL_NEXTPREV), &unb->last_target); if(!target) { DEBUG("Removing unbid %lld for target node %s which is no longer a member", (long long)unbid, unb->last_target.string); sqlite3_reset(q); return unbump_unq(unb, unbid); } if(sx_hashfs_is_node_ignored(unb->hashfs, &unb->last_target)) { /* These will be sent once there is a replacement */ DEBUG("Skipping requests for unbid %lld for target node %s which is no longer a member", (long long)unbid, unb->last_target.string); sqlite3_reset(q); return 0; /* Work complete */ } me = sx_hashfs_self(unb->hashfs); remote = sx_node_cmp(me, target); if(!remote && sx_hashfs_revision_op_begin(unb->hashfs)) { WARN("Failed to start revision operation: %s", msg_get_reason()); sqlite3_reset(q); return -1; /* Error */ } for(i=0; i<MAX_UNBUMPS;) { const sx_hash_t *revid; unsigned int bs = sqlite3_column_int(q, 2); if(sx_hashfs_check_blocksize(bs)) WARN("Removing unbid %lld with invalid block size %u", (long long)unbid, bs); else if(!(revid = sqlite3_column_blob(q, 1)) || sqlite3_column_bytes(q, 1) != sizeof(*revid)) WARN("Removing unbid %lld with bogus revision ID", (long long)unbid); else if(!(tgt = sqlite3_column_blob(q, 3)) || sqlite3_column_bytes(q, 3) != sizeof(unb->last_target.binary)) WARN("Removing unbid %lld with bogus target", (long long)unbid); else if(memcmp(tgt, &unb->last_target.binary, sizeof(unb->last_target.binary))) break; else if(remote) { /* Remote target */ if(qlen - qat < sizeof(*revid) * 2 + sizeof(",\"\":") + 32) { /* Make room for hex encoded rev, size and json glue */ qlen += 1024; qry = wrap_realloc_or_free(qry, qlen); if(!qry) { WARN("Unable to allocate query"); err = 1; break; } } qry[qat] = qat ? ',' : '{'; qry[qat+1] = '"'; qat += 2; bin2hex(revid, sizeof(*revid), &qry[qat], qlen - qat); qat += sizeof(*revid)*2; qat += snprintf(&qry[qat], qlen - qat,"\":%u", bs); } else { /* Local target */ if(sx_hashfs_revision_op(unb->hashfs, bs, revid, -1) != OK) { WARN("Failed to unbump local revision"); err = 1; break; } } revs[i++] = sqlite3_column_int64(q, 0); r = qstep(q); if(r == SQLITE_ROW) continue; else if(r != SQLITE_DONE) { WARN("Failed to retrieve next revision"); err = 1; } break; } sqlite3_reset(q); if(!remote) { /* Commit local revision ops... */ if(!err && sx_hashfs_revision_op_commit(unb->hashfs)) { WARN("Failed to commit revision operation: %s", msg_get_reason()); err = 1; } /* ... or rollback on error */ if(err) sx_hashfs_revision_op_rollback(unb->hashfs); } if(err) { free(qry); return -1; /* Error */ } if(remote && qry) { sxi_conns_t *clust = sx_hashfs_conns(unb->hashfs); sxc_client_t *sx = sx_hashfs_client(unb->hashfs); sxi_hostlist_t hlist; int qret; sxi_hostlist_init(&hlist); if(qlen - qat < 2) { qry = wrap_realloc_or_free(qry, qlen + 2); if(!qry) { WARN("Unable to allocate query"); return -1; /* Error */ } } qry[qat] = '}'; qry[qat+1] = '\0'; if(sxi_hostlist_add_host(sx, &hlist, sx_node_internal_addr(target))) { WARN("Unable to allocate hostlist"); free(qry); return -1; /* Error */ } qret = sxi_cluster_query(clust, &hlist, REQ_PUT, ".blockrevs/remove", qry, strlen(qry), NULL, NULL, NULL); free(qry); qry = NULL; sxi_hostlist_empty(&hlist); if(qret != 200) { WARN("Unbump request failed for %s (%s): HTTP status %d", unb->last_target.string, sx_node_internal_addr(target), qret); return -1; } } free(qry); while(i--) unbump_unq(unb, revs[i]); return 1; /* Some work done */ }
void fcgi_extend_tempfile(void) { rc_ty s = sx_hashfs_putfile_extend_begin(hashfs, uid, user, path); if(s != OK) quit_errmsg(rc2http(s), msg_get_reason()); create_or_extend_tempfile(NULL, NULL, 1); }
static void create_or_extend_tempfile(const sx_hashfs_volume_t *vol, const char *filename, int extending) { const struct jparse_actions acts = { JPACTS_INT64( JPACT(cb_newfile_size, JPKEY("fileSize")), JPACT(cb_newfile_seq, JPKEY("extendSeq")) ), JPACTS_STRING( JPACT(cb_newfile_block, JPKEY("fileData"), JPANYITM), JPACT(cb_newfile_addmeta, JPKEY("fileMeta"), JPANYKEY) ), JPACTS_NULL( JPACT(cb_newfile_delmeta, JPKEY("fileMeta"), JPANYKEY) ) }; struct cb_newfile_ctx yctx; hash_presence_ctx_t ctx; const char *token; jparse_t *J; int len; rc_ty s; yctx.filesize = -1; yctx.seq = -1; yctx.rc = EINVAL; J = sxi_jparse_create(&acts, &yctx, 0); if(!J) { sx_hashfs_putfile_end(hashfs); quit_errmsg(503, "Cannot create JSON parser"); } while((len = get_body_chunk(hashbuf, sizeof(hashbuf))) > 0) if(sxi_jparse_digest(J, hashbuf, len)) break; if(len || sxi_jparse_done(J)) { send_error(rc2http(yctx.rc), sxi_jparse_geterr(J)); sx_hashfs_putfile_end(hashfs); sxi_jparse_destroy(J); return; } sxi_jparse_destroy(J); auth_complete(); quit_unless_authed(); s = sx_hashfs_putfile_gettoken(hashfs, user, yctx.filesize, yctx.seq, &token, hash_presence_callback, &ctx); if (s != OK) { sx_hashfs_putfile_end(hashfs); if(!*msg_get_reason()) msg_set_reason("Cannot obtain upload token: %s", rc2str(s)); quit_errmsg((s == ENOSPC) ? 413 : rc2http(s), msg_get_reason()); } CGI_PRINTF("Content-type: application/json\r\n\r\n{\"uploadToken\":"); json_send_qstring(extending ? path : token); CGI_PUTS(",\"uploadData\":{"); ctx.h = hashfs; ctx.comma = 0; while((s = sx_hashfs_putfile_getblock(hashfs)) == OK) { /* Nothing to do here, API does a little bit of work at a time by design * We can stick keepalives in here if we ever need to */ } sx_hashfs_putfile_end(hashfs); CGI_PUTS("}"); if(s != ITER_NO_MORE) { quit_itererr("Failed to send file blocks", s); } CGI_PUTS("}"); }
void fcgi_create_file(void) { const struct jparse_actions acts = { JPACTS_INT64( JPACT(cb_newfile_size, JPKEY("fileSize")) ), JPACTS_STRING( JPACT(cb_newfile_block, JPKEY("fileData"), JPANYITM), JPACT(cb_newfile_addmeta, JPKEY("fileMeta"), JPANYKEY) ), JPACTS_NULL( JPACT(cb_newfile_delmeta, JPKEY("fileMeta"), JPANYKEY) ) }; struct cb_newfile_ctx yctx; jparse_t *J; int len; rc_ty s; sx_hash_t revid; quit_unless_has(PRIV_CLUSTER); /* Just in case */ if(!has_arg("rev") || !has_arg("revid") || strlen(get_arg("revid")) != SXI_SHA1_TEXT_LEN) quit_errmsg(500, "File revision missing"); if(hex2bin(get_arg("revid"), SXI_SHA1_TEXT_LEN, revid.b, sizeof(revid.b))) quit_errmsg(400, "Failed to parse revision ID"); s = sx_hashfs_createfile_begin(hashfs); switch (s) { case OK: break; case ENOENT: quit_errnum(404); case EINVAL: quit_errnum(400); default: WARN("sx_hashfs_createfile_begin failed: %d", s); quit_errmsg(rc2http(s), "Cannot initialize file upload"); } yctx.filesize = -1; yctx.rc = EINVAL; J = sxi_jparse_create(&acts, &yctx, 0); if(!J) { sx_hashfs_createfile_end(hashfs); quit_errmsg(503, "Cannot create JSON parser"); } while((len = get_body_chunk(hashbuf, sizeof(hashbuf))) > 0) if(sxi_jparse_digest(J, hashbuf, len)) break; if(len || sxi_jparse_done(J)) { send_error(rc2http(yctx.rc), sxi_jparse_geterr(J)); sx_hashfs_createfile_end(hashfs); sxi_jparse_destroy(J); return; } sxi_jparse_destroy(J); auth_complete(); quit_unless_authed(); s = sx_hashfs_createfile_commit(hashfs, volume, path, get_arg("rev"), &revid, yctx.filesize, 0); if(s != OK) quit_errmsg(rc2http(s), msg_get_reason()); CGI_PUTS("\r\n"); }