git: 572b77f8da5e - main - mountd: Improve error message for exports lines

From: Alexander Motin <mav_at_FreeBSD.org>
Date: Sat, 06 May 2023 19:11:58 UTC
The branch main has been updated by mav:

URL: https://cgit.FreeBSD.org/src/commit/?id=572b77f8da5e93ad8f42dbce90b5bfc18d507169

commit 572b77f8da5e93ad8f42dbce90b5bfc18d507169
Author:     Alexander Motin <mav@FreeBSD.org>
AuthorDate: 2023-05-06 18:57:14 +0000
Commit:     Alexander Motin <mav@FreeBSD.org>
CommitDate: 2023-05-06 19:02:08 +0000

    mountd: Improve error message for exports lines
    
    Currently mountd print error message "symbolic link in export path or
    statfs failed" in case some path component in an exports line fails
    validation.  This revision improves the error message by giving more
    information about the precise error as well as the path component that
    caused the issue.
    
    Submitted by:   Andrew Walker <awalker@ixsystems.com>
    Reviewed by:    mav, rmacklem
    Differential Revision:  https://reviews.freebsd.org/D39840
---
 usr.sbin/mountd/mountd.c | 87 +++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 72 insertions(+), 15 deletions(-)

diff --git a/usr.sbin/mountd/mountd.c b/usr.sbin/mountd/mountd.c
index d985dd00acf8..f64505f8602f 100644
--- a/usr.sbin/mountd/mountd.c
+++ b/usr.sbin/mountd/mountd.c
@@ -210,7 +210,9 @@ static void	add_dlist(struct dirlist **, struct dirlist *,
 		    struct grouplist *, int, struct exportlist *,
 		    struct expcred *, uint64_t);
 static void	add_mlist(char *, char *);
-static int	check_dirpath(char *);
+static int	check_path_component(const char *, char **);
+static int	check_dirpath(char *, char **);
+static int	check_statfs(const char *, struct statfs *, char **);
 static int	check_options(struct dirlist *);
 static int	checkmask(struct sockaddr *sa);
 static int	chk_host(struct dirlist *, struct sockaddr *, int *, int *,
@@ -1557,6 +1559,7 @@ get_exportlist_one(int passno)
 	struct statfs fsb;
 	struct expcred anon;
 	char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
+	char *err_msg = NULL;
 	int len, has_host, got_nondir, dirplen, netgrp;
 	uint64_t exflags;
 
@@ -1635,8 +1638,8 @@ get_exportlist_one(int passno)
 					goto nextline;
 				    }
 			    }
-			    if (check_dirpath(cp) &&
-				statfs(cp, &fsb) >= 0) {
+			    if (check_dirpath(cp, &err_msg) &&
+				check_statfs(cp, &fsb, &err_msg)) {
 				if ((fsb.f_flags & MNT_AUTOMOUNTED) != 0)
 				    syslog(LOG_ERR, "Warning: exporting of "
 					"automounted fs %s not supported", cp);
@@ -1701,8 +1704,15 @@ get_exportlist_one(int passno)
 				    dirplen = len;
 				}
 			    } else {
-				getexp_err(ep, tgrp,
-				    "symbolic link in export path or statfs failed");
+				if (err_msg != NULL) {
+					getexp_err(ep, tgrp, err_msg);
+					free(err_msg);
+					err_msg = NULL;
+				} else {
+					getexp_err(ep, tgrp,
+					    "symbolic link in export path or "
+					    "statfs failed");
+				}
 				goto nextline;
 			    }
 			    *endcp = savedc;
@@ -3786,29 +3796,76 @@ check_options(struct dirlist *dp)
 	return (0);
 }
 
+static int
+check_path_component(const char *path, char **err)
+{
+	struct stat sb;
+
+	if (lstat(path, &sb)) {
+		asprintf(err, "%s: lstat() failed: %s.\n",
+		    path, strerror(errno));
+		return (0);
+	}
+
+	switch (sb.st_mode & S_IFMT) {
+	case S_IFDIR:
+		return (1);
+	case S_IFLNK:
+		asprintf(err, "%s: path is a symbolic link.\n", path);
+		break;
+	case S_IFREG:
+		asprintf(err, "%s: path is a file rather than a directory.\n",
+		    path);
+		break;
+	default:
+		asprintf(err, "%s: path is not a directory.\n", path);
+	}
+
+	return (0);
+}
+
 /*
- * Check an absolute directory path for any symbolic links. Return true
+ * Check each path component for the presence of symbolic links. Return true
  */
 static int
-check_dirpath(char *dirp)
+check_dirpath(char *dirp, char **err)
 {
 	char *cp;
-	int ret = 1;
-	struct stat sb;
 
 	cp = dirp + 1;
-	while (*cp && ret) {
+	while (*cp) {
 		if (*cp == '/') {
 			*cp = '\0';
-			if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
-				ret = 0;
+
+			if (!check_path_component(dirp, err)) {
+				*cp = '/';
+				return (0);
+			}
+
 			*cp = '/';
 		}
 		cp++;
 	}
-	if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
-		ret = 0;
-	return (ret);
+
+	if (!check_path_component(dirp, err))
+		return (0);
+
+	return (1);
+}
+
+/*
+ * Populate statfs information. Return true on success.
+ */
+static int
+check_statfs(const char *dirp, struct statfs *fsb, char **err)
+{
+	if (statfs(dirp, fsb)) {
+		asprintf(err, "%s: statfs() failed: %s\n", dirp,
+		    strerror(errno));
+		return (0);
+	}
+
+	return (1);
 }
 
 /*