#ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #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