]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
client: Snapshot cephfs APIs
authorVenky Shankar <vshankar@redhat.com>
Tue, 24 Nov 2020 09:03:23 +0000 (04:03 -0500)
committerVenky Shankar <vshankar@redhat.com>
Thu, 17 Dec 2020 18:07:51 +0000 (13:07 -0500)
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 <vshankar@redhat.com>
src/client/Client.cc
src/client/Client.h
src/include/cephfs/libcephfs.h
src/libcephfs.cc

index 3bf2e836f866ff34a21de5d5a43e47a47d66adfe..568ebcddb17925af6cb72146bfe74837e1f066d6 100644 (file)
@@ -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
index a2f3a021aed2ff76509e1cbda364b4627a85fa68..be6480b824d1808644ebee4091bac01c8a7c8924 100644 (file)
@@ -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<std::string, std::string> 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);
index 96921ec2e32a8438b2aab188b39daea57286cc38..40f13b713b75a0d9f5a6fe33889feef578acceb0 100644 (file)
@@ -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
index a17577ac7fc2f828a2717d9ffa376e2a790fe852..15f1472a2b400d51661cba689d42f5f75d60da17 100644 (file)
@@ -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<std::string, std::string> 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);
 }