diff -up nfs-utils-1.3.0/utils/nfsdcltrack/sqlite.c.orig nfs-utils-1.3.0/utils/nfsdcltrack/sqlite.c --- nfs-utils-1.3.0/utils/nfsdcltrack/sqlite.c.orig 2016-02-10 10:46:23.100398000 -0500 +++ nfs-utils-1.3.0/utils/nfsdcltrack/sqlite.c 2016-02-10 10:46:59.540317000 -0500 @@ -29,7 +29,9 @@ * * clients: an "id" column containing a BLOB with the long-form clientid as * sent by the client, a "time" column containing a timestamp (in - * epoch seconds) of when the record was last updated. + * epoch seconds) of when the record was last updated, and a + * "has_session" column containing a boolean value indicating + * whether the client has sessions (v4.1+) or not (v4.0). */ #ifdef HAVE_CONFIG_H @@ -50,7 +52,7 @@ #include "xlog.h" -#define CLTRACK_SQLITE_LATEST_SCHEMA_VERSION 1 +#define CLTRACK_SQLITE_LATEST_SCHEMA_VERSION 2 /* in milliseconds */ #define CLTRACK_SQLITE_BUSY_TIMEOUT 10000 @@ -120,6 +122,81 @@ out: return ret; } +static int +sqlite_maindb_update_v1_to_v2(void) +{ + int ret, ret2; + char *err; + + /* begin transaction */ + ret = sqlite3_exec(dbh, "BEGIN EXCLUSIVE TRANSACTION;", NULL, NULL, + &err); + if (ret != SQLITE_OK) { + xlog(L_ERROR, "Unable to begin transaction: %s", err); + goto rollback; + } + + /* + * Check schema version again. This time, under an exclusive + * transaction to guard against racing DB setup attempts + */ + ret = sqlite_query_schema_version(); + switch (ret) { + case 1: + /* Still at v1 -- do conversion */ + break; + case CLTRACK_SQLITE_LATEST_SCHEMA_VERSION: + /* Someone else raced in and set it up */ + ret = 0; + goto rollback; + default: + /* Something went wrong -- fail! */ + ret = -EINVAL; + goto rollback; + } + + /* create v2 clients table */ + ret = sqlite3_exec(dbh, "ALTER TABLE clients ADD COLUMN " + "has_session INTEGER;", + NULL, NULL, &err); + if (ret != SQLITE_OK) { + xlog(L_ERROR, "Unable to update clients table: %s", err); + goto rollback; + } + + ret = snprintf(buf, sizeof(buf), "UPDATE parameters SET value = %d " + "WHERE key = \"version\";", + CLTRACK_SQLITE_LATEST_SCHEMA_VERSION); + if (ret < 0) { + xlog(L_ERROR, "sprintf failed!"); + goto rollback; + } else if ((size_t)ret >= sizeof(buf)) { + xlog(L_ERROR, "sprintf output too long! (%d chars)", ret); + ret = -EINVAL; + goto rollback; + } + + ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err); + if (ret != SQLITE_OK) { + xlog(L_ERROR, "Unable to update schema version: %s", err); + goto rollback; + } + + ret = sqlite3_exec(dbh, "COMMIT TRANSACTION;", NULL, NULL, &err); + if (ret != SQLITE_OK) { + xlog(L_ERROR, "Unable to commit transaction: %s", err); + goto rollback; + } +out: + sqlite3_free(err); + return ret; +rollback: + ret2 = sqlite3_exec(dbh, "ROLLBACK TRANSACTION;", NULL, NULL, &err); + if (ret2 != SQLITE_OK) + xlog(L_ERROR, "Unable to rollback transaction: %s", err); + goto out; +} + /* * Start an exclusive transaction and recheck the DB schema version. If it's * still zero (indicating a new database) then set it up. If that all works, @@ -127,9 +204,9 @@ out: * transaction. On any error, rollback the transaction. */ int -sqlite_maindb_init_v1(void) +sqlite_maindb_init_v2(void) { - int ret; + int ret, ret2; char *err = NULL; /* Start a transaction */ @@ -169,7 +246,7 @@ sqlite_maindb_init_v1(void) /* create the "clients" table */ ret = sqlite3_exec(dbh, "CREATE TABLE clients (id BLOB PRIMARY KEY, " - "time INTEGER);", + "time INTEGER, has_session INTEGER);", NULL, NULL, &err); if (ret != SQLITE_OK) { xlog(L_ERROR, "Unable to create clients table: %s", err); @@ -207,7 +284,9 @@ out: rollback: /* Attempt to rollback the transaction */ - sqlite3_exec(dbh, "ROLLBACK TRANSACTION;", NULL, NULL, &err); + ret2 = sqlite3_exec(dbh, "ROLLBACK TRANSACTION;", NULL, NULL, &err); + if (ret2 != SQLITE_OK) + xlog(L_ERROR, "Unable to rollback transaction: %s", err); goto out; } @@ -255,9 +334,15 @@ sqlite_prepare_dbh(const char *topdir) /* DB is already set up. Do nothing */ ret = 0; break; + case 1: + /* Old DB -- update to new schema */ + ret = sqlite_maindb_update_v1_to_v2(); + if (ret) + goto out_close; + break; case 0: /* Query failed -- try to set up new DB */ - ret = sqlite_maindb_init_v1(); + ret = sqlite_maindb_init_v2(); if (ret) goto out_close; break;