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>
