ceph/src/cls_rbd.cc
Josh Durgin 2e366ea8aa OSD: deprecate CLS_METHOD_PUBLIC flag
Remove all existing usage, but leave the definition so third-party
class plugins don't break.

The public flag let *any* user execute a class method, as long
as they had read and/or write access as the method required. This is
better managed by the new osd caps infrastructure, and it was
entirely undocumented and unused, so it should be safe to remove.

Signed-off-by: Josh Durgin <josh.durgin@inktank.com>
2012-10-02 15:43:37 -07:00

1996 lines
51 KiB
C++

// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/** \file
*
* This is an OSD class that implements methods for
* use with rbd.
*
* Most of these deal with the rbd header object. Methods prefixed
* with old_ deal with the original rbd design, in which clients read
* and interpreted the header object directly.
*
* The new format is meant to be opaque to clients - all their
* interactions with non-data objects should go through this
* class. The OSD class interface leaves the class to implement its
* own argument and payload serialization/deserialization, so for ease
* of implementation we use the existing ceph encoding/decoding
* methods. Something like json might be preferable, but the rbd
* kernel module has to be able understand format as well. The
* datatypes exposed to the clients are strings, unsigned integers,
* and vectors of those types. The on-wire format can be found in
* src/include/encoding.h.
*
* The methods for interacting with the new format document their
* parameters as the client sees them - it would be silly to mention
* in each one that they take an input and an output bufferlist.
*/
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <errno.h>
#include <iostream>
#include <map>
#include <sstream>
#include <vector>
#include "include/types.h"
#include "objclass/objclass.h"
#include "include/rbd_types.h"
#include "librbd/cls_rbd.h"
CLS_VER(2,0)
CLS_NAME(rbd)
cls_handle_t h_class;
cls_method_handle_t h_create;
cls_method_handle_t h_get_features;
cls_method_handle_t h_get_size;
cls_method_handle_t h_set_size;
cls_method_handle_t h_get_parent;
cls_method_handle_t h_set_parent;
cls_method_handle_t h_get_protection_status;
cls_method_handle_t h_set_protection_status;
cls_method_handle_t h_remove_parent;
cls_method_handle_t h_add_child;
cls_method_handle_t h_remove_child;
cls_method_handle_t h_get_children;
cls_method_handle_t h_get_snapcontext;
cls_method_handle_t h_get_object_prefix;
cls_method_handle_t h_get_snapshot_name;
cls_method_handle_t h_snapshot_add;
cls_method_handle_t h_snapshot_remove;
cls_method_handle_t h_get_all_features;
cls_method_handle_t h_copyup;
cls_method_handle_t h_get_id;
cls_method_handle_t h_set_id;
cls_method_handle_t h_dir_get_id;
cls_method_handle_t h_dir_get_name;
cls_method_handle_t h_dir_list;
cls_method_handle_t h_dir_add_image;
cls_method_handle_t h_dir_remove_image;
cls_method_handle_t h_dir_rename_image;
cls_method_handle_t h_old_snapshots_list;
cls_method_handle_t h_old_snapshot_add;
cls_method_handle_t h_old_snapshot_remove;
cls_method_handle_t h_assign_bid;
#define RBD_MAX_KEYS_READ 64
#define RBD_SNAP_KEY_PREFIX "snapshot_"
#define RBD_DIR_ID_KEY_PREFIX "id_"
#define RBD_DIR_NAME_KEY_PREFIX "name_"
static int snap_read_header(cls_method_context_t hctx, bufferlist& bl)
{
unsigned snap_count = 0;
uint64_t snap_names_len = 0;
int rc;
struct rbd_obj_header_ondisk *header;
CLS_LOG(20, "snapshots_list");
while (1) {
int len = sizeof(*header) +
snap_count * sizeof(struct rbd_obj_snap_ondisk) +
snap_names_len;
rc = cls_cxx_read(hctx, 0, len, &bl);
if (rc < 0)
return rc;
header = (struct rbd_obj_header_ondisk *)bl.c_str();
if ((snap_count != header->snap_count) ||
(snap_names_len != header->snap_names_len)) {
snap_count = header->snap_count;
snap_names_len = header->snap_names_len;
bl.clear();
continue;
}
break;
}
return 0;
}
static void key_from_snap_id(snapid_t snap_id, string *out)
{
ostringstream oss;
oss << RBD_SNAP_KEY_PREFIX
<< std::setw(16) << std::setfill('0') << std::hex << snap_id;
*out = oss.str();
}
static snapid_t snap_id_from_key(const string &key)
{
istringstream iss(key);
uint64_t id;
iss.ignore(strlen(RBD_SNAP_KEY_PREFIX)) >> std::hex >> id;
return id;
}
template<typename T>
static int read_key(cls_method_context_t hctx, const string &key, T *out)
{
bufferlist bl;
int r = cls_cxx_map_get_val(hctx, key, &bl);
if (r < 0) {
if (r != -ENOENT) {
CLS_ERR("error reading omap key %s: %d", key.c_str(), r);
}
return r;
}
try {
bufferlist::iterator it = bl.begin();
::decode(*out, it);
} catch (const buffer::error &err) {
CLS_ERR("error decoding %s", key.c_str());
return -EIO;
}
return 0;
}
static bool is_valid_id(const string &id) {
if (!id.size())
return false;
for (size_t i = 0; i < id.size(); ++i) {
if (!isalnum(id[i])) {
return false;
}
}
return true;
}
/**
* Initialize the header with basic metadata.
* Extra features may initialize more fields in the future.
* Everything is stored as key/value pairs as omaps in the header object.
*
* If features the OSD does not understand are requested, -ENOSYS is
* returned.
*
* Input:
* @param size number of bytes in the image (uint64_t)
* @param order bits to shift to determine the size of data objects (uint8_t)
* @param features what optional things this image will use (uint64_t)
* @param object_prefix a prefix for all the data objects
*
* Output:
* @return 0 on success, negative error code on failure
*/
int create(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
string object_prefix;
uint64_t features, size;
uint8_t order;
try {
bufferlist::iterator iter = in->begin();
::decode(size, iter);
::decode(order, iter);
::decode(features, iter);
::decode(object_prefix, iter);
} catch (const buffer::error &err) {
return -EINVAL;
}
CLS_LOG(20, "create object_prefix=%s size=%llu order=%u features=%llu",
object_prefix.c_str(), size, order, features);
if (features & ~RBD_FEATURES_ALL) {
return -ENOSYS;
}
if (!object_prefix.size()) {
return -EINVAL;
}
bufferlist stored_prefixbl;
int r = cls_cxx_map_get_val(hctx, "object_prefix", &stored_prefixbl);
if (r != -ENOENT) {
CLS_ERR("reading object_prefix returned %d", r);
return -EEXIST;
}
bufferlist sizebl;
::encode(size, sizebl);
r = cls_cxx_map_set_val(hctx, "size", &sizebl);
if (r < 0)
return r;
bufferlist orderbl;
::encode(order, orderbl);
r = cls_cxx_map_set_val(hctx, "order", &orderbl);
if (r < 0)
return r;
bufferlist featuresbl;
::encode(features, featuresbl);
r = cls_cxx_map_set_val(hctx, "features", &featuresbl);
if (r < 0)
return r;
bufferlist object_prefixbl;
::encode(object_prefix, object_prefixbl);
r = cls_cxx_map_set_val(hctx, "object_prefix", &object_prefixbl);
if (r < 0)
return r;
bufferlist snap_seqbl;
uint64_t snap_seq = 0;
::encode(snap_seq, snap_seqbl);
r = cls_cxx_map_set_val(hctx, "snap_seq", &snap_seqbl);
if (r < 0)
return r;
return 0;
}
/**
* Input:
* @param snap_id which snapshot to query, or CEPH_NOSNAP (uint64_t)
*
* Output:
* @param features list of enabled features for the given snapshot (uint64_t)
* @returns 0 on success, negative error code on failure
*/
int get_features(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
uint64_t features, snap_id;
bufferlist::iterator iter = in->begin();
try {
::decode(snap_id, iter);
} catch (const buffer::error &err) {
return -EINVAL;
}
CLS_LOG(20, "get_features snap_id=%llu", snap_id);
if (snap_id == CEPH_NOSNAP) {
int r = read_key(hctx, "features", &features);
if (r < 0) {
CLS_ERR("failed to read features off disk: %s", strerror(r));
return r;
}
} else {
cls_rbd_snap snap;
string snapshot_key;
key_from_snap_id(snap_id, &snapshot_key);
int r = read_key(hctx, snapshot_key, &snap);
if (r < 0)
return r;
features = snap.features;
}
uint64_t incompatible = features & RBD_FEATURES_INCOMPATIBLE;
::encode(features, *out);
::encode(incompatible, *out);
return 0;
}
/**
* check that given feature(s) are set
*
* @param hctx context
* @param need features needed
* @return 0 if features are set, negative error (like ENOEXEC) otherwise
*/
int require_feature(cls_method_context_t hctx, uint64_t need)
{
uint64_t features;
int r = read_key(hctx, "features", &features);
if (r == -ENOENT) // this implies it's an old-style image with no features
return -ENOEXEC;
if (r < 0)
return r;
if ((features & need) != need) {
CLS_LOG(10, "require_feature missing feature %llx, have %llx", need, features);
return -ENOEXEC;
}
return 0;
}
/**
* Input:
* @param snap_id which snapshot to query, or CEPH_NOSNAP (uint64_t)
*
* Output:
* @param order bits to shift to get the size of data objects (uint8_t)
* @param size size of the image in bytes for the given snapshot (uint64_t)
* @returns 0 on success, negative error code on failure
*/
int get_size(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
uint64_t snap_id, size;
uint8_t order;
bufferlist::iterator iter = in->begin();
try {
::decode(snap_id, iter);
} catch (const buffer::error &err) {
return -EINVAL;
}
CLS_LOG(20, "get_size snap_id=%llu", snap_id);
int r = read_key(hctx, "order", &order);
if (r < 0) {
CLS_ERR("failed to read the order off of disk: %s", strerror(r));
return r;
}
if (snap_id == CEPH_NOSNAP) {
r = read_key(hctx, "size", &size);
if (r < 0) {
CLS_ERR("failed to read the image's size off of disk: %s", strerror(r));
return r;
}
} else {
cls_rbd_snap snap;
string snapshot_key;
key_from_snap_id(snap_id, &snapshot_key);
int r = read_key(hctx, snapshot_key, &snap);
if (r < 0)
return r;
size = snap.image_size;
}
::encode(order, *out);
::encode(size, *out);
return 0;
}
/**
* Input:
* @param size new capacity of the image in bytes (uint64_t)
*
* Output:
* @returns 0 on success, negative error code on failure
*/
int set_size(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
uint64_t size;
bufferlist::iterator iter = in->begin();
try {
::decode(size, iter);
} catch (const buffer::error &err) {
return -EINVAL;
}
// check that size exists to make sure this is a header object
// that was created correctly
uint64_t orig_size;
int r = read_key(hctx, "size", &orig_size);
if (r < 0) {
CLS_ERR("Could not read image's size off disk: %s", strerror(r));
return r;
}
CLS_LOG(20, "set_size size=%llu orig_size=%llu", size);
bufferlist sizebl;
::encode(size, sizebl);
r = cls_cxx_map_set_val(hctx, "size", &sizebl);
if (r < 0) {
CLS_ERR("error writing snapshot metadata: %d", r);
return r;
}
// if we are shrinking, and have a parent, shrink our overlap with
// the parent, too.
if (size < orig_size) {
cls_rbd_parent parent;
r = read_key(hctx, "parent", &parent);
if (r == -ENOENT)
r = 0;
if (r < 0)
return r;
if (parent.exists() && parent.overlap > size) {
bufferlist parentbl;
parent.overlap = size;
::encode(parent, parentbl);
r = cls_cxx_map_set_val(hctx, "parent", &parentbl);
if (r < 0) {
CLS_ERR("error writing parent: %d", r);
return r;
}
}
}
return 0;
}
/**
* verify that the header object exists
*
* @return 0 if the object exists, -ENOENT if it does not, or other error
*/
int check_exists(cls_method_context_t hctx)
{
uint64_t size;
time_t mtime;
return cls_cxx_stat(hctx, &size, &mtime);
}
/**
* get the current protection status of the specified snapshot
*
* Input:
* @param snap_id (uint64_t) which snapshot to get the status of
*
* Output:
* @param status (uint8_t) one of:
* RBD_PROTECTION_STATUS_{PROTECTED, UNPROTECTED, UNPROTECTING}
*
* @returns 0 on success, negative error code on failure
* @returns -EINVAL if snapid is CEPH_NOSNAP
*/
int get_protection_status(cls_method_context_t hctx, bufferlist *in,
bufferlist *out)
{
snapid_t snap_id;
bufferlist::iterator iter = in->begin();
try {
::decode(snap_id, iter);
} catch (const buffer::error &err) {
CLS_LOG(20, "get_protection_status: invalid decode");
return -EINVAL;
}
int r = check_exists(hctx);
if (r < 0)
return r;
CLS_LOG(20, "get_protection_status snap_id=%llu", snap_id.val);
if (snap_id == CEPH_NOSNAP)
return -EINVAL;
cls_rbd_snap snap;
string snapshot_key;
key_from_snap_id(snap_id.val, &snapshot_key);
r = read_key(hctx, snapshot_key, &snap);
if (r < 0) {
CLS_ERR("could not read key for snapshot id %llu", snap_id.val);
return r;
}
if (snap.protection_status >= RBD_PROTECTION_STATUS_LAST) {
CLS_ERR("invalid protection status for snap id %llu: %u",
snap_id.val, snap.protection_status);
return -EIO;
}
::encode(snap.protection_status, *out);
return 0;
}
/**
* set the proctection status of a snapshot
*
* Input:
* @param snapid (uint64_t) which snapshot to set the status of
* @param status (uint8_t) one of:
* RBD_PROTECTION_STATUS_{PROTECTED, UNPROTECTED, UNPROTECTING}
*
* @returns 0 on success, negative error code on failure
* @returns -EINVAL if snapid is CEPH_NOSNAP
*/
int set_protection_status(cls_method_context_t hctx, bufferlist *in,
bufferlist *out)
{
snapid_t snap_id;
uint8_t status;
bufferlist::iterator iter = in->begin();
try {
::decode(snap_id, iter);
::decode(status, iter);
} catch (const buffer::error &err) {
CLS_LOG(20, "set_protection_status: invalid decode");
return -EINVAL;
}
int r = check_exists(hctx);
if (r < 0)
return r;
r = require_feature(hctx, RBD_FEATURE_LAYERING);
if (r < 0) {
CLS_LOG(20, "image does not support layering");
return r;
}
CLS_LOG(20, "set_protection_status snapid=%llu status=%u",
snap_id.val, status);
if (snap_id == CEPH_NOSNAP)
return -EINVAL;
if (status >= RBD_PROTECTION_STATUS_LAST) {
CLS_LOG(10, "invalid protection status for snap id %llu: %u",
snap_id.val, status);
return -EINVAL;
}
cls_rbd_snap snap;
string snapshot_key;
key_from_snap_id(snap_id.val, &snapshot_key);
r = read_key(hctx, snapshot_key, &snap);
if (r < 0) {
CLS_ERR("could not read key for snapshot id %d", snap_id.val);
return r;
}
snap.protection_status = status;
bufferlist snapshot_bl;
::encode(snap, snapshot_bl);
r = cls_cxx_map_set_val(hctx, snapshot_key, &snapshot_bl);
if (r < 0) {
CLS_ERR("error writing snapshot metadata: %d", r);
return r;
}
return 0;
}
/**
* get the current parent, if any
*
* Input:
* @param snap_id which snapshot to query, or CEPH_NOSNAP (uint64_t)
*
* Output:
* @param pool parent pool id (-1 if parent does not exist)
* @param image parent image id
* @param snapid parent snapid
* @param size portion of parent mapped under the child
*
* @returns 0 on success or parent does not exist, negative error code on failure
*/
int get_parent(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
uint64_t snap_id;
bufferlist::iterator iter = in->begin();
try {
::decode(snap_id, iter);
} catch (const buffer::error &err) {
return -EINVAL;
}
int r = check_exists(hctx);
if (r < 0)
return r;
CLS_LOG(20, "get_parent snap_id=%llu", snap_id);
cls_rbd_parent parent;
r = require_feature(hctx, RBD_FEATURE_LAYERING);
if (r == 0) {
if (snap_id == CEPH_NOSNAP) {
r = read_key(hctx, "parent", &parent);
if (r < 0 && r != -ENOENT)
return r;
} else {
cls_rbd_snap snap;
string snapshot_key;
key_from_snap_id(snap_id, &snapshot_key);
r = read_key(hctx, snapshot_key, &snap);
if (r < 0 && r != -ENOENT)
return r;
parent = snap.parent;
}
}
::encode(parent.pool, *out);
::encode(parent.id, *out);
::encode(parent.snapid, *out);
::encode(parent.overlap, *out);
return 0;
}
/**
* set the image parent
*
* Input:
* @param pool parent pool
* @param id parent image id
* @param snapid parent snapid
* @param size parent size
*
* @returns 0 on success, or negative error code
*/
int set_parent(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
int64_t pool;
string id;
snapid_t snapid;
uint64_t size;
bufferlist::iterator iter = in->begin();
try {
::decode(pool, iter);
::decode(id, iter);
::decode(snapid, iter);
::decode(size, iter);
} catch (const buffer::error &err) {
CLS_LOG(20, "cls_rbd::set_parent: invalid decode");
return -EINVAL;
}
int r = check_exists(hctx);
if (r < 0) {
CLS_LOG(20, "cls_rbd::set_parent: child already exists");
return r;
}
r = require_feature(hctx, RBD_FEATURE_LAYERING);
if (r < 0) {
CLS_LOG(20, "cls_rbd::set_parent: child does not support layering");
return r;
}
CLS_LOG(20, "set_parent pool=%lld id=%s snapid=%llu size=%llu",
pool, id.c_str(), snapid.val, size);
if (pool < 0 || id.length() == 0 || snapid == CEPH_NOSNAP || size == 0) {
return -EINVAL;
}
// make sure there isn't already a parent
cls_rbd_parent parent;
r = read_key(hctx, "parent", &parent);
if (r == 0) {
CLS_LOG(20, "set_parent existing parent pool=%lld id=%s snapid=%llu overlap=%llu",
parent.pool, parent.id.c_str(), parent.snapid.val,
parent.overlap);
return -EEXIST;
}
// our overlap is the min of our size and the parent's size.
uint64_t our_size;
r = read_key(hctx, "size", &our_size);
if (r < 0)
return r;
bufferlist parentbl;
parent.pool = pool;
parent.id = id;
parent.snapid = snapid;
parent.overlap = MIN(our_size, size);
::encode(parent, parentbl);
r = cls_cxx_map_set_val(hctx, "parent", &parentbl);
if (r < 0) {
CLS_ERR("error writing parent: %d", r);
return r;
}
return 0;
}
/**
* remove the parent pointer
*
* This can only happen on the head, not on a snapshot. No arguments.
*
* @returns 0 on success, negative error code on failure.
*/
int remove_parent(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
int r = check_exists(hctx);
if (r < 0)
return r;
r = require_feature(hctx, RBD_FEATURE_LAYERING);
if (r < 0)
return r;
cls_rbd_parent parent;
r = read_key(hctx, "parent", &parent);
if (r < 0)
return r;
r = cls_cxx_map_remove_key(hctx, "parent");
if (r < 0) {
CLS_ERR("error removing parent: %d", r);
return r;
}
return 0;
}
/**
* methods for dealing with rbd_children object
*/
static int decode_parent_common(bufferlist::iterator& it, uint64_t *pool_id,
string *image_id, snapid_t *snap_id)
{
try {
::decode(*pool_id, it);
::decode(*image_id, it);
::decode(*snap_id, it);
} catch (const buffer::error &err) {
CLS_ERR("error decoding parent spec");
return -EINVAL;
}
return 0;
}
static int decode_parent(bufferlist *in, uint64_t *pool_id,
string *image_id, snapid_t *snap_id)
{
bufferlist::iterator it = in->begin();
return decode_parent_common(it, pool_id, image_id, snap_id);
}
static int decode_parent_and_child(bufferlist *in, uint64_t *pool_id,
string *image_id, snapid_t *snap_id,
string *c_image_id)
{
bufferlist::iterator it = in->begin();
int r = decode_parent_common(it, pool_id, image_id, snap_id);
if (r < 0)
return r;
try {
::decode(*c_image_id, it);
} catch (const buffer::error &err) {
CLS_ERR("error decoding child image id");
return -EINVAL;
}
return 0;
}
static string parent_key(uint64_t pool_id, string image_id, snapid_t snap_id)
{
bufferlist key_bl;
::encode(pool_id, key_bl);
::encode(image_id, key_bl);
::encode(snap_id, key_bl);
return string(key_bl.c_str(), key_bl.length());
}
/**
* add child to rbd_children directory object
*
* rbd_children is a map of (p_pool_id, p_image_id, p_snap_id) to
* [c_image_id, [c_image_id ... ]]
*
* Input:
* @param p_pool_id parent pool id
* @param p_image_id parent image oid
* @param p_snap_id parent snapshot id
* @param c_image_id new child image oid to add
*
* @returns 0 on success, negative error on failure
*/
int add_child(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
int r;
uint64_t p_pool_id;
snapid_t p_snap_id;
string p_image_id, c_image_id;
// Use set for ease of erase() for remove_child()
std::set<string> children;
r = decode_parent_and_child(in, &p_pool_id, &p_image_id, &p_snap_id,
&c_image_id);
if (r < 0)
return r;
CLS_LOG(20, "add_child %s to (%d, %s, %d)", c_image_id.c_str(),
p_pool_id, p_image_id.c_str(), p_snap_id.val);
string key = parent_key(p_pool_id, p_image_id, p_snap_id);
// get current child list for parent, if any
r = read_key(hctx, key, &children);
if ((r < 0) && (r != -ENOENT)) {
CLS_LOG(20, "add_child: omap read failed: %d", r);
return r;
}
if (children.find(c_image_id) != children.end()) {
CLS_LOG(20, "add_child: child already exists: %s", c_image_id.c_str());
return -EEXIST;
}
// add new child
children.insert(c_image_id);
// write back
bufferlist childbl;
::encode(children, childbl);
r = cls_cxx_map_set_val(hctx, key, &childbl);
if (r < 0)
CLS_LOG(20, "add_child: omap write failed: %d", r);
return r;
}
/**
* remove child from rbd_children directory object
*
* Input:
* @param p_pool_id parent pool id
* @param p_image_id parent image oid
* @param p_snap_id parent snapshot id
* @param c_image_id new child image oid to add
*
* @returns 0 on success, negative error on failure
*/
int remove_child(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
int r;
uint64_t p_pool_id;
snapid_t p_snap_id;
string p_image_id, c_image_id;
std::set<string> children;
r = decode_parent_and_child(in, &p_pool_id, &p_image_id, &p_snap_id,
&c_image_id);
if (r < 0)
return r;
CLS_LOG(20, "remove_child %s from (%d, %s, %d)", c_image_id.c_str(),
p_pool_id, p_image_id.c_str(), p_snap_id.val);
string key = parent_key(p_pool_id, p_image_id, p_snap_id);
// get current child list for parent. Unlike add_child(), an empty list
// is an error (how can we remove something that doesn't exist?)
r = read_key(hctx, key, &children);
if (r < 0) {
CLS_LOG(20, "remove_child: read omap failed: %d", r);
return r;
}
if (children.find(c_image_id) == children.end()) {
CLS_LOG(20, "remove_child: child not found: %s", c_image_id.c_str());
return -ENOENT;
}
// find and remove child
children.erase(c_image_id);
// now empty? remove key altogether
if (children.empty()) {
r = cls_cxx_map_remove_key(hctx, key);
if (r < 0)
CLS_LOG(20, "remove_child: remove key failed: %d", r);
} else {
// write back shortened children list
bufferlist childbl;
::encode(children, childbl);
r = cls_cxx_map_set_val(hctx, key, &childbl);
if (r < 0)
CLS_LOG(20, "remove_child: write omap failed: %d ", r);
}
return r;
}
/**
* Input:
* @param p_pool_id parent pool id
* @param p_image_id parent image oid
* @param p_snap_id parent snapshot id
* @param c_image_id new child image oid to add
*
* Output:
* @param children set<string> of children
*
* @returns 0 on success, negative error on failure
*/
int get_children(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
int r;
uint64_t p_pool_id;
snapid_t p_snap_id;
string p_image_id;
std::set<string> children;
r = decode_parent(in, &p_pool_id, &p_image_id, &p_snap_id);
if (r < 0)
return r;
CLS_LOG(20, "get_children of (%d, %s, %d)",
p_pool_id, p_image_id.c_str(), p_snap_id.val);
string key = parent_key(p_pool_id, p_image_id, p_snap_id);
r = read_key(hctx, key, &children);
if (r < 0) {
if (r != -ENOENT)
CLS_LOG(20, "get_children: read omap failed: %d", r);
return r;
}
::encode(children, *out);
return 0;
}
/**
* Get the information needed to create a rados snap context for doing
* I/O to the data objects. This must include all snapshots.
*
* Output:
* @param snap_seq the highest snapshot id ever associated with the image (uint64_t)
* @param snap_ids existing snapshot ids in descending order (vector<uint64_t>)
* @returns 0 on success, negative error code on failure
*/
int get_snapcontext(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
CLS_LOG(20, "get_snapcontext");
int r;
int max_read = RBD_MAX_KEYS_READ;
vector<snapid_t> snap_ids;
string last_read = RBD_SNAP_KEY_PREFIX;
do {
set<string> keys;
r = cls_cxx_map_get_keys(hctx, last_read, max_read, &keys);
if (r < 0)
return r;
for (set<string>::const_iterator it = keys.begin();
it != keys.end(); ++it) {
snapid_t snap_id = snap_id_from_key(*it);
snap_ids.push_back(snap_id);
}
if (keys.size() > 0)
last_read = *(keys.rbegin());
} while (r == max_read);
uint64_t snap_seq;
r = read_key(hctx, "snap_seq", &snap_seq);
if (r < 0) {
CLS_ERR("could not read the image's snap_seq off disk: %s", strerror(r));
return r;
}
// snap_ids must be descending in a snap context
std::reverse(snap_ids.begin(), snap_ids.end());
::encode(snap_seq, *out);
::encode(snap_ids, *out);
return 0;
}
/**
* Output:
* @param object_prefix prefix for data object names (string)
* @returns 0 on success, negative error code on failure
*/
int get_object_prefix(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
CLS_LOG(20, "get_object_prefix");
string object_prefix;
int r = read_key(hctx, "object_prefix", &object_prefix);
if (r < 0) {
CLS_ERR("failed to read the image's object prefix off of disk: %s",
strerror(r));
return r;
}
::encode(object_prefix, *out);
return 0;
}
int get_snapshot_name(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
uint64_t snap_id;
bufferlist::iterator iter = in->begin();
try {
::decode(snap_id, iter);
} catch (const buffer::error &err) {
return -EINVAL;
}
CLS_LOG(20, "get_snapshot_name snap_id=%llu", snap_id);
if (snap_id == CEPH_NOSNAP)
return -EINVAL;
cls_rbd_snap snap;
string snapshot_key;
key_from_snap_id(snap_id, &snapshot_key);
int r = read_key(hctx, snapshot_key, &snap);
if (r < 0)
return r;
::encode(snap.name, *out);
return 0;
}
/**
* Adds a snapshot to an rbd header. Ensures the id and name are unique.
*
* Input:
* @param snap_name name of the snapshot (string)
* @param snap_id id of the snapshot (uint64_t)
*
* Output:
* @returns 0 on success, negative error code on failure.
* @returns -ESTALE if the input snap_id is less than the image's snap_seq
* @returns -EEXIST if the id or name are already used by another snapshot
*/
int snapshot_add(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
bufferlist snap_namebl, snap_idbl;
cls_rbd_snap snap_meta;
try {
bufferlist::iterator iter = in->begin();
::decode(snap_meta.name, iter);
::decode(snap_meta.id, iter);
} catch (const buffer::error &err) {
return -EINVAL;
}
CLS_LOG(20, "snapshot_add name=%s id=%llu", snap_meta.name.c_str(), snap_meta.id.val);
if (snap_meta.id > CEPH_MAXSNAP)
return -EINVAL;
uint64_t cur_snap_seq;
int r = read_key(hctx, "snap_seq", &cur_snap_seq);
if (r < 0) {
CLS_ERR("Could not read image's snap_seq off disk: %s", strerror(r));
return r;
}
// client lost a race with another snapshot creation.
// snap_seq must be monotonically increasing.
if (snap_meta.id < cur_snap_seq)
return -ESTALE;
r = read_key(hctx, "size", &snap_meta.image_size);
if (r < 0) {
CLS_ERR("Could not read image's size off disk: %s", strerror(r));
return r;
}
r = read_key(hctx, "features", &snap_meta.features);
if (r < 0) {
CLS_ERR("Could not read image's features off disk: %s", strerror(r));
return r;
}
int max_read = RBD_MAX_KEYS_READ;
string last_read = RBD_SNAP_KEY_PREFIX;
do {
map<string, bufferlist> vals;
r = cls_cxx_map_get_vals(hctx, last_read, RBD_SNAP_KEY_PREFIX,
max_read, &vals);
if (r < 0)
return r;
for (map<string, bufferlist>::iterator it = vals.begin();
it != vals.end(); ++it) {
cls_rbd_snap old_meta;
bufferlist::iterator iter = it->second.begin();
try {
::decode(old_meta, iter);
} catch (const buffer::error &err) {
snapid_t snap_id = snap_id_from_key(it->first);
CLS_ERR("error decoding snapshot metadata for snap_id: %llu", snap_id.val);
return -EIO;
}
if (snap_meta.name == old_meta.name || snap_meta.id == old_meta.id) {
CLS_LOG(20, "snap_name %s or snap_id %llu matches existing snap %s %llu",
snap_meta.name.c_str(), snap_meta.id.val,
old_meta.name.c_str(), old_meta.id.val);
return -EEXIST;
}
}
if (vals.size() > 0)
last_read = vals.rbegin()->first;
} while (r == RBD_MAX_KEYS_READ);
// snapshot inherits parent, if any
cls_rbd_parent parent;
r = read_key(hctx, "parent", &parent);
if (r < 0 && r != -ENOENT)
return r;
if (r == 0) {
snap_meta.parent = parent;
}
bufferlist snap_metabl, snap_seqbl;
::encode(snap_meta, snap_metabl);
::encode(snap_meta.id, snap_seqbl);
string snapshot_key;
key_from_snap_id(snap_meta.id, &snapshot_key);
map<string, bufferlist> vals;
vals["snap_seq"] = snap_seqbl;
vals[snapshot_key] = snap_metabl;
r = cls_cxx_map_set_vals(hctx, &vals);
if (r < 0) {
CLS_ERR("error writing snapshot metadata: %d", r);
return r;
}
return 0;
}
/**
* Removes a snapshot from an rbd header.
*
* Input:
* @param snap_id the id of the snapshot to remove (uint64_t)
*
* Output:
* @returns 0 on success, negative error code on failure
*/
int snapshot_remove(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
snapid_t snap_id;
try {
bufferlist::iterator iter = in->begin();
::decode(snap_id, iter);
} catch (const buffer::error &err) {
return -EINVAL;
}
CLS_LOG(20, "snapshot_remove id=%llu", snap_id.val);
// check if the key exists. we can't rely on remove_key doing this for
// us, since OMAPRMKEYS returns success if the key is not there.
// bug or feature? sounds like a bug, since tmap did not have this
// behavior, but cls_rgw may rely on it...
cls_rbd_snap snap;
string snapshot_key;
key_from_snap_id(snap_id, &snapshot_key);
int r = read_key(hctx, snapshot_key, &snap);
if (r == -ENOENT)
return -ENOENT;
if (snap.protection_status != RBD_PROTECTION_STATUS_UNPROTECTED)
return -EBUSY;
r = cls_cxx_map_remove_key(hctx, snapshot_key);
if (r < 0) {
CLS_ERR("error writing snapshot metadata: %d", r);
return r;
}
return 0;
}
/**
* Returns a uint64_t of all the features supported by this class.
*/
int get_all_features(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
uint64_t all_features = RBD_FEATURES_ALL;
::encode(all_features, *out);
return 0;
}
/**
* "Copy up" data from the parent of a clone to the clone's object(s).
* Used for implementing copy-on-write for a clone image. Client
* will pass down a chunk of data that fits completely within one
* clone block (one object), and is aligned (starts at beginning of block),
* but may be shorter (for non-full parent blocks). The class method
* can't know the object size to validate the requested length,
* so it just writes the data as given if the child object doesn't
* already exist, and returns success if it does.
*
* Input:
* @param in bufferlist of data to write
*
* Output:
* @returns 0 on success, or if block already exists in child
* negative error code on other error
*/
int copyup(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
// check for existence; if child object exists, just return success
if (cls_cxx_stat(hctx, NULL, NULL) == 0)
return 0;
CLS_LOG(20, "copyup: writing length %d\n", in->length());
return cls_cxx_write(hctx, 0, in->length(), in);
}
/************************ rbd_id object methods **************************/
/**
* Input:
* @param in ignored
*
* Output:
* @param id the id stored in the object
* @returns 0 on success, negative error code on failure
*/
int get_id(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
uint64_t size;
int r = cls_cxx_stat(hctx, &size, NULL);
if (r < 0)
return r;
if (size == 0)
return -ENOENT;
bufferlist read_bl;
r = cls_cxx_read(hctx, 0, size, &read_bl);
if (r < 0) {
CLS_ERR("get_id: could not read id: %d", r);
return r;
}
string id;
try {
bufferlist::iterator iter = read_bl.begin();
::decode(id, iter);
} catch (const buffer::error &err) {
return -EIO;
}
::encode(id, *out);
return 0;
};
/**
* Set the id of an image. The object must already exist.
*
* Input:
* @param id the id of the image, as an alpha-numeric string
*
* Output:
* @returns 0 on success, -EEXIST if the atomic create fails,
* negative error code on other error
*/
int set_id(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
int r = check_exists(hctx);
if (r < 0)
return r;
string id;
try {
bufferlist::iterator iter = in->begin();
::decode(id, iter);
} catch (const buffer::error &err) {
return -EINVAL;
}
if (!is_valid_id(id)) {
CLS_ERR("set_id: invalid id '%s'", id.c_str());
return -EINVAL;
}
uint64_t size;
r = cls_cxx_stat(hctx, &size, NULL);
if (r < 0)
return r;
if (size != 0)
return -EEXIST;
CLS_LOG(20, "set_id: id=%s", id.c_str());
bufferlist write_bl;
::encode(id, write_bl);
return cls_cxx_write(hctx, 0, write_bl.length(), &write_bl);
}
/*********************** methods for rbd_directory ***********************/
static const string dir_key_for_id(const string &id)
{
return RBD_DIR_ID_KEY_PREFIX + id;
}
static const string dir_key_for_name(const string &name)
{
return RBD_DIR_NAME_KEY_PREFIX + name;
}
static const string dir_name_from_key(const string &key)
{
return key.substr(strlen(RBD_DIR_NAME_KEY_PREFIX));
}
static int dir_add_image_helper(cls_method_context_t hctx,
const string &name, const string &id,
bool check_for_unique_id)
{
if (!name.size() || !is_valid_id(id)) {
CLS_ERR("dir_add_image_helper: invalid name '%s' or id '%s'",
name.c_str(), id.c_str());
return -EINVAL;
}
CLS_LOG(20, "dir_add_image_helper name=%s id=%s", name.c_str(), id.c_str());
string tmp;
string name_key = dir_key_for_name(name);
string id_key = dir_key_for_id(id);
int r = read_key(hctx, name_key, &tmp);
if (r != -ENOENT) {
CLS_LOG(10, "name already exists");
return -EEXIST;
}
r = read_key(hctx, id_key, &tmp);
if (r != -ENOENT && check_for_unique_id) {
CLS_LOG(10, "id already exists");
return -EBADF;
}
bufferlist id_bl, name_bl;
::encode(id, id_bl);
::encode(name, name_bl);
map<string, bufferlist> omap_vals;
omap_vals[name_key] = id_bl;
omap_vals[id_key] = name_bl;
return cls_cxx_map_set_vals(hctx, &omap_vals);
}
static int dir_remove_image_helper(cls_method_context_t hctx,
const string &name, const string &id)
{
CLS_LOG(20, "dir_remove_image_helper name=%s id=%s",
name.c_str(), id.c_str());
string stored_name, stored_id;
string name_key = dir_key_for_name(name);
string id_key = dir_key_for_id(id);
int r = read_key(hctx, name_key, &stored_id);
if (r < 0) {
CLS_ERR("error reading name to id mapping: %d", r);
return r;
}
r = read_key(hctx, id_key, &stored_name);
if (r < 0) {
CLS_ERR("error reading id to name mapping: %d", r);
return r;
}
// check if this op raced with a rename
if (stored_name != name || stored_id != id) {
CLS_ERR("stored name '%s' and id '%s' do not match args '%s' and '%s'",
stored_name.c_str(), stored_id.c_str(), name.c_str(), id.c_str());
return -ESTALE;
}
r = cls_cxx_map_remove_key(hctx, name_key);
if (r < 0) {
CLS_ERR("error removing name: %d", r);
return r;
}
r = cls_cxx_map_remove_key(hctx, id_key);
if (r < 0) {
CLS_ERR("error removing id: %d", r);
return r;
}
return 0;
}
/**
* Rename an image in the directory, updating both indexes
* atomically. This can't be done from the client calling
* dir_add_image and dir_remove_image in one transaction because the
* results of the first method are not visibale to later steps.
*
* Input:
* @param src original name of the image
* @param dest new name of the image
* @param id the id of the image
*
* Output:
* @returns -ESTALE if src and id do not map to each other
* @returns -ENOENT if src or id are not in the directory
* @returns -EEXIST if dest already exists
* @returns 0 on success, negative error code on failure
*/
int dir_rename_image(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
string src, dest, id;
try {
bufferlist::iterator iter = in->begin();
::decode(src, iter);
::decode(dest, iter);
::decode(id, iter);
} catch (const buffer::error &err) {
return -EINVAL;
}
int r = dir_remove_image_helper(hctx, src, id);
if (r < 0)
return r;
// ignore duplicate id because the result of
// remove_image_helper is not visible yet
return dir_add_image_helper(hctx, dest, id, false);
}
/**
* Get the id of an image given its name.
*
* Input:
* @param name the name of the image
*
* Output:
* @param id the id of the image
* @returns 0 on success, negative error code on failure
*/
int dir_get_id(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
string name;
try {
bufferlist::iterator iter = in->begin();
::decode(name, iter);
} catch (const buffer::error &err) {
return -EINVAL;
}
CLS_LOG(20, "dir_get_id: name=%s", name.c_str());
string id;
int r = read_key(hctx, dir_key_for_name(name), &id);
if (r < 0) {
CLS_ERR("error reading id for name '%s': %d", name.c_str(), r);
return r;
}
::encode(id, *out);
return 0;
}
/**
* Get the name of an image given its id.
*
* Input:
* @param id the id of the image
*
* Output:
* @param name the name of the image
* @returns 0 on success, negative error code on failure
*/
int dir_get_name(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
string id;
try {
bufferlist::iterator iter = in->begin();
::decode(id, iter);
} catch (const buffer::error &err) {
return -EINVAL;
}
CLS_LOG(20, "dir_get_name: id=%s", id.c_str());
string name;
int r = read_key(hctx, dir_key_for_id(id), &name);
if (r < 0) {
CLS_ERR("error reading name for id '%s': %d", id.c_str(), r);
return r;
}
::encode(name, *out);
return 0;
}
/**
* List the names and ids of the images in the directory, sorted by
* name.
*
* Input:
* @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 list
*
* Output:
* @param images map from name to id of up to max_return images
* @returns 0 on success, negative error code on failure
*/
int dir_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
string start_after;
uint64_t max_return;
try {
bufferlist::iterator iter = in->begin();
::decode(start_after, iter);
::decode(max_return, iter);
} catch (const buffer::error &err) {
return -EINVAL;
}
int max_read = RBD_MAX_KEYS_READ;
int r = max_read;
map<string, string> images;
string last_read = dir_key_for_name(start_after);
while (r == max_read && images.size() < max_return) {
map<string, bufferlist> vals;
CLS_LOG(20, "last_read = '%s'", last_read.c_str());
r = cls_cxx_map_get_vals(hctx, last_read, RBD_DIR_NAME_KEY_PREFIX,
max_read, &vals);
if (r < 0) {
CLS_ERR("error reading directory by name: %d", r);
return r;
}
for (map<string, bufferlist>::iterator it = vals.begin();
it != vals.end(); ++it) {
string id;
bufferlist::iterator iter = it->second.begin();
try {
::decode(id, iter);
} catch (const buffer::error &err) {
CLS_ERR("could not decode id of image '%s'", it->first.c_str());
return -EIO;
}
CLS_LOG(20, "adding '%s' -> '%s'", dir_name_from_key(it->first).c_str(), id.c_str());
images[dir_name_from_key(it->first)] = id;
if (images.size() >= max_return)
break;
}
if (vals.size() > 0) {
last_read = images.rbegin()->first;
}
}
::encode(images, *out);
return 0;
}
/**
* Add an image to the rbd directory. Creates the directory object if
* needed, and updates the index from id to name and name to id.
*
* Input:
* @param name the name of the image
* @param id the id of the image
*
* Output:
* @returns -EEXIST if the image name is already in the directory
* @returns -EBADF if the image id is already in the directory
* @returns 0 on success, negative error code on failure
*/
int dir_add_image(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
int r = cls_cxx_create(hctx, false);
if (r < 0) {
CLS_ERR("could not create directory: error %d", r);
return r;
}
string name, id;
try {
bufferlist::iterator iter = in->begin();
::decode(name, iter);
::decode(id, iter);
} catch (const buffer::error &err) {
return -EINVAL;
}
return dir_add_image_helper(hctx, name, id, true);
}
/**
* Remove an image from the rbd directory.
*
* Input:
* @param name the name of the image
* @param id the id of the image
*
* Output:
* @returns -ESTALE if the name and id do not map to each other
* @returns 0 on success, negative error code on failure
*/
int dir_remove_image(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
string name, id;
try {
bufferlist::iterator iter = in->begin();
::decode(name, iter);
::decode(id, iter);
} catch (const buffer::error &err) {
return -EINVAL;
}
return dir_remove_image_helper(hctx, name, id);
}
/****************************** Old format *******************************/
int old_snapshots_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
bufferlist bl;
struct rbd_obj_header_ondisk *header;
int rc = snap_read_header(hctx, bl);
if (rc < 0)
return rc;
header = (struct rbd_obj_header_ondisk *)bl.c_str();
bufferptr p(header->snap_names_len);
char *buf = (char *)header;
char *name = buf + sizeof(*header) + header->snap_count * sizeof(struct rbd_obj_snap_ondisk);
char *end = name + header->snap_names_len;
memcpy(p.c_str(),
buf + sizeof(*header) + header->snap_count * sizeof(struct rbd_obj_snap_ondisk),
header->snap_names_len);
::encode(header->snap_seq, *out);
::encode(header->snap_count, *out);
for (unsigned i = 0; i < header->snap_count; i++) {
string s = name;
::encode(header->snaps[i].id, *out);
::encode(header->snaps[i].image_size, *out);
::encode(s, *out);
name += strlen(name) + 1;
if (name > end)
return -EIO;
}
return 0;
}
int old_snapshot_add(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
bufferlist bl;
struct rbd_obj_header_ondisk *header;
bufferlist newbl;
bufferptr header_bp(sizeof(*header));
struct rbd_obj_snap_ondisk *new_snaps;
int rc = snap_read_header(hctx, bl);
if (rc < 0)
return rc;
header = (struct rbd_obj_header_ondisk *)bl.c_str();
int snaps_id_ofs = sizeof(*header);
int len = snaps_id_ofs;
int names_ofs = snaps_id_ofs + sizeof(*new_snaps) * header->snap_count;
const char *snap_name;
const char *snap_names = ((char *)header) + names_ofs;
const char *end = snap_names + header->snap_names_len;
bufferlist::iterator iter = in->begin();
string s;
uint64_t snap_id;
try {
::decode(s, iter);
::decode(snap_id, iter);
} catch (const buffer::error &err) {
return -EINVAL;
}
snap_name = s.c_str();
if (header->snap_seq > snap_id)
return -ESTALE;
const char *cur_snap_name;
for (cur_snap_name = snap_names; cur_snap_name < end; cur_snap_name += strlen(cur_snap_name) + 1) {
if (strncmp(cur_snap_name, snap_name, end - cur_snap_name) == 0)
return -EEXIST;
}
if (cur_snap_name > end)
return -EIO;
int snap_name_len = strlen(snap_name);
bufferptr new_names_bp(header->snap_names_len + snap_name_len + 1);
bufferptr new_snaps_bp(sizeof(*new_snaps) * (header->snap_count + 1));
/* copy snap names and append to new snap name */
char *new_snap_names = new_names_bp.c_str();
strcpy(new_snap_names, snap_name);
memcpy(new_snap_names + snap_name_len + 1, snap_names, header->snap_names_len);
/* append new snap id */
new_snaps = (struct rbd_obj_snap_ondisk *)new_snaps_bp.c_str();
memcpy(new_snaps + 1, header->snaps, sizeof(*new_snaps) * header->snap_count);
header->snap_count = header->snap_count + 1;
header->snap_names_len = header->snap_names_len + snap_name_len + 1;
header->snap_seq = snap_id;
new_snaps[0].id = snap_id;
new_snaps[0].image_size = header->image_size;
len += sizeof(*new_snaps) * header->snap_count + header->snap_names_len;
memcpy(header_bp.c_str(), header, sizeof(*header));
newbl.push_back(header_bp);
newbl.push_back(new_snaps_bp);
newbl.push_back(new_names_bp);
rc = cls_cxx_write_full(hctx, &newbl);
if (rc < 0)
return rc;
return 0;
}
int old_snapshot_remove(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
bufferlist bl;
struct rbd_obj_header_ondisk *header;
bufferlist newbl;
bufferptr header_bp(sizeof(*header));
struct rbd_obj_snap_ondisk *new_snaps;
int rc = snap_read_header(hctx, bl);
if (rc < 0)
return rc;
header = (struct rbd_obj_header_ondisk *)bl.c_str();
int snaps_id_ofs = sizeof(*header);
int names_ofs = snaps_id_ofs + sizeof(*new_snaps) * header->snap_count;
const char *snap_name;
const char *snap_names = ((char *)header) + names_ofs;
const char *orig_names = snap_names;
const char *end = snap_names + header->snap_names_len;
bufferlist::iterator iter = in->begin();
string s;
unsigned i;
bool found = false;
struct rbd_obj_snap_ondisk snap;
try {
::decode(s, iter);
} catch (const buffer::error &err) {
return -EINVAL;
}
snap_name = s.c_str();
for (i = 0; snap_names < end; i++) {
if (strcmp(snap_names, snap_name) == 0) {
snap = header->snaps[i];
found = true;
break;
}
snap_names += strlen(snap_names) + 1;
}
if (!found) {
CLS_ERR("couldn't find snap %s\n", snap_name);
return -ENOENT;
}
header->snap_names_len = header->snap_names_len - (s.length() + 1);
header->snap_count = header->snap_count - 1;
bufferptr new_names_bp(header->snap_names_len);
bufferptr new_snaps_bp(sizeof(header->snaps[0]) * header->snap_count);
memcpy(header_bp.c_str(), header, sizeof(*header));
newbl.push_back(header_bp);
if (header->snap_count) {
int snaps_len = 0;
int names_len = 0;
CLS_LOG(20, "i=%d\n", i);
if (i > 0) {
snaps_len = sizeof(header->snaps[0]) * i;
names_len = snap_names - orig_names;
memcpy(new_snaps_bp.c_str(), header->snaps, snaps_len);
memcpy(new_names_bp.c_str(), orig_names, names_len);
}
snap_names += s.length() + 1;
if (i < header->snap_count) {
memcpy(new_snaps_bp.c_str() + snaps_len,
header->snaps + i + 1,
sizeof(header->snaps[0]) * (header->snap_count - i));
memcpy(new_names_bp.c_str() + names_len, snap_names , end - snap_names);
}
newbl.push_back(new_snaps_bp);
newbl.push_back(new_names_bp);
}
rc = cls_cxx_write_full(hctx, &newbl);
if (rc < 0)
return rc;
return 0;
}
/* assign block id. This method should be called on the rbd_info object */
int rbd_assign_bid(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
struct rbd_info info;
int rc;
bufferlist bl;
rc = cls_cxx_read(hctx, 0, sizeof(info), &bl);
if (rc < 0 && rc != -EEXIST)
return rc;
if (rc && rc < (int)sizeof(info)) {
CLS_ERR("bad rbd_info object, read %d bytes, expected %d", rc, sizeof(info));
return -EIO;
}
uint64_t max_id;
if (rc) {
memcpy(&info, bl.c_str(), sizeof(info));
max_id = info.max_id + 1;
info.max_id = max_id;
} else {
memset(&info, 0, sizeof(info));
max_id = 0;
}
bufferlist newbl;
bufferptr bp(sizeof(info));
memcpy(bp.c_str(), &info, sizeof(info));
newbl.push_back(bp);
rc = cls_cxx_write_full(hctx, &newbl);
if (rc < 0) {
CLS_ERR("error writing rbd_info, got rc=%d", rc);
return rc;
}
::encode(max_id, *out);
return out->length();
}
void __cls_init()
{
CLS_LOG(20, "Loaded rbd class!");
cls_register("rbd", &h_class);
cls_register_cxx_method(h_class, "create",
CLS_METHOD_RD | CLS_METHOD_WR,
create, &h_create);
cls_register_cxx_method(h_class, "get_features",
CLS_METHOD_RD,
get_features, &h_get_features);
cls_register_cxx_method(h_class, "get_size",
CLS_METHOD_RD,
get_size, &h_get_size);
cls_register_cxx_method(h_class, "set_size",
CLS_METHOD_RD | CLS_METHOD_WR,
set_size, &h_set_size);
cls_register_cxx_method(h_class, "get_snapcontext",
CLS_METHOD_RD,
get_snapcontext, &h_get_snapcontext);
cls_register_cxx_method(h_class, "get_object_prefix",
CLS_METHOD_RD,
get_object_prefix, &h_get_object_prefix);
cls_register_cxx_method(h_class, "get_snapshot_name",
CLS_METHOD_RD,
get_snapshot_name, &h_get_snapshot_name);
cls_register_cxx_method(h_class, "snapshot_add",
CLS_METHOD_RD | CLS_METHOD_WR,
snapshot_add, &h_snapshot_add);
cls_register_cxx_method(h_class, "snapshot_remove",
CLS_METHOD_RD | CLS_METHOD_WR,
snapshot_remove, &h_snapshot_remove);
cls_register_cxx_method(h_class, "get_all_features",
CLS_METHOD_RD,
get_all_features, &h_get_all_features);
cls_register_cxx_method(h_class, "copyup",
CLS_METHOD_RD | CLS_METHOD_WR,
copyup, &h_copyup);
cls_register_cxx_method(h_class, "get_parent",
CLS_METHOD_RD,
get_parent, &h_get_parent);
cls_register_cxx_method(h_class, "set_parent",
CLS_METHOD_RD | CLS_METHOD_WR,
set_parent, &h_set_parent);
cls_register_cxx_method(h_class, "remove_parent",
CLS_METHOD_RD | CLS_METHOD_WR,
remove_parent, &h_remove_parent);
cls_register_cxx_method(h_class, "set_protection_status",
CLS_METHOD_RD | CLS_METHOD_WR,
set_protection_status, &h_set_protection_status);
cls_register_cxx_method(h_class, "get_protection_status",
CLS_METHOD_RD,
get_protection_status, &h_get_protection_status);
/* methods for the rbd_children object */
cls_register_cxx_method(h_class, "add_child",
CLS_METHOD_RD | CLS_METHOD_WR,
add_child, &h_add_child);
cls_register_cxx_method(h_class, "remove_child",
CLS_METHOD_RD | CLS_METHOD_WR,
remove_child, &h_remove_child);
cls_register_cxx_method(h_class, "get_children",
CLS_METHOD_RD | CLS_METHOD_WR,
get_children, &h_get_children);
/* methods for the rbd_id.$image_name objects */
cls_register_cxx_method(h_class, "get_id",
CLS_METHOD_RD,
get_id, &h_get_id);
cls_register_cxx_method(h_class, "set_id",
CLS_METHOD_RD | CLS_METHOD_WR,
set_id, &h_set_id);
/* methods for the rbd_directory object */
cls_register_cxx_method(h_class, "dir_get_id",
CLS_METHOD_RD,
dir_get_id, &h_dir_get_id);
cls_register_cxx_method(h_class, "dir_get_name",
CLS_METHOD_RD,
dir_get_name, &h_dir_get_name);
cls_register_cxx_method(h_class, "dir_list",
CLS_METHOD_RD,
dir_list, &h_dir_list);
cls_register_cxx_method(h_class, "dir_add_image",
CLS_METHOD_RD | CLS_METHOD_WR,
dir_add_image, &h_dir_add_image);
cls_register_cxx_method(h_class, "dir_remove_image",
CLS_METHOD_RD | CLS_METHOD_WR,
dir_remove_image, &h_dir_remove_image);
cls_register_cxx_method(h_class, "dir_rename_image",
CLS_METHOD_RD | CLS_METHOD_WR,
dir_rename_image, &h_dir_rename_image);
/* methods for the old format */
cls_register_cxx_method(h_class, "snap_list",
CLS_METHOD_RD,
old_snapshots_list, &h_old_snapshots_list);
cls_register_cxx_method(h_class, "snap_add",
CLS_METHOD_RD | CLS_METHOD_WR,
old_snapshot_add, &h_old_snapshot_add);
cls_register_cxx_method(h_class, "snap_remove",
CLS_METHOD_RD | CLS_METHOD_WR,
old_snapshot_remove, &h_old_snapshot_remove);
/* assign a unique block id for rbd blocks */
cls_register_cxx_method(h_class, "assign_bid",
CLS_METHOD_RD | CLS_METHOD_WR,
rbd_assign_bid, &h_assign_bid);
return;
}