blob: 65e53c1304ba3e4afa0f86cc2eef52a44c532549 [file]
#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_statfs64_data {
const char *pathname;
struct statfs64 *statbuf;
int ret;
int err;
};
static void
nfsd_statfs64func(void *data)
{
struct nfsd_statfs64_data *d = data;
d->ret = statfs64(d->pathname, d->statbuf);
if (d->ret < 0)
d->err = errno;
}
static int
nfsd_run_statfs64(struct xthread_workqueue *wq,
const char *pathname,
struct statfs64 *statbuf)
{
struct nfsd_statfs64_data data = {
pathname,
statbuf,
0,
0
};
xthread_work_run_sync(wq, nfsd_statfs64func, &data);
if (data.ret < 0)
errno = data.err;
return data.ret;
}
int
nfsd_path_statfs64(const char *pathname, struct statfs64 *statbuf)
{
if (!nfsd_wq)
return statfs64(pathname, statbuf);
return nfsd_run_statfs64(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