cls_method_handle_t h_old_snapshot_remove;
#define RBD_MAX_KEYS_READ 64
-#define RBD_MAX_METAADATA_KEYS 1024
#define RBD_SNAP_KEY_PREFIX "snapshot_"
#define RBD_DIR_ID_KEY_PREFIX "id_"
#define RBD_DIR_NAME_KEY_PREFIX "name_"
/**
* Input:
- * @param in ignored
- *
+ * @param start_after which name to begin listing after
+ * (use the empty string to start at the beginning)
+ * @param max_return the maximum number of names to lis(if 0 means no limit)
+
* Output:
* @param value
* @returns 0 on success, negative error code on failure
*/
int metadata_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
- map<string, bufferlist> raw_data, data;
- int r = cls_cxx_map_get_vals(hctx, RBD_METADATA_KEY_PREFIX, RBD_METADATA_KEY_PREFIX,
- RBD_MAX_METAADATA_KEYS, &raw_data);
- if (r < 0) {
- CLS_ERR("failed to read the vals off of disk: %s", cpp_strerror(r).c_str());
- return r;
- }
+ string start_after;
+ uint64_t max_return;
- bufferlist::iterator iter;
- string v;
- for (map<string, bufferlist>::iterator it = raw_data.begin(); it != raw_data.end(); ++it) {
- assert(it->first.find(RBD_METADATA_KEY_PREFIX, 0) == 0);
- data[metadata_name_from_key(it->first)].swap(it->second);
+ try {
+ bufferlist::iterator iter = in->begin();
+ ::decode(start_after, iter);
+ ::decode(max_return, iter);
+ } catch (const buffer::error &err) {
+ return -EINVAL;
}
+ map<string, bufferlist> data;
+ string last_read = metadata_key_for_name(start_after);
+ int max_read = max_return ? MIN(RBD_MAX_KEYS_READ, max_return) : RBD_MAX_KEYS_READ;
+ int r;
+
+ do {
+ map<string, bufferlist> raw_data;
+ r = cls_cxx_map_get_vals(hctx, last_read, RBD_METADATA_KEY_PREFIX,
+ max_read, &raw_data);
+ if (r < 0) {
+ CLS_ERR("failed to read the vals off of disk: %s", cpp_strerror(r).c_str());
+ return r;
+ }
+ if (raw_data.empty())
+ break;
+
+ map<string, bufferlist>::iterator it = raw_data.begin();
+ if (metadata_name_from_key(it->first) == last_read)
+ ++it;
+ for (; it != raw_data.end(); ++it)
+ data[metadata_name_from_key(it->first)].swap(it->second);
+
+ last_read = raw_data.rbegin()->first;
+ if (max_return)
+ max_read = MIN(RBD_MAX_KEYS_READ, max_return-data.size());
+ } while (max_return && max_read);
+
::encode(data, *out);
return 0;
}
}
int metadata_list(librados::IoCtx *ioctx, const std::string &oid,
- map<string, bufferlist> *pairs)
+ const std::string &start, uint64_t max_return,
+ map<string, bufferlist> *pairs)
{
assert(pairs);
bufferlist in, out;
+ ::encode(start, in);
+ ::encode(max_return, in);
int r = ioctx->exec(oid, "rbd", "metadata_list", in, out);
if (r < 0)
return r;
int set_stripe_unit_count(librados::IoCtx *ioctx, const std::string &oid,
uint64_t stripe_unit, uint64_t stripe_count);
int metadata_list(librados::IoCtx *ioctx, const std::string &oid,
+ const std::string &start, uint64_t max_return,
map<string, bufferlist> *pairs);
int metadata_set(librados::IoCtx *ioctx, const std::string &oid,
const map<std::string, bufferlist> &data);
* of the image, with a '\0' after each.
*
* @param image which image (and implicitly snapshot) to list clones of
- * @param pools buffer in which to store pool names
- * @param pools_len number of bytes in pools buffer
- * @param images buffer in which to store image names
- * @param images_len number of bytes in images buffer
+ * @param start_after which name to begin listing after
+ * (use the empty string to start at the beginning)
+ * @param max the maximum number of names to lis(if 0 means no limit)
+ * @param keys buffer in which to store pool names
+ * @param keys_len number of bytes in pools buffer
+ * @param values buffer in which to store image names
+ * @param vals_len number of bytes in images buffer
* @returns number of children on success, negative error code on failure
* @returns -ERANGE if either buffer is too short
*/
-CEPH_RBD_API int rbd_metadata_list(rbd_image_t image, char *key, size_t *key_len, char *value, size_t *val_len);
+CEPH_RBD_API int rbd_metadata_list(rbd_image_t image, const char *start, uint64_t max,
+ char *keys, size_t *key_len, char *values, size_t *vals_len);
#ifdef __cplusplus
/**
* Returns a pair of key/value for this image
*/
- int metadata_list(std::map<std::string, ceph::bufferlist> *pairs);
+ int metadata_list(const std::string &start, uint64_t max, std::map<std::string, ceph::bufferlist> *pairs);
private:
friend class RBD;
goto err_close_child;
}
- r = cls_client::metadata_list(&p_ioctx, p_imctx->header_oid, &pairs);
+ r = cls_client::metadata_list(&p_ioctx, p_imctx->header_oid, "", 0, &pairs);
if (r < 0) {
lderr(cct) << "couldn't list metadata: " << r << dendl;
goto err_close_child;
int r;
map<string, bufferlist> pairs;
- r = cls_client::metadata_list(&src->md_ctx, src->header_oid, &pairs);
+ r = cls_client::metadata_list(&src->md_ctx, src->header_oid, "", 0, &pairs);
if (r < 0) {
lderr(cct) << "couldn't list metadata: " << r << dendl;
return r;
return cls_client::metadata_remove(&ictx->md_ctx, ictx->header_oid, key);
}
- int metadata_list(ImageCtx *ictx, map<string, bufferlist> *pairs)
+ int metadata_list(ImageCtx *ictx, const string &start, uint64_t max, map<string, bufferlist> *pairs)
{
CephContext *cct = ictx->cct;
ldout(cct, 20) << "metadata_list " << ictx << dendl;
return r;
}
- return cls_client::metadata_list(&ictx->md_ctx, ictx->header_oid, pairs);
+ return cls_client::metadata_list(&ictx->md_ctx, ictx->header_oid, start, max, pairs);
}
int aio_discard(ImageCtx *ictx, uint64_t off, uint64_t len, AioCompletion *c)
int flush(ImageCtx *ictx);
int _flush(ImageCtx *ictx);
int invalidate_cache(ImageCtx *ictx);
- int metadata_list(ImageCtx *ictx, map<string, bufferlist> *pairs);
+ int metadata_list(ImageCtx *ictx, const string &last, uint64_t max, map<string, bufferlist> *pairs);
int metadata_get(ImageCtx *ictx, const std::string &key, std::string *value);
int metadata_set(ImageCtx *ictx, const std::string &key, const std::string &value);
int metadata_remove(ImageCtx *ictx, const std::string &key);
return r;
}
- int Image::metadata_list(map<string, bufferlist> *pairs)
+ int Image::metadata_list(const std::string &start, uint64_t max, map<string, bufferlist> *pairs)
{
ImageCtx *ictx = (ImageCtx *)ctx;
tracepoint(librbd, metadata_list_enter, ictx);
- int r = librbd::metadata_list(ictx, pairs);
+ int r = librbd::metadata_list(ictx, start, max, pairs);
if (r >= 0) {
for (map<string, bufferlist>::iterator it = pairs->begin();
it != pairs->end(); ++it) {
return r;
}
-extern "C" int rbd_metadata_list(rbd_image_t image, char *key, size_t *key_len, char *value, size_t *val_len)
+extern "C" int rbd_metadata_list(rbd_image_t image, const char *start, uint64_t max,
+ char *key, size_t *key_len, char *value, size_t *val_len)
{
librbd::ImageCtx *ictx = (librbd::ImageCtx *)image;
tracepoint(librbd, metadata_list_enter, ictx);
map<string, bufferlist> pairs;
- int r = librbd::metadata_list(ictx, &pairs);
+ int r = librbd::metadata_list(ictx, start, max, &pairs);
size_t key_total_len = 0, val_total_len = 0;
bool too_short = false;
for (map<string, bufferlist>::iterator it = pairs.begin();
int r;
TextTable tbl;
- r = image.metadata_list(&pairs);
+ r = image.metadata_list("", 0, &pairs);
if (r < 0)
return r;
ioctx.close();
}
-
TEST_F(TestClsRbd, metadata)
{
librados::IoCtx ioctx;
map<string, bufferlist> pairs;
string value;
- ASSERT_EQ(0, metadata_list(&ioctx, oid, &pairs));
+ ASSERT_EQ(0, metadata_list(&ioctx, oid, "", 0, &pairs));
ASSERT_TRUE(pairs.empty());
pairs["key1"].append("value1");
ASSERT_EQ(0, metadata_get(&ioctx, oid, "key1", &value));
ASSERT_EQ(0, strcmp("value1", value.c_str()));
pairs.clear();
- ASSERT_EQ(0, metadata_list(&ioctx, oid, &pairs));
+ ASSERT_EQ(0, metadata_list(&ioctx, oid, "", 0, &pairs));
ASSERT_EQ(2U, pairs.size());
ASSERT_EQ(0, strncmp("value1", pairs["key1"].c_str(), 6));
ASSERT_EQ(0, strncmp("value2", pairs["key2"].c_str(), 6));
ASSERT_EQ(0, metadata_remove(&ioctx, oid, "key1"));
ASSERT_EQ(0, metadata_remove(&ioctx, oid, "key3"));
ASSERT_TRUE(metadata_get(&ioctx, oid, "key1", &value) < 0);
- ASSERT_EQ(0, metadata_list(&ioctx, oid, &pairs));
+ ASSERT_EQ(0, metadata_list(&ioctx, oid, "", 0, &pairs));
ASSERT_EQ(1U, pairs.size());
ASSERT_EQ(0, strncmp("value2", pairs["key2"].c_str(), 6));
+ pairs.clear();
+ char key[10], val[20];
+ for (int i = 0; i < 1024; i++) {
+ sprintf(key, "key%d", i);
+ sprintf(val, "value%d", i);
+ pairs[key].append(val, strlen(val));
+ }
+ ASSERT_EQ(0, metadata_set(&ioctx, oid, pairs));
+
+ string last_read = "";
+ uint64_t max_read = 48, r;
+ uint64_t size = 0;
+ map<string, bufferlist> data;
+ do {
+ map<string, bufferlist> cur;
+ metadata_list(&ioctx, oid, last_read, max_read, &cur);
+ size += cur.size();
+ for (map<string, bufferlist>::iterator it = cur.begin();
+ it != cur.end(); ++it)
+ data[it->first] = it->second;
+ last_read = cur.rbegin()->first;
+ r = cur.size();
+ } while (r == max_read);
+ ASSERT_EQ(size, 1024U);
+ for (map<string, bufferlist>::iterator it = data.begin();
+ it != data.end(); ++it) {
+ ASSERT_TRUE(it->second.contents_equal(pairs[it->first]));
+ }
+
ioctx.close();
}
librbd::Image image1;
ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
map<string, bufferlist> pairs;
- ASSERT_EQ(0, image1.metadata_list(&pairs));
+ ASSERT_EQ(0, image1.metadata_list("", 0, &pairs));
ASSERT_TRUE(pairs.empty());
ASSERT_EQ(0, image1.metadata_set("key1", "value1"));
ASSERT_EQ(0, image1.metadata_set("key2", "value2"));
ASSERT_EQ(0, image1.metadata_get("key1", &value));
ASSERT_EQ(0, strcmp("value1", value.c_str()));
- ASSERT_EQ(0, image1.metadata_list(&pairs));
+ ASSERT_EQ(0, image1.metadata_list("", 0, &pairs));
ASSERT_EQ(2U, pairs.size());
- ASSERT_EQ(0, strcmp("value1", pairs["key1"].c_str()));
- ASSERT_EQ(0, strcmp("value2", pairs["key2"].c_str()));
+ const char * ddd = pairs["key1"].c_str();
+ ASSERT_EQ(0, strncmp("value1", pairs["key1"].c_str(), 6));
+ ASSERT_EQ(0, strncmp("value2", pairs["key2"].c_str(), 6));
pairs.clear();
ASSERT_EQ(0, image1.metadata_remove("key1"));
ASSERT_EQ(0, image1.metadata_remove("key3"));
ASSERT_TRUE(image1.metadata_get("key3", &value) < 0);
- ASSERT_EQ(0, image1.metadata_list(&pairs));
+ ASSERT_EQ(0, image1.metadata_list("", 0, &pairs));
ASSERT_EQ(1U, pairs.size());
- ASSERT_EQ(0, strcmp("value2", pairs["key2"].c_str()));
+ ASSERT_EQ(0, strncmp("value2", pairs["key2"].c_str(), 6));
// test metadata with snapshot adding
ASSERT_EQ(0, image1.snap_create("snap1"));
pairs.clear();
ASSERT_EQ(0, image1.metadata_set("key1", "value1"));
ASSERT_EQ(0, image1.metadata_set("key3", "value3"));
- ASSERT_EQ(0, image1.metadata_list(&pairs));
+ ASSERT_EQ(0, image1.metadata_list("", 0, &pairs));
ASSERT_EQ(3U, pairs.size());
- ASSERT_EQ(0, strcmp("value1", pairs["key1"].c_str()));
- ASSERT_EQ(0, strcmp("value2", pairs["key2"].c_str()));
- ASSERT_EQ(0, strcmp("value3", pairs["key3"].c_str()));
+ ASSERT_EQ(0, strncmp("value1", pairs["key1"].c_str(), 6));
+ ASSERT_EQ(0, strncmp("value2", pairs["key2"].c_str(), 6));
+ ASSERT_EQ(0, strncmp("value3", pairs["key3"].c_str(), 6));
ASSERT_EQ(0, image1.snap_set(NULL));
- ASSERT_EQ(0, image1.metadata_list(&pairs));
+ ASSERT_EQ(0, image1.metadata_list("", 0, &pairs));
ASSERT_EQ(3U, pairs.size());
- ASSERT_EQ(0, strcmp("value1", pairs["key1"].c_str()));
- ASSERT_EQ(0, strcmp("value2", pairs["key2"].c_str()));
- ASSERT_EQ(0, strcmp("value3", pairs["key3"].c_str()));
+ ASSERT_EQ(0, strncmp("value1", pairs["key1"].c_str(), 6));
+ ASSERT_EQ(0, strncmp("value2", pairs["key2"].c_str(), 6));
+ ASSERT_EQ(0, strncmp("value3", pairs["key3"].c_str(), 6));
// test metadata with cloning
string cname = get_temp_image_name();
ASSERT_EQ(0, rbd.open(ioctx, image2, cname.c_str(), NULL));
ASSERT_EQ(0, image2.metadata_set("key4", "value4"));
pairs.clear();
- ASSERT_EQ(0, image2.metadata_list(&pairs));
+ ASSERT_EQ(0, image2.metadata_list("", 0, &pairs));
ASSERT_EQ(4U, pairs.size());
pairs.clear();
- ASSERT_EQ(0, image1.metadata_list(&pairs));
+ ASSERT_EQ(0, image1.metadata_list("", 0, &pairs));
ASSERT_EQ(3U, pairs.size());
ASSERT_EQ(-ENOENT, image1.metadata_get("key4", &value));
}