1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.Collections; 6 using System.IO; 7 8 // We keep this class in Common to allow utilizing it without taking a dependency on System.CodeDom 9 #if CODEDOM 10 namespace System.CodeDom.Compiler 11 #else 12 namespace System.IO.Internal 13 #endif 14 { 15 // Explicitly not [Serializable], so as to avoid accidentally deleting 16 // files specified in a serialized payload. 17 18 #if MONO 19 [Serializable] 20 #endif 21 22 #if CODEDOM 23 public 24 #else 25 internal 26 #endif 27 class TempFileCollection : ICollection, IDisposable 28 { 29 private string _basePath; 30 private readonly string _tempDir; 31 private readonly Hashtable _files; 32 TempFileCollection()33 public TempFileCollection() : this(null, false) 34 { 35 } 36 TempFileCollection(string tempDir)37 public TempFileCollection(string tempDir) : this(tempDir, false) 38 { 39 } 40 TempFileCollection(string tempDir, bool keepFiles)41 public TempFileCollection(string tempDir, bool keepFiles) 42 { 43 KeepFiles = keepFiles; 44 _tempDir = tempDir; 45 _files = new Hashtable(StringComparer.OrdinalIgnoreCase); 46 } 47 IDisposable.Dispose()48 void IDisposable.Dispose() 49 { 50 Dispose(true); 51 GC.SuppressFinalize(this); 52 } 53 Dispose(bool disposing)54 protected virtual void Dispose(bool disposing) 55 { 56 SafeDelete(); 57 } 58 ~TempFileCollection()59 ~TempFileCollection() 60 { 61 Dispose(false); 62 } 63 AddExtension(string fileExtension)64 public string AddExtension(string fileExtension) => AddExtension(fileExtension, KeepFiles); 65 AddExtension(string fileExtension, bool keepFile)66 public string AddExtension(string fileExtension, bool keepFile) 67 { 68 if (string.IsNullOrEmpty(fileExtension)) 69 { 70 throw new ArgumentException(SR.Format(SR.InvalidNullEmptyArgument, nameof(fileExtension)), nameof(fileExtension)); 71 } 72 73 string fileName = BasePath + "." + fileExtension; 74 AddFile(fileName, keepFile); 75 return fileName; 76 } 77 AddFile(string fileName, bool keepFile)78 public void AddFile(string fileName, bool keepFile) 79 { 80 if (string.IsNullOrEmpty(fileName)) 81 { 82 throw new ArgumentException(SR.Format(SR.InvalidNullEmptyArgument, nameof(fileName)), nameof(fileName)); 83 } 84 85 if (_files[fileName] != null) 86 { 87 throw new ArgumentException(SR.Format(SR.DuplicateFileName, fileName), nameof(fileName)); 88 } 89 90 _files.Add(fileName, keepFile); 91 } 92 GetEnumerator()93 public IEnumerator GetEnumerator() => _files.Keys.GetEnumerator(); 94 IEnumerable.GetEnumerator()95 IEnumerator IEnumerable.GetEnumerator() => _files.Keys.GetEnumerator(); 96 ICollection.CopyTo(Array array, int start)97 void ICollection.CopyTo(Array array, int start) => _files.Keys.CopyTo(array, start); 98 CopyTo(string[] fileNames, int start)99 public void CopyTo(string[] fileNames, int start) => _files.Keys.CopyTo(fileNames, start); 100 101 public int Count => _files.Count; 102 103 int ICollection.Count => _files.Count; 104 105 object ICollection.SyncRoot => null; 106 107 bool ICollection.IsSynchronized => false; 108 109 public string TempDir => _tempDir ?? string.Empty; 110 111 public string BasePath 112 { 113 get 114 { 115 EnsureTempNameCreated(); 116 return _basePath; 117 } 118 } 119 EnsureTempNameCreated()120 private void EnsureTempNameCreated() 121 { 122 if (_basePath == null) 123 { 124 string tempFileName = null; 125 bool uniqueFile = false; 126 int retryCount = 5000; 127 do 128 { 129 _basePath = Path.Combine( 130 string.IsNullOrEmpty(TempDir) ? Path.GetTempPath() : TempDir, 131 Path.GetFileNameWithoutExtension(Path.GetRandomFileName())); 132 tempFileName = _basePath + ".tmp"; 133 134 try 135 { 136 new FileStream(tempFileName, FileMode.CreateNew, FileAccess.Write).Dispose(); 137 uniqueFile = true; 138 } 139 catch (IOException ex) 140 { 141 retryCount--; 142 if (retryCount == 0 || ex is DirectoryNotFoundException) 143 { 144 throw; 145 } 146 uniqueFile = false; 147 } 148 } while (!uniqueFile); 149 _files.Add(tempFileName, KeepFiles); 150 } 151 } 152 153 public bool KeepFiles { get; set; } 154 KeepFile(string fileName)155 private bool KeepFile(string fileName) 156 { 157 object keep = _files[fileName]; 158 return keep != null ? (bool)keep : false; 159 } 160 Delete()161 public void Delete() 162 { 163 SafeDelete(); 164 } 165 Delete(string fileName)166 internal void Delete(string fileName) 167 { 168 try 169 { 170 File.Delete(fileName); 171 } 172 catch 173 { 174 // Ignore all exceptions 175 } 176 } 177 SafeDelete()178 internal void SafeDelete() 179 { 180 if (_files != null && _files.Count > 0) 181 { 182 string[] fileNames = new string[_files.Count]; 183 _files.Keys.CopyTo(fileNames, 0); 184 foreach (string fileName in fileNames) 185 { 186 if (!KeepFile(fileName)) 187 { 188 Delete(fileName); 189 _files.Remove(fileName); 190 } 191 } 192 } 193 } 194 } 195 } 196