Esempio n. 1
0
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);
    }
}
Esempio n. 2
0
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("}}");
}
Esempio n. 3
0
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());
}
Esempio n. 4
0
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);
}
Esempio n. 5
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;
    }
}
Esempio n. 6
0
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;
}
Esempio n. 7
0
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;
    }
}
Esempio n. 8
0
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;
    }
}
Esempio n. 9
0
File: blockmgr.c Progetto: s3v3ns/sx
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);
    }
}
Esempio n. 10
0
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);
}
Esempio n. 11
0
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());
}
Esempio n. 12
0
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);
}
Esempio n. 13
0
File: blockmgr.c Progetto: s3v3ns/sx
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 */

}
Esempio n. 14
0
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);
}
Esempio n. 15
0
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("}");
}
Esempio n. 16
0
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");
}