void load_cb (flux_t *h, flux_msg_handler_t *mh, const flux_msg_t *msg, void *arg) { sqlite_ctx_t *ctx = arg; const char *blobref = "-"; int blobref_size; uint8_t hash[BLOBREF_MAX_DIGEST_SIZE]; int hash_len; const void *data = NULL; int size = 0; int uncompressed_size; int rc = -1; int old_state; //delay cancellation to ensure lock-correctness in sqlite pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state); if (flux_request_decode_raw (msg, NULL, (const void **)&blobref, &blobref_size) < 0) { flux_log_error (h, "load: request decode failed"); goto done; } if (!blobref || blobref[blobref_size - 1] != '\0') { errno = EPROTO; flux_log_error (h, "load: malformed blobref"); goto done; } if ((hash_len = blobref_strtohash (blobref, hash, sizeof (hash))) < 0) { errno = ENOENT; flux_log_error (h, "load: unexpected foreign blobref"); goto done; } if (sqlite3_bind_text (ctx->load_stmt, 1, (char *)hash, hash_len, SQLITE_STATIC) != SQLITE_OK) { log_sqlite_error (ctx, "load: binding key"); set_errno_from_sqlite_error (ctx); goto done; } if (sqlite3_step (ctx->load_stmt) != SQLITE_ROW) { //log_sqlite_error (ctx, "load: executing stmt"); errno = ENOENT; goto done; } size = sqlite3_column_bytes (ctx->load_stmt, 0); if (sqlite3_column_type (ctx->load_stmt, 0) != SQLITE_BLOB && size > 0) { flux_log (h, LOG_ERR, "load: selected value is not a blob"); errno = EINVAL; goto done; } data = sqlite3_column_blob (ctx->load_stmt, 0); if (sqlite3_column_type (ctx->load_stmt, 1) != SQLITE_INTEGER) { flux_log (h, LOG_ERR, "load: selected value is not an integer"); errno = EINVAL; goto done; } uncompressed_size = sqlite3_column_int (ctx->load_stmt, 1); if (uncompressed_size != -1) { if (ctx->lzo_bufsize < uncompressed_size && grow_lzo_buf (ctx, uncompressed_size) < 0) goto done; int r = LZ4_decompress_safe (data, ctx->lzo_buf, size, uncompressed_size); if (r < 0) { errno = EINVAL; goto done; } if (r != uncompressed_size) { flux_log (h, LOG_ERR, "load: blob size mismatch"); errno = EINVAL; goto done; } data = ctx->lzo_buf; size = uncompressed_size; } rc = 0; done: if (rc < 0) { if (flux_respond_error (h, msg, errno, NULL) < 0) flux_log_error (h, "load: flux_respond_error"); } else { if (flux_respond_raw (h, msg, data, size) < 0) flux_log_error (h, "load: flux_respond_raw"); } (void )sqlite3_reset (ctx->load_stmt); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_state); }
void store_cb (flux_t *h, flux_msg_handler_t *mh, const flux_msg_t *msg, void *arg) { sqlite_ctx_t *ctx = arg; const void *data; int size, hash_len; uint8_t hash[BLOBREF_MAX_DIGEST_SIZE]; char blobref[BLOBREF_MAX_STRING_SIZE] = "-"; int uncompressed_size = -1; int rc = -1; int old_state; //delay cancellation to ensure lock-correctness in sqlite pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state); if (flux_request_decode_raw (msg, NULL, &data, &size) < 0) { flux_log_error (h, "store: request decode failed"); goto done; } if (size > ctx->blob_size_limit) { errno = EFBIG; goto done; } if (blobref_hash (ctx->hashfun, (uint8_t *)data, size, blobref, sizeof (blobref)) < 0) goto done; if ((hash_len = blobref_strtohash (blobref, hash, sizeof (hash))) < 0) goto done; if (size >= compression_threshold) { int r; int out_len = LZ4_compressBound(size); if (ctx->lzo_bufsize < out_len && grow_lzo_buf (ctx, out_len) < 0) goto done; r = LZ4_compress_default (data, ctx->lzo_buf, size, out_len); if (r == 0) { errno = EINVAL; goto done; } uncompressed_size = size; size = r; data = ctx->lzo_buf; } if (sqlite3_bind_text (ctx->store_stmt, 1, (char *)hash, hash_len, SQLITE_STATIC) != SQLITE_OK) { log_sqlite_error (ctx, "store: binding key"); set_errno_from_sqlite_error (ctx); goto done; } if (sqlite3_bind_int (ctx->store_stmt, 2, uncompressed_size) != SQLITE_OK) { log_sqlite_error (ctx, "store: binding size"); set_errno_from_sqlite_error (ctx); goto done; } if (sqlite3_bind_blob (ctx->store_stmt, 3, data, size, SQLITE_STATIC) != SQLITE_OK) { log_sqlite_error (ctx, "store: binding data"); set_errno_from_sqlite_error (ctx); goto done; } if (sqlite3_step (ctx->store_stmt) != SQLITE_DONE && sqlite3_errcode (ctx->db) != SQLITE_CONSTRAINT) { log_sqlite_error (ctx, "store: executing stmt"); set_errno_from_sqlite_error (ctx); goto done; } rc = 0; done: if (rc < 0) { if (flux_respond_error (h, msg, errno, NULL) < 0) flux_log_error (h, "store: flux_respond_error"); } else { if (flux_respond_raw (h, msg, blobref, strlen (blobref) + 1) < 0) flux_log_error (h, "store: flux_respond_raw"); } (void) sqlite3_reset (ctx->store_stmt); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_state); }
int main(int argc, char** argv) { blobref_t ref; blobref_t ref2; uint8_t digest[BLOBREF_MAX_DIGEST_SIZE]; uint8_t data[1024]; plan (NO_PLAN); memset (data, 7, sizeof (data)); /* invalid args */ errno = 0; ok (blobref_hash ("nerf", data, sizeof (data), ref) < 0 && errno == EINVAL, "blobref_hash fails EINVAL with unknown hash name"); errno = 0; ok (blobref_strtohash (badref[0], digest, sizeof (digest)) < 0 && errno == EINVAL, "blobref_strtohash fails EINVAL with unknown hash prefix"); errno = 0; ok (blobref_strtohash (badref[1], digest, sizeof (digest)) < 0 && errno == EINVAL, "blobref_strtohash fails EINVAL with missing hash prefix separator"); errno = 0; ok (blobref_strtohash (badref[2], digest, sizeof (digest)) < 0 && errno == EINVAL, "blobref_strtohash fails EINVAL with wrong blobref length for prefix"); errno = 0; ok (blobref_strtohash (badref[3], digest, sizeof (digest)) < 0 && errno == EINVAL, "blobref_strtohash fails EINVAL with out of range blobref chars"); errno = 0; ok (blobref_strtohash (goodref[0], digest, 2) < 0 && errno == EINVAL, "blobref_strtohash fails EINVAL with runt digest size"); memset (digest, 6, sizeof (digest)); errno = 0; ok (blobref_hashtostr ("nerf", digest, SHA1_DIGEST_SIZE, ref) < 0 && errno == EINVAL, "blobref_hashtostr fails EINVAL with unknown hash"); errno = 0; ok (blobref_hashtostr ("sha1", digest, SHA256_BLOCK_SIZE, ref) < 0 && errno == EINVAL, "blobref_hashtostr fails EINVAL with wrong digest size for hash"); /* sha1 */ ok (blobref_hash ("sha1", NULL, 0, ref) == 0, "blobref_hash sha1 handles zero length data"); diag ("%s", ref); ok (blobref_hash ("sha1", data, sizeof (data), ref) == 0, "blobref_hash sha1 works"); diag ("%s", ref); ok (blobref_strtohash (ref, digest, sizeof (digest)) == SHA1_DIGEST_SIZE, "blobref_strtohash returns expected size hash"); ok (blobref_hashtostr ("sha1", digest, SHA1_DIGEST_SIZE, ref2) == 0, "blobref_hashtostr back again works"); diag ("%s", ref2); ok (strcmp (ref, ref2) == 0, "and blobrefs match"); /* sha256 */ ok (blobref_hash ("sha256", NULL, 0, ref) == 0, "blobref_hash sha256 handles zero length data"); diag ("%s", ref); ok (blobref_hash ("sha256", data, sizeof (data), ref) == 0, "blobref_hash sha256 works"); diag ("%s", ref); ok (blobref_strtohash (ref, digest, sizeof (digest)) == SHA256_BLOCK_SIZE, "blobref_strtohash returns expected size hash"); ok (blobref_hashtostr ("sha256", digest, SHA256_BLOCK_SIZE, ref2) == 0, "blobref_hashtostr back again works"); diag ("%s", ref2); ok (strcmp (ref, ref2) == 0, "and blobrefs match"); /* blobref_validate */ const char **pp; pp = &goodref[0]; while (*pp) { ok (blobref_validate (*pp) == 0, "blobref_validate: %s", *pp); pp++; } pp = &badref[0]; while (*pp) { errno = 0; ok (blobref_validate (*pp) < 0 && errno == EINVAL, "blobref_validate not: %s", *pp); pp++; } /* blobref_validate_hashtype */ ok (blobref_validate_hashtype ("sha1") == 0, "blobref_validate_hashtype sha1 is valid"); ok (blobref_validate_hashtype ("sha256") == 0, "blobref_validate_hashtype sha256 is valid"); ok (blobref_validate_hashtype ("nerf") == -1, "blobref_validate_hashtype nerf is invalid"); ok (blobref_validate_hashtype (NULL) == -1, "blobref_validate_hashtype NULL is invalid"); done_testing(); }