summaryrefslogtreecommitdiffstats
path: root/support/misc/nfsd_path.c
diff options
context:
space:
mode:
Diffstat (limited to 'support/misc/nfsd_path.c')
-rw-r--r--support/misc/nfsd_path.c409
1 files changed, 409 insertions, 0 deletions
diff --git a/support/misc/nfsd_path.c b/support/misc/nfsd_path.c
new file mode 100644
index 0000000..c3dea4f
--- /dev/null
+++ b/support/misc/nfsd_path.c
@@ -0,0 +1,409 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/vfs.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "conffile.h"
+#include "xmalloc.h"
+#include "xlog.h"
+#include "xstat.h"
+#include "nfslib.h"
+#include "nfsd_path.h"
+#include "workqueue.h"
+
+static struct xthread_workqueue *nfsd_wq;
+
+static int
+nfsd_path_isslash(const char *path)
+{
+ return path[0] == '/' && path[1] == '/';
+}
+
+static int
+nfsd_path_isdot(const char *path)
+{
+ return path[0] == '.' && path[1] == '/';
+}
+
+static const char *
+nfsd_path_strip(const char *path)
+{
+ if (!path || *path == '\0')
+ goto out;
+ for (;;) {
+ if (nfsd_path_isslash(path)) {
+ path++;
+ continue;
+ }
+ if (nfsd_path_isdot(path)) {
+ path += 2;
+ continue;
+ }
+ break;
+ }
+out:
+ return path;
+}
+
+const char *
+nfsd_path_nfsd_rootdir(void)
+{
+ const char *rootdir;
+
+ rootdir = nfsd_path_strip(conf_get_str("exports", "rootdir"));
+ if (!rootdir || rootdir[0] == '\0')
+ return NULL;
+ if (rootdir[0] == '/' && rootdir[1] == '\0')
+ return NULL;
+ return rootdir;
+}
+
+char *
+nfsd_path_strip_root(char *pathname)
+{
+ char buffer[PATH_MAX];
+ const char *dir = nfsd_path_nfsd_rootdir();
+
+ if (!dir)
+ goto out;
+
+ if (realpath(dir, buffer))
+ return strstr(pathname, buffer) == pathname ?
+ pathname + strlen(buffer) : NULL;
+
+ xlog(D_GENERAL, "%s: failed to resolve path %s: %m", __func__, dir);
+out:
+ return pathname;
+}
+
+char *
+nfsd_path_prepend_dir(const char *dir, const char *pathname)
+{
+ size_t len, dirlen;
+ char *ret;
+
+ dirlen = strlen(dir);
+ while (dirlen > 0 && dir[dirlen - 1] == '/')
+ dirlen--;
+ if (!dirlen)
+ return NULL;
+ while (pathname[0] == '/')
+ pathname++;
+ len = dirlen + strlen(pathname) + 1;
+ ret = xmalloc(len + 1);
+ snprintf(ret, len+1, "%.*s/%s", (int)dirlen, dir, pathname);
+ return ret;
+}
+
+static void
+nfsd_setup_workqueue(void)
+{
+ const char *rootdir = nfsd_path_nfsd_rootdir();
+
+ if (!rootdir)
+ return;
+
+ nfsd_wq = xthread_workqueue_alloc();
+ if (!nfsd_wq)
+ return;
+ xthread_workqueue_chroot(nfsd_wq, rootdir);
+}
+
+void
+nfsd_path_init(void)
+{
+ nfsd_setup_workqueue();
+}
+
+struct nfsd_stat_data {
+ const char *pathname;
+ struct stat *statbuf;
+ int ret;
+ int err;
+};
+
+static void
+nfsd_statfunc(void *data)
+{
+ struct nfsd_stat_data *d = data;
+
+ d->ret = xstat(d->pathname, d->statbuf);
+ if (d->ret < 0)
+ d->err = errno;
+}
+
+static void
+nfsd_lstatfunc(void *data)
+{
+ struct nfsd_stat_data *d = data;
+
+ d->ret = xlstat(d->pathname, d->statbuf);
+ if (d->ret < 0)
+ d->err = errno;
+}
+
+static int
+nfsd_run_stat(struct xthread_workqueue *wq,
+ void (*func)(void *),
+ const char *pathname,
+ struct stat *statbuf)
+{
+ struct nfsd_stat_data data = {
+ pathname,
+ statbuf,
+ 0,
+ 0
+ };
+ xthread_work_run_sync(wq, func, &data);
+ if (data.ret < 0)
+ errno = data.err;
+ return data.ret;
+}
+
+int
+nfsd_path_stat(const char *pathname, struct stat *statbuf)
+{
+ if (!nfsd_wq)
+ return xstat(pathname, statbuf);
+ return nfsd_run_stat(nfsd_wq, nfsd_statfunc, pathname, statbuf);
+}
+
+int
+nfsd_path_lstat(const char *pathname, struct stat *statbuf)
+{
+ if (!nfsd_wq)
+ return xlstat(pathname, statbuf);
+ return nfsd_run_stat(nfsd_wq, nfsd_lstatfunc, pathname, statbuf);
+}
+
+struct nfsd_statfs_data {
+ const char *pathname;
+ struct statfs *statbuf;
+ int ret;
+ int err;
+};
+
+static void
+nfsd_statfsfunc(void *data)
+{
+ struct nfsd_statfs_data *d = data;
+
+ d->ret = statfs(d->pathname, d->statbuf);
+ if (d->ret < 0)
+ d->err = errno;
+}
+
+static int
+nfsd_run_statfs(struct xthread_workqueue *wq,
+ const char *pathname,
+ struct statfs *statbuf)
+{
+ struct nfsd_statfs_data data = {
+ pathname,
+ statbuf,
+ 0,
+ 0
+ };
+ xthread_work_run_sync(wq, nfsd_statfsfunc, &data);
+ if (data.ret < 0)
+ errno = data.err;
+ return data.ret;
+}
+
+int
+nfsd_path_statfs(const char *pathname, struct statfs *statbuf)
+{
+ if (!nfsd_wq)
+ return statfs(pathname, statbuf);
+ return nfsd_run_statfs(nfsd_wq, pathname, statbuf);
+}
+
+struct nfsd_realpath_data {
+ const char *pathname;
+ char *resolved;
+ int err;
+};
+
+static void
+nfsd_realpathfunc(void *data)
+{
+ struct nfsd_realpath_data *d = data;
+
+ d->resolved = realpath(d->pathname, d->resolved);
+ if (!d->resolved)
+ d->err = errno;
+}
+
+char *
+nfsd_realpath(const char *path, char *resolved_path)
+{
+ struct nfsd_realpath_data data = {
+ path,
+ resolved_path,
+ 0
+ };
+
+ if (!nfsd_wq)
+ return realpath(path, resolved_path);
+
+ xthread_work_run_sync(nfsd_wq, nfsd_realpathfunc, &data);
+ if (!data.resolved)
+ errno = data.err;
+ return data.resolved;
+}
+
+struct nfsd_read_data {
+ int fd;
+ char *buf;
+ size_t len;
+ ssize_t ret;
+ int err;
+};
+
+static void
+nfsd_readfunc(void *data)
+{
+ struct nfsd_read_data *d = data;
+
+ d->ret = read(d->fd, d->buf, d->len);
+ if (d->ret < 0)
+ d->err = errno;
+}
+
+static ssize_t
+nfsd_run_read(struct xthread_workqueue *wq, int fd, char *buf, size_t len)
+{
+ struct nfsd_read_data data = {
+ fd,
+ buf,
+ len,
+ 0,
+ 0
+ };
+ xthread_work_run_sync(wq, nfsd_readfunc, &data);
+ if (data.ret < 0)
+ errno = data.err;
+ return data.ret;
+}
+
+ssize_t
+nfsd_path_read(int fd, char *buf, size_t len)
+{
+ if (!nfsd_wq)
+ return read(fd, buf, len);
+ return nfsd_run_read(nfsd_wq, fd, buf, len);
+}
+
+struct nfsd_write_data {
+ int fd;
+ const char *buf;
+ size_t len;
+ ssize_t ret;
+ int err;
+};
+
+static void
+nfsd_writefunc(void *data)
+{
+ struct nfsd_write_data *d = data;
+
+ d->ret = write(d->fd, d->buf, d->len);
+ if (d->ret < 0)
+ d->err = errno;
+}
+
+static ssize_t
+nfsd_run_write(struct xthread_workqueue *wq, int fd, const char *buf, size_t len)
+{
+ struct nfsd_write_data data = {
+ fd,
+ buf,
+ len,
+ 0,
+ 0
+ };
+ xthread_work_run_sync(wq, nfsd_writefunc, &data);
+ if (data.ret < 0)
+ errno = data.err;
+ return data.ret;
+}
+
+ssize_t
+nfsd_path_write(int fd, const char *buf, size_t len)
+{
+ if (!nfsd_wq)
+ return write(fd, buf, len);
+ return nfsd_run_write(nfsd_wq, fd, buf, len);
+}
+
+#if defined(HAVE_NAME_TO_HANDLE_AT)
+struct nfsd_handle_data {
+ int fd;
+ const char *path;
+ struct file_handle *fh;
+ int *mount_id;
+ int flags;
+ int ret;
+ int err;
+};
+
+static void
+nfsd_name_to_handle_func(void *data)
+{
+ struct nfsd_handle_data *d = data;
+
+ d->ret = name_to_handle_at(d->fd, d->path,
+ d->fh, d->mount_id, d->flags);
+ if (d->ret < 0)
+ d->err = errno;
+}
+
+static int
+nfsd_run_name_to_handle_at(struct xthread_workqueue *wq,
+ int fd, const char *path, struct file_handle *fh,
+ int *mount_id, int flags)
+{
+ struct nfsd_handle_data data = {
+ fd,
+ path,
+ fh,
+ mount_id,
+ flags,
+ 0,
+ 0
+ };
+
+ xthread_work_run_sync(wq, nfsd_name_to_handle_func, &data);
+ if (data.ret < 0)
+ errno = data.err;
+ return data.ret;
+}
+
+int
+nfsd_name_to_handle_at(int fd, const char *path, struct file_handle *fh,
+ int *mount_id, int flags)
+{
+ if (!nfsd_wq)
+ return name_to_handle_at(fd, path, fh, mount_id, flags);
+
+ return nfsd_run_name_to_handle_at(nfsd_wq, fd, path, fh,
+ mount_id, flags);
+}
+#else
+int
+nfsd_name_to_handle_at(int UNUSED(fd), const char *UNUSED(path),
+ struct file_handle *UNUSED(fh),
+ int *UNUSED(mount_id), int UNUSED(flags))
+{
+ errno = ENOSYS;
+ return -1;
+}
+#endif