diff options
Diffstat (limited to 'src/libcephfs_proxy/libcephfsd.c')
-rw-r--r-- | src/libcephfs_proxy/libcephfsd.c | 1823 |
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; +} |