diff mbox series

[RFC,6/6] export: Garbage collect orphaned subvolumes upon start

Message ID 20220217131531.2890-7-richard@nod.at (mailing list archive)
State New, archived
Headers show
Series nfs-utils: Improving NFS re-exports | expand

Commit Message

Richard Weinberger Feb. 17, 2022, 1:15 p.m. UTC
Make sure the database contains no orphaned subvolumes.
We have to be careful.

Signed-off-by: Richard Weinberger <richard@nod.at>
---
 support/export/cache.c  | 97 +++++++++++++++++++++++++++++++++++++++++
 support/export/export.h |  3 ++
 utils/exportd/exportd.c | 17 +++++++-
 utils/mountd/mountd.c   |  1 +
 utils/mountd/svc_run.c  | 18 ++++++++
 5 files changed, 135 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/support/export/cache.c b/support/export/cache.c
index b5763b1d..94a0d79a 100644
--- a/support/export/cache.c
+++ b/support/export/cache.c
@@ -1181,6 +1181,103 @@  lookup_export(char *dom, char *path, struct addrinfo *ai)
 	return found;
 }
 
+static char *get_export_path(char *path)
+{
+	int i;
+	nfs_export *exp;
+	nfs_export *found = NULL;
+
+	for (i = 0; i < MCL_MAXTYPES; i++) {
+		for (exp = exportlist[i].p_head; exp; exp = exp->m_next) {
+			if (!path_matches(exp, path))
+				continue;
+
+			if (!found) {
+				found = exp;
+				continue;
+			}
+
+			/* Always prefer non-V4ROOT exports */
+			if (exp->m_export.e_flags & NFSEXP_V4ROOT)
+				continue;
+			if (found->m_export.e_flags & NFSEXP_V4ROOT) {
+				found = exp;
+				continue;
+			}
+
+			/* If one is a CROSSMOUNT, then prefer the longest path */
+			if (((found->m_export.e_flags & NFSEXP_CROSSMOUNT) ||
+			     (exp->m_export.e_flags & NFSEXP_CROSSMOUNT)) &&
+			    strlen(found->m_export.e_path) !=
+			    strlen(exp->m_export.e_path)) {
+
+				if (strlen(exp->m_export.e_path) >
+				    strlen(found->m_export.e_path)) {
+					found = exp;
+				}
+				continue;
+			}
+		}
+	}
+
+	if (found)
+		return found->m_export.e_path;
+	else
+		return NULL;
+}
+
+int export_subvol_orphaned(char *path)
+{
+	struct statfs st, stp;
+	char *path_parent;
+	int ret;
+
+	path_parent = get_export_path(path);
+	if (!path_parent)
+		/*
+		 * Path has no parent in export list.
+		 * Must be orphaned.
+		 */
+		return 1;
+
+	ret = statfs(path_parent, &stp);
+	if (ret == -1)
+		/*
+		 * Parent path is not statfs'able. Maybe not yet mounted?
+		 * Can't be sure, don't treat path as orphaned.
+		 */
+		return 0;
+
+	if (strcmp(path_parent, path) == 0)
+		/*
+		 * This is not a subvolume, it is listed in exports.
+		 * No need to keep tack of it.
+		 */
+		return 1;
+
+	if (stp.f_type != 0x6969)
+		/*
+		 * Parent is not a NFS mount. Maybe not yet mounted?
+		 * Can't be sure either.
+		 */
+		return 0;
+
+	ret = statfs(path, &st);
+	if (ret == -1) {
+		if (errno == ENOENT)
+			/*
+			 * Parent is a NFS mount but path is gone.
+			 * Must be orphaned.
+			 */
+			return 1;
+	}
+
+	/*
+	 * For all remaining cases we can't be sure either.
+	 */
+	return 0;
+}
+
 #ifdef HAVE_JUNCTION_SUPPORT
 
 #include <libxml/parser.h>
diff --git a/support/export/export.h b/support/export/export.h
index 8d5a0d30..45dd3da4 100644
--- a/support/export/export.h
+++ b/support/export/export.h
@@ -38,4 +38,7 @@  static inline bool is_ipaddr_client(char *dom)
 {
 	return dom[0] == '$';
 }
+
+int export_subvol_orphaned(char *path);
+
 #endif /* EXPORT__H */
diff --git a/utils/exportd/exportd.c b/utils/exportd/exportd.c
index 4ddfed35..6dc51a32 100644
--- a/utils/exportd/exportd.c
+++ b/utils/exportd/exportd.c
@@ -208,6 +208,12 @@  read_exportd_conf(char *progname, char **argv)
 		default_ttl = ttl;
 }
 
+static void subvol_cb(char *path)
+{
+	if (export_subvol_orphaned(path))
+		reexpdb_drop_subvolume_unlocked(path);
+}
+
 int
 main(int argc, char **argv)
 {
@@ -297,7 +303,16 @@  main(int argc, char **argv)
 	/* Open files now to avoid sharing descriptors among forked processes */
 	cache_open();
 	v4clients_init();
-	reexpdb_init();
+	if (reexpdb_init() != 0) {
+		xlog(L_ERROR, "%s: Failed to init reexport database", __func__);
+		exit(1);
+	}
+
+	/*
+	 * Load exports into memory and garbage collect orphaned subvolumes.
+	 */
+	auth_reload();
+	reexpdb_uncover_subvolumes(subvol_cb);
 
 	/* Process incoming upcalls */
 	cache_process_loop();
diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
index bcf749fa..8555d746 100644
--- a/utils/mountd/mountd.c
+++ b/utils/mountd/mountd.c
@@ -32,6 +32,7 @@ 
 #include "nfsd_path.h"
 #include "nfslib.h"
 #include "export.h"
+#include "reexport.h"
 
 extern void my_svc_run(void);
 
diff --git a/utils/mountd/svc_run.c b/utils/mountd/svc_run.c
index 167b9757..9a891ff0 100644
--- a/utils/mountd/svc_run.c
+++ b/utils/mountd/svc_run.c
@@ -57,6 +57,7 @@ 
 #include <rpc/rpc_com.h>
 #endif
 #include "export.h"
+#include "reexport.h"
 
 void my_svc_run(void);
 
@@ -87,6 +88,12 @@  my_svc_getreqset (fd_set *readfds)
 
 #endif
 
+static void subvol_cb(char *path)
+{
+	if (export_subvol_orphaned(path))
+		reexpdb_drop_subvolume_unlocked(path);
+}
+
 /*
  * The heart of the server.  A crib from libc for the most part...
  */
@@ -96,6 +103,17 @@  my_svc_run(void)
 	fd_set	readfds;
 	int	selret;
 
+	if (reexpdb_init() != 0) {
+		xlog(L_ERROR, "%s: Failed to init reexport database", __func__);
+		return;
+	}
+
+	/*
+	 * Load exports into memory and garbage collect orphaned subvolumes.
+	 */
+	auth_reload();
+	reexpdb_uncover_subvolumes(subvol_cb);
+
 	for (;;) {
 
 		readfds = svc_fdset;