[v3,04/11] Add utilities for resolving nfsd paths and stat()ing them
diff mbox series

Message ID 20190528203122.11401-5-trond.myklebust@hammerspace.com
State New
Headers show
Series
  • Add the "[exports] rootdir" option to nfs.conf
Related show

Commit Message

Trond Myklebust May 28, 2019, 8:31 p.m. UTC
Add helper functions that can resolve nfsd paths by prepending the
necessary prefix if the admin has specified a root path in the
nfs.conf file.

Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
---
 configure.ac                |   2 +-
 support/include/Makefile.am |   2 +
 support/include/nfsd_path.h |  16 ++++
 support/include/xstat.h     |  11 +++
 support/misc/Makefile.am    |   3 +-
 support/misc/nfsd_path.c    | 168 ++++++++++++++++++++++++++++++++++++
 support/misc/xstat.c        |  33 +++++++
 7 files changed, 233 insertions(+), 2 deletions(-)
 create mode 100644 support/include/nfsd_path.h
 create mode 100644 support/include/xstat.h
 create mode 100644 support/misc/nfsd_path.c
 create mode 100644 support/misc/xstat.c

Comments

J. Bruce Fields May 31, 2019, 3:52 p.m. UTC | #1
On Tue, May 28, 2019 at 04:31:15PM -0400, Trond Myklebust wrote:
> +char *
> +nfsd_path_strip_root(char *pathname)
> +{
> +	const char *dir = nfsd_path_nfsd_rootdir();
> +	char *ret;
> +
> +	ret = strstr(pathname, dir);
> +	if (!ret || ret != pathname)
> +		return pathname;

Shouldn't we return NULL or an error or something here?  It seems a
little strange not to care if the path began with root or not.  I guess
I need to look at the caller....

--b.

> +	return pathname + strlen(dir);
> +}
> +
> +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;
> +	len = dirlen + strlen(pathname) + 1;
> +	ret = xmalloc(len + 1);
> +	snprintf(ret, len, "%.*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);
> +}
> diff --git a/support/misc/xstat.c b/support/misc/xstat.c
> new file mode 100644
> index 000000000000..d092f73dfd65
> --- /dev/null
> +++ b/support/misc/xstat.c
> @@ -0,0 +1,33 @@
> +#include <sys/types.h>
> +#include <fcntl.h>
> +#include <sys/stat.h>
> +#include <unistd.h>
> +
> +#include "config.h"
> +#include "xstat.h"
> +
> +#ifdef HAVE_FSTATAT
> +
> +int xlstat(const char *pathname, struct stat *statbuf)
> +{
> +	return fstatat(AT_FDCWD, pathname, statbuf, AT_NO_AUTOMOUNT |
> +			AT_SYMLINK_NOFOLLOW);
> +}
> +
> +int xstat(const char *pathname, struct stat *statbuf)
> +{
> +	return fstatat(AT_FDCWD, pathname, statbuf, AT_NO_AUTOMOUNT);
> +}
> +
> +#else
> +
> +int xlstat(const char *pathname, struct stat *statbuf)
> +{
> +	return lstat(pathname, statbuf);
> +}
> +
> +int xstat(const char *pathname, struct stat *statbuf)
> +{
> +	return stat(pathname, statbuf);
> +}
> +#endif
> -- 
> 2.21.0
Steve Dickson June 3, 2019, 2:21 p.m. UTC | #2
On 5/31/19 11:52 AM, J. Bruce Fields wrote:
> On Tue, May 28, 2019 at 04:31:15PM -0400, Trond Myklebust wrote:
>> +char *
>> +nfsd_path_strip_root(char *pathname)
>> +{
>> +	const char *dir = nfsd_path_nfsd_rootdir();
>> +	char *ret;
>> +
>> +	ret = strstr(pathname, dir);
>> +	if (!ret || ret != pathname)
>> +		return pathname;
> 
> Shouldn't we return NULL or an error or something here?  It seems a
> little strange not to care if the path began with root or not.  I guess
> I need to look at the caller....
Well pathname will never be NULL... It is returning what is passed in, 
but it might be nice to know about the memory failure.

steved.

> 
> --b.
> 
>> +	return pathname + strlen(dir);
>> +}
>> +
>> +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;
>> +	len = dirlen + strlen(pathname) + 1;
>> +	ret = xmalloc(len + 1);
>> +	snprintf(ret, len, "%.*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);
>> +}
>> diff --git a/support/misc/xstat.c b/support/misc/xstat.c
>> new file mode 100644
>> index 000000000000..d092f73dfd65
>> --- /dev/null
>> +++ b/support/misc/xstat.c
>> @@ -0,0 +1,33 @@
>> +#include <sys/types.h>
>> +#include <fcntl.h>
>> +#include <sys/stat.h>
>> +#include <unistd.h>
>> +
>> +#include "config.h"
>> +#include "xstat.h"
>> +
>> +#ifdef HAVE_FSTATAT
>> +
>> +int xlstat(const char *pathname, struct stat *statbuf)
>> +{
>> +	return fstatat(AT_FDCWD, pathname, statbuf, AT_NO_AUTOMOUNT |
>> +			AT_SYMLINK_NOFOLLOW);
>> +}
>> +
>> +int xstat(const char *pathname, struct stat *statbuf)
>> +{
>> +	return fstatat(AT_FDCWD, pathname, statbuf, AT_NO_AUTOMOUNT);
>> +}
>> +
>> +#else
>> +
>> +int xlstat(const char *pathname, struct stat *statbuf)
>> +{
>> +	return lstat(pathname, statbuf);
>> +}
>> +
>> +int xstat(const char *pathname, struct stat *statbuf)
>> +{
>> +	return stat(pathname, statbuf);
>> +}
>> +#endif
>> -- 
>> 2.21.0
Trond Myklebust June 3, 2019, 4:32 p.m. UTC | #3
On Mon, 2019-06-03 at 10:21 -0400, Steve Dickson wrote:
> 
> On 5/31/19 11:52 AM, J. Bruce Fields wrote:
> > On Tue, May 28, 2019 at 04:31:15PM -0400, Trond Myklebust wrote:
> > > +char *
> > > +nfsd_path_strip_root(char *pathname)
> > > +{
> > > +	const char *dir = nfsd_path_nfsd_rootdir();
> > > +	char *ret;
> > > +
> > > +	ret = strstr(pathname, dir);
> > > +	if (!ret || ret != pathname)
> > > +		return pathname;
> > 
> > Shouldn't we return NULL or an error or something here?  It seems a
> > little strange not to care if the path began with root or not.  I
> > guess
> > I need to look at the caller....
> Well pathname will never be NULL... It is returning what is passed
> in, 
> but it might be nice to know about the memory failure.
> 

Either way, I figure we also want to canonicalise 'dir' before we apply
it, just in case people have amused themselves by composing rootdir
values of the form '/foo/./bar/./../bar/'.
Another patch forthcoming for this.

Patch
diff mbox series

diff --git a/configure.ac b/configure.ac
index c6c2d73b06dd..e870862a8abb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -321,7 +321,7 @@  AC_CHECK_FUNC([getservbyname], ,
 AC_CHECK_LIB([crypt], [crypt], [LIBCRYPT="-lcrypt"])
 
 AC_CHECK_HEADERS([sched.h], [], [])
-AC_CHECK_FUNCS([unshare], [] , [])
+AC_CHECK_FUNCS([unshare fstatat], [] , [])
 AC_LIBPTHREAD([])
 
 if test "$enable_nfsv4" = yes; then
diff --git a/support/include/Makefile.am b/support/include/Makefile.am
index df5e47836d29..1373891a7c76 100644
--- a/support/include/Makefile.am
+++ b/support/include/Makefile.am
@@ -10,6 +10,7 @@  noinst_HEADERS = \
 	misc.h \
 	nfs_mntent.h \
 	nfs_paths.h \
+	nfsd_path.h \
 	nfslib.h \
 	nfsrpc.h \
 	nls.h \
@@ -24,6 +25,7 @@  noinst_HEADERS = \
 	xlog.h \
 	xmalloc.h \
 	xcommon.h \
+	xstat.h \
 	conffile.h
 
 MAINTAINERCLEANFILES = Makefile.in
diff --git a/support/include/nfsd_path.h b/support/include/nfsd_path.h
new file mode 100644
index 000000000000..db9b41a179ad
--- /dev/null
+++ b/support/include/nfsd_path.h
@@ -0,0 +1,16 @@ 
+/*
+ * Copyright (C) 2019 Trond Myklebust <trond.myklebust@hammerspace.com>
+ */
+#ifndef NFSD_PATH_H
+#define NFSD_PATH_H
+
+void 		nfsd_path_init(void);
+
+const char *	nfsd_path_nfsd_rootdir(void);
+char *		nfsd_path_strip_root(char *pathname);
+char *		nfsd_path_prepend_dir(const char *dir, const char *pathname);
+
+int 		nfsd_path_stat(const char *pathname, struct stat *statbuf);
+int 		nfsd_path_lstat(const char *pathname, struct stat *statbuf);
+
+#endif
diff --git a/support/include/xstat.h b/support/include/xstat.h
new file mode 100644
index 000000000000..f1241bbfdc0e
--- /dev/null
+++ b/support/include/xstat.h
@@ -0,0 +1,11 @@ 
+/*
+ * Copyright (C) 2019 Trond Myklebust <trond.myklebust@hammerspace.com>
+ */
+#ifndef XSTAT_H
+#define XSTAT_H
+
+struct stat;
+
+int xlstat(const char *pathname, struct stat *statbuf);
+int xstat(const char *pathname, struct stat *statbuf);
+#endif
diff --git a/support/misc/Makefile.am b/support/misc/Makefile.am
index d0bff8feb6ae..f9993e3ac897 100644
--- a/support/misc/Makefile.am
+++ b/support/misc/Makefile.am
@@ -1,6 +1,7 @@ 
 ## Process this file with automake to produce Makefile.in
 
 noinst_LIBRARIES = libmisc.a
-libmisc_a_SOURCES = tcpwrapper.c from_local.c mountpoint.c file.c workqueue.c
+libmisc_a_SOURCES = tcpwrapper.c from_local.c mountpoint.c file.c \
+		    nfsd_path.c workqueue.c xstat.c
 
 MAINTAINERCLEANFILES = Makefile.in
diff --git a/support/misc/nfsd_path.c b/support/misc/nfsd_path.c
new file mode 100644
index 000000000000..fe2c011b1521
--- /dev/null
+++ b/support/misc/nfsd_path.c
@@ -0,0 +1,168 @@ 
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "conffile.h"
+#include "xmalloc.h"
+#include "xlog.h"
+#include "xstat.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)
+{
+	const char *dir = nfsd_path_nfsd_rootdir();
+	char *ret;
+
+	ret = strstr(pathname, dir);
+	if (!ret || ret != pathname)
+		return pathname;
+	return pathname + strlen(dir);
+}
+
+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;
+	len = dirlen + strlen(pathname) + 1;
+	ret = xmalloc(len + 1);
+	snprintf(ret, len, "%.*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);
+}
diff --git a/support/misc/xstat.c b/support/misc/xstat.c
new file mode 100644
index 000000000000..d092f73dfd65
--- /dev/null
+++ b/support/misc/xstat.c
@@ -0,0 +1,33 @@ 
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "xstat.h"
+
+#ifdef HAVE_FSTATAT
+
+int xlstat(const char *pathname, struct stat *statbuf)
+{
+	return fstatat(AT_FDCWD, pathname, statbuf, AT_NO_AUTOMOUNT |
+			AT_SYMLINK_NOFOLLOW);
+}
+
+int xstat(const char *pathname, struct stat *statbuf)
+{
+	return fstatat(AT_FDCWD, pathname, statbuf, AT_NO_AUTOMOUNT);
+}
+
+#else
+
+int xlstat(const char *pathname, struct stat *statbuf)
+{
+	return lstat(pathname, statbuf);
+}
+
+int xstat(const char *pathname, struct stat *statbuf)
+{
+	return stat(pathname, statbuf);
+}
+#endif