diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslCachedSystemStoreProvider.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslCachedSystemStoreProvider.cs index 409170a40f2656..1aa22406e4fd4e 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslCachedSystemStoreProvider.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslCachedSystemStoreProvider.cs @@ -114,7 +114,7 @@ private static bool LastWriteTimesHaveChanged() if (s_rootStoreFile != null) { - _ = TryStatFile(s_rootStoreFile, out DateTime lastModified); + _ = TryStatFile(s_rootStoreFile, out DateTime lastModified, out _); if (lastModified != s_fileLastWrite) { return true; @@ -149,6 +149,7 @@ private static Tuple LoadMachineStores var uniqueRootCerts = new HashSet(); var uniqueIntermediateCerts = new HashSet(); + var processedFiles = new HashSet<(long Ino, long Dev)>(); bool firstLoad = (s_nativeCollections == null); if (firstLoad) @@ -197,23 +198,23 @@ bool ProcessDir(string dir, out DateTime lastModified) foreach (string file in Directory.EnumerateFiles(dir)) { - hasStoreData |= ProcessFile(file, out _, skipStat: true); + hasStoreData |= ProcessFile(file, out _); } return hasStoreData; } - bool ProcessFile(string file, out DateTime lastModified, bool skipStat = false) + bool ProcessFile(string file, out DateTime lastModified) { bool readData = false; - - if (skipStat) + if (!TryStatFile(file, out lastModified, out (long, long) fileId)) { - lastModified = default; + return false; } - else if (!TryStatFile(file, out lastModified)) + + if (processedFiles.Contains(fileId)) { - return false; + return true; } using (SafeBioHandle fileBio = Interop.Crypto.BioNewFile(file, "rb")) @@ -281,6 +282,11 @@ bool ProcessFile(string file, out DateTime lastModified, bool skipStat = false) } } + if (readData) + { + processedFiles.Add(fileId); + } + return readData; } @@ -307,7 +313,6 @@ bool ProcessFile(string file, out DateTime lastModified, bool skipStat = false) // In order to maintain "finalization-free" the GetNativeCollections method would need to // DangerousAddRef, and the callers would need to DangerousRelease, adding more interlocked operations // on every call. - Volatile.Write(ref s_nativeCollections, newCollections); s_recheckStopwatch.Restart(); return newCollections; @@ -361,24 +366,24 @@ private static string[] GetRootStoreDirectories(out bool isDefault) return directories; } - private static bool TryStatFile(string path, out DateTime lastModified) - => TryStat(path, Interop.Sys.FileTypes.S_IFREG, out lastModified); - private static bool TryStatDirectory(string path, out DateTime lastModified) - => TryStat(path, Interop.Sys.FileTypes.S_IFDIR, out lastModified); + => TryStat(path, Interop.Sys.FileTypes.S_IFDIR, out lastModified, out _); - private static bool TryStat(string path, int fileType, out DateTime lastModified) + private static bool TryStatFile(string path, out DateTime lastModified, out (long, long) fileId) + => TryStat(path, Interop.Sys.FileTypes.S_IFREG, out lastModified, out fileId); + + private static bool TryStat(string path, int fileType, out DateTime lastModified, out (long, long) fileId) { lastModified = default; - - Interop.Sys.FileStatus status; + fileId = default; // Use Stat to follow links. - if (Interop.Sys.Stat(path, out status) < 0 || + if (Interop.Sys.Stat(path, out Interop.Sys.FileStatus status) < 0 || (status.Mode & Interop.Sys.FileTypes.S_IFMT) != fileType) { return false; } + fileId = (status.Ino, status.Dev); lastModified = DateTime.UnixEpoch + TimeSpan.FromTicks(status.MTime * TimeSpan.TicksPerSecond + status.MTimeNsec / TimeSpan.NanosecondsPerTick); return true; }