diff options
Diffstat (limited to 'support/misc/nfsd_path.c')
-rw-r--r-- | support/misc/nfsd_path.c | 409 |
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 |