2019-05-28 20:38:18

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH v3 03/11] Allow callers to check mountpoint status using a custom lstat function

Signed-off-by: Trond Myklebust <[email protected]>
---
support/include/misc.h | 7 ++++++-
support/misc/mountpoint.c | 8 +++++---
2 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/support/include/misc.h b/support/include/misc.h
index 06e2a0c7b061..2b0fef26cb11 100644
--- a/support/include/misc.h
+++ b/support/include/misc.h
@@ -18,7 +18,12 @@ int weakrandomkey(unsigned char *keyout, int len);
char *generic_make_pathname(const char *, const char *);
_Bool generic_setup_basedir(const char *, const char *, char *, const size_t);

-extern int is_mountpoint(char *path);
+struct stat;
+
+extern int check_is_mountpoint(const char *path,
+ int (mystat)(const char *, struct stat *));
+#define is_mountpoint(path) \
+ check_is_mountpoint(path, NULL)

/* size of the file pointer buffers for rpc procfs files */
#define RPC_CHAN_BUF_SIZE 32768
diff --git a/support/misc/mountpoint.c b/support/misc/mountpoint.c
index 9f9ce44ec1e3..c6217f2458d1 100644
--- a/support/misc/mountpoint.c
+++ b/support/misc/mountpoint.c
@@ -9,8 +9,10 @@
#include "misc.h"

int
-is_mountpoint(char *path)
+check_is_mountpoint(const char *path, int (mystat)(const char *, struct stat *))
{
+ if (!mystat)
+ mystat = lstat;
/* Check if 'path' is a current mountpoint.
* Possibly we should also check it is the mountpoint of the
* filesystem holding the target directory, but there doesn't
@@ -26,8 +28,8 @@ is_mountpoint(char *path)
dotdot = xmalloc(strlen(path)+4);

strcat(strcpy(dotdot, path), "/..");
- if (lstat(path, &stb) != 0 ||
- lstat(dotdot, &pstb) != 0)
+ if (mystat(path, &stb) != 0 ||
+ mystat(dotdot, &pstb) != 0)
rv = 0;
else
if (stb.st_dev != pstb.st_dev ||
--
2.21.0


2019-05-28 20:38:18

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH v3 04/11] Add utilities for resolving nfsd paths and stat()ing them

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 <[email protected]>
---
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

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 <[email protected]>
+ */
+#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 <[email protected]>
+ */
+#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
--
2.21.0

2019-05-31 15:52:41

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH v3 04/11] Add utilities for resolving nfsd paths and stat()ing them

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

2019-06-03 14:24:17

by Steve Dickson

[permalink] [raw]
Subject: Re: [PATCH v3 04/11] Add utilities for resolving nfsd paths and stat()ing them



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

2019-06-03 17:48:10

by Trond Myklebust

[permalink] [raw]
Subject: Re: [PATCH v3 04/11] Add utilities for resolving nfsd paths and stat()ing them

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.

--
Trond Myklebust
Linux NFS client maintainer, Hammerspace
[email protected]