Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 7 May 2019 00:50:10 +0000
From:      Rick Macklem <rmacklem@uoguelph.ca>
To:        Peter Eriksson <pen@lysator.liu.se>
Cc:        "freebsd-fs@freebsd.org" <freebsd-fs@FreeBSD.org>
Subject:   patch to improve perf for handling exports of large numbers of file systems
Message-ID:  <YQBPR0101MB22608FE3367E1DBD64ED01E1DD310@YQBPR0101MB2260.CANPRD01.PROD.OUTLOOK.COM>

next in thread | raw e-mail | index | archive | help

[-- Attachment #1 --]
Hi,

Peter has reported to me via email that updating exports when mountd receives
a SIGHUP takes about 16seconds.  This isn't surprising, since he has 20,000+
file systems to export.
The current code deletes all 20,000+ exports in the kernel and then re-exports
everything in the exports file while having the nfsd suspended (assuming the "-S"
option is being used). It was designed decades ago when a system only had a
few file systems, but sometimes many exported directories within a file system.

The attached patch is a first draft (minimally tested) for the addition of a "-I" option
for mountd. When mountd is started with this "-I" option, it processes the exports
file(s) into the structures that mountd uses and then compares the old and new
structures, only doing unexport/re-export for the ones that have changed,
when it receives a SIGHUP.

At the moment, the algorithm for doing the comparison isn't efficient, but at least
it isn't doing a lot of system calls to unexport/re-export. (A simple change from using a single linked list to a hash table of linked lists for the "struct exportlist" would help the
comparison algorithm a lot, but I wanted to hold off on that until what I have
has seen more testing, since it will make the patch even more complex.)

I hope that Peter can test this (and anyone else willing to do so, please do
since there are many variants within the exports file(s) and I might not think
of them all to test).
The patch is "noisy", since it puts a bunch of debug stuff out via syslog().
You can get rid of this by simply deleting the syslog(..); lines that are not indented.

Anyone who can test this, it would be appreciated, rick
ps: Without "-I", the behaviour should not have changed.

[-- Attachment #2 --]
--- usr.sbin/mountd/mountd.c.sav	2019-04-30 21:48:23.155419000 -0400
+++ usr.sbin/mountd/mountd.c	2019-05-06 20:16:00.981674000 -0400
@@ -114,10 +114,13 @@ struct dirlist {
 struct exportlist {
 	struct dirlist	*ex_dirl;
 	struct dirlist	*ex_defdir;
+	struct grouplist *ex_grphead;
 	int		ex_flag;
 	fsid_t		ex_fs;
 	char		*ex_fsdir;
 	char		*ex_indexfile;
+	struct xucred	ex_defanon;
+	int		ex_defexflags;
 	int		ex_numsecflavors;
 	int		ex_secflavors[MAXSECFLAVORS];
 	int		ex_defnumsecflavors;
@@ -127,6 +130,9 @@ struct exportlist {
 };
 /* ex_flag bits */
 #define	EX_LINKED	0x1
+#define	EX_DONE		0x2
+
+SLIST_HEAD(exportlisthead, exportlist);
 
 struct netmsk {
 	struct sockaddr_storage nt_net;
@@ -143,6 +149,9 @@ struct grouplist {
 	int gr_type;
 	union grouptypes gr_ptr;
 	struct grouplist *gr_next;
+	struct xucred gr_anon;
+	int gr_exflags;
+	int gr_flag;
 	int gr_numsecflavors;
 	int gr_secflavors[MAXSECFLAVORS];
 };
@@ -153,6 +162,9 @@ struct grouplist {
 #define	GT_DEFAULT	0x3
 #define GT_IGNORE	0x5
 
+/* Group flags */
+#define	GR_FND		0x1
+
 struct hostlist {
 	int		 ht_flag;	/* Uses DP_xx bits */
 	struct grouplist *ht_grp;
@@ -185,6 +197,7 @@ static void	complete_service(struct netc
 static void	clearout_service(void);
 static void	del_mlist(char *hostp, char *dirp);
 static struct dirlist	*dirp_search(struct dirlist *, char *);
+static int	do_export_mount(struct exportlist *, struct statfs *);
 static int	do_mount(struct exportlist *, struct grouplist *, int,
 		    struct xucred *, char *, int, struct statfs *);
 static int	do_opt(char **, char **, struct exportlist *,
@@ -195,7 +208,16 @@ static void	free_dir(struct dirlist *);
 static void	free_exp(struct exportlist *);
 static void	free_grp(struct grouplist *);
 static void	free_host(struct hostlist *);
-static void	get_exportlist(void);
+static void	free_v4rootexp(void);
+static void	get_exportlist_one(int);
+static void	get_exportlist(int passno);
+static void	read_exportfile(int passno);
+static void	free_exports(struct exportlisthead *);
+static int	compare_nmount_exportlist(struct iovec *, int, char *);
+static int	compare_export(struct exportlist *, struct exportlist *);
+static int	compare_cred(struct xucred *, struct xucred *);
+static int	compare_secflavor(int *, int *, int);
+static void	delete_export(struct iovec *, int, struct statfs *, char *);
 static int	get_host(char *, struct grouplist *, struct grouplist *);
 static struct hostlist *get_ht(void);
 static int	get_line(void);
@@ -204,7 +226,7 @@ static int	get_net(char *, struct netmsk
 static void	getexp_err(struct exportlist *, struct grouplist *, const char *);
 static struct grouplist	*get_grp(void);
 static void	hang_dirp(struct dirlist *, struct grouplist *,
-				struct exportlist *, int);
+		    struct exportlist *, int, struct xucred *, int);
 static void	huphandler(int sig);
 static int	makemask(struct sockaddr_storage *ssp, int bitlen);
 static void	mntsrv(struct svc_req *, SVCXPRT *);
@@ -227,9 +249,9 @@ static int	xdr_fhs(XDR *, caddr_t);
 static int	xdr_mlist(XDR *, caddr_t);
 static void	terminate(int);
 
-static SLIST_HEAD(, exportlist) exphead = SLIST_HEAD_INITIALIZER(exphead);
-static SLIST_HEAD(, mountlist) mlhead = SLIST_HEAD_INITIALIZER(mlhead);
-static struct grouplist *grphead;
+static struct exportlisthead exphead = SLIST_HEAD_INITIALIZER(&exphead);
+static struct exportlisthead oldexphead;
+static SLIST_HEAD(, mountlist) mlhead = SLIST_HEAD_INITIALIZER(&mlhead);
 static char *exnames_default[2] = { _PATH_EXPORTS, NULL };
 static char **exnames;
 static char **hosts = NULL;
@@ -260,6 +282,8 @@ static int have_v6 = 1;
 
 static int v4root_phase = 0;
 static char v4root_dirpath[PATH_MAX + 1];
+static struct exportlist *v4root_ep = NULL;
+static struct grouplist *v4root_grp = NULL;
 static int has_publicfh = 0;
 
 static struct pidfh *pfh = NULL;
@@ -368,9 +392,10 @@ main(int argc, char **argv)
 	in_port_t svcport;
 	int c, k, s;
 	int maxrec = RPC_MAXDATASIZE;
-	int attempt_cnt, port_len, port_pos, ret;
+	int attempt_cnt, passno, port_len, port_pos, ret;
 	char **port_list;
 
+	passno = 0;
 	/* Check that another mountd isn't already running. */
 	pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &otherpid);
 	if (pfh == NULL) {
@@ -385,7 +410,7 @@ main(int argc, char **argv)
 	else
 		close(s);
 
-	while ((c = getopt(argc, argv, "2deh:lnp:rS")) != -1)
+	while ((c = getopt(argc, argv, "2deh:Ilnp:rS")) != -1)
 		switch (c) {
 		case '2':
 			force_v2 = 1;
@@ -437,6 +462,9 @@ main(int argc, char **argv)
 		case 'S':
 			suspend_nfsd = 1;
 			break;
+		case 'I':
+			passno = 1;
+			break;
 		default:
 			usage();
 		}
@@ -449,7 +477,6 @@ main(int argc, char **argv)
 
 	argc -= optind;
 	argv += optind;
-	grphead = (struct grouplist *)NULL;
 	if (argc > 0)
 		exnames = argv;
 	else
@@ -457,7 +484,7 @@ main(int argc, char **argv)
 	openlog("mountd", LOG_PID, LOG_DAEMON);
 	if (debug)
 		warnx("getting export list");
-	get_exportlist();
+	get_exportlist(0);
 	if (debug)
 		warnx("getting mount list");
 	get_mountlist();
@@ -628,7 +655,7 @@ main(int argc, char **argv)
 	/* Expand svc_run() here so that we can call get_exportlist(). */
 	for (;;) {
 		if (got_sighup) {
-			get_exportlist();
+			get_exportlist(passno);
 			got_sighup = 0;
 		}
 		readfds = svc_fdset;
@@ -1416,7 +1443,7 @@ static FILE *exp_file;
  * Get the export list from one, currently open file
  */
 static void
-get_exportlist_one(void)
+get_exportlist_one(int passno)
 {
 	struct exportlist *ep;
 	struct grouplist *grp, *tgrp;
@@ -1653,11 +1680,15 @@ get_exportlist_one(void)
 		 * Loop through hosts, pushing the exports into the kernel.
 		 * After loop, tgrp points to the start of the list and
 		 * grp points to the last entry in the list.
+		 * Do not do the do_mount() for passno == 1, since the
+		 * second pass will do it, as required.
 		 */
 		grp = tgrp;
 		do {
-			if (do_mount(ep, grp, exflags, &anon, dirp, dirplen,
-			    &fsb)) {
+			grp->gr_exflags = exflags;
+			grp->gr_anon = anon;
+			if (passno != 1 && do_mount(ep, grp, exflags, &anon,
+			    dirp, dirplen, &fsb)) {
 				getexp_err(ep, tgrp, NULL);
 				goto nextline;
 			}
@@ -1668,15 +1699,26 @@ get_exportlist_one(void)
 		 */
 		if (v4root_phase > 0 && v4root_phase <= 2) {
 			/*
-			 * Since these structures aren't used by mountd,
+			 * These structures are used for the "-I" reload,
+			 * so save them for that case.  Otherwise, just
 			 * free them up now.
 			 */
-			if (ep != NULL)
-				free_exp(ep);
-			while (tgrp != NULL) {
-				grp = tgrp;
-				tgrp = tgrp->gr_next;
-				free_grp(grp);
+			if (passno == 1) {
+				if (v4root_ep != NULL) {
+					getexp_err(ep, tgrp,
+					    "multiple V4 lines");
+					goto nextline;
+				}
+				v4root_ep = ep;
+				v4root_grp = tgrp;
+			} else {
+				if (ep != NULL)
+					free_exp(ep);
+				while (tgrp != NULL) {
+					grp = tgrp;
+					tgrp = tgrp->gr_next;
+					free_grp(grp);
+				}
 			}
 			goto nextline;
 		}
@@ -1685,12 +1727,12 @@ get_exportlist_one(void)
 		 * Success. Update the data structures.
 		 */
 		if (has_host) {
-			hang_dirp(dirhead, tgrp, ep, opt_flags);
-			grp->gr_next = grphead;
-			grphead = tgrp;
+			hang_dirp(dirhead, tgrp, ep, opt_flags, &anon, exflags);
+			grp->gr_next = ep->ex_grphead;
+			ep->ex_grphead = tgrp;
 		} else {
 			hang_dirp(dirhead, (struct grouplist *)NULL, ep,
-				opt_flags);
+				opt_flags, &anon, exflags);
 			free_grp(grp);
 		}
 		dirhead = (struct dirlist *)NULL;
@@ -1712,45 +1754,43 @@ nextline:
  * Get the export list from all specified files
  */
 static void
-get_exportlist(void)
+get_exportlist(int passno)
 {
-	struct exportlist *ep, *ep2;
-	struct grouplist *grp, *tgrp;
 	struct export_args export;
 	struct iovec *iov;
 	struct statfs *fsp, *mntbufp;
-	struct xvfsconf vfc;
 	char errmsg[255];
 	int num, i;
 	int iovlen;
-	int done;
 	struct nfsex_args eargs;
 
-	if (suspend_nfsd != 0)
-		(void)nfssvc(NFSSVC_SUSPENDNFSD, NULL);
+syslog(LOG_ERR, "passno=%d", passno);
 	v4root_dirpath[0] = '\0';
+	free_v4rootexp();
+	if (passno == 1) {
+		/*
+		 * Save the current lists as old ones, so that the new lists
+		 * can be compared with the old ones in the 2nd pass.
+		 */
+		SLIST_FIRST(&oldexphead) = SLIST_FIRST(&exphead);
+		SLIST_INIT(&exphead);
+
+		/* Read the export file(s) and process them */
+		read_exportfile(passno);
+syslog(LOG_ERR, "readexp1");
+	} else {
+		/* Just make the old lists empty. */
+		SLIST_INIT(&oldexphead);
+	}
+
 	bzero(&export, sizeof(export));
 	export.ex_flags = MNT_DELEXPORT;
 	iov = NULL;
 	iovlen = 0;
 	bzero(errmsg, sizeof(errmsg));
 
-	/*
-	 * First, get rid of the old list
-	 */
-	SLIST_FOREACH_SAFE(ep, &exphead, entries, ep2) {
-		SLIST_REMOVE(&exphead, ep, exportlist, entries);
-		free_exp(ep);
-	}
-
-	grp = grphead;
-	while (grp) {
-		tgrp = grp;
-		grp = grp->gr_next;
-		free_grp(tgrp);
-	}
-	grphead = (struct grouplist *)NULL;
-
+	if (suspend_nfsd != 0)
+		(void)nfssvc(NFSSVC_SUSPENDNFSD, NULL);
 	/*
 	 * and the old V4 root dir.
 	 */
@@ -1765,62 +1805,52 @@ get_exportlist(void)
 	 */
 	has_publicfh = 0;
 
+	build_iovec(&iov, &iovlen, "fstype", NULL, 0);
+	build_iovec(&iov, &iovlen, "fspath", NULL, 0);
+	build_iovec(&iov, &iovlen, "from", NULL, 0);
+	build_iovec(&iov, &iovlen, "update", NULL, 0);
+	build_iovec(&iov, &iovlen, "export", &export,
+	    sizeof(export));
+	build_iovec(&iov, &iovlen, "errmsg", errmsg,
+	    sizeof(errmsg));
+
 	/*
-	 * And delete exports that are in the kernel for all local
-	 * filesystems.
-	 * XXX: Should know how to handle all local exportable filesystems.
+	 * For passno == 1, compare the old and new lists updating the kernel
+	 * exports for any cases that have changed.
+	 * This call is doing the second pass through the lists.
+	 * If it fails, fall back on the bulk reload.
 	 */
-	num = getmntinfo(&mntbufp, MNT_NOWAIT);
-
-	if (num > 0) {
-		build_iovec(&iov, &iovlen, "fstype", NULL, 0);
-		build_iovec(&iov, &iovlen, "fspath", NULL, 0);
-		build_iovec(&iov, &iovlen, "from", NULL, 0);
-		build_iovec(&iov, &iovlen, "update", NULL, 0);
-		build_iovec(&iov, &iovlen, "export", &export, sizeof(export));
-		build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
-	}
-
-	for (i = 0; i < num; i++) {
-		fsp = &mntbufp[i];
-		if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) {
-			syslog(LOG_ERR, "getvfsbyname() failed for %s",
-			    fsp->f_fstypename);
-			continue;
-		}
-
+	if (passno == 1 && compare_nmount_exportlist(iov, iovlen, errmsg) ==
+	    0) {
+syslog(LOG_ERR, "compareok");
+		/* Free up the old lists. */
+		free_exports(&oldexphead);
+	} else {
+syslog(LOG_ERR, "doin passno=0");
 		/*
-		 * We do not need to delete "export" flag from
-		 * filesystems that do not have it set.
+		 * First, get rid of the old lists.
 		 */
-		if (!(fsp->f_flags & MNT_EXPORTED))
-		    continue;
+		free_exports(&exphead);
+		free_exports(&oldexphead);
+		free_v4rootexp();
+
 		/*
-		 * Do not delete export for network filesystem by
-		 * passing "export" arg to nmount().
-		 * It only makes sense to do this for local filesystems.
+		 * And delete exports that are in the kernel for all local
+		 * filesystems.
+		 * XXX: Should know how to handle all local exportable
+		 * filesystems.
 		 */
-		if (vfc.vfc_flags & VFCF_NETWORK)
-			continue;
-
-		iov[1].iov_base = fsp->f_fstypename;
-		iov[1].iov_len = strlen(fsp->f_fstypename) + 1;
-		iov[3].iov_base = fsp->f_mntonname;
-		iov[3].iov_len = strlen(fsp->f_mntonname) + 1;
-		iov[5].iov_base = fsp->f_mntfromname;
-		iov[5].iov_len = strlen(fsp->f_mntfromname) + 1;
-		errmsg[0] = '\0';
-
-		/*
-		 * EXDEV is returned when path exists but is not a
-		 * mount point.  May happens if raced with unmount.
-		 */
-		if (nmount(iov, iovlen, fsp->f_flags) < 0 &&
-		    errno != ENOENT && errno != ENOTSUP && errno != EXDEV) {
-			syslog(LOG_ERR,
-			    "can't delete exports for %s: %m %s",
-			    fsp->f_mntonname, errmsg);
+		num = getmntinfo(&mntbufp, MNT_NOWAIT);
+	
+		for (i = 0; i < num; i++) {
+			fsp = &mntbufp[i];
+			delete_export(iov, iovlen, fsp, errmsg);
 		}
+
+
+		/* Read the export file(s) and process them */
+		read_exportfile(0);
+syslog(LOG_ERR, "readexp0");
 	}
 
 	if (iov != NULL) {
@@ -1838,6 +1868,25 @@ get_exportlist(void)
 	}
 
 	/*
+	 * If there was no public fh, clear any previous one set.
+	 */
+	if (has_publicfh == 0)
+		(void) nfssvc(NFSSVC_NOPUBLICFH, NULL);
+
+	/* Resume the nfsd. If they weren't suspended, this is harmless. */
+	(void)nfssvc(NFSSVC_RESUMENFSD, NULL);
+syslog(LOG_ERR, "eo get_exportlist");
+}
+
+/*
+ * Read the exports file(s) and call get_exportlist_one() for each line.
+ */
+static void
+read_exportfile(int passno)
+{
+	int done, i;
+
+	/*
 	 * Read in the exports file and build the list, calling
 	 * nmount() as we go along to push the export rules into the kernel.
 	 */
@@ -1849,7 +1898,7 @@ get_exportlist(void)
 			syslog(LOG_WARNING, "can't open %s", exnames[i]);
 			continue;
 		}
-		get_exportlist_one();
+		get_exportlist_one(passno);
 		fclose(exp_file);
 		done++;
 	}
@@ -1857,15 +1906,267 @@ get_exportlist(void)
 		syslog(LOG_ERR, "can't open any exports file");
 		exit(2);
 	}
+}
+
+/*
+ * Compare the export lists against the old ones and do nmount() operations
+ * for any cases that have changed.  This avoids doing nmount() for entries
+ * that have not changed.
+ * Return 0 upon success, 1 otherwise.
+ */
+static int
+compare_nmount_exportlist(struct iovec *iov, int iovlen, char *errmsg)
+{
+	struct exportlist *ep, *oep;
+	struct grouplist *grp;
+	struct statfs fs, ofs;
+	int ret;
 
 	/*
-	 * If there was no public fh, clear any previous one set.
+	 * Loop through the current list and look for an entry in the old
+	 * list.
+	 * If found, check to see if it the same.
+	 *        If it is not the same, delete and re-export.
+	 *        Then mark it done on the old list.
+	 * else (not found)
+	 *        export it.
+	 * Any entries left in the old list after processing must have their
+	 * exports deleted.
 	 */
-	if (has_publicfh == 0)
-		(void) nfssvc(NFSSVC_NOPUBLICFH, NULL);
+	SLIST_FOREACH(ep, &exphead, entries) {
+syslog(LOG_ERR, "foreach ep");
+		if (statfs(ep->ex_fsdir, &fs) < 0)
+			return (1);
+syslog(LOG_ERR, "fsv0=%d fsv1=%d", fs.f_fsid.val[0], fs.f_fsid.val[1]);
+		SLIST_FOREACH(oep, &oldexphead, entries) {
+syslog(LOG_ERR, "foreach oep");
+			if (ep->ex_fs.val[0] == oep->ex_fs.val[0] &&
+			    ep->ex_fs.val[1] == oep->ex_fs.val[1]) {
+				/*
+				 * Statfs both file systems and sanity check
+				 * that they still refer to the same file
+				 * system.
+				 * If not, return 1 so that the reload of the
+				 * exports will be done in bulk, the
+				 * passno == 0 way.
+				 */
+syslog(LOG_ERR, "fnd ex");
+				if (statfs(oep->ex_fsdir, &ofs) < 0 ||
+				    ep->ex_fs.val[0] != fs.f_fsid.val[0] ||
+				    ep->ex_fs.val[1] != fs.f_fsid.val[1] ||
+				    fs.f_fsid.val[0] != ofs.f_fsid.val[0] ||
+				    fs.f_fsid.val[1] != ofs.f_fsid.val[1])
+					return (1);
+				/*
+				 * Test to see if the entry is the same.
+				 * If not the same delete exports and
+				 * re-export.
+				 */
+syslog(LOG_ERR, "at compare");
+				if (compare_export(ep, oep) != 0) {
+					delete_export(iov, iovlen, &fs, errmsg);
+					ret = do_export_mount(ep, &fs);
+					if (ret != 0)
+						return (ret);
+				}
+				oep->ex_flag |= EX_DONE;
+syslog(LOG_ERR, "exdone");
+				break;
+			}
+		}
+		if (oep == NULL) {
+syslog(LOG_ERR, "not fnd so ex");
+			/* Not found, so do export. */
+			ret = do_export_mount(ep, &fs);
+			if (ret != 0)
+				return (ret);
+		}
+	}
+	SLIST_FOREACH(oep, &oldexphead, entries) {
+		/* Delete exports not done. */
+		if ((oep->ex_flag & EX_DONE) == 0) {
+syslog(LOG_ERR, "not done delete");
+			if (statfs(oep->ex_fsdir, &ofs) >= 0 &&
+			    oep->ex_fs.val[0] == ofs.f_fsid.val[0] &&
+			    oep->ex_fs.val[1] == ofs.f_fsid.val[1])
+syslog(LOG_ERR, "do delete");
+				delete_export(iov, iovlen, &ofs, errmsg);
+		}
+	}
 
-	/* Resume the nfsd. If they weren't suspended, this is harmless. */
-	(void)nfssvc(NFSSVC_RESUMENFSD, NULL);
+	/* Do the V4 root exports, as required. */
+	grp = v4root_grp;
+	v4root_phase = 2;
+	while (v4root_ep != NULL && grp != NULL) {
+syslog(LOG_ERR, "v4ex path=%s gr_type=0x%x gr_exflags=0x%x", v4root_dirpath, grp->gr_type, grp->gr_exflags);
+syslog(LOG_ERR, "v4rootd=%s", v4root_dirpath);
+		ret = do_mount(v4root_ep, grp, grp->gr_exflags, &grp->gr_anon,
+		    v4root_dirpath, strlen(v4root_dirpath), &fs);
+		if (ret != 0) {
+			v4root_phase = 0;
+			return (ret);
+		}
+		grp = grp->gr_next;
+	}
+	v4root_phase = 0;
+	free_v4rootexp();
+	return (0);
+}
+
+/*
+ * Compare old and current exportlist entries for the fsid and return 0
+ * if they are the same, 1 otherwise.
+ */
+static int
+compare_export(struct exportlist *ep, struct exportlist *oep)
+{
+	struct grouplist *grp, *ogrp;
+
+	if (strcmp(ep->ex_fsdir, oep->ex_fsdir) != 0)
+		return (1);
+	if ((ep->ex_defdir != NULL && oep->ex_defdir == NULL) ||
+	    (ep->ex_defdir == NULL && oep->ex_defdir != NULL))
+		return (1);
+	if (ep->ex_defdir != NULL) {
+		if ((ep->ex_defdir->dp_flag & DP_DEFSET) != 0) {
+			if ((oep->ex_defdir->dp_flag & DP_DEFSET) == 0)
+				return (1);
+			if (ep->ex_defnumsecflavors !=
+			    oep->ex_defnumsecflavors ||
+			    ep->ex_defexflags != oep->ex_defexflags ||
+			    compare_cred(&ep->ex_defanon, &oep->ex_defanon) !=
+			    0 || compare_secflavor(ep->ex_defsecflavors,
+			    oep->ex_defsecflavors, ep->ex_defnumsecflavors) !=
+			    0)
+				return (1);
+		}
+	}
+
+	/* Now, check all the groups. */
+	for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next)
+		ogrp->gr_flag = 0;
+	for (grp = ep->ex_grphead; grp != NULL; grp = grp->gr_next) {
+		for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp =
+		    ogrp->gr_next)
+			if ((ogrp->gr_flag & GR_FND) == 0 &&
+			    grp->gr_numsecflavors == ogrp->gr_numsecflavors &&
+			    grp->gr_exflags == ogrp->gr_exflags &&
+			    compare_cred(&grp->gr_anon, &ogrp->gr_anon) == 0 &&
+			    compare_secflavor(grp->gr_secflavors,
+			    ogrp->gr_secflavors, grp->gr_numsecflavors) == 0)
+				break;
+		if (ogrp != NULL)
+			ogrp->gr_flag |= GR_FND;
+		else
+			return (1);
+	}
+	for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next)
+		if ((ogrp->gr_flag & GR_FND) == 0)
+			return (1);
+	return (0);
+}
+
+/*
+ * Compare to struct xucred's.  Return 0 if the same and 1 otherwise.
+ * This algorithm is O(N**2) but fortunately N is always small.
+ */
+static int
+compare_cred(struct xucred *cr0, struct xucred *cr1)
+{
+	int i, j;
+
+	if (cr0->cr_uid != cr1->cr_uid || cr0->cr_ngroups != cr1->cr_ngroups)
+		return (1);
+	for (i = 0; i < cr0->cr_ngroups; i++) {
+		for (j = 0; j < cr0->cr_ngroups; j++)
+			if (cr0->cr_groups[i] == cr1->cr_groups[j])
+				break;
+		if (j == cr0->cr_ngroups)
+			return (1);
+	}
+	return (0);
+}
+
+/*
+ * Compare two lists of security flavors.  Return 0 if the same and 1 otherwise.
+ * This algorithm is O(N**2) but fortunately N is always small.
+ */
+static int
+compare_secflavor(int *sec1, int *sec2, int nsec)
+{
+	int i, j;
+
+	for (i = 0; i < nsec; i++) {
+		for (j = 0; j < nsec; j++)
+			if (sec1[i] == sec2[j])
+				break;
+		if (j == nsec)
+			return (1);
+	}
+	return (0);
+}
+
+/*
+ * Delete an exports entry.
+ */
+static void
+delete_export(struct iovec *iov, int iovlen, struct statfs *fsp, char *errmsg)
+{
+	struct xvfsconf vfc;
+
+	if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) {
+		syslog(LOG_ERR, "getvfsbyname() failed for %s",
+		    fsp->f_fstypename);
+		return;
+	}
+	
+	/*
+	 * We do not need to delete "export" flag from
+	 * filesystems that do not have it set.
+	 */
+	if (!(fsp->f_flags & MNT_EXPORTED))
+		return;
+	/*
+	 * Do not delete export for network filesystem by
+	 * passing "export" arg to nmount().
+	 * It only makes sense to do this for local filesystems.
+	 */
+	if (vfc.vfc_flags & VFCF_NETWORK)
+		return;
+	
+	iov[1].iov_base = fsp->f_fstypename;
+	iov[1].iov_len = strlen(fsp->f_fstypename) + 1;
+	iov[3].iov_base = fsp->f_mntonname;
+	iov[3].iov_len = strlen(fsp->f_mntonname) + 1;
+	iov[5].iov_base = fsp->f_mntfromname;
+	iov[5].iov_len = strlen(fsp->f_mntfromname) + 1;
+	errmsg[0] = '\0';
+	
+	/*
+	 * EXDEV is returned when path exists but is not a
+	 * mount point.  May happens if raced with unmount.
+	 */
+	if (nmount(iov, iovlen, fsp->f_flags) < 0 && errno != ENOENT &&
+	    errno != ENOTSUP && errno != EXDEV) {
+		syslog(LOG_ERR,
+		    "can't delete exports for %s: %m %s",
+		    fsp->f_mntonname, errmsg);
+	}
+}
+
+/*
+ * Free up the exports lists passed in as arguments.
+ */
+static void
+free_exports(struct exportlisthead *exhp)
+{
+	struct exportlist *ep, *ep2;
+
+	SLIST_FOREACH_SAFE(ep, exhp, entries, ep2) {
+		SLIST_REMOVE(exhp, ep, exportlist, entries);
+		free_exp(ep);
+	}
+	SLIST_INIT(exhp);
 }
 
 /*
@@ -1965,7 +2266,7 @@ add_expdir(struct dirlist **dpp, char *c
  */
 static void
 hang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep,
-	int flags)
+	int flags, struct xucred *anoncrp, int exflags)
 {
 	struct hostlist *hp;
 	struct dirlist *dp2;
@@ -1982,6 +2283,8 @@ hang_dirp(struct dirlist *dp, struct gro
 			if (ep->ex_numsecflavors > 0)
 				memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
 				    sizeof(ep->ex_secflavors));
+			ep->ex_defanon = *anoncrp;
+			ep->ex_defexflags = exflags;
 		} else while (grp) {
 			hp = get_ht();
 			hp->ht_grp = grp;
@@ -2403,6 +2706,7 @@ get_host(char *cp, struct grouplist *grp
 static void
 free_exp(struct exportlist *ep)
 {
+	struct grouplist *grp, *tgrp;
 
 	if (ep->ex_defdir) {
 		free_host(ep->ex_defdir->dp_hosts);
@@ -2413,10 +2717,37 @@ free_exp(struct exportlist *ep)
 	if (ep->ex_indexfile)
 		free(ep->ex_indexfile);
 	free_dir(ep->ex_dirl);
+	tgrp = ep->ex_grphead;
+	while (tgrp != NULL) {
+		grp = tgrp;
+		tgrp = tgrp->gr_next;
+		free_grp(grp);
+	}
 	free((caddr_t)ep);
 }
 
 /*
+ * Free up the v4root exports.
+ */
+static void
+free_v4rootexp(void)
+{
+	struct grouplist *grp, *tgrp;
+
+	if (v4root_ep != NULL) {
+		free_exp(v4root_ep);
+		v4root_ep = NULL;
+	}
+	tgrp = v4root_grp;
+	v4root_grp = NULL;
+	while (tgrp != NULL) {
+		grp = tgrp;
+		tgrp = tgrp->gr_next;
+		free_grp(grp);
+	}
+}
+
+/*
  * Free hosts.
  */
 static void
@@ -2456,6 +2787,44 @@ out_of_mem(void)
 }
 
 /*
+ * Call do_mount() from the struct exportlist, for each case needed.
+ */
+static int
+do_export_mount(struct exportlist *ep, struct statfs *fsp)
+{
+	struct dirlist *dp;
+	struct grouplist *grp, defgrp;
+	int ret;
+	size_t dirlen;
+
+syslog(LOG_ERR, "do_mount_export=%s", ep->ex_fsdir);
+	dirlen = strlen(ep->ex_fsdir);
+	dp = ep->ex_defdir;
+	if (dp != NULL && (dp->dp_flag & DP_DEFSET) != 0) {
+		defgrp.gr_type = GT_DEFAULT;
+		defgrp.gr_next = NULL;
+		/* We have an entry for all other hosts/nets. */
+syslog(LOG_ERR, "ex_defexflags=0x%x", ep->ex_defexflags);
+		ret = do_mount(ep, &defgrp, ep->ex_defexflags, &ep->ex_defanon,
+		    ep->ex_fsdir, dirlen, fsp);
+		if (ret != 0)
+			return (ret);
+	}
+
+	/* Do a mount for each group. */
+	grp = ep->ex_grphead;
+	while (grp != NULL) {
+syslog(LOG_ERR, "do mount gr_type=0x%x gr_exflags=0x%x", grp->gr_type, grp->gr_exflags);
+		ret = do_mount(ep, grp, grp->gr_exflags, &grp->gr_anon,
+		    ep->ex_fsdir, dirlen, fsp);
+		if (ret != 0)
+			return (ret);
+		grp = grp->gr_next;
+	}
+	return (0);
+}
+
+/*
  * Do the nmount() syscall with the update flag to push the export info into
  * the kernel.
  */
@@ -2487,6 +2856,7 @@ do_mount(struct exportlist *ep, struct g
 	bzero(errmsg, sizeof(errmsg));
 	eap->ex_flags = exflags;
 	eap->ex_anon = *anoncrp;
+syslog(LOG_ERR, "do_exflags=0x%x", exflags);
 	eap->ex_indexfile = ep->ex_indexfile;
 	if (grp->gr_type == GT_HOST)
 		ai = grp->gr_ptr.gt_addrinfo;

Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?YQBPR0101MB22608FE3367E1DBD64ED01E1DD310>