review of changes to mountd.c to add experimental server support

Rick Macklem rmacklem at uoguelph.ca
Thu May 21 16:04:22 UTC 2009


In case anyone would like to review them, here are my proposed changes to
src/usr.sbin/mountd.c so that it supports the experimental server as well
as the regular one.

It will run the experimental server if that is the only one loaded into
the kernel or the "-4" option is specified on the command line.

It parses one additional line in the exports file, which defines where the
root of the nfsv4 file system is. This line is parsed but ignored for the
regular server.

Thanks in advance for any comments, rick
--- diff -u mountd.c ---
--- freebsd-svn/usr-src/usr.sbin/mountd/mountd.c	2009-05-17 15:59:31.000000000 -0400
+++ usr-src/usr.sbin/mountd/mountd.c	2009-05-19 09:47:58.000000000 -0400
@@ -61,7 +61,9 @@
  #include <rpcsvc/mount.h>
  #include <nfs/rpcv2.h>
  #include <nfs/nfsproto.h>
+#include <nfs/nfssvc.h>
  #include <nfsserver/nfs.h>
+#include <fs/nfs/nfsport.h>

  #include <arpa/inet.h>

@@ -200,6 +202,7 @@
      struct sockaddr *samask);
  int	scan_tree(struct dirlist *, struct sockaddr *);
  static void usage(void);
+void	parse_v4root(char *, char *, int, struct xucred *);
  int	xdr_dir(XDR *, char *);
  int	xdr_explist(XDR *, caddr_t);
  int	xdr_explist_brief(XDR *, caddr_t);
@@ -233,6 +236,10 @@
  int opt_flags;
  static int have_v6 = 1;

+int v4root_phase = 0;
+int run_v4server = 0;
+int has_publicfh = 0;
+
  struct pidfh *pfh = NULL;
  /* Bits for opt_flags above */
  #define	OP_MAPROOT	0x01
@@ -288,17 +295,15 @@
  		have_v6 = 0;
  	else
  		close(s);
-	if (modfind("nfsserver") < 0) {
-		/* Not present in kernel, try loading it */
-		if (kldload("nfsserver") < 0 || modfind("nfsserver") < 0)
-			errx(1, "NFS server is not available or loadable");
-	}

-	while ((c = getopt(argc, argv, "2dh:lnp:r")) != -1)
+	while ((c = getopt(argc, argv, "24dh:lnp:r")) != -1)
  		switch (c) {
  		case '2':
  			force_v2 = 1;
  			break;
+		case '4':
+			run_v4server = 1;
+			break;
  		case 'n':
  			resvport_only = 0;
  			break;
@@ -343,6 +348,26 @@
  		default:
  			usage();
  		};
+
+	/*
+	 * If the "-4" option was specified OR only the nfsd module is
+	 * found in the server, run "nfsd".
+	 * Otherwise, try and run "nfsserver".
+	 */
+	if (run_v4server > 0) {
+		if (modfind("nfsd") < 0) {
+			/* Not present in kernel, try loading it */
+			if (kldload("nfsd") < 0 || modfind("nfsd") < 0)
+				errx(1, "NFS server is not available");
+		}
+	} else if (modfind("nfsserver") < 0 && modfind("nfsd") >= 0) {
+		run_v4server = 1;
+	} else if (modfind("nfsserver") < 0) {
+		/* Not present in kernel, try loading it */
+		if (kldload("nfsserver") < 0 || modfind("nfsserver") < 0)
+			errx(1, "NFS server is not available");
+	}
+
  	argc -= optind;
  	argv += optind;
  	grphead = (struct grouplist *)NULL;
@@ -707,7 +732,7 @@
  usage()
  {
  	fprintf(stderr,
-		"usage: mountd [-2] [-d] [-l] [-n] [-p <port>] [-r] "
+		"usage: mountd [-2] [-4] [-d] [-l] [-n] [-p <port>] [-r] "
  		"[-h <bindip>] [export_file ...]\n");
  	exit(1);
  }
@@ -1166,6 +1191,26 @@
  		ep = (struct exportlist *)NULL;

  		/*
+		 * Handle the V4 root dir.
+		 */
+		if (*cp == 'V' && *(cp + 1) == '4' && *(cp + 2) == ':') {
+			/*
+			 * V4: just indicates that it is the v4 root point,
+			 * so skip over that and set v4root_phase.
+			 */
+			if (v4root_phase > 0) {
+				syslog(LOG_ERR, "V4:duplicate line, ignored");
+				goto nextline;
+			}
+			v4root_phase = 1;
+			cp += 3;
+			nextfield(&cp, &endcp);
+			if (run_v4server > 0)
+				parse_v4root(cp, endcp, exflags, &anon);
+			goto nextline;
+		}
+
+		/*
  		 * Create new exports list entry
  		 */
  		len = endcp-cp;
@@ -1382,7 +1427,9 @@
  	int dirplen, num, i;
  	int iovlen;
  	int done;
+	struct nfsex_args eargs;

+	v4root_phase = 0;
  	bzero(&export, sizeof(export));
  	export.ex_flags = MNT_DELEXPORT;
  	dirp = NULL;
@@ -1411,6 +1458,21 @@
  	grphead = (struct grouplist *)NULL;

  	/*
+	 * and the old V4 root dir.
+	 */
+	bzero(&eargs, sizeof (eargs));
+	eargs.export.ex_flags = MNT_DELEXPORT;
+	if (run_v4server > 0 &&
+	    nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&eargs) < 0 &&
+	    errno != ENOENT)
+		syslog(LOG_ERR, "Can't delete exports for V4:");
+
+	/*
+	 * and clear flag that notes if a public fh has been exported.
+	 */
+	has_publicfh = 0;
+
+	/*
  	 * And delete exports that are in the kernel for all local
  	 * filesystems.
  	 * XXX: Should know how to handle all local exportable filesystems.
@@ -1491,6 +1553,12 @@
  		syslog(LOG_ERR, "can't open any exports file");
  		exit(2);
  	}
+
+	/*
+	 * If there was no public fh, clear any previous one set.
+	 */
+	if (run_v4server > 0 && has_publicfh == 0)
+		(void) nfssvc(NFSSVC_NOPUBLICFH, NULL);
  }

  /*
@@ -1936,6 +2004,12 @@
  			syslog(LOG_ERR, "bad opt %s", cpopt);
  			return (1);
  		}
+		if (v4root_phase == 1 &&
+		    ((*exflagsp & ~MNT_EXPORTED) ||
+		     (opt_flags & ~OP_SEC))) {
+			syslog(LOG_ERR, "Bad opt %s on V4:", cpopt);
+			return (1);
+		}
  		if (usedarg >= 0) {
  			*endcp = savedc2;
  			**endcpp = savedc;
@@ -2233,6 +2307,29 @@
  				goto error_exit;
  			}
  		}
+
+		/*
+		 * For the experimental server:
+		 * If this is the public directory, get the file handle
+		 * and load it into the kernel via the nfssvc() syscall.
+		 */
+		if (run_v4server > 0 && (exflags & MNT_EXPUBLIC) != 0) {
+			fhandle_t fh;
+			char *public_name;
+
+			if (eap.ex_indexfile != NULL)
+				public_name = eap.ex_indexfile;
+			else
+				public_name = dirp;
+			if (getfh(public_name, &fh) < 0)
+				syslog(LOG_ERR,
+				    "Can't get public fh for %s", public_name);
+			else if (nfssvc(NFSSVC_PUBLICFH, (caddr_t)&fh) < 0)
+				syslog(LOG_ERR,
+				    "Can't set public fh for %s", public_name);
+			else
+				has_publicfh = 1;
+		}
  skip:
  		if (ai != NULL)
  			ai = ai->ai_next;
@@ -2688,7 +2785,7 @@
  	struct dirlist *dp;
  {

-	if (dp == (struct dirlist *)NULL)
+	if (v4root_phase != 1 && dp == NULL)
  	    return (1);
  	if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
  	    syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
@@ -2710,6 +2807,12 @@
  	    syslog(LOG_ERR, "-alldirs has multiple directories");
  	    return (1);
  	}
+	if (v4root_phase == 1) {
+	    if ((opt_flags & ~OP_SEC) != 0) {
+		syslog(LOG_ERR, "only -sec option allowed on V4:");
+		return (1);
+	    }
+	}
  	return (0);
  }

@@ -2871,3 +2974,86 @@
  	rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL);
  	exit (0);
  }
+
+/*
+ * Parse the V4: line.
+ */
+void
+parse_v4root(char *cp, char *endcp, int exflags, struct xucred *anonp)
+{
+	char *dirp, savedc;
+	struct grouplist gr;
+	struct exportlist ea;
+	struct nfsex_args nfsea;
+	int len, has_host, i;
+
+	exflags = MNT_EXPORTED;
+	bzero(&nfsea, sizeof (nfsea));
+	bzero(&ea, sizeof (ea));
+	bzero(&gr, sizeof (gr));
+	dirp = NULL;
+	len = endcp - cp;
+	while (len > 0) {
+		if (len > RPCMNT_NAMELEN) {
+			syslog(LOG_ERR, "V4: line too long, ignored");
+			v4root_phase = 2;
+			return;
+		}
+		if (*cp == '-') {
+			if (debug)
+				warnx("doing opt %s", cp);
+			if (do_opt(&cp, &endcp, &ea, &gr, &has_host,
+			    &exflags, anonp)) {
+				v4root_phase = 2;
+				return;
+			}
+		} else if (*cp == '/') {
+			savedc = *endcp;
+			*endcp = '\0';
+			if (check_dirpath(cp)) {
+				if (dirp != NULL) {
+					syslog(LOG_ERR, "Multiple V4 dirs");
+					v4root_phase = 2;
+					return;
+				}
+				dirp = cp;
+			} else {
+				syslog(LOG_ERR, "V4: dir %s invalid", cp);
+				v4root_phase = 2;
+				return;
+			}
+			*endcp = savedc;
+		}
+		cp = endcp;
+		nextfield(&cp, &endcp);
+		len = endcp - cp;
+	}
+
+	/* Check the options */
+	if (check_options(NULL)) {
+		v4root_phase = 2;
+		return;
+	}
+
+	/*
+	 * Now, do the nfssvc() syscall.
+	 */
+	nfsea.export.ex_flags = exflags;
+	nfsea.export.ex_anon = *anonp;
+	if (ea.ex_numsecflavors == 0) {
+		nfsea.export.ex_numsecflavors = 4;
+		nfsea.export.ex_secflavors[0] = AUTH_SYS;
+		nfsea.export.ex_secflavors[1] = RPCSEC_GSS_KRB5;
+		nfsea.export.ex_secflavors[2] = RPCSEC_GSS_KRB5I;
+		nfsea.export.ex_secflavors[3] = RPCSEC_GSS_KRB5P;
+	} else {
+		nfsea.export.ex_numsecflavors = ea.ex_numsecflavors;
+		for (i = 0; i < ea.ex_numsecflavors; i++)
+			nfsea.export.ex_secflavors[i] = ea.ex_secflavors[i];
+	}
+	nfsea.fspec = dirp;
+	if (nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&nfsea) < 0)
+		syslog(LOG_ERR, "Exporting V4: failed");
+	v4root_phase = 2;
+}
+


More information about the freebsd-current mailing list