diff --git a/include/spdk/blob.h b/include/spdk/blob.h index d863e3a77..b29895387 100644 --- a/include/spdk/blob.h +++ b/include/spdk/blob.h @@ -208,6 +208,15 @@ uint64_t spdk_blob_get_num_clusters(struct spdk_blob *blob); struct spdk_blob_opts { uint64_t num_clusters; + /* Number of attributes */ + size_t xattr_count; + /* Array of attribute names. Caller should free this array after use. */ + char **xattr_names; + /* User context passed to get_xattr_value function */ + void *xattr_ctx; + /* Callback that will return value for each attribute name. */ + void (*get_xattr_value)(void *xattr_ctx, const char *name, + const void **value, size_t *value_len); }; /* Initialize an spdk_blob_opts structure to the default blob option values. */ diff --git a/lib/blob/blobstore.c b/lib/blob/blobstore.c index dd36a992f..2fc2b0e40 100644 --- a/lib/blob/blobstore.c +++ b/lib/blob/blobstore.c @@ -87,6 +87,10 @@ void spdk_blob_opts_init(struct spdk_blob_opts *opts) { opts->num_clusters = 0; + opts->xattr_count = 0; + opts->xattr_names = NULL; + opts->xattr_ctx = NULL; + opts->get_xattr_value = NULL; } static struct spdk_blob_data * @@ -2702,6 +2706,29 @@ _spdk_bs_create_blob_cpl(spdk_bs_sequence_t *seq, void *cb_arg, int bserrno) spdk_bs_sequence_finish(seq, bserrno); } +static int +_spdk_blob_set_xattrs(struct spdk_blob *blob, const struct spdk_blob_opts *opts) +{ + uint64_t i; + size_t value_len = 0; + int rc; + const void *value = NULL; + if (opts->xattr_count > 0 && opts->get_xattr_value == NULL) { + return -EINVAL; + } + for (i = 0; i < opts->xattr_count; i++) { + opts->get_xattr_value(opts->xattr_ctx, opts->xattr_names[i], &value, &value_len); + if (value == NULL || value_len == 0) { + return -EINVAL; + } + rc = spdk_blob_set_xattr(blob, opts->xattr_names[i], value, value_len); + if (rc < 0) { + return rc; + } + } + return 0; +} + void spdk_bs_create_blob_ext(struct spdk_blob_store *bs, const struct spdk_blob_opts *opts, spdk_blob_op_with_id_complete cb_fn, void *cb_arg) { @@ -2711,6 +2738,7 @@ void spdk_bs_create_blob_ext(struct spdk_blob_store *bs, const struct spdk_blob_ struct spdk_blob_opts opts_default; spdk_bs_sequence_t *seq; spdk_blob_id id; + int rc; page_idx = spdk_bit_array_find_first_clear(bs->used_md_pages, 0); if (page_idx >= spdk_bit_array_capacity(bs->used_md_pages)) { @@ -2734,7 +2762,12 @@ void spdk_bs_create_blob_ext(struct spdk_blob_store *bs, const struct spdk_blob_ spdk_blob_opts_init(&opts_default); opts = &opts_default; } - + rc = _spdk_blob_set_xattrs(__data_to_blob(blob), opts); + if (rc < 0) { + _spdk_blob_free(blob); + cb_fn(cb_arg, 0, rc); + return; + } spdk_blob_resize(__data_to_blob(blob), opts->num_clusters); cpl.type = SPDK_BS_CPL_TYPE_BLOBID; cpl.u.blobid.cb_fn = cb_fn; diff --git a/test/unit/lib/blob/blob.c/blob_ut.c b/test/unit/lib/blob/blob.c/blob_ut.c index a7fa2b45b..6d3965d0c 100644 --- a/test/unit/lib/blob/blob.c/blob_ut.c +++ b/test/unit/lib/blob/blob.c/blob_ut.c @@ -47,6 +47,9 @@ struct spdk_blob *g_blob; int g_bserrno; struct spdk_xattr_names *g_names; int g_done; +char *g_xattr_names[] = {"first", "second", "third"}; +char *g_xattr_values[] = {"one", "two", "three"}; +uint64_t g_ctx = 1729; bool g_scheduler_delay = false; @@ -2265,6 +2268,133 @@ bs_version(void) CU_ASSERT(super->used_blobid_mask_len == 0); } +static void +_get_xattr_value(void *arg, const char *name, + const void **value, size_t *value_len) +{ + uint64_t i; + + SPDK_CU_ASSERT_FATAL(value_len != NULL); + SPDK_CU_ASSERT_FATAL(value != NULL); + CU_ASSERT(arg == &g_ctx) + + for (i = 0; i < sizeof(g_xattr_names); i++) { + if (!strcmp(name, g_xattr_names[i])) { + *value_len = strlen(g_xattr_values[i]); + *value = g_xattr_values[i]; + break; + } + } +} + +static void +_get_xattr_value_null(void *arg, const char *name, + const void **value, size_t *value_len) +{ + SPDK_CU_ASSERT_FATAL(value_len != NULL); + SPDK_CU_ASSERT_FATAL(value != NULL); + CU_ASSERT(arg == NULL) + + *value_len = 0; + *value = NULL; +} + +static void +blob_set_xattrs(void) +{ + struct spdk_blob_store *bs; + struct spdk_bs_dev *dev; + struct spdk_blob *blob; + struct spdk_blob_opts opts; + spdk_blob_id blobid; + const void *value; + size_t value_len; + int rc; + + dev = init_dev(); + + spdk_bs_init(dev, NULL, bs_op_with_handle_complete, NULL); + CU_ASSERT(g_bserrno == 0); + SPDK_CU_ASSERT_FATAL(g_bs != NULL); + bs = g_bs; + + /* Create blob with extra attributes */ + spdk_blob_opts_init(&opts); + + opts.xattr_names = g_xattr_names; + opts.get_xattr_value = _get_xattr_value; + opts.xattr_count = 3; + opts.xattr_ctx = &g_ctx; + + spdk_bs_create_blob_ext(bs, &opts, blob_op_with_id_complete, NULL); + CU_ASSERT(g_bserrno == 0); + CU_ASSERT(g_blobid != SPDK_BLOBID_INVALID); + blobid = g_blobid; + + spdk_bs_open_blob(bs, blobid, blob_op_with_handle_complete, NULL); + CU_ASSERT(g_bserrno == 0); + SPDK_CU_ASSERT_FATAL(g_blob != NULL); + blob = g_blob; + + /* Get the xattrs */ + value = NULL; + + rc = spdk_blob_get_xattr_value(blob, g_xattr_names[0], &value, &value_len); + CU_ASSERT(rc == 0); + SPDK_CU_ASSERT_FATAL(value != NULL); + CU_ASSERT(value_len == strlen(g_xattr_values[0])); + CU_ASSERT_NSTRING_EQUAL_FATAL(value, g_xattr_values[0], value_len); + + rc = spdk_blob_get_xattr_value(blob, g_xattr_names[1], &value, &value_len); + CU_ASSERT(rc == 0); + SPDK_CU_ASSERT_FATAL(value != NULL); + CU_ASSERT(value_len == strlen(g_xattr_values[1])); + CU_ASSERT_NSTRING_EQUAL((char *)value, g_xattr_values[1], value_len); + + rc = spdk_blob_get_xattr_value(blob, g_xattr_names[2], &value, &value_len); + CU_ASSERT(rc == 0); + SPDK_CU_ASSERT_FATAL(value != NULL); + CU_ASSERT(value_len == strlen(g_xattr_values[2])); + CU_ASSERT_NSTRING_EQUAL((char *)value, g_xattr_values[2], value_len); + + /* Try to get non existing attribute */ + + rc = spdk_blob_get_xattr_value(blob, "foobar", &value, &value_len); + CU_ASSERT(rc == -ENOENT); + + spdk_blob_close(blob, blob_op_complete, NULL); + CU_ASSERT(g_bserrno == 0); + blob = NULL; + g_blob = NULL; + g_blobid = SPDK_BLOBID_INVALID; + + /* NULL callback */ + spdk_blob_opts_init(&opts); + opts.xattr_names = g_xattr_names; + opts.get_xattr_value = NULL; + opts.xattr_count = 1; + opts.xattr_ctx = &g_ctx; + + spdk_bs_create_blob_ext(bs, &opts, blob_op_with_id_complete, NULL); + CU_ASSERT(g_bserrno == -EINVAL); + CU_ASSERT(g_blobid != SPDK_BLOBID_INVALID); + + /* NULL values */ + spdk_blob_opts_init(&opts); + opts.xattr_names = g_xattr_names; + opts.get_xattr_value = _get_xattr_value_null; + opts.xattr_count = 1; + opts.xattr_ctx = NULL; + + spdk_bs_create_blob_ext(bs, &opts, blob_op_with_id_complete, NULL); + CU_ASSERT(g_bserrno == -EINVAL); + + spdk_bs_unload(g_bs, bs_op_complete, NULL); + CU_ASSERT(g_bserrno == 0); + g_bs = NULL; + +} + int main(int argc, char **argv) { CU_pSuite suite = NULL; @@ -2309,7 +2439,8 @@ int main(int argc, char **argv) CU_add_test(suite, "super_block_crc", super_block_crc) == NULL || CU_add_test(suite, "blob_dirty_shutdown", blob_dirty_shutdown) == NULL || CU_add_test(suite, "blob_flags", blob_flags) == NULL || - CU_add_test(suite, "bs_version", bs_version) == NULL + CU_add_test(suite, "bs_version", bs_version) == NULL || + CU_add_test(suite, "blob_set_xattrs", blob_set_xattrs) == NULL ) { CU_cleanup_registry(); return CU_get_error();