[RFC,PATCH 1/4] Dynamic Pseudo Root

Steve Dickson SteveD at redhat.com
Fri Dec 7 14:15:48 EST 2007


commit fd0c72a6c41c20c4039d9c4365f04633e09e2f9e
Author: Steve Dickson <steved at redhat.com>
Date:   Thu Nov 29 15:58:04 2007 -0500

    Initial attempt at creating a dynamic pseudo-root for nfs4 exports.
    This will eliminate the need to define a pseudo root with
    the 'fsid=0' export option allowing existing export configurations
    to just work with nfs4 mounts.
    
    Signed-off-by: Steve Dickson <steved at redhat.com>

---
 support/export/Makefile.am  |    2 +-
 support/export/v4root.c     |  201 +++++++++++++++++++++++++++++++++++++++++++
 support/export/xtab.c       |    5 +
 support/include/Makefile.am |    2 +
 support/include/execute.h   |   38 ++++++++
 support/include/exportfs.h  |   11 +++
 support/include/mounts.h    |   61 +++++++++++++
 support/include/nfslib.h    |    3 +
 support/nfs/Makefile.am     |    2 +-
 support/nfs/execute.c       |   92 ++++++++++++++++++++
 utils/mountd/auth.c         |    5 +
 utils/mountd/cache.c        |   46 ++++++-----
 utils/mountd/mountd.c       |    1 +
 13 files changed, 446 insertions(+), 23 deletions(-)
 create mode 100644 support/export/v4root.c
 create mode 100644 support/include/execute.h
 create mode 100644 support/include/mounts.h
 create mode 100644 support/nfs/execute.c

diff --git a/support/export/Makefile.am b/support/export/Makefile.am
index 1ea1539..8a324b1 100644
--- a/support/export/Makefile.am
+++ b/support/export/Makefile.am
@@ -11,7 +11,7 @@ EXTRA_DIST	= mount.x
 
 noinst_LIBRARIES = libexport.a
 libexport_a_SOURCES = client.c export.c hostname.c nfsctl.c rmtab.c \
-		      xtab.c mount_clnt.c mount_xdr.c
+		      xtab.c mount_clnt.c mount_xdr.c v4root.c
 BUILT_SOURCES 	= $(GENFILES)
 
 noinst_HEADERS = mount.h
diff --git a/support/export/v4root.c b/support/export/v4root.c
new file mode 100644
index 0000000..c91fe8d
--- /dev/null
+++ b/support/export/v4root.c
@@ -0,0 +1,201 @@
+/*
+ * support/export/v4root.c
+ *
+ * Routines that create and destroy v4 psuedo roots 
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "xlog.h"
+#include "exportfs.h"
+#include "nfslib.h"
+#include "misc.h"
+#include "mounts.h"
+#include "execute.h"
+
+#ifndef _PATH_PSEUDO_ROOT
+#define _PATH_PSEUDO_ROOT		NFS_STATEDIR "/v4root"
+#endif
+
+void v4root_destroy(void);
+void v4root_create(void);
+void v4root_umountall(void);
+
+int need_v4root;
+static char errbuf[BUFSIZ];
+static int v4root_mkroot(void);
+static int v4root_mkroot(void);
+
+static struct exportent pf_export = {
+	.e_hostname = "*",
+	.e_path     = _PATH_PSEUDO_ROOT,
+	.m_path = _PATH_PSEUDO_ROOT,
+	.e_flags = NFSEXP_READONLY | NFSEXP_INSECURE_PORT
+			| NFSEXP_NOSUBTREECHECK | NFSEXP_FSID
+			| NFSEXP_CROSSMOUNT,
+	.e_anonuid = 65534,
+	.e_anongid = 65534,
+	.e_squids = NULL,
+	.e_nsquids = 0,
+	.e_sqgids = NULL,
+	.e_nsqgids = 0,
+	.e_fsid = 0,
+	.e_mountpoint = NULL,
+};
+static struct psuedo_ent {
+	struct exportent *export;
+	char *fh;
+} psuedo_export = {NULL, NULL};
+
+int
+v4root_mount(struct mountargs *args)
+{
+	int status;
+
+	status = mount (args->spec, args->node, args->type, args->flags, args->data);
+	if (status < 0) {
+		snprintf(errbuf, BUFSIZ, "mounting %s failed: ",  args->node);
+		syserror(errbuf);
+	}
+}
+
+int
+v4root_umount(struct mountargs *args)
+{
+	int status;
+
+	if (args->flags)
+		status = umount2(args->node, args->flags);
+	else
+		status = umount(args->node);
+	if (status < 0) {
+		snprintf(errbuf, BUFSIZ, "unmounting %s failed: ",  args->node);
+		syserror(errbuf);
+	}
+}
+
+/*
+ * Destroy the psuedo root
+ */
+void
+v4root_destroy()
+{
+	if (access(_PATH_PSEUDO_ROOT, F_OK) < 0)
+		return;
+
+	psuedo_export.export = NULL;
+	psuedo_export.fh = NULL;
+	lazy_umount(_PATH_PSEUDO_ROOT);
+	exec_rmdir(_PATH_PSEUDO_ROOT);
+}
+
+/*
+ * Create the psuedo root directory
+ */
+static int
+v4root_mkroot()
+{
+	struct stat sb;
+
+	if (stat(_PATH_PSEUDO_ROOT, &sb) < 0) {
+		if (errno != ENOENT) {
+			syserror(_PATH_PSEUDO_ROOT);
+			return 1;
+		}
+		if (mkdir(_PATH_PSEUDO_ROOT, 0755) < 0) {
+			syserror("Unable to create " _PATH_PSEUDO_ROOT);
+			return 1;
+		}
+	} else if (!S_ISDIR(sb.st_mode)) {
+		errno =  ENOTDIR;
+		syserror(_PATH_PSEUDO_ROOT);
+		return 1;
+	}
+
+	return 0;
+}
+/*
+ * Creat the psuedo tree by running through
+ * the exports and bind mounting them to 
+ * directories in the tree
+ */
+void
+v4root_create()
+{
+	nfs_export	*exp, *nxt, *root;
+	char path[BUFSIZ];
+	int		i;
+
+	if (!need_v4root)
+		return;
+
+	if (v4root_mkroot())
+		return;
+
+	if (!is_mountpoint(_PATH_PSEUDO_ROOT))
+		tmpfs_mount(_PATH_PSEUDO_ROOT);
+
+	for (i = 0; i < MCL_MAXTYPES; i++) {
+		for (exp = exportlist[i]; exp; exp = nxt) {
+			nxt = exp->m_next;
+			if (strcmp(exp->m_export.e_path, "/")) 
+				snprintf(path, BUFSIZ, "%s/%s", _PATH_PSEUDO_ROOT, 
+					exp->m_export.e_path);
+			if (!is_mountpoint(path)) {
+				exec_mkpath(path);
+				bind_mount(exp->m_export.e_path, path);
+			}
+		}
+	}
+	psuedo_export.export = &pf_export;
+}
+
+void
+v4root_umountall()
+{
+	v4root_destroy();
+}
+struct exportent *
+v4root_chkroot(int fsidtype, unsigned int fsidnum, char *fhuuid)
+{
+	struct exportent *psuedo_root = NULL;
+	char u[16];
+
+	if (psuedo_export.export == NULL)
+		return NULL;
+
+	switch(fsidtype) {
+		case FSID_NUM:
+			if (fsidnum == 0)
+				psuedo_root = psuedo_export.export;
+			break;
+	}
+
+	return psuedo_root;
+}
+char *
+v4root_maproot(char *path)
+{
+	char *mpath;
+
+	if (psuedo_export.export == NULL)
+		return NULL;
+
+	if (strstr(path, _PATH_PSEUDO_ROOT) == NULL)
+		return NULL;
+
+	if (strcmp(path, _PATH_PSEUDO_ROOT)) 
+		mpath = path + strlen(_PATH_PSEUDO_ROOT);
+	else
+		mpath = strlen(_PATH_PSEUDO_ROOT);
+
+	return mpath;
+}
diff --git a/support/export/xtab.c b/support/export/xtab.c
index 990113e..fee6ad2 100644
--- a/support/export/xtab.c
+++ b/support/export/xtab.c
@@ -20,6 +20,7 @@
 #include "xio.h"
 #include "xlog.h"
 
+extern int need_v4root, clean_v4root;
 static void cond_rename(char *newfile, char *oldfile);
 
 static int
@@ -36,6 +37,8 @@ xtab_read(char *xtab, int is_export)
 	if ((lockid = xflock(xtab, "r")) < 0)
 		return 0;
 	setexportent(xtab, "r");
+	if (is_export == 1) 
+		need_v4root = 1;
 	while ((xp = getexportent(is_export==0, 0)) != NULL) {
 		if (!(exp = export_lookup(xp->e_hostname, xp->e_path, is_export != 1)) &&
 		    !(exp = export_create(xp, is_export!=1))) {
@@ -48,6 +51,8 @@ xtab_read(char *xtab, int is_export)
 		case 1:
 			exp->m_xtabent = 1;
 			exp->m_mayexport = 1;
+			if ((xp->e_flags & NFSEXP_FSID) && xp->e_fsid == 0)
+				need_v4root = 0;
 			break;
 		case 2:
 			exp->m_exported = -1;/* may be exported */
diff --git a/support/include/Makefile.am b/support/include/Makefile.am
index 718abda..dfa3c46 100644
--- a/support/include/Makefile.am
+++ b/support/include/Makefile.am
@@ -3,9 +3,11 @@
 SUBDIRS = nfs rpcsvc sys
 
 noinst_HEADERS = \
+	execute.h \
 	exportfs.h \
 	ha-callout.h \
 	misc.h \
+	mounts.h \
 	nfs_mntent.h \
 	nfs_paths.h \
 	nfslib.h \
diff --git a/support/include/execute.h b/support/include/execute.h
new file mode 100644
index 0000000..71aa30c
--- /dev/null
+++ b/support/include/execute.h
@@ -0,0 +1,38 @@
+/*
+ * support/include/execute.h
+ *
+ * Routines that will execute random shell commands.
+ *
+ */
+
+#ifndef EXECUTE_H
+#define EXECUTE_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef _RMDIR_CMD
+#define _RMDIR_CMD "/bin/rmdir"
+#endif
+
+#ifndef _MKDIR_CMD
+#define _MKDIR_CMD "/bin/mkdir"
+#endif
+
+inline int
+exec_rmdir(char *dir)
+{
+	char *const myargv[] = {_RMDIR_CMD, dir, NULL};
+
+	return execute_cmd(_RMDIR_CMD, myargv);
+}
+inline int
+exec_mkpath(char *dir)
+{
+	char *const myargv[] = {_MKDIR_CMD, "-p", dir, NULL};
+
+	return execute_cmd(_MKDIR_CMD, myargv);
+}
+
+#endif /* EXECUTE_H */
diff --git a/support/include/exportfs.h b/support/include/exportfs.h
index c1ba543..a0d612e 100644
--- a/support/include/exportfs.h
+++ b/support/include/exportfs.h
@@ -12,6 +12,17 @@
 #include <netdb.h>
 #include "nfslib.h"
 
+enum nfsd_fsid {
+	FSID_DEV = 0,
+	FSID_NUM,
+	FSID_MAJOR_MINOR,
+	FSID_ENCODE_DEV,
+	FSID_UUID4_INUM,
+	FSID_UUID8,
+	FSID_UUID16,
+	FSID_UUID16_INUM,
+};
+
 enum {
 	MCL_FQDN = 0,
 	MCL_SUBNETWORK,
diff --git a/support/include/mounts.h b/support/include/mounts.h
new file mode 100644
index 0000000..4406150
--- /dev/null
+++ b/support/include/mounts.h
@@ -0,0 +1,61 @@
+/*
+ * support/include/mounts.h
+ *
+ * Routines used to mount and umount different
+ * type of filesystems.
+ *
+ */
+
+#ifndef MOUNTS_H
+#define MOUNTS_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/mount.h>
+//#include "misc.h"
+
+struct mountargs {
+	const char *spec;
+	const char *node;
+	const char *type;
+	int flags;
+	void *data;
+};
+extern int v4root_mount(struct mountargs *args);
+extern int v4root_umount(struct mountargs *args);
+
+#ifndef MNT_DETACH
+#define MNT_DETACH  0x00000002  /* Just detach from the tree */
+#endif
+
+inline int
+bind_mount(char *dev, char *dir)
+{
+	struct mountargs args = {dev, dir, "bind", MS_BIND, NULL};
+
+	xlog(D_CALL, "mount --bind %s %s", dev, dir);
+
+	return v4root_mount(&args);
+}
+inline int
+tmpfs_mount(char *dir)
+{
+	struct mountargs args = {"tmpfs", dir, "tmpfs", 0, NULL};
+
+	xlog(D_CALL, "mount -t tmpfs tmpfs %s", dir);
+
+	return v4root_mount(&args);
+}
+inline int
+lazy_umount(char *dir)
+{
+	struct mountargs args = {NULL, dir, NULL, MNT_DETACH, NULL};
+
+	xlog(D_CALL, "umount -l %s", dir);
+
+	return v4root_umount(&args);
+}
+
+#endif /* MOUNTS_H */
diff --git a/support/include/nfslib.h b/support/include/nfslib.h
index 422b012..7315416 100644
--- a/support/include/nfslib.h
+++ b/support/include/nfslib.h
@@ -155,4 +155,7 @@ void closeall(int min);
 int			svctcp_socket (u_long __number, int __reuse);
 int			svcudp_socket (u_long __number, int __reuse);
 
+extern int execute_cmd(const char *cmd,  char *const myargv[]);
+extern void syserror(char *message);
+
 #endif /* NFSLIB_H */
diff --git a/support/nfs/Makefile.am b/support/nfs/Makefile.am
index 87f3949..925a0a2 100644
--- a/support/nfs/Makefile.am
+++ b/support/nfs/Makefile.am
@@ -4,7 +4,7 @@ noinst_LIBRARIES = libnfs.a
 libnfs_a_SOURCES = exports.c rmtab.c xio.c rpcmisc.c rpcdispatch.c \
 		   xlog.c xcommon.c wildmat.c nfssvc.c nfsclient.c \
 		   nfsexport.c getfh.c nfsctl.c \
-		   svc_socket.c cacheio.c closeall.c nfs_mntent.c
+		   svc_socket.c cacheio.c closeall.c nfs_mntent.c execute.c
 
 MAINTAINERCLEANFILES = Makefile.in
 
diff --git a/support/nfs/execute.c b/support/nfs/execute.c
new file mode 100644
index 0000000..55d2d31
--- /dev/null
+++ b/support/nfs/execute.c
@@ -0,0 +1,92 @@
+#include <sys/wait.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include "xlog.h"
+
+/*
+ * Log system errors or error messages.
+ */
+void 
+syserror(char *message)
+{
+	if (errno)
+		xlog(L_ERROR, "%s: errno %d (%s)", message, errno, strerror(errno));
+	else
+		xlog(L_ERROR, "Error: %s", message);
+}
+/*
+ * Log the the given argv[] if debugging is on.
+ */
+static void
+show_argv(char *msg, char *const argv[])
+{
+	char buf[BUFSIZ], *ptr;
+	int i, cc=0;
+
+	if (!xlog_enabled(D_CALL))
+		return;
+
+	ptr = buf;
+	if (msg) {
+		sprintf(ptr+cc, "%s ", msg);
+		cc = strlen(buf);
+	}
+	for (i=0; argv[i] != NULL && cc < BUFSIZ; i++) {
+		if ((cc + strlen(argv[i])) >= BUFSIZ)
+			break;
+		sprintf(ptr+cc, "%s ", argv[i]);
+		cc = strlen(buf);
+	}
+	xlog(D_CALL, "%s", buf);
+}
+/*
+ *  Execute the give command using the 
+ *  given argv as the arguments.
+ */
+int
+execute_cmd(const char *cmd,  char *const myargv[])
+{
+	int pfd[2], pid, status, cc;
+	char buf[BUFSIZ], *ch;
+
+	show_argv("executing:", myargv);
+
+	if (pipe(pfd) < 0) {
+		syserror("pipe() failed");
+		return errno;
+	}
+	switch((pid = fork())) {
+	case -1: 
+		syserror("fork() failed");
+		break;
+	case 0:  /* child */
+		close(pfd[0]);
+		if (dup2(pfd[1], fileno(stdout)) < 0)
+			perror("dup2(stdout)");
+		if (dup2(pfd[1], fileno(stderr)) < 0)
+			perror("dup2(stderr)");
+		close(pfd[1]);
+		execvp(cmd, myargv);
+		perror("execv");
+		_exit(255);
+		break;
+	default: /* parent */
+		status = 0;
+		close(pfd[1]);
+		waitpid(pid, &status, 0);
+		if (WIFEXITED(status) /* && (kiderr = WEXITSTATUS(status))*/ ) {
+			cc = read(pfd[0], buf, BUFSIZ);
+			if (cc > 0) { 
+				if ((ch = strrchr(buf, '\n')) != NULL)
+					*ch = '\0';
+				errno = 0;
+				syserror(buf);
+			}
+		}
+		break;
+	}
+	return errno;
+}
diff --git a/utils/mountd/auth.c b/utils/mountd/auth.c
index 2bb708f..df309c9 100644
--- a/utils/mountd/auth.c
+++ b/utils/mountd/auth.c
@@ -21,6 +21,7 @@
 #include "mountd.h"
 #include "xmalloc.h"
 
+
 enum auth_error
 {
   bad_path,
@@ -84,6 +85,7 @@ auth_reload()
 	static int		last_fd;
 	static unsigned int	counter;
 	int			fd;
+	extern void v4root_destroy(), v4root_create();
 
 	if ((fd = open(_PATH_ETAB, O_RDONLY)) < 0) {
 		xlog(L_FATAL, "couldn't open %s", _PATH_ETAB);
@@ -98,9 +100,12 @@ auth_reload()
 		last_inode = stb.st_ino;
 	}
 
+
+	v4root_destroy();
 	export_freeall();
 	memset(&my_client, 0, sizeof(my_client));
 	xtab_export_read();
+	v4root_create();
 	check_useipaddr();
 	++counter;
 
diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c
index fd317cd..f367d2b 100644
--- a/utils/mountd/cache.c
+++ b/utils/mountd/cache.c
@@ -36,18 +36,6 @@
 #include "blkid/blkid.h"
 #endif
 
-
-enum nfsd_fsid {
-	FSID_DEV = 0,
-	FSID_NUM,
-	FSID_MAJOR_MINOR,
-	FSID_ENCODE_DEV,
-	FSID_UUID4_INUM,
-	FSID_UUID8,
-	FSID_UUID16,
-	FSID_UUID16_INUM,
-};
-
 /*
  * Support routines for text-based upcalls.
  * Fields are separated by spaces.
@@ -58,7 +46,6 @@ enum nfsd_fsid {
  */
 int cache_export_ent(char *domain, struct exportent *exp, char *p);
 
-
 char *lbuf  = NULL;
 int lbuflen = 0;
 extern int use_ipaddr;
@@ -81,6 +68,8 @@ void auth_unix_ip(FILE *f)
 	if (readline(fileno(f), &lbuf, &lbuflen) != 1)
 		return;
 
+	xlog(D_CALL, "auth_unix_ip: '%s'", lbuf);
+
 	cp = lbuf;
 
 	if (qword_get(&cp, class, 20) <= 0 ||
@@ -131,6 +120,8 @@ void auth_unix_gid(FILE *f)
 	if (readline(fileno(f), &lbuf, &lbuflen) != 1)
 		return;
 
+	xlog(D_CALL, "auth_unix_gid: '%s'", lbuf);
+
 	cp = lbuf;
 	if (qword_get_int(&cp, &uid) != 0)
 		return;
@@ -278,12 +269,14 @@ void nfsd_fh(FILE *f)
 	int dev_missing = 0;
 	int uuidlen = 0;
 	char *fhuuid = NULL;
+	extern struct exportent *v4root_export(void);
 
 	if (readline(fileno(f), &lbuf, &lbuflen) != 1)
 		return;
 
-	cp = lbuf;
+	xlog(D_CALL, "nfsd_fh: '%s'", lbuf);
 
+	cp = lbuf;
 	dom = malloc(strlen(cp));
 	if (dom == NULL)
 		return;
@@ -365,6 +358,10 @@ void nfsd_fh(FILE *f)
 
 	auth_reload();
 
+	if ((found = v4root_chkroot(fsidtype, fsidnum, fhuuid))) {
+		found_path = strdup(found->e_path);
+		goto found;
+	}
 	/* Now determine export point for this fsid/domain */
 	for (i=0 ; i < MCL_MAXTYPES; i++) {
 		nfs_export *next_exp;
@@ -409,6 +406,7 @@ void nfsd_fh(FILE *f)
 			if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) {
 				continue;
 			}
+
 			switch(fsidtype){
 			case FSID_DEV:
 			case FSID_MAJOR_MINOR:
@@ -438,8 +436,7 @@ void nfsd_fh(FILE *f)
 				if (exp->m_export.e_uuid)
 					get_uuid(NULL, exp->m_export.e_uuid,
 						 uuidlen, u);
-				else if (get_uuid(path, NULL,
-						  uuidlen, u) == 0)
+				else if (get_uuid(path, NULL, uuidlen, u) == 0)
 					continue;
 
 				if (memcmp(u, fhuuid, uuidlen) != 0)
@@ -471,6 +468,7 @@ void nfsd_fh(FILE *f)
 			}
 		}
 	}
+
 	if (found && 
 	    found->e_mountpoint &&
 	    !is_mountpoint(found->e_mountpoint[0]?
@@ -490,11 +488,12 @@ void nfsd_fh(FILE *f)
 		 */
 		goto out;
 	}
-
+found:
 	if (found)
 		if (cache_export_ent(dom, found, found_path) < 0)
 			found = 0;
 
+	xlog(D_CALL, "nfsd_fh: found %s", found ? found->e_path : NULL);
 	qword_print(f, dom);
 	qword_printint(f, fsidtype);
 	qword_printhex(f, fsid, fsidlen);
@@ -597,7 +596,7 @@ void nfsd_export(FILE *f)
 
 	char *cp;
 	int i;
-	char *dom, *path;
+	char *dom, *path, *mpath;
 	nfs_export *exp, *found = NULL;
 	int found_type = 0;
 	struct in_addr addr;
@@ -607,6 +606,8 @@ void nfsd_export(FILE *f)
 	if (readline(fileno(f), &lbuf, &lbuflen) != 1)
 		return;
 
+	xlog(D_CALL, "nfsd_export: '%s'", lbuf);
+
 	cp = lbuf;
 	dom = malloc(strlen(cp));
 	path = malloc(strlen(cp));
@@ -619,6 +620,9 @@ void nfsd_export(FILE *f)
 	if (qword_get(&cp, path, strlen(lbuf)) <= 0)
 		goto out;
 
+	if ((mpath = v4root_maproot(path)) == NULL)
+		mpath = path;
+
 	auth_reload();
 
 	/* now find flags for this export point in this domain */
@@ -629,14 +633,14 @@ void nfsd_export(FILE *f)
 			if (exp->m_export.e_flags & NFSEXP_CROSSMOUNT) {
 				/* if path is a mountpoint below e_path, then OK */
 				int l = strlen(exp->m_export.e_path);
-				if (strcmp(path, exp->m_export.e_path) == 0 ||
-				    (strncmp(path, exp->m_export.e_path, l) == 0 &&
+				if (strcmp(mpath, exp->m_export.e_path) == 0 ||
+				    (strncmp(mpath, exp->m_export.e_path, l) == 0 &&
 				     path[l] == '/' &&
 				     is_mountpoint(path)))
 					/* ok */;
 				else
 					continue;
-			} else if (strcmp(path, exp->m_export.e_path) != 0)
+			} else if (strcmp(mpath, exp->m_export.e_path) != 0)
 				continue;
 			if (use_ipaddr) {
 				if (he == NULL) {
diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
index 4a50588..a7f3592 100644
--- a/utils/mountd/mountd.c
+++ b/utils/mountd/mountd.c
@@ -169,6 +169,7 @@ killer (int sig)
 		kill(0, SIGTERM);
 		wait_for_workers();
 	}
+	v4root_umountall();
 	xlog (L_FATAL, "Caught signal %d, un-registering and exiting.", sig);
 }
 
-- 
1.5.3.4


More information about the NFSv4 mailing list