Blame SOURCES/nfs-utils-1.3.0-nfsdcltrack-v2schema.patch

64c563
diff --git a/Makefile.am b/Makefile.am
64c563
index c9e9f87..6f3f3d6 100644
64c563
--- a/Makefile.am
64c563
+++ b/Makefile.am
64c563
@@ -9,34 +9,6 @@ MAINTAINERCLEANFILES = Makefile.in
64c563
 EXTRA_DIST = \
64c563
 	autogen.sh \
64c563
 	\
64c563
-	debian/changelog \
64c563
-	debian/control \
64c563
-	debian/copyright \
64c563
-	debian/etc.exports \
64c563
-	debian/idmapd.conf \
64c563
-	debian/nfs-common.conffiles \
64c563
-	debian/nfs-common.default \
64c563
-	debian/nfs-common.dirs \
64c563
-	debian/nfs-common.files \
64c563
-	debian/nfs-common.init \
64c563
-	debian/nfs-common.install \
64c563
-	debian/nfs-common.postinst \
64c563
-	debian/nfs-common.postrm \
64c563
-	debian/nfs-common.prerm \
64c563
-	debian/nfs-kernel-server.NEWS \
64c563
-	debian/nfs-kernel-server.conffiles \
64c563
-	debian/nfs-kernel-server.default \
64c563
-	debian/nfs-kernel-server.dirs \
64c563
-	debian/nfs-kernel-server.init \
64c563
-	debian/nfs-kernel-server.postinst \
64c563
-	debian/nfs-kernel-server.postrm \
64c563
-	debian/nfs-kernel-server.prerm \
64c563
-	debian/nhfsstone.dirs \
64c563
-	debian/nhfsstone.files \
64c563
-	debian/nhfsstone.postinst \
64c563
-	debian/nhfsstone.prerm \
64c563
-	debian/rules \
64c563
-	\
64c563
 	aclocal/bsdsignals.m4 \
64c563
 	aclocal/nfs-utils.m4 \
64c563
 	aclocal/kerberos5.m4 \
64c563
diff --git a/support/include/Makefile.am b/support/include/Makefile.am
64c563
index 4b33ee9..5c80c8b 100644
64c563
--- a/support/include/Makefile.am
64c563
+++ b/support/include/Makefile.am
64c563
@@ -3,6 +3,7 @@
64c563
 SUBDIRS = nfs rpcsvc sys
64c563
 
64c563
 noinst_HEADERS = \
64c563
+	cld.h \
64c563
 	exportfs.h \
64c563
 	ha-callout.h \
64c563
 	misc.h \
64c563
@@ -10,9 +11,13 @@ noinst_HEADERS = \
64c563
 	nfs_paths.h \
64c563
 	nfslib.h \
64c563
 	nfsrpc.h \
64c563
+	nls.h \
64c563
 	nsm.h \
64c563
+	pseudoflavors.h \
64c563
 	rpcmisc.h \
64c563
+	sockaddr.h \
64c563
 	tcpwrapper.h \
64c563
+	v4root.h \
64c563
 	xio.h \
64c563
 	xlog.h \
64c563
 	xmalloc.h \
64c563
diff --git a/tests/Makefile.am b/tests/Makefile.am
64c563
index faa8197..1f96264 100644
64c563
--- a/tests/Makefile.am
64c563
+++ b/tests/Makefile.am
64c563
@@ -11,3 +11,4 @@ SUBDIRS = nsm_client
64c563
 MAINTAINERCLEANFILES = Makefile.in
64c563
 
64c563
 TESTS = t0001-statd-basic-mon-unmon.sh
64c563
+EXTRA_DIST = test-lib.sh $(TESTS)
64c563
diff --git a/tests/nsm_client/Makefile.am b/tests/nsm_client/Makefile.am
64c563
index 4c15346..a8fc131 100644
64c563
--- a/tests/nsm_client/Makefile.am
64c563
+++ b/tests/nsm_client/Makefile.am
64c563
@@ -7,6 +7,7 @@ GENFILES_H	= nlm_sm_inter.h
64c563
 
64c563
 GENFILES	= $(GENFILES_CLNT) $(GENFILES_SVC) $(GENFILES_XDR) $(GENFILES_H)
64c563
 
64c563
+EXTRA_DIST = nlm_sm_inter.x
64c563
 
64c563
 check_PROGRAMS	= nsm_client
64c563
 nsm_client_SOURCES = $(GENFILES) nsm_client.c
64c563
diff --git a/utils/gssd/Makefile.am b/utils/gssd/Makefile.am
64c563
index af59791..a4e9c56 100644
64c563
--- a/utils/gssd/Makefile.am
64c563
+++ b/utils/gssd/Makefile.am
64c563
@@ -8,7 +8,6 @@ sbin_PREFIXED	= gssd svcgssd
64c563
 sbin_PROGRAMS	= $(sbin_PREFIXED)
64c563
 
64c563
 EXTRA_DIST = \
64c563
-	gss_destroy_creds \
64c563
 	$(man8_MANS)
64c563
 
64c563
 COMMON_SRCS = \
64c563
diff --git a/utils/idmapd/Makefile.am b/utils/idmapd/Makefile.am
64c563
index 58b33ec..c2f8ba1 100644
64c563
--- a/utils/idmapd/Makefile.am
64c563
+++ b/utils/idmapd/Makefile.am
64c563
@@ -7,8 +7,7 @@ KPREFIX		= @kprefix@
64c563
 sbin_PROGRAMS	= idmapd
64c563
 
64c563
 EXTRA_DIST = \
64c563
-	$(man8_MANS) \
64c563
-	idmapd.conf
64c563
+	$(man8_MANS)
64c563
 
64c563
 idmapd_SOURCES = \
64c563
 	idmapd.c \
64c563
diff --git a/utils/mount/Makefile.am b/utils/mount/Makefile.am
64c563
index 5810936..e24f3bd 100644
64c563
--- a/utils/mount/Makefile.am
64c563
+++ b/utils/mount/Makefile.am
64c563
@@ -8,19 +8,21 @@ man8_MANS	= mount.nfs.man umount.nfs.man
64c563
 man5_MANS	= nfs.man
64c563
 
64c563
 sbin_PROGRAMS	= mount.nfs
64c563
-EXTRA_DIST = nfsmount.x $(man8_MANS) $(man5_MANS)
64c563
+EXTRA_DIST = nfsmount.conf $(man8_MANS) $(man5_MANS)
64c563
 mount_common = error.c network.c token.c \
64c563
 		    parse_opt.c parse_dev.c \
64c563
 		    nfsmount.c nfs4mount.c stropts.c\
64c563
 		    mount_constants.h error.h network.h token.h \
64c563
 		    parse_opt.h parse_dev.h \
64c563
-		    nfs4_mount.h nfs_mount4.h stropts.h version.h \
64c563
-		    mount_config.h utils.c utils.h
64c563
+		    nfs4_mount.h stropts.h version.h \
64c563
+		    mount_config.h utils.c utils.h \
64c563
+		    nfs_mount.h
64c563
 
64c563
 if MOUNT_CONFIG
64c563
 mount_common += configfile.c
64c563
 man5_MANS += nfsmount.conf.man
64c563
-EXTRA_DIST += nfsmount.conf
64c563
+else
64c563
+EXTRA_DIST += nfsmount.conf.man
64c563
 endif
64c563
 
64c563
 mount_nfs_LDADD = ../../support/nfs/libnfs.a \
64c563
diff --git a/utils/mountd/Makefile.am b/utils/mountd/Makefile.am
64c563
index 7db968b..9e1ab5c 100644
64c563
--- a/utils/mountd/Makefile.am
64c563
+++ b/utils/mountd/Makefile.am
64c563
@@ -7,6 +7,7 @@ RPCPREFIX	= rpc.
64c563
 KPREFIX		= @kprefix@
64c563
 sbin_PROGRAMS	= mountd
64c563
 
64c563
+noinst_HEADERS = fsloc.h
64c563
 mountd_SOURCES = mountd.c mount_dispatch.c auth.c rmtab.c cache.c \
64c563
 		 svc_run.c fsloc.c v4root.c mountd.h
64c563
 mountd_LDADD = ../../support/export/libexport.a \
64c563
diff --git a/utils/nfsd/Makefile.am b/utils/nfsd/Makefile.am
64c563
index 1536065..39a6e6f 100644
64c563
--- a/utils/nfsd/Makefile.am
64c563
+++ b/utils/nfsd/Makefile.am
64c563
@@ -7,6 +7,7 @@ RPCPREFIX	= rpc.
64c563
 KPREFIX		= @kprefix@
64c563
 sbin_PROGRAMS	= nfsd
64c563
 
64c563
+noinst_HEADERS = nfssvc.h
64c563
 nfsd_SOURCES = nfsd.c nfssvc.c
64c563
 nfsd_LDADD = ../../support/nfs/libnfs.a $(LIBTIRPC)
64c563
 
64c563
diff --git a/utils/nfsdcltrack/Makefile.am b/utils/nfsdcltrack/Makefile.am
64c563
index a860ffb..d603f92 100644
64c563
--- a/utils/nfsdcltrack/Makefile.am
64c563
+++ b/utils/nfsdcltrack/Makefile.am
64c563
@@ -6,6 +6,8 @@ EXTRA_DIST	= $(man8_MANS)
64c563
 AM_CFLAGS	+= -D_LARGEFILE64_SOURCE
64c563
 sbin_PROGRAMS	= nfsdcltrack
64c563
 
64c563
+noinst_HEADERS	= sqlite.h
64c563
+
64c563
 nfsdcltrack_SOURCES = nfsdcltrack.c sqlite.c
64c563
 nfsdcltrack_LDADD = ../../support/nfs/libnfs.a $(LIBSQLITE) $(LIBCAP)
64c563
 
64c563
diff --git a/utils/nfsdcltrack/nfsdcltrack.c b/utils/nfsdcltrack/nfsdcltrack.c
64c563
index 4334340..fcdda7f 100644
64c563
--- a/utils/nfsdcltrack/nfsdcltrack.c
64c563
+++ b/utils/nfsdcltrack/nfsdcltrack.c
64c563
@@ -37,6 +37,7 @@
64c563
 #include <libgen.h>
64c563
 #include <sys/inotify.h>
64c563
 #include <dirent.h>
64c563
+#include <limits.h>
64c563
 #ifdef HAVE_SYS_CAPABILITY_H
64c563
 #include <sys/prctl.h>
64c563
 #include <sys/capability.h>
64c563
@@ -49,6 +50,8 @@
64c563
 #define CLD_DEFAULT_STORAGEDIR NFS_STATEDIR "/nfsdcltrack"
64c563
 #endif
64c563
 
64c563
+#define NFSD_END_GRACE_FILE "/proc/fs/nfsd/v4_end_grace"
64c563
+
64c563
 /* defined by RFC 3530 */
64c563
 #define NFS4_OPAQUE_LIMIT	1024
64c563
 
64c563
@@ -210,6 +213,64 @@ cltrack_set_caps(void)
64c563
 	return ret;
64c563
 }
64c563
 
64c563
+/* Inform the kernel that it's OK to lift nfsd's grace period */
64c563
+static void
64c563
+cltrack_lift_grace_period(void)
64c563
+{
64c563
+	int fd;
64c563
+
64c563
+	fd = open(NFSD_END_GRACE_FILE, O_WRONLY);
64c563
+	if (fd < 0) {
64c563
+		/* Don't warn if file isn't present */
64c563
+		if (errno != ENOENT)
64c563
+			xlog(L_WARNING, "Unable to open %s: %m",
64c563
+				NFSD_END_GRACE_FILE);
64c563
+		return;
64c563
+	}
64c563
+
64c563
+	if (write(fd, "Y", 1) < 0)
64c563
+		xlog(L_WARNING, "Unable to write to %s: %m",
64c563
+				NFSD_END_GRACE_FILE);
64c563
+
64c563
+	close(fd);
64c563
+	return;
64c563
+}
64c563
+
64c563
+/*
64c563
+ * Fetch the contents of the NFSDCLTRACK_GRACE_START env var. If it's not set
64c563
+ * or there's an error converting it to time_t, then return LONG_MAX.
64c563
+ */
64c563
+static time_t
64c563
+cltrack_get_grace_start(void)
64c563
+{
64c563
+	time_t grace_start;
64c563
+	char *end;
64c563
+	char *grace_start_str = getenv("NFSDCLTRACK_GRACE_START");
64c563
+
64c563
+	if (!grace_start_str)
64c563
+		return LONG_MAX;
64c563
+
64c563
+	errno = 0;
64c563
+	grace_start = strtol(grace_start_str, &end, 0);
64c563
+	/* Problem converting or value is too large? */
64c563
+	if (errno)
64c563
+		return LONG_MAX;
64c563
+
64c563
+	return grace_start;
64c563
+}
64c563
+
64c563
+static bool
64c563
+cltrack_reclaims_complete(void)
64c563
+{
64c563
+	time_t grace_start = cltrack_get_grace_start();
64c563
+
64c563
+	/* Don't query DB if we didn't get a valid boot time */
64c563
+	if (grace_start == LONG_MAX)
64c563
+		return false;
64c563
+
64c563
+	return !sqlite_query_reclaiming(grace_start);
64c563
+}
64c563
+
64c563
 static int
64c563
 cltrack_init(const char __attribute__((unused)) *unused)
64c563
 {
64c563
@@ -241,7 +302,7 @@ cltrack_init(const char __attribute__((unused)) *unused)
64c563
 	}
64c563
 
64c563
 	/* set up storage db */
64c563
-	ret = sqlite_maindb_init(storagedir);
64c563
+	ret = sqlite_prepare_dbh(storagedir);
64c563
 	if (ret) {
64c563
 		xlog(L_ERROR, "Failed to init database: %d", ret);
64c563
 		/*
64c563
@@ -250,15 +311,36 @@ cltrack_init(const char __attribute__((unused)) *unused)
64c563
 		 * stop upcalling until the problem is resolved.
64c563
 		 */
64c563
 		ret = -EACCES;
64c563
+	} else {
64c563
+		if (cltrack_reclaims_complete())
64c563
+			cltrack_lift_grace_period();
64c563
 	}
64c563
+
64c563
 	return ret;
64c563
 }
64c563
 
64c563
+/*
64c563
+ * Fetch the contents of the NFSDCLTRACK_CLIENT_HAS_SESSION env var. If
64c563
+ * it's set and the first character is 'Y' then return true. Otherwise
64c563
+ * return false.
64c563
+ */
64c563
+static bool
64c563
+cltrack_client_has_session(void)
64c563
+{
64c563
+	char *has_session = getenv("NFSDCLTRACK_CLIENT_HAS_SESSION");
64c563
+
64c563
+	if (has_session && *has_session == 'Y')
64c563
+		return true;
64c563
+
64c563
+	return false;
64c563
+}
64c563
+
64c563
 static int
64c563
 cltrack_create(const char *id)
64c563
 {
64c563
 	int ret;
64c563
 	ssize_t len;
64c563
+	bool has_session;
64c563
 
64c563
 	xlog(D_GENERAL, "%s: create client record.", __func__);
64c563
 
64c563
@@ -270,7 +352,12 @@ cltrack_create(const char *id)
64c563
 	if (len < 0)
64c563
 		return (int)len;
64c563
 
64c563
-	ret = sqlite_insert_client(blob, len);
64c563
+	has_session = cltrack_client_has_session();
64c563
+
64c563
+	ret = sqlite_insert_client(blob, len, has_session, false);
64c563
+
64c563
+	if (!ret && has_session && cltrack_reclaims_complete())
64c563
+		cltrack_lift_grace_period();
64c563
 
64c563
 	return ret ? -EREMOTEIO : ret;
64c563
 }
64c563
@@ -297,7 +384,8 @@ cltrack_remove(const char *id)
64c563
 }
64c563
 
64c563
 static int
64c563
-cltrack_check_legacy(const unsigned char *blob, const ssize_t len)
64c563
+cltrack_check_legacy(const unsigned char *blob, const ssize_t len,
64c563
+			bool has_session)
64c563
 {
64c563
 	int ret;
64c563
 	struct stat st;
64c563
@@ -323,7 +411,7 @@ cltrack_check_legacy(const unsigned char *blob, const ssize_t len)
64c563
 	}
64c563
 
64c563
 	/* Dir exists, try to insert record into db */
64c563
-	ret = sqlite_insert_client(blob, len);
64c563
+	ret = sqlite_insert_client(blob, len, has_session, has_session);
64c563
 	if (ret) {
64c563
 		xlog(D_GENERAL, "Failed to insert client: %d", ret);
64c563
 		return -EREMOTEIO;
64c563
@@ -343,6 +431,7 @@ cltrack_check(const char *id)
64c563
 {
64c563
 	int ret;
64c563
 	ssize_t len;
64c563
+	bool has_session;
64c563
 
64c563
 	xlog(D_GENERAL, "%s: check client record", __func__);
64c563
 
64c563
@@ -354,9 +443,11 @@ cltrack_check(const char *id)
64c563
 	if (len < 0)
64c563
 		return (int)len;
64c563
 
64c563
-	ret = sqlite_check_client(blob, len);
64c563
+	has_session = cltrack_client_has_session();
64c563
+
64c563
+	ret = sqlite_check_client(blob, len, has_session);
64c563
 	if (ret)
64c563
-		ret = cltrack_check_legacy(blob, len);
64c563
+		ret = cltrack_check_legacy(blob, len, has_session);
64c563
 
64c563
 	return ret ? -EPERM : ret;
64c563
 }
64c563
diff --git a/utils/nfsdcltrack/sqlite.c b/utils/nfsdcltrack/sqlite.c
64c563
index bac6789..eb1711a 100644
64c563
--- a/utils/nfsdcltrack/sqlite.c
64c563
+++ b/utils/nfsdcltrack/sqlite.c
64c563
@@ -21,17 +21,15 @@
64c563
  * Explanation:
64c563
  *
64c563
  * This file contains the code to manage the sqlite backend database for the
64c563
- * clstated upcall daemon.
64c563
+ * nfsdcltrack usermodehelper upcall program.
64c563
  *
64c563
  * The main database is called main.sqlite and contains the following tables:
64c563
  *
64c563
  * parameters: simple key/value pairs for storing database info
64c563
  *
64c563
- * clients: one column containing a BLOB with the as sent by the client
64c563
- * 	    and a timestamp (in epoch seconds) of when the record was
64c563
- * 	    established
64c563
- *
64c563
- * FIXME: should we also record the fsid being accessed?
64c563
+ * clients: an "id" column containing a BLOB with the long-form clientid as
64c563
+ * 	    sent by the client, a "time" column containing a timestamp (in
64c563
+ * 	    epoch seconds) of when the record was last updated.
64c563
  */
64c563
 
64c563
 #ifdef HAVE_CONFIG_H
64c563
@@ -52,10 +50,10 @@
64c563
 
64c563
 #include "xlog.h"
64c563
 
64c563
-#define CLD_SQLITE_SCHEMA_VERSION 1
64c563
+#define CLTRACK_SQLITE_LATEST_SCHEMA_VERSION 1
64c563
 
64c563
 /* in milliseconds */
64c563
-#define CLD_SQLITE_BUSY_TIMEOUT 10000
64c563
+#define CLTRACK_SQLITE_BUSY_TIMEOUT 10000
64c563
 
64c563
 /* private data structures */
64c563
 
64c563
@@ -90,135 +88,192 @@ mkdir_if_not_exist(const char *dirname)
64c563
 	return ret;
64c563
 }
64c563
 
64c563
-/* Open the database and set up the database handle for it */
64c563
-int
64c563
-sqlite_prepare_dbh(const char *topdir)
64c563
+static int
64c563
+sqlite_query_schema_version(void)
64c563
 {
64c563
 	int ret;
64c563
+	sqlite3_stmt *stmt = NULL;
64c563
 
64c563
-	/* Do nothing if the database handle is already set up */
64c563
-	if (dbh)
64c563
-		return 0;
64c563
-
64c563
-	ret = snprintf(buf, PATH_MAX - 1, "%s/main.sqlite", topdir);
64c563
-	if (ret < 0)
64c563
-		return ret;
64c563
-
64c563
-	buf[PATH_MAX - 1] = '\0';
64c563
-
64c563
-	ret = sqlite3_open(buf, &dbh;;
64c563
+	/* prepare select query */
64c563
+	ret = sqlite3_prepare_v2(dbh,
64c563
+		"SELECT value FROM parameters WHERE key == \"version\";",
64c563
+		 -1, &stmt, NULL);
64c563
 	if (ret != SQLITE_OK) {
64c563
-		xlog(L_ERROR, "Unable to open main database: %d", ret);
64c563
-		dbh = NULL;
64c563
-		return ret;
64c563
+		xlog(L_ERROR, "Unable to prepare select statement: %s",
64c563
+			sqlite3_errmsg(dbh));
64c563
+		ret = 0;
64c563
+		goto out;
64c563
 	}
64c563
 
64c563
-	ret = sqlite3_busy_timeout(dbh, CLD_SQLITE_BUSY_TIMEOUT);
64c563
-	if (ret != SQLITE_OK) {
64c563
-		xlog(L_ERROR, "Unable to set sqlite busy timeout: %d", ret);
64c563
-		sqlite3_close(dbh);
64c563
-		dbh = NULL;
64c563
+	/* query schema version */
64c563
+	ret = sqlite3_step(stmt);
64c563
+	if (ret != SQLITE_ROW) {
64c563
+		xlog(L_ERROR, "Select statement execution failed: %s",
64c563
+				sqlite3_errmsg(dbh));
64c563
+		ret = 0;
64c563
+		goto out;
64c563
 	}
64c563
 
64c563
+	ret = sqlite3_column_int(stmt, 0);
64c563
+out:
64c563
+	sqlite3_finalize(stmt);
64c563
 	return ret;
64c563
 }
64c563
 
64c563
 /*
64c563
- * Open the "main" database, and attempt to initialize it by creating the
64c563
- * parameters table and inserting the schema version into it. Ignore any errors
64c563
- * from that, and then attempt to select the version out of it again. If the
64c563
- * version appears wrong, then assume that the DB is corrupt or has been
64c563
- * upgraded, and return an error. If all of that works, then attempt to create
64c563
- * the "clients" table.
64c563
+ * Start an exclusive transaction and recheck the DB schema version. If it's
64c563
+ * still zero (indicating a new database) then set it up. If that all works,
64c563
+ * then insert schema version into the parameters table and commit the
64c563
+ * transaction. On any error, rollback the transaction.
64c563
  */
64c563
 int
64c563
-sqlite_maindb_init(const char *topdir)
64c563
+sqlite_maindb_init_v1(void)
64c563
 {
64c563
 	int ret;
64c563
 	char *err = NULL;
64c563
-	sqlite3_stmt *stmt = NULL;
64c563
 
64c563
-	ret = mkdir_if_not_exist(topdir);
64c563
-	if (ret)
64c563
+	/* Start a transaction */
64c563
+	ret = sqlite3_exec(dbh, "BEGIN EXCLUSIVE TRANSACTION;", NULL, NULL,
64c563
+				&err;;
64c563
+	if (ret != SQLITE_OK) {
64c563
+		xlog(L_ERROR, "Unable to begin transaction: %s", err);
64c563
 		return ret;
64c563
+	}
64c563
 
64c563
-	ret = sqlite_prepare_dbh(topdir);
64c563
-	if (ret)
64c563
-		return ret;
64c563
+	/*
64c563
+	 * Check schema version again. This time, under an exclusive
64c563
+	 * transaction to guard against racing DB setup attempts
64c563
+	 */
64c563
+	ret = sqlite_query_schema_version();
64c563
+	switch (ret) {
64c563
+	case 0:
64c563
+		/* Query failed again -- set up DB */
64c563
+		break;
64c563
+	case CLTRACK_SQLITE_LATEST_SCHEMA_VERSION:
64c563
+		/* Someone else raced in and set it up */
64c563
+		ret = 0;
64c563
+		goto rollback;
64c563
+	default:
64c563
+		/* Something went wrong -- fail! */
64c563
+		ret = -EINVAL;
64c563
+		goto rollback;
64c563
+	}
64c563
 
64c563
-	/* Try to create table */
64c563
-	ret = sqlite3_exec(dbh, "CREATE TABLE IF NOT EXISTS parameters "
64c563
+	ret = sqlite3_exec(dbh, "CREATE TABLE parameters "
64c563
 				"(key TEXT PRIMARY KEY, value TEXT);",
64c563
 				NULL, NULL, &err;;
64c563
 	if (ret != SQLITE_OK) {
64c563
-		xlog(L_ERROR, "Unable to create parameter table: %d", ret);
64c563
-		goto out_err;
64c563
+		xlog(L_ERROR, "Unable to create parameter table: %s", err);
64c563
+		goto rollback;
64c563
 	}
64c563
 
64c563
-	/* insert version into table -- ignore error if it fails */
64c563
-	ret = snprintf(buf, sizeof(buf),
64c563
-		       "INSERT OR IGNORE INTO parameters values (\"version\", "
64c563
-		       "\"%d\");", CLD_SQLITE_SCHEMA_VERSION);
64c563
+	/* create the "clients" table */
64c563
+	ret = sqlite3_exec(dbh, "CREATE TABLE clients (id BLOB PRIMARY KEY, "
64c563
+				"time INTEGER);",
64c563
+				NULL, NULL, &err;;
64c563
+	if (ret != SQLITE_OK) {
64c563
+		xlog(L_ERROR, "Unable to create clients table: %s", err);
64c563
+		goto rollback;
64c563
+	}
64c563
+
64c563
+
64c563
+	/* insert version into parameters table */
64c563
+	ret = snprintf(buf, sizeof(buf), "INSERT OR FAIL INTO parameters "
64c563
+			"values (\"version\", \"%d\");",
64c563
+			CLTRACK_SQLITE_LATEST_SCHEMA_VERSION);
64c563
 	if (ret < 0) {
64c563
-		goto out_err;
64c563
+		xlog(L_ERROR, "sprintf failed!");
64c563
+		goto rollback;
64c563
 	} else if ((size_t)ret >= sizeof(buf)) {
64c563
+		xlog(L_ERROR, "sprintf output too long! (%d chars)", ret);
64c563
 		ret = -EINVAL;
64c563
-		goto out_err;
64c563
+		goto rollback;
64c563
 	}
64c563
 
64c563
 	ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err;;
64c563
 	if (ret != SQLITE_OK) {
64c563
-		xlog(L_ERROR, "Unable to insert into parameter table: %d",
64c563
-				ret);
64c563
-		goto out_err;
64c563
+		xlog(L_ERROR, "Unable to insert into parameter table: %s", err);
64c563
+		goto rollback;
64c563
 	}
64c563
 
64c563
-	ret = sqlite3_prepare_v2(dbh,
64c563
-		"SELECT value FROM parameters WHERE key == \"version\";",
64c563
-		 -1, &stmt, NULL);
64c563
+	ret = sqlite3_exec(dbh, "COMMIT TRANSACTION;", NULL, NULL, &err;;
64c563
 	if (ret != SQLITE_OK) {
64c563
-		xlog(L_ERROR, "Unable to prepare select statement: %d", ret);
64c563
-		goto out_err;
64c563
+		xlog(L_ERROR, "Unable to commit transaction: %s", err);
64c563
+		goto rollback;
64c563
 	}
64c563
+out:
64c563
+	sqlite3_free(err);
64c563
+	return ret;
64c563
 
64c563
-	/* check schema version */
64c563
-	ret = sqlite3_step(stmt);
64c563
-	if (ret != SQLITE_ROW) {
64c563
-		xlog(L_ERROR, "Select statement execution failed: %s",
64c563
+rollback:
64c563
+	/* Attempt to rollback the transaction */
64c563
+	sqlite3_exec(dbh, "ROLLBACK TRANSACTION;", NULL, NULL, &err;;
64c563
+	goto out;
64c563
+}
64c563
+
64c563
+/* Open the database and set up the database handle for it */
64c563
+int
64c563
+sqlite_prepare_dbh(const char *topdir)
64c563
+{
64c563
+	int ret;
64c563
+
64c563
+	/* Do nothing if the database handle is already set up */
64c563
+	if (dbh)
64c563
+		return 0;
64c563
+
64c563
+	ret = snprintf(buf, PATH_MAX - 1, "%s/main.sqlite", topdir);
64c563
+	if (ret < 0)
64c563
+		return ret;
64c563
+
64c563
+	buf[PATH_MAX - 1] = '\0';
64c563
+
64c563
+	/* open a new DB handle */
64c563
+	ret = sqlite3_open(buf, &dbh;;
64c563
+	if (ret != SQLITE_OK) {
64c563
+		/* try to create the dir */
64c563
+		ret = mkdir_if_not_exist(topdir);
64c563
+		if (ret)
64c563
+			goto out_close;
64c563
+
64c563
+		/* retry open */
64c563
+		ret = sqlite3_open(buf, &dbh;;
64c563
+		if (ret != SQLITE_OK)
64c563
+			goto out_close;
64c563
+	}
64c563
+
64c563
+	/* set busy timeout */
64c563
+	ret = sqlite3_busy_timeout(dbh, CLTRACK_SQLITE_BUSY_TIMEOUT);
64c563
+	if (ret != SQLITE_OK) {
64c563
+		xlog(L_ERROR, "Unable to set sqlite busy timeout: %s",
64c563
 				sqlite3_errmsg(dbh));
64c563
-		goto out_err;
64c563
+		goto out_close;
64c563
 	}
64c563
 
64c563
-	/* process SELECT result */
64c563
-	ret = sqlite3_column_int(stmt, 0);
64c563
-	if (ret != CLD_SQLITE_SCHEMA_VERSION) {
64c563
+	ret = sqlite_query_schema_version();
64c563
+	switch (ret) {
64c563
+	case CLTRACK_SQLITE_LATEST_SCHEMA_VERSION:
64c563
+		/* DB is already set up. Do nothing */
64c563
+		ret = 0;
64c563
+		break;
64c563
+	case 0:
64c563
+		/* Query failed -- try to set up new DB */
64c563
+		ret = sqlite_maindb_init_v1();
64c563
+		if (ret)
64c563
+			goto out_close;
64c563
+		break;
64c563
+	default:
64c563
+		/* Unknown DB version -- downgrade? Fail */
64c563
 		xlog(L_ERROR, "Unsupported database schema version! "
64c563
 			"Expected %d, got %d.",
64c563
-			CLD_SQLITE_SCHEMA_VERSION, ret);
64c563
+			CLTRACK_SQLITE_LATEST_SCHEMA_VERSION, ret);
64c563
 		ret = -EINVAL;
64c563
-		goto out_err;
64c563
-	}
64c563
-
64c563
-	/* now create the "clients" table */
64c563
-	ret = sqlite3_exec(dbh, "CREATE TABLE IF NOT EXISTS clients "
64c563
-				"(id BLOB PRIMARY KEY, time INTEGER);",
64c563
-				NULL, NULL, &err;;
64c563
-	if (ret != SQLITE_OK) {
64c563
-		xlog(L_ERROR, "Unable to create clients table: %s", err);
64c563
-		goto out_err;
64c563
+		goto out_close;
64c563
 	}
64c563
 
64c563
-	sqlite3_free(err);
64c563
-	sqlite3_finalize(stmt);
64c563
-	return 0;
64c563
-
64c563
-out_err:
64c563
-	if (err) {
64c563
-		xlog(L_ERROR, "sqlite error: %s", err);
64c563
-		sqlite3_free(err);
64c563
-	}
64c563
-	sqlite3_finalize(stmt);
64c563
+	return ret;
64c563
+out_close:
64c563
 	sqlite3_close(dbh);
64c563
+	dbh = NULL;
64c563
 	return ret;
64c563
 }
64c563
 
64c563
@@ -228,14 +283,20 @@ out_err:
64c563
  * Returns a non-zero sqlite error code, or SQLITE_OK (aka 0)
64c563
  */
64c563
 int
64c563
-sqlite_insert_client(const unsigned char *clname, const size_t namelen)
64c563
+sqlite_insert_client(const unsigned char *clname, const size_t namelen,
64c563
+			const bool has_session, const bool zerotime)
64c563
 {
64c563
 	int ret;
64c563
 	sqlite3_stmt *stmt = NULL;
64c563
 
64c563
-	ret = sqlite3_prepare_v2(dbh, "INSERT OR REPLACE INTO clients VALUES "
64c563
-				      "(?, strftime('%s', 'now'));", -1,
64c563
-					&stmt, NULL);
64c563
+	if (zerotime)
64c563
+		ret = sqlite3_prepare_v2(dbh, "INSERT OR REPLACE INTO clients "
64c563
+				"VALUES (?, 0, ?);", -1, &stmt, NULL);
64c563
+	else
64c563
+		ret = sqlite3_prepare_v2(dbh, "INSERT OR REPLACE INTO clients "
64c563
+				"VALUES (?, strftime('%s', 'now'), ?);", -1,
64c563
+				&stmt, NULL);
64c563
+
64c563
 	if (ret != SQLITE_OK) {
64c563
 		xlog(L_ERROR, "%s: insert statement prepare failed: %s",
64c563
 			__func__, sqlite3_errmsg(dbh));
64c563
@@ -250,6 +311,13 @@ sqlite_insert_client(const unsigned char *clname, const size_t namelen)
64c563
 		goto out_err;
64c563
 	}
64c563
 
64c563
+	ret = sqlite3_bind_int(stmt, 2, (int)has_session);
64c563
+	if (ret != SQLITE_OK) {
64c563
+		xlog(L_ERROR, "%s: bind int failed: %s", __func__,
64c563
+				sqlite3_errmsg(dbh));
64c563
+		goto out_err;
64c563
+	}
64c563
+
64c563
 	ret = sqlite3_step(stmt);
64c563
 	if (ret == SQLITE_DONE)
64c563
 		ret = SQLITE_OK;
64c563
@@ -305,7 +373,8 @@ out_err:
64c563
  * return an error.
64c563
  */
64c563
 int
64c563
-sqlite_check_client(const unsigned char *clname, const size_t namelen)
64c563
+sqlite_check_client(const unsigned char *clname, const size_t namelen,
64c563
+			const bool has_session)
64c563
 {
64c563
 	int ret;
64c563
 	sqlite3_stmt *stmt = NULL;
64c563
@@ -340,6 +409,12 @@ sqlite_check_client(const unsigned char *clname, const size_t namelen)
64c563
 		goto out_err;
64c563
 	}
64c563
 
64c563
+	/* Only update timestamp for v4.0 clients */
64c563
+	if (has_session) {
64c563
+		ret = SQLITE_OK;
64c563
+		goto out_err;
64c563
+	}
64c563
+
64c563
 	sqlite3_finalize(stmt);
64c563
 	stmt = NULL;
64c563
 	ret = sqlite3_prepare_v2(dbh, "UPDATE OR FAIL clients SET "
64c563
@@ -398,3 +473,43 @@ sqlite_remove_unreclaimed(time_t grace_start)
64c563
 	sqlite3_free(err);
64c563
 	return ret;
64c563
 }
64c563
+
64c563
+/*
64c563
+ * Are there any clients that are possibly still reclaiming? Return a positive
64c563
+ * integer (usually number of clients) if so. If not, then return 0. On any
64c563
+ * error, return non-zero.
64c563
+ */
64c563
+int
64c563
+sqlite_query_reclaiming(const time_t grace_start)
64c563
+{
64c563
+	int ret;
64c563
+	sqlite3_stmt *stmt = NULL;
64c563
+
64c563
+	ret = sqlite3_prepare_v2(dbh, "SELECT count(*) FROM clients WHERE "
64c563
+				      "time < ? OR has_session != 1", -1, &stmt, NULL);
64c563
+	if (ret != SQLITE_OK) {
64c563
+		xlog(L_ERROR, "%s: unable to prepare select statement: %s",
64c563
+				__func__, sqlite3_errmsg(dbh));
64c563
+		return ret;
64c563
+	}
64c563
+
64c563
+	ret = sqlite3_bind_int64(stmt, 1, (sqlite3_int64)grace_start);
64c563
+	if (ret != SQLITE_OK) {
64c563
+		xlog(L_ERROR, "%s: bind int64 failed: %s",
64c563
+				__func__, sqlite3_errmsg(dbh));
64c563
+		return ret;
64c563
+	}
64c563
+
64c563
+	ret = sqlite3_step(stmt);
64c563
+	if (ret != SQLITE_ROW) {
64c563
+		xlog(L_ERROR, "%s: unexpected return code from select: %s",
64c563
+				__func__, sqlite3_errmsg(dbh));
64c563
+		return ret;
64c563
+	}
64c563
+
64c563
+	ret = sqlite3_column_int(stmt, 0);
64c563
+	sqlite3_finalize(stmt);
64c563
+	xlog(D_GENERAL, "%s: there are %d clients that have not completed "
64c563
+			"reclaim", __func__, ret);
64c563
+	return ret;
64c563
+}
64c563
diff --git a/utils/nfsdcltrack/sqlite.h b/utils/nfsdcltrack/sqlite.h
64c563
index ebf04c3..06e7c04 100644
64c563
--- a/utils/nfsdcltrack/sqlite.h
64c563
+++ b/utils/nfsdcltrack/sqlite.h
64c563
@@ -21,10 +21,12 @@
64c563
 #define _SQLITE_H_
64c563
 
64c563
 int sqlite_prepare_dbh(const char *topdir);
64c563
-int sqlite_maindb_init(const char *topdir);
64c563
-int sqlite_insert_client(const unsigned char *clname, const size_t namelen);
64c563
+int sqlite_insert_client(const unsigned char *clname, const size_t namelen,
64c563
+				const bool has_session, const bool zerotime);
64c563
 int sqlite_remove_client(const unsigned char *clname, const size_t namelen);
64c563
-int sqlite_check_client(const unsigned char *clname, const size_t namelen);
64c563
+int sqlite_check_client(const unsigned char *clname, const size_t namelen,
64c563
+				const bool has_session);
64c563
 int sqlite_remove_unreclaimed(const time_t grace_start);
64c563
+int sqlite_query_reclaiming(const time_t grace_start);
64c563
 
64c563
 #endif /* _SQLITE_H */
64c563
diff --git a/utils/nfsidmap/Makefile.am b/utils/nfsidmap/Makefile.am
64c563
index 737a219..91cedfd 100644
64c563
--- a/utils/nfsidmap/Makefile.am
64c563
+++ b/utils/nfsidmap/Makefile.am
64c563
@@ -7,4 +7,4 @@ nfsidmap_SOURCES = nfsidmap.c
64c563
 nfsidmap_LDADD = $(LIBNFSIDMAP) -lkeyutils ../../support/nfs/libnfs.a
64c563
 
64c563
 MAINTAINERCLEANFILES = Makefile.in
64c563
-EXTRA_DIST = id_resolver.conf
64c563
+EXTRA_DIST = id_resolver.conf $(man8_MANS)
64c563
diff --git a/utils/osd_login/Makefile.am b/utils/osd_login/Makefile.am
64c563
index 20c2d8c..ded1fd3 100644
64c563
--- a/utils/osd_login/Makefile.am
64c563
+++ b/utils/osd_login/Makefile.am
64c563
@@ -4,6 +4,6 @@
64c563
 # overridden at config time.
64c563
 sbindir = /sbin
64c563
 
64c563
-sbin_SCRIPTS = osd_login
64c563
+dist_sbin_SCRIPTS = osd_login
64c563
 
64c563
 MAINTAINERCLEANFILES = Makefile.in
64c563
diff --git a/utils/statd/Makefile.am b/utils/statd/Makefile.am
64c563
index dc2bfc4..152b680 100644
64c563
--- a/utils/statd/Makefile.am
64c563
+++ b/utils/statd/Makefile.am
64c563
@@ -8,7 +8,7 @@ sbin_PROGRAMS	= statd sm-notify
64c563
 dist_sbin_SCRIPTS	= start-statd
64c563
 statd_SOURCES = callback.c notlist.c misc.c monitor.c hostname.c \
64c563
 	        simu.c stat.c statd.c svc_run.c rmtcall.c \
64c563
-	        notlist.h statd.h system.h version.h
64c563
+	        notlist.h statd.h system.h
64c563
 sm_notify_SOURCES = sm-notify.c
64c563
 
64c563
 BUILT_SOURCES = $(GENFILES)
64c563
@@ -20,7 +20,7 @@ sm_notify_LDADD = ../../support/nsm/libnsm.a \
64c563
 		  ../../support/nfs/libnfs.a \
64c563
 		  $(LIBNSL) $(LIBCAP) $(LIBTIRPC)
64c563
 
64c563
-EXTRA_DIST = sim_sm_inter.x $(man8_MANS) COPYRIGHT simulate.c
64c563
+EXTRA_DIST = sim_sm_inter.x $(man8_MANS) simulate.c
64c563
 
64c563
 if CONFIG_RPCGEN
64c563
 RPCGEN	= $(top_builddir)/tools/rpcgen/rpcgen