From: Venky Shankar Date: Tue, 24 Nov 2020 09:03:23 +0000 (-0500) Subject: client: Snapshot cephfs APIs X-Git-Tag: v16.1.0~184^2~2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=7a1d3f64364c69d43f03af3e6ab23782dfb82c14;p=ceph.git client: Snapshot cephfs APIs Introduce calls to create and delete a snapshot. The difference b/w this and the traditional mkdir() call to create a snapshot is the additional metadata field that can be persistent along side a snapshot (on creation). However, rmsnap() does nothing special and is included for completeness. Additionally, get_snap_info() API allows to fetch snapshot info which includes things like snaphot ID and metadata (if any, which was set via mksnap() API). Signed-off-by: Venky Shankar --- diff --git a/src/client/Client.cc b/src/client/Client.cc index 3bf2e836f866..568ebcddb179 100644 --- a/src/client/Client.cc +++ b/src/client/Client.cc @@ -10854,6 +10854,28 @@ int Client::_flock(Fh *fh, int cmd, uint64_t owner) return ret; } +int Client::get_snap_info(const char *path, const UserPerm &perms, SnapInfo *snap_info) { + RWRef_t mref_reader(mount_state, CLIENT_MOUNTING); + if (!mref_reader.is_state_satisfied()) { + return -ENOTCONN; + } + + std::unique_lock locker(client_lock); + InodeRef in; + int r = Client::path_walk(path, &in, perms, true); + if (r < 0) { + return r; + } + + if (in->snapid == CEPH_NOSNAP) { + return -EINVAL; + } + + snap_info->id = in->snapid; + snap_info->metadata = in->snap_metadata; + return 0; +} + int Client::ll_statfs(Inode *in, struct statvfs *stbuf, const UserPerm& perms) { /* Since the only thing this does is wrap a call to statfs, and diff --git a/src/client/Client.h b/src/client/Client.h index a2f3a021aed2..be6480b824d1 100644 --- a/src/client/Client.h +++ b/src/client/Client.h @@ -259,6 +259,13 @@ public: Client *m_client; }; + // snapshot info returned via get_snap_info(). nothing to do + // with SnapInfo on the MDS. + struct SnapInfo { + snapid_t id; + std::map metadata; + }; + Client(Messenger *m, MonClient *mc, Objecter *objecter_); Client(const Client&) = delete; Client(const Client&&) = delete; @@ -440,6 +447,8 @@ public: int sync_fs(); int64_t drop_caches(); + int get_snap_info(const char *path, const UserPerm &perms, SnapInfo *snap_info); + // hpc lazyio int lazyio(int fd, int enable); int lazyio_propagate(int fd, loff_t offset, size_t count); diff --git a/src/include/cephfs/libcephfs.h b/src/include/cephfs/libcephfs.h index 96921ec2e32a..40f13b713b75 100644 --- a/src/include/cephfs/libcephfs.h +++ b/src/include/cephfs/libcephfs.h @@ -98,6 +98,19 @@ typedef struct Inode Inode; struct ceph_mount_info; struct ceph_dir_result; +// user supplied key,value pair to be associated with a snapshot. +// callers can supply an array of this struct via ceph_mksnap(). +struct snap_metadata { + const char *key; + const char *value; +}; + +struct snap_info { + uint64_t id; + size_t nr_snap_metadata; + struct snap_metadata *snap_metadata; +}; + /* setattr mask bits */ #ifndef CEPH_SETATTR_MODE # define CEPH_SETATTR_MODE 1 @@ -638,6 +651,32 @@ void ceph_seekdir(struct ceph_mount_info *cmount, struct ceph_dir_result *dirp, */ int ceph_mkdir(struct ceph_mount_info *cmount, const char *path, mode_t mode); +/** + * Create a snapshot + * + * @param cmount the ceph mount handle to use for making the directory. + * @param path the path of the directory to create snapshot. This must be either an + * absolute path or a relative path off of the current working directory. + * @param name snapshot name + * @param mode the permissions the directory should have once created. + * @param snap_metadata array of snap metadata structs + * @param nr_snap_metadata number of snap metadata struct entries + * @returns 0 on success or a negative return code on error. + */ +int ceph_mksnap(struct ceph_mount_info *cmount, const char *path, const char *name, + mode_t mode, struct snap_metadata *snap_metadata, size_t nr_snap_metadata); + +/** + * Remove a snapshot + * + * @param cmount the ceph mount handle to use for making the directory. + * @param path the path of the directory to create snapshot. This must be either an + * absolute path or a relative path off of the current working directory. + * @param name snapshot name + * @returns 0 on success or a negative return code on error. + */ +int ceph_rmsnap(struct ceph_mount_info *cmount, const char *path, const char *name); + /** * Create multiple directories at once. * @@ -1899,6 +1938,24 @@ void ceph_finish_reclaim(struct ceph_mount_info *cmount); */ void ceph_ll_register_callbacks(struct ceph_mount_info *cmount, struct ceph_client_callback_args *args); + +/** + * Get snapshot info + * + * @param cmount the ceph mount handle to use for making the directory. + * @param path the path of the snapshot. This must be either an + * absolute path or a relative path off of the current working directory. + * @returns 0 on success or a negative return code on error. + */ +int ceph_get_snap_info(struct ceph_mount_info *cmount, + const char *path, struct snap_info *snap_info); + +/** + * Free snapshot info buffers + * + * @param snap_info snapshot info struct (fetched via call to ceph_get_snap_info()). + */ +void ceph_free_snap_info_buffer(struct snap_info *snap_info); #ifdef __cplusplus } #endif diff --git a/src/libcephfs.cc b/src/libcephfs.cc index a17577ac7fc2..15f1472a2b40 100644 --- a/src/libcephfs.cc +++ b/src/libcephfs.cc @@ -715,6 +715,27 @@ extern "C" int ceph_mkdir(struct ceph_mount_info *cmount, const char *path, mode return cmount->get_client()->mkdir(path, mode, cmount->default_perms); } +extern "C" int ceph_mksnap(struct ceph_mount_info *cmount, const char *path, const char *name, + mode_t mode, struct snap_metadata *snap_metadata, size_t nr_snap_metadata) +{ + if (!cmount->is_mounted()) + return -ENOTCONN; + size_t i = 0; + std::map metadata; + while (i < nr_snap_metadata) { + metadata.emplace(snap_metadata[i].key, snap_metadata[i].value); + ++i; + } + return cmount->get_client()->mksnap(path, name, cmount->default_perms, mode, metadata); +} + +extern "C" int ceph_rmsnap(struct ceph_mount_info *cmount, const char *path, const char *name) +{ + if (!cmount->is_mounted()) + return -ENOTCONN; + return cmount->get_client()->rmsnap(path, name, cmount->default_perms); +} + extern "C" int ceph_mkdirs(struct ceph_mount_info *cmount, const char *path, mode_t mode) { if (!cmount->is_mounted()) @@ -2015,4 +2036,61 @@ extern "C" void ceph_ll_register_callbacks(class ceph_mount_info *cmount, struct ceph_client_callback_args *args) { cmount->get_client()->ll_register_callbacks(args); + +} + + +extern "C" int ceph_get_snap_info(struct ceph_mount_info *cmount, + const char *path, struct snap_info *snap_info) { + Client::SnapInfo info; + int r = cmount->get_client()->get_snap_info(path, cmount->default_perms, &info); + if (r < 0) { + return r; + } + + size_t i = 0; + auto nr_metadata = info.metadata.size(); + + snap_info->id = info.id.val; + snap_info->nr_snap_metadata = nr_metadata; + if (nr_metadata) { + snap_info->snap_metadata = (struct snap_metadata *)calloc(nr_metadata, sizeof(struct snap_metadata)); + if (!snap_info->snap_metadata) { + return -ENOMEM; + } + + // fill with key, value pairs + for (auto &[key, value] : info.metadata) { + // len(key) + '\0' + len(value) + '\0' + char *kvp = (char *)malloc(key.size() + value.size() + 2); + if (!kvp) { + break; + } + char *_key = kvp; + char *_value = kvp + key.size() + 1; + + memcpy(_key, key.c_str(), key.size()); + _key[key.size()] = '\0'; + memcpy(_value, value.c_str(), value.size()); + _value[value.size()] = '\0'; + + snap_info->snap_metadata[i].key = _key; + snap_info->snap_metadata[i].value = _value; + ++i; + } + } + + if (nr_metadata && i != nr_metadata) { + ceph_free_snap_info_buffer(snap_info); + return -ENOMEM; + } + + return 0; +} + +extern "C" void ceph_free_snap_info_buffer(struct snap_info *snap_info) { + for (size_t i = 0; i < snap_info->nr_snap_metadata; ++i) { + free((void *)snap_info->snap_metadata[i].key); // malloc'd memory is key+value composite + } + free(snap_info->snap_metadata); }