]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/libcephfs.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / libcephfs.cc
index 6b99359d8c83b94f0319c64b99ca7c726abab037..424cbb85a10e61ef2550a6da9458fbcef247abab 100644 (file)
 #include "auth/Crypto.h"
 #include "client/Client.h"
 #include "librados/RadosClient.h"
-#include "common/Mutex.h"
+#include "common/async/context_pool.h"
 #include "common/ceph_argparse.h"
 #include "common/common_init.h"
 #include "common/config.h"
 #include "common/version.h"
 #include "mon/MonClient.h"
 #include "include/str_list.h"
+#include "include/stringify.h"
 #include "messages/MMonMap.h"
 #include "msg/Messenger.h"
-#include "include/assert.h"
+#include "include/ceph_assert.h"
 #include "mds/MDSMap.h"
 
 #include "include/cephfs/libcephfs.h"
 
+#define DEFAULT_UMASK 002
+
+using namespace std;
+
+static mode_t umask_cb(void *);
+namespace {
+// Set things up this way so we don't start up threads until mount and
+// kill them off when the last mount goes away, but are tolerant to
+// multiple mounts of overlapping duration.
+std::shared_ptr<ceph::async::io_context_pool> get_icp(CephContext* cct)
+{
+  static std::mutex m;
+  static std::weak_ptr<ceph::async::io_context_pool> icwp;
+
+
+  std::unique_lock l(m);
+
+  auto icp = icwp.lock();
+  if (icp)
+    return icp;
+
+  icp = std::make_shared<ceph::async::io_context_pool>();
+  icwp = icp;
+  icp->start(cct->_conf.get_val<std::uint64_t>("client_asio_thread_count"));
+  return icp;
+}
+}
 
 struct ceph_mount_info
 {
+  mode_t umask = DEFAULT_UMASK;
+  std::shared_ptr<ceph::async::io_context_pool> icp;
 public:
   explicit ceph_mount_info(CephContext *cct_)
     : default_perms(),
       mounted(false),
       inited(false),
-      client(NULL),
-      monclient(NULL),
-      messenger(NULL),
+      client(nullptr),
+      monclient(nullptr),
+      messenger(nullptr),
       cct(cct_)
   {
-    if (cct_ != nullptr) {
+    if (cct_) {
       cct->get();
     }
   }
@@ -58,7 +88,7 @@ public:
       shutdown();
       if (cct) {
        cct->put();
-       cct = NULL;
+       cct = nullptr;
       }
     }
     catch (const std::exception& e) {
@@ -73,12 +103,24 @@ public:
 
   int init()
   {
-    common_init_finish(cct);
-
     int ret;
 
+    if (!cct->_log->is_started()) {
+      cct->_log->start();
+    }
+    icp = get_icp(cct);
+
+    {
+      MonClient mc_bootstrap(cct, icp->get_io_context());
+      ret = mc_bootstrap.get_monmap_and_config();
+      if (ret < 0)
+       return ret;
+    }
+
+    common_init_finish(cct);
+
     //monmap
-    monclient = new MonClient(cct);
+    monclient = new MonClient(cct, icp->get_io_context());
     ret = -CEPHFS_ERROR_MON_MAP_BUILD; //defined in libcephfs.h;
     if (monclient->build_initial_monmap() < 0)
       goto fail;
@@ -88,7 +130,7 @@ public:
 
     //at last the client
     ret = -CEPHFS_ERROR_NEW_CLIENT; //defined in libcephfs.h;
-    client = new StandaloneClient(messenger, monclient);
+    client = new StandaloneClient(messenger, monclient, icp->get_io_context());
     if (!client)
       goto fail;
 
@@ -100,6 +142,13 @@ public:
     if (ret)
       goto fail;
 
+    {
+      ceph_client_callback_args args = {};
+      args.handle = this;
+      args.umask_cb = umask_cb;
+      client->ll_register_callbacks(&args);
+    }
+
     default_perms = Client::pick_my_perms(cct);
     inited = true;
     return 0;
@@ -109,6 +158,21 @@ public:
     return ret;
   }
 
+  int select_filesystem(const std::string &fs_name_)
+  {
+    if (mounted) {
+      return -EISCONN;
+    }
+
+    fs_name = fs_name_;
+    return 0;
+  }
+
+  const std::string& get_filesystem(void)
+  {
+    return fs_name;
+  }
+
   int mount(const std::string &mount_root, const UserPerm& perms)
   {
     int ret;
@@ -123,7 +187,7 @@ public:
       }
     }
 
-    ret = client->mount(mount_root, perms);
+    ret = client->mount(mount_root, perms, false, fs_name);
     if (ret) {
       shutdown();
       return ret;
@@ -140,6 +204,14 @@ public:
     shutdown();
     return 0;
   }
+  int abort_conn()
+  {
+    if (mounted) {
+      client->abort_conn();
+      mounted = false;
+    }
+    return 0;
+  }
 
   void shutdown()
   {
@@ -155,15 +227,16 @@ public:
       messenger->shutdown();
       messenger->wait();
       delete messenger;
-      messenger = NULL;
+      messenger = nullptr;
     }
+    icp.reset();
     if (monclient) {
       delete monclient;
-      monclient = NULL;
+      monclient = nullptr;
     }
     if (client) {
       delete client;
-      client = NULL;
+      client = nullptr;
     }
   }
 
@@ -177,53 +250,60 @@ public:
     return mounted;
   }
 
+  mode_t set_umask(mode_t umask)
+  {
+    this->umask = umask;
+    return umask;
+  }
+
+  std::string getaddrs()
+  {
+    CachedStackStringStream cos;
+    *cos << messenger->get_myaddrs();
+    return std::string(cos->strv());
+  }
+
   int conf_read_file(const char *path_list)
   {
-    int ret = cct->_conf->parse_config_files(path_list, NULL, 0);
+    int ret = cct->_conf.parse_config_files(path_list, nullptr, 0);
     if (ret)
       return ret;
-    cct->_conf->apply_changes(NULL);
-    cct->_conf->complain_about_parse_errors(cct);
+    cct->_conf.apply_changes(nullptr);
+    cct->_conf.complain_about_parse_error(cct);
     return 0;
   }
 
   int conf_parse_argv(int argc, const char **argv)
   {
-    int ret;
-    vector<const char*> args;
-    argv_to_vec(argc, argv, args);
-    ret = cct->_conf->parse_argv(args);
+    auto args = argv_to_vec(argc, argv);
+    int ret = cct->_conf.parse_argv(args);
     if (ret)
        return ret;
-    cct->_conf->apply_changes(NULL);
+    cct->_conf.apply_changes(nullptr);
     return 0;
   }
 
   int conf_parse_env(const char *name)
   {
-    md_config_t *conf = cct->_conf;
-    vector<const char*> args;
-    env_to_vec(args, name);
-    int ret = conf->parse_argv(args);
-    if (ret)
-      return ret;
-    conf->apply_changes(NULL);
+    auto& conf = cct->_conf;
+    conf.parse_env(cct->get_module_type(), name);
+    conf.apply_changes(nullptr);
     return 0;
   }
 
   int conf_set(const char *option, const char *value)
   {
-    int ret = cct->_conf->set_val(option, value);
+    int ret = cct->_conf.set_val(option, value);
     if (ret)
       return ret;
-    cct->_conf->apply_changes(NULL);
+    cct->_conf.apply_changes(nullptr);
     return 0;
   }
 
   int conf_get(const char *option, char *buf, size_t len)
   {
     char *tmp = buf;
-    return cct->_conf->get_val(option, &tmp, len);
+    return cct->_conf.get_val(option, &tmp, len);
   }
 
   Client *get_client()
@@ -255,8 +335,14 @@ private:
   Messenger *messenger;
   CephContext *cct;
   std::string cwd;
+  std::string fs_name;
 };
 
+static mode_t umask_cb(void *handle)
+{
+  return ((struct ceph_mount_info *)handle)->umask;
+}
+
 static void do_out_buffer(bufferlist& outbl, char **outbuf, size_t *outbuflen)
 {
   if (outbuf) {
@@ -264,7 +350,7 @@ static void do_out_buffer(bufferlist& outbl, char **outbuf, size_t *outbuflen)
       *outbuf = (char *)malloc(outbl.length());
       memcpy(*outbuf, outbl.c_str(), outbl.length());
     } else {
-      *outbuf = NULL;
+      *outbuf = nullptr;
     }
   }
   if (outbuflen)
@@ -278,7 +364,7 @@ static void do_out_buffer(string& outbl, char **outbuf, size_t *outbuflen)
       *outbuf = (char *)malloc(outbl.length());
       memcpy(*outbuf, outbl.c_str(), outbl.length());
     } else {
-      *outbuf = NULL;
+      *outbuf = nullptr;
     }
   }
   if (outbuflen)
@@ -308,7 +394,7 @@ extern "C" const char *ceph_version(int *pmajor, int *pminor, int *ppatch)
     *pminor = (n >= 2) ? minor : 0;
   if (ppatch)
     *ppatch = (n >= 3) ? patch : 0;
-  return VERSION;
+  return PROJECT_VERSION;
 }
 
 extern "C" int ceph_create_with_context(struct ceph_mount_info **cmount, CephContext *cct)
@@ -333,10 +419,11 @@ extern "C" int ceph_create(struct ceph_mount_info **cmount, const char * const i
   }
 
   CephContext *cct = common_preinit(iparams, CODE_ENVIRONMENT_LIBRARY, 0);
-  cct->_conf->parse_env(); // environment variables coverride
-  cct->_conf->apply_changes(NULL);
+  cct->_conf.parse_env(cct->get_module_type()); // environment variables coverride
+  cct->_conf.apply_changes(nullptr);
   int ret = ceph_create_with_context(cmount, cct);
   cct->put();
+  cct = nullptr;
   return ret;
 }
 
@@ -345,11 +432,17 @@ extern "C" int ceph_unmount(struct ceph_mount_info *cmount)
   return cmount->unmount();
 }
 
+extern "C" int ceph_abort_conn(struct ceph_mount_info *cmount)
+{
+  return cmount->abort_conn();
+}
+
 extern "C" int ceph_release(struct ceph_mount_info *cmount)
 {
   if (cmount->is_mounted())
     return -EISCONN;
   delete cmount;
+  cmount = nullptr;
   return 0;
 }
 
@@ -357,6 +450,23 @@ extern "C" void ceph_shutdown(struct ceph_mount_info *cmount)
 {
   cmount->shutdown();
   delete cmount;
+  cmount = nullptr;
+}
+
+extern "C" uint64_t ceph_get_instance_id(struct ceph_mount_info *cmount)
+{
+  if (cmount->is_initialized())
+    return cmount->get_client()->get_nodeid().v;
+  return 0;
+}
+
+extern "C" int ceph_getaddrs(struct ceph_mount_info *cmount, char** addrs)
+{
+  if (!cmount->is_initialized())
+    return -ENOTCONN;
+  auto s = cmount->getaddrs();
+  *addrs = strdup(s.c_str());
+  return 0;
 }
 
 extern "C" int ceph_conf_read_file(struct ceph_mount_info *cmount, const char *path)
@@ -364,6 +474,11 @@ extern "C" int ceph_conf_read_file(struct ceph_mount_info *cmount, const char *p
   return cmount->conf_read_file(path);
 }
 
+extern "C" mode_t ceph_umask(struct ceph_mount_info *cmount, mode_t mode)
+{
+  return cmount->set_umask(mode);
+}
+
 extern "C" int ceph_conf_parse_argv(struct ceph_mount_info *cmount, int argc,
                                     const char **argv)
 {
@@ -384,12 +499,21 @@ extern "C" int ceph_conf_set(struct ceph_mount_info *cmount, const char *option,
 extern "C" int ceph_conf_get(struct ceph_mount_info *cmount, const char *option,
                             char *buf, size_t len)
 {
-  if (buf == NULL) {
+  if (!buf) {
     return -EINVAL;
   }
   return cmount->conf_get(option, buf, len);
 }
 
+extern "C" int ceph_set_mount_timeout(struct ceph_mount_info *cmount, uint32_t timeout) {
+  if (cmount->is_mounted()) {
+    return -EINVAL;
+  }
+
+  auto timeout_str = stringify(timeout);
+  return ceph_conf_set(cmount, "client_mount_timeout", timeout_str.c_str());
+}
+
 extern "C" int ceph_mds_command(struct ceph_mount_info *cmount,
     const char *mds_spec,
     const char **cmd,
@@ -441,6 +565,16 @@ extern "C" int ceph_init(struct ceph_mount_info *cmount)
   return cmount->init();
 }
 
+extern "C" int ceph_select_filesystem(struct ceph_mount_info *cmount,
+                                      const char *fs_name)
+{
+  if (fs_name == nullptr) {
+    return -EINVAL;
+  }
+
+  return cmount->select_filesystem(fs_name);
+}
+
 extern "C" int ceph_mount(struct ceph_mount_info *cmount, const char *root)
 {
   std::string mount_root;
@@ -459,6 +593,22 @@ extern "C" struct UserPerm *ceph_mount_perms(struct ceph_mount_info *cmount)
   return &cmount->default_perms;
 }
 
+extern "C" int64_t ceph_get_fs_cid(struct ceph_mount_info *cmount)
+{
+  if (!cmount->is_mounted())
+    return -ENOTCONN;
+  return cmount->get_client()->get_fs_cid();
+}
+
+extern "C" int ceph_mount_perms_set(struct ceph_mount_info *cmount,
+                                   struct UserPerm *perms)
+{
+  if (cmount->is_mounted())
+    return -EISCONN;
+  cmount->default_perms = *perms;
+  return 0;
+}
+
 extern "C" int ceph_statfs(struct ceph_mount_info *cmount, const char *path,
                           struct statvfs *stbuf)
 {
@@ -494,6 +644,14 @@ extern "C" int ceph_opendir(struct ceph_mount_info *cmount,
   return cmount->get_client()->opendir(name, (dir_result_t **)dirpp, cmount->default_perms);
 }
 
+extern "C" int ceph_fdopendir(struct ceph_mount_info *cmount, int dirfd,
+                              struct ceph_dir_result **dirpp)
+{
+  if (!cmount->is_mounted())
+    return -ENOTCONN;
+  return cmount->get_client()->fdopendir(dirfd, (dir_result_t **)dirpp, cmount->default_perms);
+}
+
 extern "C" int ceph_closedir(struct ceph_mount_info *cmount, struct ceph_dir_result *dirp)
 {
   if (!cmount->is_mounted())
@@ -506,7 +664,7 @@ extern "C" struct dirent * ceph_readdir(struct ceph_mount_info *cmount, struct c
   if (!cmount->is_mounted()) {
     /* Client::readdir also sets errno to signal errors. */
     errno = ENOTCONN;
-    return NULL;
+    return nullptr;
   }
   return cmount->get_client()->readdir(reinterpret_cast<dir_result_t*>(dirp));
 }
@@ -566,6 +724,13 @@ extern "C" void ceph_seekdir(struct ceph_mount_info *cmount, struct ceph_dir_res
   cmount->get_client()->seekdir(reinterpret_cast<dir_result_t*>(dirp), offset);
 }
 
+extern "C" int ceph_may_delete(struct ceph_mount_info *cmount, const char *path)
+{
+  if (!cmount->is_mounted())
+    return -ENOTCONN;
+  return cmount->get_client()->may_delete(path, cmount->default_perms);
+}
+
 extern "C" int ceph_link (struct ceph_mount_info *cmount, const char *existing,
                          const char *newname)
 {
@@ -581,6 +746,13 @@ extern "C" int ceph_unlink(struct ceph_mount_info *cmount, const char *path)
   return cmount->get_client()->unlink(path, cmount->default_perms);
 }
 
+extern "C" int ceph_unlinkat(struct ceph_mount_info *cmount, int dirfd, const char *relpath, int flags)
+{
+  if (!cmount->is_mounted())
+    return -ENOTCONN;
+  return cmount->get_client()->unlinkat(dirfd, relpath, flags, cmount->default_perms);
+}
+
 extern "C" int ceph_rename(struct ceph_mount_info *cmount, const char *from,
                           const char *to)
 {
@@ -597,6 +769,35 @@ 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_mkdirat(struct ceph_mount_info *cmount, int dirfd, const char *relpath,
+                            mode_t mode)
+{
+  if (!cmount->is_mounted())
+    return -ENOTCONN;
+  return cmount->get_client()->mkdirat(dirfd, relpath, 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, true);
+}
+
 extern "C" int ceph_mkdirs(struct ceph_mount_info *cmount, const char *path, mode_t mode)
 {
   if (!cmount->is_mounted())
@@ -620,6 +821,14 @@ extern "C" int ceph_readlink(struct ceph_mount_info *cmount, const char *path,
   return cmount->get_client()->readlink(path, buf, size, cmount->default_perms);
 }
 
+extern "C" int ceph_readlinkat(struct ceph_mount_info *cmount, int dirfd,
+                               const char *relpath, char *buf, int64_t size)
+{
+  if (!cmount->is_mounted())
+    return -ENOTCONN;
+  return cmount->get_client()->readlinkat(dirfd, relpath, buf, size, cmount->default_perms);
+}
+
 extern "C" int ceph_symlink(struct ceph_mount_info *cmount, const char *existing,
                            const char *newname)
 {
@@ -628,6 +837,14 @@ extern "C" int ceph_symlink(struct ceph_mount_info *cmount, const char *existing
   return cmount->get_client()->symlink(existing, newname, cmount->default_perms);
 }
 
+extern "C" int ceph_symlinkat(struct ceph_mount_info *cmount, const char *existing, int dirfd,
+                            const char *newname)
+{
+  if (!cmount->is_mounted())
+    return -ENOTCONN;
+  return cmount->get_client()->symlinkat(existing, dirfd, newname, cmount->default_perms);
+}
+
 extern "C" int ceph_fstatx(struct ceph_mount_info *cmount, int fd, struct ceph_statx *stx,
                             unsigned int want, unsigned int flags)
 {
@@ -639,6 +856,17 @@ extern "C" int ceph_fstatx(struct ceph_mount_info *cmount, int fd, struct ceph_s
                                       want, flags);
 }
 
+extern "C" int ceph_statxat(struct ceph_mount_info *cmount, int dirfd, const char *relpath,
+                            struct ceph_statx *stx, unsigned int want, unsigned int flags)
+{
+  if (!cmount->is_mounted())
+    return -ENOTCONN;
+  if (flags & ~CEPH_REQ_FLAG_MASK)
+    return -EINVAL;
+  return cmount->get_client()->statxat(dirfd, relpath, stx, cmount->default_perms,
+                                       want, flags);
+}
+
 extern "C" int ceph_statx(struct ceph_mount_info *cmount, const char *path,
                          struct ceph_statx *stx, unsigned int want, unsigned int flags)
 {
@@ -757,18 +985,53 @@ extern "C" int ceph_fsetxattr(struct ceph_mount_info *cmount, int fd, const char
 }
 /* end xattr support */
 
+extern "C" int ceph_stat(struct ceph_mount_info *cmount, const char *path, struct stat *stbuf)
+{
+  if (!cmount->is_mounted())
+    return -ENOTCONN;
+  return cmount->get_client()->stat(path, stbuf, cmount->default_perms);
+}
+
+extern "C" int ceph_fstat(struct ceph_mount_info *cmount, int fd, struct stat *stbuf)
+{
+  if (!cmount->is_mounted())
+    return -ENOTCONN;
+  return cmount->get_client()->fstat(fd, stbuf, cmount->default_perms);
+}
+
+extern int ceph_lstat(struct ceph_mount_info *cmount, const char *path, struct stat *stbuf)
+{
+   if (!cmount->is_mounted())
+    return -ENOTCONN;
+  return cmount->get_client()->lstat(path, stbuf, cmount->default_perms);
+}
+
 extern "C" int ceph_chmod(struct ceph_mount_info *cmount, const char *path, mode_t mode)
 {
   if (!cmount->is_mounted())
     return -ENOTCONN;
   return cmount->get_client()->chmod(path, mode, cmount->default_perms);
 }
+extern "C" int ceph_lchmod(struct ceph_mount_info *cmount, const char *path, mode_t mode)
+{
+  if (!cmount->is_mounted())
+    return -ENOTCONN;
+  return cmount->get_client()->lchmod(path, mode, cmount->default_perms);
+}
 extern "C" int ceph_fchmod(struct ceph_mount_info *cmount, int fd, mode_t mode)
 {
   if (!cmount->is_mounted())
     return -ENOTCONN;
   return cmount->get_client()->fchmod(fd, mode, cmount->default_perms);
 }
+
+extern "C" int ceph_chmodat(struct ceph_mount_info *cmount, int dirfd, const char *relpath,
+                            mode_t mode, int flags) {
+  if (!cmount->is_mounted())
+    return -ENOTCONN;
+  return cmount->get_client()->chmodat(dirfd, relpath, mode, flags, cmount->default_perms);
+}
+
 extern "C" int ceph_chown(struct ceph_mount_info *cmount, const char *path,
                          int uid, int gid)
 {
@@ -791,6 +1054,12 @@ extern "C" int ceph_lchown(struct ceph_mount_info *cmount, const char *path,
   return cmount->get_client()->lchown(path, uid, gid, cmount->default_perms);
 }
 
+extern "C" int ceph_chownat(struct ceph_mount_info *cmount, int dirfd, const char *relpath,
+                            uid_t uid, gid_t gid, int flags) {
+  if (!cmount->is_mounted())
+    return -ENOTCONN;
+  return cmount->get_client()->chownat(dirfd, relpath, uid, gid, flags, cmount->default_perms);
+}
 
 extern "C" int ceph_utime(struct ceph_mount_info *cmount, const char *path,
                          struct utimbuf *buf)
@@ -800,6 +1069,53 @@ extern "C" int ceph_utime(struct ceph_mount_info *cmount, const char *path,
   return cmount->get_client()->utime(path, buf, cmount->default_perms);
 }
 
+extern "C" int ceph_futime(struct ceph_mount_info *cmount, int fd,
+                           struct utimbuf *buf)
+{
+  if (!cmount->is_mounted())
+    return -ENOTCONN;
+  return cmount->get_client()->futime(fd, buf, cmount->default_perms);
+}
+
+extern "C" int ceph_utimes(struct ceph_mount_info *cmount, const char *path,
+                           struct timeval times[2])
+{
+  if (!cmount->is_mounted())
+    return -ENOTCONN;
+  return cmount->get_client()->utimes(path, times, cmount->default_perms);
+}
+
+extern "C" int ceph_lutimes(struct ceph_mount_info *cmount, const char *path,
+                            struct timeval times[2])
+{
+  if (!cmount->is_mounted())
+    return -ENOTCONN;
+  return cmount->get_client()->lutimes(path, times, cmount->default_perms);
+}
+
+extern "C" int ceph_futimes(struct ceph_mount_info *cmount, int fd,
+                            struct timeval times[2])
+{
+  if (!cmount->is_mounted())
+    return -ENOTCONN;
+  return cmount->get_client()->futimes(fd, times, cmount->default_perms);
+}
+
+extern "C" int ceph_futimens(struct ceph_mount_info *cmount, int fd,
+                            struct timespec times[2])
+{
+  if (!cmount->is_mounted())
+    return -ENOTCONN;
+  return cmount->get_client()->futimens(fd, times, cmount->default_perms);
+}
+
+extern "C" int ceph_utimensat(struct ceph_mount_info *cmount, int dirfd, const char *relpath,
+                              struct timespec times[2], int flags) {
+  if (!cmount->is_mounted())
+    return -ENOTCONN;
+  return cmount->get_client()->utimensat(dirfd, relpath, times, flags, cmount->default_perms);
+}
+
 extern "C" int ceph_flock(struct ceph_mount_info *cmount, int fd, int operation,
                          uint64_t owner)
 {
@@ -833,6 +1149,14 @@ extern "C" int ceph_open(struct ceph_mount_info *cmount, const char *path,
   return cmount->get_client()->open(path, flags, cmount->default_perms, mode);
 }
 
+extern "C" int ceph_openat(struct ceph_mount_info *cmount, int dirfd, const char *relpath,
+                           int flags, mode_t mode)
+{
+  if (!cmount->is_mounted())
+    return -ENOTCONN;
+  return cmount->get_client()->openat(dirfd, relpath, flags, cmount->default_perms, mode);
+}
+
 extern "C" int ceph_open_layout(struct ceph_mount_info *cmount, const char *path, int flags,
     mode_t mode, int stripe_unit, int stripe_count, int object_size, const char *data_pool)
 {
@@ -912,6 +1236,29 @@ extern "C" int ceph_fallocate(struct ceph_mount_info *cmount, int fd, int mode,
   return cmount->get_client()->fallocate(fd, mode, offset, length);
 }
 
+extern "C" int ceph_lazyio(class ceph_mount_info *cmount,
+                           int fd, int enable)
+{
+  return (cmount->get_client()->lazyio(fd, enable));
+}
+
+extern "C" int ceph_lazyio_propagate(class ceph_mount_info *cmount,
+                           int fd, int64_t offset, size_t count)
+{
+  if (!cmount->is_mounted())
+    return -ENOTCONN;
+  return (cmount->get_client()->lazyio_propagate(fd, offset, count));
+}
+
+extern "C" int ceph_lazyio_synchronize(class ceph_mount_info *cmount,
+                           int fd, int64_t offset, size_t count)
+{
+  if (!cmount->is_mounted())
+    return -ENOTCONN;
+  return (cmount->get_client()->lazyio_synchronize(fd, offset, count));
+}
+
+
 extern "C" int ceph_sync_fs(struct ceph_mount_info *cmount)
 {
   if (!cmount->is_mounted())
@@ -919,7 +1266,6 @@ extern "C" int ceph_sync_fs(struct ceph_mount_info *cmount)
   return cmount->get_client()->sync_fs();
 }
 
-
 extern "C" int ceph_get_file_stripe_unit(struct ceph_mount_info *cmount, int fh)
 {
   file_layout_t l;
@@ -1384,6 +1730,14 @@ extern "C" struct Inode *ceph_ll_get_inode(class ceph_mount_info *cmount,
 }
 
 
+extern "C" int ceph_ll_lookup_vino(
+    struct ceph_mount_info *cmount,
+    vinodeno_t vino,
+    Inode **inode)
+{
+  return (cmount->get_client())->ll_lookup_vino(vino, cmount->default_perms, inode);
+}
+
 /**
  * Populates the client cache with the requested inode, and its
  * parent dentry.
@@ -1459,7 +1813,7 @@ extern "C" int ceph_ll_read(class ceph_mount_info *cmount, Fh* filehandle,
   r = cmount->get_client()->ll_read(filehandle, off, len, &bl);
   if (r >= 0)
     {
-      bl.copy(0, bl.length(), buf);
+      bl.begin().copy(bl.length(), buf);
       r = bl.length();
     }
   return r;
@@ -1511,6 +1865,12 @@ extern "C" int ceph_ll_sync_inode(class ceph_mount_info *cmount,
   return (cmount->get_client()->ll_sync_inode(in, syncdataonly));
 }
 
+extern "C" int ceph_ll_fallocate(class ceph_mount_info *cmount, Fh *fh,
+                                int mode, int64_t offset, int64_t length)
+{
+  return cmount->get_client()->ll_fallocate(fh, mode, offset, length);
+}
+
 extern "C" off_t ceph_ll_lseek(class ceph_mount_info *cmount,
                                Fh *fh, off_t offset, int whence)
 {
@@ -1528,14 +1888,14 @@ extern "C" int64_t ceph_ll_readv(class ceph_mount_info *cmount,
                                 struct Fh *fh, const struct iovec *iov,
                                 int iovcnt, int64_t off)
 {
-  return -1; // TODO:  implement
+  return (cmount->get_client()->ll_readv(fh, iov, iovcnt, off));
 }
 
 extern "C" int64_t ceph_ll_writev(class ceph_mount_info *cmount,
                                  struct Fh *fh, const struct iovec *iov,
                                  int iovcnt, int64_t off)
 {
-  return -1; // TODO:  implement
+  return (cmount->get_client()->ll_writev(fh, iov, iovcnt, off));
 }
 
 extern "C" int ceph_ll_close(class ceph_mount_info *cmount, Fh* fh)
@@ -1597,8 +1957,7 @@ extern "C" int ceph_ll_opendir(class ceph_mount_info *cmount,
 extern "C" int ceph_ll_releasedir(class ceph_mount_info *cmount,
                                  ceph_dir_result *dir)
 {
-  (void) cmount->get_client()->ll_releasedir(reinterpret_cast<dir_result_t*>(dir));
-  return (0);
+  return cmount->get_client()->ll_releasedir(reinterpret_cast<dir_result_t*>(dir));
 }
 
 extern "C" int ceph_ll_rename(class ceph_mount_info *cmount,
@@ -1696,6 +2055,12 @@ extern "C" int ceph_ll_setlk(struct ceph_mount_info *cmount,
   return (cmount->get_client()->ll_setlk(fh, fl, owner, sleep));
 }
 
+extern "C" int ceph_ll_lazyio(class ceph_mount_info *cmount,
+                             Fh *fh, int enable)
+{
+  return (cmount->get_client()->ll_lazyio(fh, enable));
+}
+
 extern "C" int ceph_ll_delegation(struct ceph_mount_info *cmount, Fh *fh,
                                  unsigned cmd, ceph_deleg_cb_t cb, void *priv)
 {
@@ -1771,3 +2136,99 @@ extern "C" int ceph_set_deleg_timeout(class ceph_mount_info *cmount, uint32_t ti
     return -ENOTCONN;
   return cmount->get_client()->set_deleg_timeout(timeout);
 }
+
+extern "C" void ceph_set_session_timeout(class ceph_mount_info *cmount, unsigned timeout)
+{
+  cmount->get_client()->set_session_timeout(timeout);
+}
+
+extern "C" void ceph_set_uuid(class ceph_mount_info *cmount, const char *uuid)
+{
+  cmount->get_client()->set_uuid(std::string(uuid));
+}
+
+extern "C" int ceph_start_reclaim(class ceph_mount_info *cmount,
+                                 const char *uuid, unsigned flags)
+{
+  if (!cmount->is_initialized()) {
+    int ret = cmount->init();
+    if (ret != 0)
+      return ret;
+  }
+  return cmount->get_client()->start_reclaim(std::string(uuid), flags,
+                                            cmount->get_filesystem());
+}
+
+extern "C" void ceph_finish_reclaim(class ceph_mount_info *cmount)
+{
+  cmount->get_client()->finish_reclaim();
+}
+
+// This is deprecated, use ceph_ll_register_callbacks2 instead.
+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_ll_register_callbacks2(class ceph_mount_info *cmount,
+                                           struct ceph_client_callback_args *args)
+{
+  return cmount->get_client()->ll_register_callbacks2(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);
+}