[3/7] mountd: Fix up path checking helper same_path()
diff mbox series

Message ID 20200416221252.82102-4-trondmy@kernel.org
State New
Headers show
Series
  • nfs-utils fixes
Related show

Commit Message

trondmy@kernel.org April 16, 2020, 10:12 p.m. UTC
From: Trond Myklebust <trond.myklebust@hammerspace.com>

Convert 'same_path()' so that it works when 'rootdir'
is set in the [exports] section of nfs.conf.

Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
---
 utils/mountd/cache.c | 83 +++++++++++++++++++++++++++++++-------------
 1 file changed, 59 insertions(+), 24 deletions(-)

Patch
diff mbox series

diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c
index 7d8657c91323..94e9e44b46b9 100644
--- a/utils/mountd/cache.c
+++ b/utils/mountd/cache.c
@@ -72,6 +72,18 @@  static ssize_t cache_write(int fd, const char *buf, size_t len)
 	return nfsd_path_write(fd, buf, len);
 }
 
+static bool path_lookup_error(int err)
+{
+	switch (err) {
+	case ELOOP:
+	case ENAMETOOLONG:
+	case ENOENT:
+	case ENOTDIR:
+		return 1;
+	}
+	return 0;
+}
+
 /*
  * Support routines for text-based upcalls.
  * Fields are separated by spaces.
@@ -430,23 +442,9 @@  static inline int count_slashes(char *p)
 	return cnt;
 }
 
-static int same_path(char *child, char *parent, int len)
+#if defined(HAVE_STRUCT_FILE_HANDLE)
+static int check_same_path_by_handle(const char *child, const char *parent)
 {
-	static char p[PATH_MAX];
-	struct stat sc, sp;
-
-	if (len <= 0)
-		len = strlen(child);
-	strncpy(p, child, len);
-	p[len] = 0;
-	if (strcmp(p, parent) == 0)
-		return 1;
-
-	/* If number of '/' are different, they must be different */
-	if (count_slashes(p) != count_slashes(parent))
-		return 0;
-
-#if defined(HAVE_NAME_TO_HANDLE_AT) && defined(HAVE_STRUCT_FILE_HANDLE)
 	struct {
 		struct file_handle fh;
 		unsigned char handle[128];
@@ -455,13 +453,17 @@  static int same_path(char *child, char *parent, int len)
 
 	fchild.fh.handle_bytes = 128;
 	fparent.fh.handle_bytes = 128;
-	if (name_to_handle_at(AT_FDCWD, p, &fchild.fh, &mnt_child, 0) != 0) {
-		if (errno == ENOSYS)
-			goto fallback;
-		return 0;
+
+	/* This process should have the CAP_DAC_READ_SEARCH capability */
+	if (nfsd_name_to_handle_at(AT_FDCWD, child, &fchild.fh, &mnt_child, 0) < 0)
+		return -1;
+	if (nfsd_name_to_handle_at(AT_FDCWD, parent, &fparent.fh, &mnt_parent, 0) < 0) {
+		/* If the child resolved, but the parent did not, they differ */
+		if (path_lookup_error(errno))
+			return 0;
+		/* Otherwise, we just don't know */
+		return -1;
 	}
-	if (name_to_handle_at(AT_FDCWD, parent, &fparent.fh, &mnt_parent, 0) != 0)
-		return 0;
 
 	if (mnt_child != mnt_parent ||
 	    fchild.fh.handle_bytes != fparent.fh.handle_bytes ||
@@ -471,14 +473,24 @@  static int same_path(char *child, char *parent, int len)
 		return 0;
 
 	return 1;
-fallback:
+}
+#else
+static int check_same_path_by_handle(const char *child, const char *parent)
+{
+	errno = ENOSYS;
+	return -1;
+}
 #endif
 
+static int check_same_path_by_inode(const char *child, const char *parent)
+{
+	struct stat sc, sp;
+
 	/* This is nearly good enough.  However if a directory is
 	 * bind-mounted in two places and both are exported, it
 	 * could give a false positive
 	 */
-	if (nfsd_path_lstat(p, &sc) != 0)
+	if (nfsd_path_lstat(child, &sc) != 0)
 		return 0;
 	if (nfsd_path_lstat(parent, &sp) != 0)
 		return 0;
@@ -490,6 +502,29 @@  fallback:
 	return 1;
 }
 
+static int same_path(char *child, char *parent, int len)
+{
+	static char p[PATH_MAX];
+	int err;
+
+	if (len <= 0)
+		len = strlen(child);
+	strncpy(p, child, len);
+	p[len] = 0;
+	if (strcmp(p, parent) == 0)
+		return 1;
+
+	/* If number of '/' are different, they must be different */
+	if (count_slashes(p) != count_slashes(parent))
+		return 0;
+
+	/* Try to use filehandle approach before falling back to stat() */
+	err = check_same_path_by_handle(p, parent);
+	if (err != -1)
+		return err;
+	return check_same_path_by_inode(p, parent);
+}
+
 static int is_subdirectory(char *child, char *parent)
 {
 	/* Check is child is strictly a subdirectory of