summaryrefslogtreecommitdiffstats
path: root/src/libcephfs_proxy/libcephfsd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcephfs_proxy/libcephfsd.c')
-rw-r--r--src/libcephfs_proxy/libcephfsd.c1823
1 files changed, 1823 insertions, 0 deletions
diff --git a/src/libcephfs_proxy/libcephfsd.c b/src/libcephfs_proxy/libcephfsd.c
new file mode 100644
index 00000000000..ee2d99a0aae
--- /dev/null
+++ b/src/libcephfs_proxy/libcephfsd.c
@@ -0,0 +1,1823 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <endian.h>
+
+#include "include/cephfs/libcephfs.h"
+
+#include "proxy_manager.h"
+#include "proxy_link.h"
+#include "proxy_helpers.h"
+#include "proxy_log.h"
+#include "proxy_requests.h"
+#include "proxy_mount.h"
+
+typedef struct _proxy_server {
+ proxy_link_t link;
+ proxy_manager_t *manager;
+} proxy_server_t;
+
+typedef struct _proxy_client {
+ proxy_worker_t worker;
+ proxy_link_t *link;
+ proxy_random_t random;
+ void *buffer;
+ uint32_t buffer_size;
+ int32_t sd;
+} proxy_client_t;
+
+typedef struct _proxy {
+ proxy_manager_t manager;
+ proxy_log_handler_t log_handler;
+ const char *socket_path;
+} proxy_t;
+
+typedef int32_t (*proxy_handler_t)(proxy_client_t *, proxy_req_t *,
+ const void *data, int32_t data_size);
+
+/* This is used for requests that are not associated with a cmount. */
+static proxy_random_t global_random;
+
+static int32_t send_error(proxy_client_t *client, int32_t error)
+{
+ proxy_link_ans_t ans;
+ struct iovec iov[1];
+
+ iov[0].iov_base = &ans;
+ iov[0].iov_len = sizeof(ans);
+
+ return proxy_link_ans_send(client->sd, error, iov, 1);
+}
+
+static uint64_t uint64_checksum(uint64_t value)
+{
+ value = (value & 0xff00ff00ff00ffULL) +
+ ((value >> 8) & 0xff00ff00ff00ffULL);
+ value += value >> 16;
+ value += value >> 32;
+
+ return value & 0xff;
+}
+
+static uint64_t ptr_checksum(proxy_random_t *rnd, void *ptr)
+{
+ uint64_t value;
+
+ if (ptr == NULL) {
+ return 0;
+ }
+
+ value = (uint64_t)(uintptr_t)ptr;
+ /* Many current processors don't use the full 64-bits for the virtual
+ * address space, and Linux assigns the lower 128 TiB (47 bits) for
+ * user-space applications on most architectures, so the highest 8 bits
+ * of all valid addressess are always 0.
+ *
+ * We use this to encode a checksum in the high byte of the address to
+ * be able to do a verification before dereferencing the pointer,
+ * avoiding crashes if the client passes an invalid or corrupted pointer
+ * value.
+ *
+ * Alternatives like using indexes in a table or registering valid
+ * pointers require access to a shared data structure that will require
+ * thread synchronization, making it slower. */
+ if ((value & 0xff00000000000007ULL) != 0) {
+ proxy_log(LOG_ERR, EINVAL,
+ "Unexpected pointer value");
+ abort();
+ }
+
+ value -= uint64_checksum(value) << 56;
+
+ return random_scramble(rnd, value);
+}
+
+static int32_t ptr_check(proxy_random_t *rnd, uint64_t value, void **pptr)
+{
+ if (value == 0) {
+ *pptr = NULL;
+ return 0;
+ }
+
+ value = random_unscramble(rnd, value);
+
+ if ((uint64_checksum(value) != 0) || ((value & 7) != 0)) {
+ proxy_log(LOG_ERR, EFAULT, "Unexpected pointer value");
+ return -EFAULT;
+ }
+
+ *pptr = (void *)(uintptr_t)(value & 0xffffffffffffffULL);
+
+ return 0;
+}
+
+/* Macro to simplify request handling. */
+#define CEPH_COMPLETE(_client, _err, _ans) \
+ ({ \
+ int32_t __err = (_err); \
+ if (__err < 0) { \
+ __err = send_error(_client, __err); \
+ } else { \
+ __err = CEPH_RET(_client->sd, __err, _ans); \
+ } \
+ __err; \
+ })
+
+#ifdef PROXY_TRACE
+#define TRACE(_fmt, _args...) printf(_fmt "\n", ##_args)
+#else
+#define TRACE(_fmt, _args...) do { } while (0)
+#endif
+
+static int32_t libcephfsd_version(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_version, ans, 1);
+ const char *text;
+ int32_t major, minor, patch;
+
+ text = ceph_version(&major, &minor, &patch);
+ TRACE("ceph_version(%d, %d, %d) -> %s", major, minor, patch, text);
+
+ ans.major = major;
+ ans.minor = minor;
+ ans.patch = patch;
+
+ CEPH_STR_ADD(ans, text, text);
+
+ return CEPH_RET(client->sd, 0, ans);
+}
+
+static int32_t libcephfsd_userperm_new(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_userperm_new, ans, 0);
+ UserPerm *userperm;
+ int32_t err;
+
+ userperm = ceph_userperm_new(req->userperm_new.uid,
+ req->userperm_new.gid,
+ req->userperm_new.groups, (gid_t *)data);
+ TRACE("ceph_userperm_new(%u, %u, %u) -> %p", req->userperm_new.uid,
+ req->userperm_new.gid, req->userperm_new.groups, userperm);
+
+ err = -ENOMEM;
+ if (userperm != NULL) {
+ ans.userperm = ptr_checksum(&global_random, userperm);
+ err = 0;
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_userperm_destroy(proxy_client_t *client,
+ proxy_req_t *req, const void *data,
+ int32_t data_size)
+{
+ CEPH_DATA(ceph_userperm_destroy, ans, 0);
+ UserPerm *perms;
+ int32_t err;
+
+ err = ptr_check(&global_random, req->userperm_destroy.userperm,
+ (void **)&perms);
+
+ if (err >= 0) {
+ ceph_userperm_destroy(perms);
+ TRACE("ceph_userperm_destroy(%p)", perms);
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_create(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_create, ans, 0);
+ proxy_mount_t *mount;
+ const char *id;
+ int32_t err;
+
+ id = CEPH_STR_GET(req->create, id, data);
+
+ err = proxy_mount_create(&mount, id);
+ TRACE("ceph_create(%p, '%s') -> %d", mount, id, err);
+
+ if (err >= 0) {
+ ans.cmount = ptr_checksum(&client->random, mount);
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_release(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_release, ans, 0);
+ proxy_mount_t *mount;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->release.cmount, (void **)&mount);
+ if (err >= 0) {
+ err = proxy_mount_release(mount);
+ TRACE("ceph_release(%p) -> %d", mount, err);
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_conf_read_file(proxy_client_t *client,
+ proxy_req_t *req, const void *data,
+ int32_t data_size)
+{
+ CEPH_DATA(ceph_conf_read_file, ans, 0);
+ proxy_mount_t *mount;
+ const char *path;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->conf_read_file.cmount,
+ (void **)&mount);
+ if (err >= 0) {
+ path = CEPH_STR_GET(req->conf_read_file, path, data);
+
+ err = proxy_mount_config(mount, path);
+ TRACE("ceph_conf_read_file(%p, '%s') ->%d", mount, path, err);
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_conf_get(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_conf_get, ans, 1);
+ proxy_mount_t *mount;
+ const char *option;
+ void *buffer;
+ uint32_t size;
+ int32_t err;
+
+ buffer = client->buffer;
+ size = client->buffer_size;
+ if (req->conf_get.size < size) {
+ size = req->conf_get.size;
+ }
+ err = ptr_check(&client->random, req->conf_get.cmount, (void **)&mount);
+ if (err >= 0) {
+ option = CEPH_STR_GET(req->conf_get, option, data);
+
+ err = proxy_mount_get(mount, option, buffer, size);
+ TRACE("ceph_conf_get(%p, '%s', '%s') -> %d", mount, option,
+ (char *)buffer, err);
+
+ if (err >= 0) {
+ CEPH_DATA_ADD(ans, value, buffer, strlen(buffer) + 1);
+ }
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_conf_set(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_conf_set, ans, 0);
+ proxy_mount_t *mount;
+ const char *option, *value;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->conf_set.cmount, (void **)&mount);
+ if (err >= 0) {
+ option = CEPH_STR_GET(req->conf_set, option, data);
+ value = CEPH_STR_GET(req->conf_set, value,
+ data + req->conf_set.option);
+
+ err = proxy_mount_set(mount, option, value);
+ TRACE("ceph_conf_set(%p, '%s', '%s') -> %d", mount, option,
+ value, err);
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_init(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_init, ans, 0);
+ proxy_mount_t *mount;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->init.cmount, (void **)&mount);
+ if (err >= 0) {
+ err = proxy_mount_init(mount);
+ TRACE("ceph_init(%p) -> %d", mount, err);
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_select_filesystem(proxy_client_t *client,
+ proxy_req_t *req, const void *data,
+ int32_t data_size)
+{
+ CEPH_DATA(ceph_select_filesystem, ans, 0);
+ proxy_mount_t *mount;
+ const char *fs;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->select_filesystem.cmount,
+ (void **)&mount);
+ if (err >= 0) {
+ fs = CEPH_STR_GET(req->select_filesystem, fs, data);
+
+ err = proxy_mount_select(mount, fs);
+ TRACE("ceph_select_filesystem(%p, '%s') -> %d", mount, fs, err);
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_mount(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_mount, ans, 0);
+ proxy_mount_t *mount;
+ const char *root;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->mount.cmount, (void **)&mount);
+ if (err >= 0) {
+ root = CEPH_STR_GET(req->mount, root, data);
+
+ err = proxy_mount_mount(mount, root);
+ TRACE("ceph_mount(%p, '%s') -> %d", mount, root, err);
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_unmount(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_unmount, ans, 0);
+ proxy_mount_t *mount;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->unmount.cmount, (void **)&mount);
+
+ if (err >= 0) {
+ err = proxy_mount_unmount(mount);
+ TRACE("ceph_unmount(%p) -> %d", mount, err);
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_statfs(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_statfs, ans, 1);
+ struct statvfs st;
+ proxy_mount_t *mount;
+ struct Inode *inode;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->ll_statfs.cmount,
+ (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_statfs.inode,
+ (void **)&inode);
+ }
+
+ if (err >= 0) {
+ CEPH_BUFF_ADD(ans, &st, sizeof(st));
+
+ err = ceph_ll_statfs(proxy_cmount(mount), inode, &st);
+ TRACE("ceph_ll_statfs(%p, %p) -> %d", mount, inode, err);
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_lookup(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_lookup, ans, 1);
+ struct ceph_statx stx;
+ proxy_mount_t *mount;
+ struct Inode *parent, *out;
+ const char *name;
+ UserPerm *perms;
+ uint32_t want, flags;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->ll_lookup.cmount,
+ (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_lookup.parent,
+ (void **)&parent);
+ }
+ if (err >= 0) {
+ err = ptr_check(&global_random, req->ll_lookup.userperm,
+ (void **)&perms);
+ }
+ if (err >= 0) {
+ want = req->ll_lookup.want;
+ flags = req->ll_lookup.flags;
+ name = CEPH_STR_GET(req->ll_lookup, name, data);
+
+ CEPH_BUFF_ADD(ans, &stx, sizeof(stx));
+
+ if (name == NULL) {
+ err = proxy_log(LOG_ERR, EINVAL,
+ "NULL name passed to ceph_ll_lookup()");
+ } else {
+ // Forbid going outside of the root mount point
+ if ((parent == mount->root) &&
+ (strcmp(name, "..") == 0)) {
+ name = ".";
+ }
+
+ err = ceph_ll_lookup(proxy_cmount(mount), parent, name,
+ &out, &stx, want, flags, perms);
+ }
+
+ TRACE("ceph_ll_lookup(%p, %p, '%s', %p, %x, %x, %p) -> %d",
+ mount, parent, name, out, want, flags, perms, err);
+
+ if (err >= 0) {
+ ans.inode = ptr_checksum(&client->random, out);
+ }
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_lookup_inode(proxy_client_t *client,
+ proxy_req_t *req, const void *data,
+ int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_lookup_inode, ans, 0);
+ proxy_mount_t *mount;
+ struct Inode *inode;
+ struct inodeno_t ino;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->ll_lookup_inode.cmount,
+ (void **)&mount);
+ if (err >= 0) {
+ ino = req->ll_lookup_inode.ino;
+
+ err = ceph_ll_lookup_inode(proxy_cmount(mount), ino, &inode);
+ TRACE("ceph_ll_lookup_inode(%p, %lu, %p) -> %d", mount, ino.val,
+ inode, err);
+
+ if (err >= 0) {
+ ans.inode = ptr_checksum(&client->random, inode);
+ }
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_lookup_root(proxy_client_t *client,
+ proxy_req_t *req, const void *data,
+ int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_lookup_root, ans, 0);
+ proxy_mount_t *mount;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->ll_lookup_root.cmount,
+ (void **)&mount);
+ if (err >= 0) {
+ /* The libcephfs view of the root of the mount could be
+ * different than ours, so we can't rely on
+ * ceph_ll_lookup_root(). We fake it by returning the cached
+ * root inode at the time of mount. */
+ err = proxy_inode_ref(mount, mount->root_ino);
+ TRACE("ceph_ll_lookup_root(%p, %p) -> %d", mount, mount->root,
+ err);
+
+ if (err >= 0) {
+ ans.inode = ptr_checksum(&client->random, mount->root);
+ }
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_put(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_put, ans, 0);
+ proxy_mount_t *mount;
+ struct Inode *inode;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->ll_put.cmount, (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_put.inode,
+ (void **)&inode);
+ }
+
+ if (err >= 0) {
+ err = ceph_ll_put(proxy_cmount(mount), inode);
+ TRACE("ceph_ll_put(%p, %p) -> %d", mount, inode, err);
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_walk(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_walk, ans, 1);
+ struct ceph_statx stx;
+ proxy_mount_t *mount;
+ struct Inode *inode;
+ const char *path;
+ UserPerm *perms;
+ uint32_t want, flags;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->ll_walk.cmount, (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&global_random, req->ll_walk.userperm,
+ (void **)&perms);
+ }
+ if (err >= 0) {
+ want = req->ll_walk.want;
+ flags = req->ll_walk.flags;
+ path = CEPH_STR_GET(req->ll_walk, path, data);
+
+ CEPH_BUFF_ADD(ans, &stx, sizeof(stx));
+
+ err = proxy_path_resolve(mount, path, &inode, &stx, want, flags,
+ perms, NULL);
+ TRACE("ceph_ll_walk(%p, '%s', %p, %x, %x, %p) -> %d", mount,
+ path, inode, want, flags, perms, err);
+
+ if (err >= 0) {
+ ans.inode = ptr_checksum(&client->random, inode);
+ }
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_chdir(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_chdir, ans, 0);
+ struct ceph_statx stx;
+ proxy_mount_t *mount;
+ struct Inode *inode;
+ const char *path;
+ char *realpath;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->chdir.cmount, (void **)&mount);
+ if (err >= 0) {
+ path = CEPH_STR_GET(req->chdir, path, data);
+
+ /* Since the libcephfs mount may be shared, we can't really
+ * change the current directory to avoid interferences with
+ * other users, so we just lookup the new directory and keep an
+ * internal reference. */
+ err = proxy_path_resolve(mount, path, &inode, &stx,
+ CEPH_STATX_INO, 0, mount->perms,
+ &realpath);
+ TRACE("ceph_chdir(%p, '%s') -> %d", mount, path, err);
+ if (err >= 0) {
+ ceph_ll_put(proxy_cmount(mount), mount->cwd);
+ mount->cwd = inode;
+ mount->cwd_ino = stx.stx_ino;
+
+ /* TODO: This path may become outdated if the parent
+ * directories are moved, however this seems the
+ * best we can do for now. */
+ proxy_free(mount->cwd_path);
+ mount->cwd_path = realpath;
+ mount->cwd_path_len = strlen(realpath);
+ }
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_getcwd(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_getcwd, ans, 1);
+ proxy_mount_t *mount;
+ const char *path;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->getcwd.cmount, (void **)&mount);
+
+ if (err >= 0) {
+ /* We just return the cached name from the last chdir(). */
+ path = mount->cwd_path;
+ TRACE("ceph_getcwd(%p) -> '%s'", mount, path);
+ CEPH_STR_ADD(ans, path, path);
+ err = 0;
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_readdir(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_readdir, ans, 1);
+ struct dirent de;
+ proxy_mount_t *mount;
+ struct ceph_dir_result *dirp;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->readdir.cmount, (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->readdir.dir,
+ (void **)&dirp);
+ }
+
+ if (err >= 0) {
+ err = ceph_readdir_r(proxy_cmount(mount), dirp, &de);
+ TRACE("ceph_readdir_r(%p, %p, %p) -> %d", mount, dirp, &de,
+ err);
+ ans.eod = true;
+ if (err > 0) {
+ ans.eod = false;
+ CEPH_BUFF_ADD(ans, &de,
+ offset_of(struct dirent, d_name) +
+ strlen(de.d_name) + 1);
+ }
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_rewinddir(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_rewinddir, ans, 0);
+ proxy_mount_t *mount;
+ struct ceph_dir_result *dirp;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->rewinddir.cmount,
+ (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->rewinddir.dir,
+ (void **)&dirp);
+ }
+
+ if (err >= 0) {
+ ceph_rewinddir(proxy_cmount(mount), dirp);
+ TRACE("ceph_rewinddir(%p, %p)", mount, dirp);
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_open(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_open, ans, 0);
+ proxy_mount_t *mount;
+ struct Inode *inode;
+ UserPerm *perms;
+ struct Fh *fh;
+ int32_t flags, err;
+
+ err = ptr_check(&client->random, req->ll_open.cmount, (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_open.inode,
+ (void **)&inode);
+ }
+ if (err >= 0) {
+ err = ptr_check(&global_random, req->ll_open.userperm,
+ (void **)&perms);
+ }
+ if (err >= 0) {
+ flags = req->ll_open.flags;
+
+ err = ceph_ll_open(proxy_cmount(mount), inode, flags, &fh,
+ perms);
+ TRACE("ceph_ll_open(%p, %p, %x, %p, %p) -> %d", mount, inode,
+ flags, fh, perms, err);
+
+ if (err >= 0) {
+ ans.fh = ptr_checksum(&client->random, fh);
+ }
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_create(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_create, ans, 1);
+ struct ceph_statx stx;
+ proxy_mount_t *mount;
+ struct Inode *parent, *inode;
+ struct Fh *fh;
+ const char *name;
+ UserPerm *perms;
+ mode_t mode;
+ uint32_t want, flags;
+ int32_t oflags, err;
+
+ err = ptr_check(&client->random, req->ll_create.cmount,
+ (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_create.parent,
+ (void **)&parent);
+ }
+ if (err >= 0) {
+ err = ptr_check(&global_random, req->ll_create.userperm,
+ (void **)&perms);
+ }
+ if (err >= 0) {
+ mode = req->ll_create.mode;
+ oflags = req->ll_create.oflags;
+ want = req->ll_create.want;
+ flags = req->ll_create.flags;
+ name = CEPH_STR_GET(req->ll_create, name, data);
+
+ CEPH_BUFF_ADD(ans, &stx, sizeof(stx));
+
+ err = ceph_ll_create(proxy_cmount(mount), parent, name, mode,
+ oflags, &inode, &fh, &stx, want, flags,
+ perms);
+ TRACE("ceph_ll_create(%p, %p, '%s', %o, %x, %p, %p, %x, %x, "
+ "%p) -> %d",
+ mount, parent, name, mode, oflags, inode, fh, want, flags,
+ perms, err);
+
+ if (err >= 0) {
+ ans.fh = ptr_checksum(&client->random, fh);
+ ans.inode = ptr_checksum(&client->random, inode);
+ }
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_mknod(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_mknod, ans, 1);
+ struct ceph_statx stx;
+ proxy_mount_t *mount;
+ struct Inode *parent, *inode;
+ const char *name;
+ UserPerm *perms;
+ dev_t rdev;
+ mode_t mode;
+ uint32_t want, flags;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->ll_mknod.cmount, (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_mknod.parent,
+ (void **)&parent);
+ }
+ if (err >= 0) {
+ err = ptr_check(&global_random, req->ll_mknod.userperm,
+ (void **)&perms);
+ }
+ if (err >= 0) {
+ mode = req->ll_mknod.mode;
+ rdev = req->ll_mknod.rdev;
+ want = req->ll_mknod.want;
+ flags = req->ll_mknod.flags;
+ name = CEPH_STR_GET(req->ll_mknod, name, data);
+
+ CEPH_BUFF_ADD(ans, &stx, sizeof(stx));
+
+ err = ceph_ll_mknod(proxy_cmount(mount), parent, name, mode,
+ rdev, &inode, &stx, want, flags, perms);
+ TRACE("ceph_ll_mknod(%p, %p, '%s', %o, %lx, %p, %x, %x, %p) -> "
+ "%d",
+ mount, parent, name, mode, rdev, inode, want, flags,
+ perms, err);
+
+ if (err >= 0) {
+ ans.inode = ptr_checksum(&client->random, inode);
+ }
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_close(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_close, ans, 0);
+ proxy_mount_t *mount;
+ struct Fh *fh;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->ll_close.cmount, (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_close.fh,
+ (void **)&fh);
+ }
+
+ if (err >= 0) {
+ err = ceph_ll_close(proxy_cmount(mount), fh);
+ TRACE("ceph_ll_close(%p, %p) -> %d", mount, fh, err);
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_rename(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_rename, ans, 0);
+ proxy_mount_t *mount;
+ struct Inode *old_parent, *new_parent;
+ const char *old_name, *new_name;
+ UserPerm *perms;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->ll_rename.cmount,
+ (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_rename.old_parent,
+ (void **)&old_parent);
+ }
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_rename.new_parent,
+ (void **)&new_parent);
+ }
+ if (err >= 0) {
+ err = ptr_check(&global_random, req->ll_rename.userperm,
+ (void **)&perms);
+ }
+ if (err >= 0) {
+ old_name = CEPH_STR_GET(req->ll_rename, old_name, data);
+ new_name = CEPH_STR_GET(req->ll_rename, new_name,
+ data + req->ll_rename.old_name);
+
+ err = ceph_ll_rename(proxy_cmount(mount), old_parent, old_name,
+ new_parent, new_name, perms);
+ TRACE("ceph_ll_rename(%p, %p, '%s', %p, '%s', %p) -> %d", mount,
+ old_parent, old_name, new_parent, new_name, perms, err);
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_lseek(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_lseek, ans, 0);
+ proxy_mount_t *mount;
+ struct Fh *fh;
+ off_t offset, pos;
+ int32_t whence, err;
+
+ err = ptr_check(&client->random, req->ll_lseek.cmount, (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_lseek.fh,
+ (void **)&fh);
+ }
+ if (err >= 0) {
+ offset = req->ll_lseek.offset;
+ whence = req->ll_lseek.whence;
+
+ pos = ceph_ll_lseek(proxy_cmount(mount), fh, offset, whence);
+ err = -errno;
+ TRACE("ceph_ll_lseek(%p, %p, %ld, %d) -> %ld (%d)", mount, fh,
+ offset, whence, pos, -err);
+
+ if (pos >= 0) {
+ ans.offset = pos;
+ err = 0;
+ }
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_read(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_read, ans, 1);
+ proxy_mount_t *mount;
+ struct Fh *fh;
+ void *buffer;
+ uint64_t len;
+ int64_t offset;
+ uint32_t size;
+ int32_t err;
+
+ buffer = client->buffer;
+
+ err = ptr_check(&client->random, req->ll_read.cmount, (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_read.fh, (void **)&fh);
+ }
+ if (err >= 0) {
+ offset = req->ll_read.offset;
+ len = req->ll_read.len;
+
+ size = client->buffer_size;
+ if (len > size) {
+ buffer = proxy_malloc(len);
+ if (buffer == NULL) {
+ err = -ENOMEM;
+ }
+ }
+ if (err >= 0) {
+ err = ceph_ll_read(proxy_cmount(mount), fh, offset, len,
+ buffer);
+ TRACE("ceph_ll_read(%p, %p, %ld, %lu) -> %d", mount, fh,
+ offset, len, err);
+
+ if (err >= 0) {
+ CEPH_BUFF_ADD(ans, buffer, err);
+ }
+ }
+ }
+
+ err = CEPH_COMPLETE(client, err, ans);
+
+ if (buffer != client->buffer) {
+ proxy_free(buffer);
+ }
+
+ return err;
+}
+
+static int32_t libcephfsd_ll_write(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_write, ans, 0);
+ proxy_mount_t *mount;
+ struct Fh *fh;
+ uint64_t len;
+ int64_t offset;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->ll_write.cmount, (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_write.fh,
+ (void **)&fh);
+ }
+ if (err >= 0) {
+ offset = req->ll_write.offset;
+ len = req->ll_write.len;
+
+ err = ceph_ll_write(proxy_cmount(mount), fh, offset, len, data);
+ TRACE("ceph_ll_write(%p, %p, %ld, %lu) -> %d", mount, fh,
+ offset, len, err);
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_link(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_link, ans, 0);
+ proxy_mount_t *mount;
+ struct Inode *parent, *inode;
+ const char *name;
+ UserPerm *perms;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->ll_link.cmount, (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_link.inode,
+ (void **)&inode);
+ }
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_link.parent,
+ (void **)&parent);
+ }
+ if (err >= 0) {
+ err = ptr_check(&global_random, req->ll_link.userperm,
+ (void **)&perms);
+ }
+ if (err >= 0) {
+ name = CEPH_STR_GET(req->ll_link, name, data);
+
+ err = ceph_ll_link(proxy_cmount(mount), inode, parent, name,
+ perms);
+ TRACE("ceph_ll_link(%p, %p, %p, '%s', %p) -> %d", mount, inode,
+ parent, name, perms, err);
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_unlink(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_unlink, ans, 0);
+ proxy_mount_t *mount;
+ struct Inode *parent;
+ const char *name;
+ UserPerm *perms;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->ll_unlink.cmount,
+ (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_unlink.parent,
+ (void **)&parent);
+ }
+ if (err >= 0) {
+ err = ptr_check(&global_random, req->ll_unlink.userperm,
+ (void **)&perms);
+ }
+ if (err >= 0) {
+ name = CEPH_STR_GET(req->ll_unlink, name, data);
+
+ err = ceph_ll_unlink(proxy_cmount(mount), parent, name, perms);
+ TRACE("ceph_ll_unlink(%p, %p, '%s', %p) -> %d", mount, parent,
+ name, perms, err);
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_getattr(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_getattr, ans, 1);
+ struct ceph_statx stx;
+ proxy_mount_t *mount;
+ struct Inode *inode;
+ UserPerm *perms;
+ uint32_t want, flags;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->ll_getattr.cmount,
+ (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_getattr.inode,
+ (void **)&inode);
+ }
+ if (err >= 0) {
+ err = ptr_check(&global_random, req->ll_getattr.userperm,
+ (void **)&perms);
+ }
+ if (err >= 0) {
+ want = req->ll_getattr.want;
+ flags = req->ll_getattr.flags;
+
+ CEPH_BUFF_ADD(ans, &stx, sizeof(stx));
+
+ err = ceph_ll_getattr(proxy_cmount(mount), inode, &stx, want,
+ flags, perms);
+ TRACE("ceph_ll_getattr(%p, %p, %x, %x, %p) -> %d", mount, inode,
+ want, flags, perms, err);
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_setattr(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_setattr, ans, 0);
+ proxy_mount_t *mount;
+ struct Inode *inode;
+ UserPerm *perms;
+ int32_t mask, err;
+
+ err = ptr_check(&client->random, req->ll_setattr.cmount,
+ (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_setattr.inode,
+ (void **)&inode);
+ }
+ if (err >= 0) {
+ err = ptr_check(&global_random, req->ll_setattr.userperm,
+ (void **)&perms);
+ }
+ if (err >= 0) {
+ mask = req->ll_setattr.mask;
+
+ err = ceph_ll_setattr(proxy_cmount(mount), inode, (void *)data,
+ mask, perms);
+ TRACE("ceph_ll_setattr(%p, %p, %x, %p) -> %d", mount, inode,
+ mask, perms, err);
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_fallocate(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_fallocate, ans, 0);
+ proxy_mount_t *mount;
+ struct Fh *fh;
+ int64_t offset, len;
+ mode_t mode;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->ll_fallocate.cmount,
+ (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_fallocate.fh,
+ (void **)&fh);
+ }
+ if (err >= 0) {
+ mode = req->ll_fallocate.mode;
+ offset = req->ll_fallocate.offset;
+ len = req->ll_fallocate.length;
+
+ err = ceph_ll_fallocate(proxy_cmount(mount), fh, mode, offset,
+ len);
+ TRACE("ceph_ll_fallocate(%p, %p, %o, %ld, %lu) -> %d", mount,
+ fh, mode, offset, len, err);
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_fsync(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_fsync, ans, 0);
+ proxy_mount_t *mount;
+ struct Fh *fh;
+ int32_t dataonly, err;
+
+ err = ptr_check(&client->random, req->ll_fsync.cmount, (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_fsync.fh,
+ (void **)&fh);
+ }
+ if (err >= 0) {
+ dataonly = req->ll_fsync.dataonly;
+
+ err = ceph_ll_fsync(proxy_cmount(mount), fh, dataonly);
+ TRACE("ceph_ll_fsync(%p, %p, %d) -> %d", mount, fh, dataonly,
+ err);
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_listxattr(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_listxattr, ans, 1);
+ proxy_mount_t *mount;
+ struct Inode *inode;
+ UserPerm *perms;
+ size_t size;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->ll_listxattr.cmount,
+ (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_listxattr.inode,
+ (void **)&inode);
+ }
+ if (err >= 0) {
+ err = ptr_check(&global_random, req->ll_listxattr.userperm,
+ (void **)&perms);
+ }
+ if (err >= 0) {
+ size = req->ll_listxattr.size;
+ if (size > client->buffer_size) {
+ size = client->buffer_size;
+ }
+ err = ceph_ll_listxattr(proxy_cmount(mount), inode,
+ client->buffer, size, &size, perms);
+ TRACE("ceph_ll_listxattr(%p, %p, %lu, %p) -> %d", mount, inode,
+ size, perms, err);
+
+ if (err >= 0) {
+ ans.size = size;
+ CEPH_BUFF_ADD(ans, client->buffer, size);
+ }
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_getxattr(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_getxattr, ans, 1);
+ proxy_mount_t *mount;
+ struct Inode *inode;
+ const char *name;
+ UserPerm *perms;
+ size_t size;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->ll_getxattr.cmount,
+ (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_getxattr.inode,
+ (void **)&inode);
+ }
+ if (err >= 0) {
+ err = ptr_check(&global_random, req->ll_getxattr.userperm,
+ (void **)&perms);
+ }
+ if (err >= 0) {
+ size = req->ll_getxattr.size;
+ name = CEPH_STR_GET(req->ll_getxattr, name, data);
+
+ if (size > client->buffer_size) {
+ size = client->buffer_size;
+ }
+ err = ceph_ll_getxattr(proxy_cmount(mount), inode, name,
+ client->buffer, size, perms);
+ TRACE("ceph_ll_getxattr(%p, %p, '%s', %p) -> %d", mount, inode,
+ name, perms, err);
+
+ if (err >= 0) {
+ CEPH_BUFF_ADD(ans, client->buffer, err);
+ }
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_setxattr(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_setxattr, ans, 0);
+ proxy_mount_t *mount;
+ struct Inode *inode;
+ const char *name, *value;
+ UserPerm *perms;
+ size_t size;
+ int32_t flags, err;
+
+ err = ptr_check(&client->random, req->ll_setxattr.cmount,
+ (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_setxattr.inode,
+ (void **)&inode);
+ }
+ if (err >= 0) {
+ err = ptr_check(&global_random, req->ll_setxattr.userperm,
+ (void **)&perms);
+ }
+ if (err >= 0) {
+ name = CEPH_STR_GET(req->ll_setxattr, name, data);
+ value = data + req->ll_setxattr.name;
+ size = req->ll_setxattr.size;
+ flags = req->ll_setxattr.flags;
+
+ err = ceph_ll_setxattr(proxy_cmount(mount), inode, name, value,
+ size, flags, perms);
+ TRACE("ceph_ll_setxattr(%p, %p, '%s', %p, %x, %p) -> %d", mount,
+ inode, name, value, flags, perms, err);
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_removexattr(proxy_client_t *client,
+ proxy_req_t *req, const void *data,
+ int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_removexattr, ans, 0);
+ proxy_mount_t *mount;
+ struct Inode *inode;
+ const char *name;
+ UserPerm *perms;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->ll_removexattr.cmount,
+ (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_removexattr.inode,
+ (void **)&inode);
+ }
+ if (err >= 0) {
+ err = ptr_check(&global_random, req->ll_removexattr.userperm,
+ (void **)&perms);
+ }
+ if (err >= 0) {
+ name = CEPH_STR_GET(req->ll_removexattr, name, data);
+
+ err = ceph_ll_removexattr(proxy_cmount(mount), inode, name,
+ perms);
+ TRACE("ceph_ll_removexattr(%p, %p, '%s', %p) -> %d", mount,
+ inode, name, perms, err);
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_readlink(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_readlink, ans, 0);
+ proxy_mount_t *mount;
+ struct Inode *inode;
+ UserPerm *perms;
+ size_t size;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->ll_readlink.cmount,
+ (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_readlink.inode,
+ (void **)&inode);
+ }
+ if (err >= 0) {
+ err = ptr_check(&global_random, req->ll_readlink.userperm,
+ (void **)&perms);
+ }
+ if (err >= 0) {
+ size = req->ll_readlink.size;
+
+ if (size > client->buffer_size) {
+ size = client->buffer_size;
+ }
+ err = ceph_ll_readlink(proxy_cmount(mount), inode,
+ client->buffer, size, perms);
+ TRACE("ceph_ll_readlink(%p, %p, %p) -> %d", mount, inode, perms,
+ err);
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_symlink(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_symlink, ans, 1);
+ struct ceph_statx stx;
+ proxy_mount_t *mount;
+ struct Inode *parent, *inode;
+ UserPerm *perms;
+ const char *name, *value;
+ uint32_t want, flags;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->ll_symlink.cmount,
+ (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_symlink.parent,
+ (void **)&parent);
+ }
+ if (err >= 0) {
+ err = ptr_check(&global_random, req->ll_symlink.userperm,
+ (void **)&perms);
+ }
+ if (err >= 0) {
+ name = CEPH_STR_GET(req->ll_symlink, name, data);
+ value = CEPH_STR_GET(req->ll_symlink, target,
+ data + req->ll_symlink.name);
+ want = req->ll_symlink.want;
+ flags = req->ll_symlink.flags;
+
+ CEPH_BUFF_ADD(ans, &stx, sizeof(stx));
+
+ err = ceph_ll_symlink(proxy_cmount(mount), parent, name, value,
+ &inode, &stx, want, flags, perms);
+ TRACE("ceph_ll_symlink(%p, %p, '%s', '%s', %p, %x, %x, %p) -> "
+ "%d",
+ mount, parent, name, value, inode, want, flags, perms,
+ err);
+
+ if (err >= 0) {
+ ans.inode = ptr_checksum(&client->random, inode);
+ }
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_opendir(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_opendir, ans, 0);
+ proxy_mount_t *mount;
+ struct Inode *inode;
+ struct ceph_dir_result *dirp;
+ UserPerm *perms;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->ll_opendir.cmount,
+ (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_opendir.inode,
+ (void **)&inode);
+ }
+ if (err >= 0) {
+ err = ptr_check(&global_random, req->ll_opendir.userperm,
+ (void **)&perms);
+ }
+
+ if (err >= 0) {
+ err = ceph_ll_opendir(proxy_cmount(mount), inode, &dirp, perms);
+ TRACE("ceph_ll_opendir(%p, %p, %p, %p) -> %d", mount, inode,
+ dirp, perms, err);
+
+ if (err >= 0) {
+ ans.dir = ptr_checksum(&client->random, dirp);
+ }
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_mkdir(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_mkdir, ans, 1);
+ struct ceph_statx stx;
+ proxy_mount_t *mount;
+ struct Inode *parent, *inode;
+ const char *name;
+ UserPerm *perms;
+ mode_t mode;
+ uint32_t want, flags;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->ll_mkdir.cmount, (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_mkdir.parent,
+ (void **)&parent);
+ }
+ if (err >= 0) {
+ err = ptr_check(&global_random, req->ll_mkdir.userperm,
+ (void **)&perms);
+ }
+ if (err >= 0) {
+ mode = req->ll_mkdir.mode;
+ want = req->ll_mkdir.want;
+ flags = req->ll_mkdir.flags;
+ name = CEPH_STR_GET(req->ll_mkdir, name, data);
+
+ CEPH_BUFF_ADD(ans, &stx, sizeof(stx));
+
+ err = ceph_ll_mkdir(proxy_cmount(mount), parent, name, mode,
+ &inode, &stx, want, flags, perms);
+ TRACE("ceph_ll_mkdir(%p, %p, '%s', %o, %p, %x, %x, %p) -> %d",
+ mount, parent, name, mode, inode, want, flags, perms,
+ err);
+
+ if (err >= 0) {
+ ans.inode = ptr_checksum(&client->random, inode);
+ }
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_rmdir(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_rmdir, ans, 0);
+ proxy_mount_t *mount;
+ struct Inode *parent;
+ const char *name;
+ UserPerm *perms;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->ll_rmdir.cmount, (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_rmdir.parent,
+ (void **)&parent);
+ }
+ if (err >= 0) {
+ err = ptr_check(&global_random, req->ll_rmdir.userperm,
+ (void **)&perms);
+ }
+ if (err >= 0) {
+ name = CEPH_STR_GET(req->ll_rmdir, name, data);
+
+ err = ceph_ll_rmdir(proxy_cmount(mount), parent, name, perms);
+ TRACE("ceph_ll_rmdir(%p, %p, '%s', %p) -> %d", mount, parent,
+ name, perms, err);
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_ll_releasedir(proxy_client_t *client,
+ proxy_req_t *req, const void *data,
+ int32_t data_size)
+{
+ CEPH_DATA(ceph_ll_releasedir, ans, 0);
+ proxy_mount_t *mount;
+ struct ceph_dir_result *dirp;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->ll_releasedir.cmount,
+ (void **)&mount);
+ if (err >= 0) {
+ err = ptr_check(&client->random, req->ll_releasedir.dir,
+ (void **)&dirp);
+ }
+
+ if (err >= 0) {
+ err = ceph_ll_releasedir(proxy_cmount(mount), dirp);
+ TRACE("ceph_ll_releasedir(%p, %p) -> %d", mount, dirp, err);
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static int32_t libcephfsd_mount_perms(proxy_client_t *client, proxy_req_t *req,
+ const void *data, int32_t data_size)
+{
+ CEPH_DATA(ceph_mount_perms, ans, 0);
+ proxy_mount_t *mount;
+ UserPerm *perms;
+ int32_t err;
+
+ err = ptr_check(&client->random, req->mount_perms.cmount,
+ (void **)&mount);
+ if (err >= 0) {
+ perms = ceph_mount_perms(proxy_cmount(mount));
+ TRACE("ceph_mount_perms(%p) -> %p", mount, perms);
+
+ ans.userperm = ptr_checksum(&global_random, perms);
+ }
+
+ return CEPH_COMPLETE(client, err, ans);
+}
+
+static proxy_handler_t libcephfsd_handlers[LIBCEPHFSD_OP_TOTAL_OPS] = {
+ [LIBCEPHFSD_OP_VERSION] = libcephfsd_version,
+ [LIBCEPHFSD_OP_USERPERM_NEW] = libcephfsd_userperm_new,
+ [LIBCEPHFSD_OP_USERPERM_DESTROY] = libcephfsd_userperm_destroy,
+ [LIBCEPHFSD_OP_CREATE] = libcephfsd_create,
+ [LIBCEPHFSD_OP_RELEASE] = libcephfsd_release,
+ [LIBCEPHFSD_OP_CONF_READ_FILE] = libcephfsd_conf_read_file,
+ [LIBCEPHFSD_OP_CONF_GET] = libcephfsd_conf_get,
+ [LIBCEPHFSD_OP_CONF_SET] = libcephfsd_conf_set,
+ [LIBCEPHFSD_OP_INIT] = libcephfsd_init,
+ [LIBCEPHFSD_OP_SELECT_FILESYSTEM] = libcephfsd_select_filesystem,
+ [LIBCEPHFSD_OP_MOUNT] = libcephfsd_mount,
+ [LIBCEPHFSD_OP_UNMOUNT] = libcephfsd_unmount,
+ [LIBCEPHFSD_OP_LL_STATFS] = libcephfsd_ll_statfs,
+ [LIBCEPHFSD_OP_LL_LOOKUP] = libcephfsd_ll_lookup,
+ [LIBCEPHFSD_OP_LL_LOOKUP_INODE] = libcephfsd_ll_lookup_inode,
+ [LIBCEPHFSD_OP_LL_LOOKUP_ROOT] = libcephfsd_ll_lookup_root,
+ [LIBCEPHFSD_OP_LL_PUT] = libcephfsd_ll_put,
+ [LIBCEPHFSD_OP_LL_WALK] = libcephfsd_ll_walk,
+ [LIBCEPHFSD_OP_CHDIR] = libcephfsd_chdir,
+ [LIBCEPHFSD_OP_GETCWD] = libcephfsd_getcwd,
+ [LIBCEPHFSD_OP_READDIR] = libcephfsd_readdir,
+ [LIBCEPHFSD_OP_REWINDDIR] = libcephfsd_rewinddir,
+ [LIBCEPHFSD_OP_LL_OPEN] = libcephfsd_ll_open,
+ [LIBCEPHFSD_OP_LL_CREATE] = libcephfsd_ll_create,
+ [LIBCEPHFSD_OP_LL_MKNOD] = libcephfsd_ll_mknod,
+ [LIBCEPHFSD_OP_LL_CLOSE] = libcephfsd_ll_close,
+ [LIBCEPHFSD_OP_LL_RENAME] = libcephfsd_ll_rename,
+ [LIBCEPHFSD_OP_LL_LSEEK] = libcephfsd_ll_lseek,
+ [LIBCEPHFSD_OP_LL_READ] = libcephfsd_ll_read,
+ [LIBCEPHFSD_OP_LL_WRITE] = libcephfsd_ll_write,
+ [LIBCEPHFSD_OP_LL_LINK] = libcephfsd_ll_link,
+ [LIBCEPHFSD_OP_LL_UNLINK] = libcephfsd_ll_unlink,
+ [LIBCEPHFSD_OP_LL_GETATTR] = libcephfsd_ll_getattr,
+ [LIBCEPHFSD_OP_LL_SETATTR] = libcephfsd_ll_setattr,
+ [LIBCEPHFSD_OP_LL_FALLOCATE] = libcephfsd_ll_fallocate,
+ [LIBCEPHFSD_OP_LL_FSYNC] = libcephfsd_ll_fsync,
+ [LIBCEPHFSD_OP_LL_LISTXATTR] = libcephfsd_ll_listxattr,
+ [LIBCEPHFSD_OP_LL_GETXATTR] = libcephfsd_ll_getxattr,
+ [LIBCEPHFSD_OP_LL_SETXATTR] = libcephfsd_ll_setxattr,
+ [LIBCEPHFSD_OP_LL_REMOVEXATTR] = libcephfsd_ll_removexattr,
+ [LIBCEPHFSD_OP_LL_READLINK] = libcephfsd_ll_readlink,
+ [LIBCEPHFSD_OP_LL_SYMLINK] = libcephfsd_ll_symlink,
+ [LIBCEPHFSD_OP_LL_OPENDIR] = libcephfsd_ll_opendir,
+ [LIBCEPHFSD_OP_LL_MKDIR] = libcephfsd_ll_mkdir,
+ [LIBCEPHFSD_OP_LL_RMDIR] = libcephfsd_ll_rmdir,
+ [LIBCEPHFSD_OP_LL_RELEASEDIR] = libcephfsd_ll_releasedir,
+ [LIBCEPHFSD_OP_MOUNT_PERMS] = libcephfsd_mount_perms,
+};
+
+static void serve_binary(proxy_client_t *client)
+{
+ proxy_req_t req;
+ CEPH_DATA(hello, ans, 0);
+ struct iovec req_iov[2];
+ void *buffer;
+ uint32_t size;
+ int32_t err;
+
+ /* This buffer will be used by most of the requests. For requests that
+ * require more space (probably just some writes), a new temporary
+ * buffer will be allocated by proxy_link_req_recv() code. */
+ size = 65536;
+ buffer = proxy_malloc(size);
+ if (buffer == NULL) {
+ return;
+ }
+
+ ans.major = LIBCEPHFSD_MAJOR;
+ ans.minor = LIBCEPHFSD_MINOR;
+ err = proxy_link_send(client->sd, ans_iov, ans_count);
+ if (err < 0) {
+ proxy_free(buffer);
+ return;
+ }
+
+ while (true) {
+ req_iov[0].iov_base = &req;
+ req_iov[0].iov_len = sizeof(req);
+ req_iov[1].iov_base = buffer;
+ req_iov[1].iov_len = size;
+
+ err = proxy_link_req_recv(client->sd, req_iov, 2);
+ if (err > 0) {
+ if (req.header.op >= LIBCEPHFSD_OP_TOTAL_OPS) {
+ err = send_error(client, -ENOSYS);
+ } else if (libcephfsd_handlers[req.header.op] == NULL) {
+ err = send_error(client, -EOPNOTSUPP);
+ } else {
+ err = libcephfsd_handlers[req.header.op](
+ client, &req, req_iov[1].iov_base,
+ req.header.data_len);
+ }
+ }
+
+ if (req_iov[1].iov_base != buffer) {
+ /* Free the buffer if it was temporarily allocated. */
+ proxy_free(req_iov[1].iov_base);
+ }
+
+ if (err < 0) {
+ break;
+ }
+ }
+
+ proxy_free(buffer);
+}
+
+static void serve_connection(proxy_worker_t *worker)
+{
+ CEPH_DATA(hello, req, 0);
+ proxy_client_t *client;
+ int32_t err;
+
+ client = container_of(worker, proxy_client_t, worker);
+
+ err = proxy_link_recv(client->sd, req_iov, req_count);
+ if (err >= 0) {
+ if (req.id == LIBCEPHFS_LIB_CLIENT) {
+ serve_binary(client);
+ } else {
+ proxy_log(LOG_ERR, EINVAL,
+ "Invalid client initial message");
+ }
+ }
+
+ close(client->sd);
+}
+
+static void destroy_connection(proxy_worker_t *worker)
+{
+ proxy_client_t *client;
+
+ client = container_of(worker, proxy_client_t, worker);
+
+ proxy_free(client->buffer);
+ proxy_free(client);
+}
+
+static int32_t accept_connection(proxy_link_t *link, int32_t sd)
+{
+ proxy_server_t *server;
+ proxy_client_t *client;
+ int32_t err;
+
+ server = container_of(link, proxy_server_t, link);
+
+ client = proxy_malloc(sizeof(proxy_client_t));
+ if (client == NULL) {
+ err = -ENOMEM;
+ goto failed_close;
+ }
+
+ client->buffer_size = 65536;
+ client->buffer = proxy_malloc(client->buffer_size);
+ if (client->buffer == NULL) {
+ err = -ENOMEM;
+ goto failed_client;
+ }
+
+ random_init(&client->random);
+ client->sd = sd;
+ client->link = link;
+
+ /* TODO: Make request management asynchronous and avoid creating a
+ * thread for each connection. */
+ err = proxy_manager_launch(server->manager, &client->worker,
+ serve_connection, destroy_connection);
+ if (err < 0) {
+ goto failed_buffer;
+ }
+
+ return 0;
+
+failed_buffer:
+ proxy_free(client->buffer);
+
+failed_client:
+ proxy_free(client);
+
+failed_close:
+ close(sd);
+
+ return err;
+}
+
+static bool check_stop(proxy_link_t *link)
+{
+ proxy_server_t *server;
+
+ server = container_of(link, proxy_server_t, link);
+
+ return proxy_manager_stop(server->manager);
+}
+
+static int32_t server_start(proxy_manager_t *manager)
+{
+ proxy_server_t server;
+ proxy_t *proxy;
+
+ proxy = container_of(manager, proxy_t, manager);
+
+ server.manager = manager;
+
+ return proxy_link_server(&server.link, proxy->socket_path,
+ accept_connection, check_stop);
+}
+
+static void log_print(proxy_log_handler_t *handler, int32_t level, int32_t err,
+ const char *msg)
+{
+ printf("[%d] %s\n", level, msg);
+}
+
+static struct option main_opts[] = {
+ {"socket", required_argument, NULL, 's'},
+ {}
+};
+
+int32_t main(int32_t argc, char *argv[])
+{
+ struct timespec now;
+ proxy_t proxy;
+ char *env;
+ int32_t err, val;
+
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ srand(now.tv_nsec);
+
+ random_init(&global_random);
+
+ proxy_log_register(&proxy.log_handler, log_print);
+
+ proxy.socket_path = PROXY_SOCKET;
+
+ env = getenv(PROXY_SOCKET_ENV);
+ if (env != NULL) {
+ proxy.socket_path = env;
+ }
+
+ while ((val = getopt_long(argc, argv, ":s:", main_opts, NULL)) >= 0) {
+ if (val == 's') {
+ proxy.socket_path = optarg;
+ } else if (val == ':') {
+ proxy_log(LOG_ERR, ENODATA,
+ "Argument missing for '%s'\n", optopt);
+ return 1;
+ } else if (val == '?') {
+ proxy_log(LOG_ERR, EINVAL,
+ "Unknown option '%s'\n", optopt);
+ return 1;
+ } else {
+ proxy_log(LOG_ERR, EINVAL,
+ "Unexpected error parsing the options\n");
+ return 1;
+ }
+ }
+ if (optind < argc) {
+ proxy_log(LOG_ERR, EINVAL,
+ "Unexpected arguments in command line");
+ return 1;
+ }
+
+ err = proxy_manager_run(&proxy.manager, server_start);
+
+ proxy_log_deregister(&proxy.log_handler);
+
+ return err < 0 ? 1 : 0;
+}