From 4254f5b599007c5d21e2772415e0c4a933bada3d Mon Sep 17 00:00:00 2001 From: Caelan Sayler Date: Wed, 26 Apr 2023 11:40:05 +0100 Subject: [PATCH] Prevent concurrent temp folder access (closes #122) --- src/Squirrel/Internal/Utility.cs | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/Squirrel/Internal/Utility.cs b/src/Squirrel/Internal/Utility.cs index 43099cb2a..b8eec9008 100644 --- a/src/Squirrel/Internal/Utility.cs +++ b/src/Squirrel/Internal/Utility.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.Contracts; @@ -391,25 +391,42 @@ public static IDisposable WithTempDirectory(out string path, string localAppDire var names = Enumerable.Range(0, 1 << 20).Select(x => tempNameForIndex(x, "temp")); + IDisposable folderMutex = null; + foreach (var name in names) { var target = Path.Combine(di.FullName, name); if (!File.Exists(target) && !Directory.Exists(target)) { Directory.CreateDirectory(target); tempDir = new DirectoryInfo(target); - break; + + // Directory.CreateDirectory will work even if the directory already exists. If two instances of + // Squirrel attempt to create the same temp directory at the same time, they can end up reading + // and writing on top of each other. So, here we create a new file in the temp folder we've selected + // and attempt to open it exclusively. This will act like a mutex as only one Squirre instance will + // be able to open this file. + + try { + folderMutex = File.Create(Path.Combine(tempDir.FullName, "lock"), 1, FileOptions.DeleteOnClose); + break; + } catch (IOException ex) { + Log().WarnException($"Selected temp folder '{tempDir.FullName}' but unable to open file mutex.", ex); + } } } path = tempDir.FullName; - return Disposable.Create(() => DeleteFileOrDirectoryHardOrGiveUp(tempDir.FullName)); + return Disposable.Create(() => { + folderMutex?.Dispose(); + DeleteFileOrDirectoryHardOrGiveUp(tempDir.FullName); + }); } public static IDisposable WithTempFile(out string path, string localAppDirectory = null) { var di = GetTempDirectory(localAppDirectory); - var names = Enumerable.Range(0, 1 << 20).Select(x => tempNameForIndex(x, "temp")); + var names = Enumerable.Range(0, 1 << 20).Select(x => tempNameForIndex(x, "tempfile")); path = ""; foreach (var name in names) {