diff --git a/GVFS/GVFS.Common/Database/SqliteDatabase.cs b/GVFS/GVFS.Common/Database/SqliteDatabase.cs
index 0416cec80..8cd9ac6c8 100644
--- a/GVFS/GVFS.Common/Database/SqliteDatabase.cs
+++ b/GVFS/GVFS.Common/Database/SqliteDatabase.cs
@@ -21,7 +21,7 @@ public static bool HasIssue(string databasePath, PhysicalFileSystem filesystem,
try
{
- string sqliteConnectionString = CreateConnectionString(databasePath);
+ string sqliteConnectionString = $"data source={databasePath};Pooling=False";
using (SqliteConnection integrityConnection = new SqliteConnection(sqliteConnectionString))
{
integrityConnection.Open();
diff --git a/GVFS/GVFS.Common/Database/SqliteErrorCodes.cs b/GVFS/GVFS.Common/Database/SqliteErrorCodes.cs
new file mode 100644
index 000000000..2ed11d79a
--- /dev/null
+++ b/GVFS/GVFS.Common/Database/SqliteErrorCodes.cs
@@ -0,0 +1,15 @@
+namespace GVFS.Common.Database
+{
+ ///
+ /// SQLite result codes used for error classification.
+ /// See https://www.sqlite.org/rescode.html
+ ///
+ public static class SqliteErrorCodes
+ {
+ /// SQLITE_CORRUPT (11) — database disk image is malformed
+ public const int Corrupt = 11;
+
+ /// SQLITE_NOTADB (26) — file is not a database
+ public const int NotADatabase = 26;
+ }
+}
diff --git a/GVFS/GVFS.FunctionalTests/Tests/MultiEnlistmentTests/SharedCacheTests.cs b/GVFS/GVFS.FunctionalTests/Tests/MultiEnlistmentTests/SharedCacheTests.cs
index 8837c6660..afd235bae 100644
--- a/GVFS/GVFS.FunctionalTests/Tests/MultiEnlistmentTests/SharedCacheTests.cs
+++ b/GVFS/GVFS.FunctionalTests/Tests/MultiEnlistmentTests/SharedCacheTests.cs
@@ -60,7 +60,6 @@ public void SecondCloneDoesNotDownloadAdditionalObjects()
}
[TestCase]
- [SkipInCI("Product bug: repair does not fully restore corrupt BlobSizes.sql — mount crashes after repair")]
public void RepairFixesCorruptBlobSizesDatabase()
{
GVFSFunctionalTestEnlistment enlistment = this.CloneAndMountEnlistment();
@@ -74,9 +73,12 @@ public void RepairFixesCorruptBlobSizesDatabase()
blobSizesDbPath.ShouldBeAFile(this.fileSystem);
this.fileSystem.WriteAllText(blobSizesDbPath, "0000");
- // GVFS now tolerates corrupt blob sizes DB on mount (recreates
- // in-memory), but repair should still fix the underlying file.
+ // Repair should detect and fix the corrupt database
enlistment.Repair(confirm: true);
+
+ // Verify repair actually cleaned up the corrupt file
+ blobSizesRoot.ShouldNotExistOnDisk(this.fileSystem);
+
enlistment.MountGVFS();
}
diff --git a/GVFS/GVFS.Virtualization/BlobSize/BlobSizes.cs b/GVFS/GVFS.Virtualization/BlobSize/BlobSizes.cs
index a4d59f316..d4eb621a0 100644
--- a/GVFS/GVFS.Virtualization/BlobSize/BlobSizes.cs
+++ b/GVFS/GVFS.Virtualization/BlobSize/BlobSizes.cs
@@ -54,6 +54,54 @@ public virtual void Initialize()
string folderPath = Path.GetDirectoryName(this.databasePath);
this.fileSystem.CreateDirectory(folderPath);
+ try
+ {
+ this.InitializeDatabase();
+ }
+ catch (SqliteException ex) when (ex.SqliteErrorCode == SqliteErrorCodes.Corrupt || ex.SqliteErrorCode == SqliteErrorCodes.NotADatabase)
+ {
+ EventMetadata metadata = this.CreateEventMetadata(ex);
+ metadata.Add("SqliteErrorCode", ex.SqliteErrorCode);
+ this.tracer.RelatedWarning(metadata, $"{nameof(BlobSizes)}.{nameof(this.Initialize)}: database corrupt, deleting and recreating");
+
+ SqliteConnection.ClearAllPools();
+ this.DeleteDatabaseFiles();
+ this.InitializeDatabase();
+ }
+
+ this.flushDataThread = new Thread(this.FlushDbThreadMain);
+ this.flushDataThread.IsBackground = true;
+ this.flushDataThread.Start();
+ }
+
+ public virtual void Shutdown()
+ {
+ this.isStopping = true;
+ this.wakeUpFlushThread.Set();
+ this.flushDataThread.Join();
+ }
+
+ public virtual void AddSize(Sha1Id sha, long size)
+ {
+ this.queuedSizes.Enqueue(new BlobSize(sha, size));
+ }
+
+ public virtual void Flush()
+ {
+ this.wakeUpFlushThread.Set();
+ }
+
+ public void Dispose()
+ {
+ if (this.wakeUpFlushThread != null)
+ {
+ this.wakeUpFlushThread.Dispose();
+ this.wakeUpFlushThread = null;
+ }
+ }
+
+ private void InitializeDatabase()
+ {
using (SqliteConnection connection = new SqliteConnection(this.sqliteConnectionString))
{
connection.Open();
@@ -125,36 +173,13 @@ public virtual void Initialize()
createTableCommand.ExecuteNonQuery();
}
}
-
- this.flushDataThread = new Thread(this.FlushDbThreadMain);
- this.flushDataThread.IsBackground = true;
- this.flushDataThread.Start();
}
- public virtual void Shutdown()
+ private void DeleteDatabaseFiles()
{
- this.isStopping = true;
- this.wakeUpFlushThread.Set();
- this.flushDataThread.Join();
- }
-
- public virtual void AddSize(Sha1Id sha, long size)
- {
- this.queuedSizes.Enqueue(new BlobSize(sha, size));
- }
-
- public virtual void Flush()
- {
- this.wakeUpFlushThread.Set();
- }
-
- public void Dispose()
- {
- if (this.wakeUpFlushThread != null)
- {
- this.wakeUpFlushThread.Dispose();
- this.wakeUpFlushThread = null;
- }
+ this.fileSystem.TryDeleteFile(this.databasePath);
+ this.fileSystem.TryDeleteFile(this.databasePath + "-wal");
+ this.fileSystem.TryDeleteFile(this.databasePath + "-shm");
}
private void FlushDbThreadMain()