1 //------------------------------------------------------------------------------
2 // <copyright file="TempFiles.cs" company="Microsoft">
3 //
4 // <OWNER>Microsoft</OWNER>
5 //     Copyright (c) Microsoft Corporation.  All rights reserved.
6 // </copyright>
7 //------------------------------------------------------------------------------
8 
9 namespace System.CodeDom.Compiler {
10     using System;
11     using System.Collections;
12     using System.Diagnostics;
13     using System.IO;
14     using System.Runtime.InteropServices;
15     using System.Text;
16     using Microsoft.Win32;
17     using System.Security;
18     using System.Security.Permissions;
19     using System.Security.Principal;
20     using System.ComponentModel;
21     using System.Security.Cryptography;
22     using System.Globalization;
23     using System.Runtime.Versioning;
24 
25     /// <devdoc>
26     ///    <para>Represents a collection of temporary file names that are all based on a
27     ///       single base filename located in a temporary directory.</para>
28     /// </devdoc>
29     [PermissionSet(SecurityAction.LinkDemand, Name="FullTrust")]
30     [PermissionSet(SecurityAction.InheritanceDemand, Name="FullTrust")]
31     [Serializable]
32     public class TempFileCollection : ICollection, IDisposable {
33         string basePath;
34         string tempDir;
35         bool keepFiles;
36         Hashtable files;
37 
38         /// <devdoc>
39         ///    <para>[To be supplied.]</para>
40         /// </devdoc>
TempFileCollection()41         public TempFileCollection() : this(null, false) {
42         }
43 
44         /// <devdoc>
45         ///    <para>[To be supplied.]</para>
46         /// </devdoc>
TempFileCollection(string tempDir)47         public TempFileCollection(string tempDir) : this(tempDir, false) {
48         }
49 
50         /// <devdoc>
51         ///    <para>[To be supplied.]</para>
52         /// </devdoc>
TempFileCollection(string tempDir, bool keepFiles)53         public TempFileCollection(string tempDir, bool keepFiles) {
54             this.keepFiles = keepFiles;
55             this.tempDir = tempDir;
56 #if !FEATURE_CASE_SENSITIVE_FILESYSTEM
57             files = new Hashtable(StringComparer.OrdinalIgnoreCase);
58 #else
59             files = new Hashtable();
60 #endif
61         }
62 
63         /// <internalonly/>
64         /// <devdoc>
65         /// <para> To allow it's stuff to be cleaned up</para>
66         /// </devdoc>
IDisposable.Dispose()67         void IDisposable.Dispose() {
68             Dispose(true);
69             GC.SuppressFinalize(this);
70         }
Dispose(bool disposing)71         protected virtual void Dispose(bool disposing) {
72             // It is safe to call Delete from here even if Dispose is called from Finalizer
73             // because the graph of objects is guaranteed to be there and
74             // neither Hashtable nor String have a finalizer of their own that could
75             // be called before TempFileCollection Finalizer
76             Delete();
77         }
78 
79         /// <devdoc>
80         ///    <para>[To be supplied.]</para>
81         /// </devdoc>
~TempFileCollection()82         ~TempFileCollection() {
83             Dispose(false);
84         }
85 
86         /// <devdoc>
87         ///    <para>[To be supplied.]</para>
88         /// </devdoc>
AddExtension(string fileExtension)89         public string AddExtension(string fileExtension) {
90             return AddExtension(fileExtension, keepFiles);
91         }
92 
93         /// <devdoc>
94         ///    <para>[To be supplied.]</para>
95         /// </devdoc>
AddExtension(string fileExtension, bool keepFile)96         public string AddExtension(string fileExtension, bool keepFile) {
97             if (fileExtension == null || fileExtension.Length == 0)
98                 throw new ArgumentException(SR.GetString(SR.InvalidNullEmptyArgument, "fileExtension"), "fileExtension");  // fileExtension not specified
99             string fileName = BasePath + "." + fileExtension;
100             AddFile(fileName, keepFile);
101             return fileName;
102         }
103 
104         /// <devdoc>
105         ///    <para>[To be supplied.]</para>
106         /// </devdoc>
AddFile(string fileName, bool keepFile)107         public void AddFile(string fileName, bool keepFile) {
108             if (fileName == null || fileName.Length == 0)
109                 throw new ArgumentException(SR.GetString(SR.InvalidNullEmptyArgument, "fileName"), "fileName");  // fileName not specified
110 
111             if (files[fileName] != null)
112                 throw new ArgumentException(SR.GetString(SR.DuplicateFileName, fileName), "fileName");  // duplicate fileName
113             files.Add(fileName, (object)keepFile);
114         }
115 
116         /// <devdoc>
117         ///    <para>[To be supplied.]</para>
118         /// </devdoc>
GetEnumerator()119         public IEnumerator GetEnumerator() {
120             return files.Keys.GetEnumerator();
121         }
122 
123         /// <internalonly/>
IEnumerable.GetEnumerator()124         IEnumerator IEnumerable.GetEnumerator() {
125             return files.Keys.GetEnumerator();
126         }
127 
128         /// <internalonly/>
ICollection.CopyTo(Array array, int start)129         void ICollection.CopyTo(Array array, int start) {
130             files.Keys.CopyTo(array, start);
131         }
132 
133         /// <devdoc>
134         ///    <para>[To be supplied.]</para>
135         /// </devdoc>
CopyTo(string[] fileNames, int start)136         public void CopyTo(string[] fileNames, int start) {
137             files.Keys.CopyTo(fileNames, start);
138         }
139 
140         /// <devdoc>
141         ///    <para>[To be supplied.]</para>
142         /// </devdoc>
143         public int Count {
144             get {
145                 return files.Count;
146             }
147         }
148 
149         /// <internalonly/>
150         int ICollection.Count {
151             get { return files.Count; }
152         }
153 
154         /// <internalonly/>
155         object ICollection.SyncRoot {
156             get { return null; }
157         }
158 
159         /// <internalonly/>
160         bool ICollection.IsSynchronized {
161             get { return false; }
162         }
163 
164         /// <devdoc>
165         ///    <para>[To be supplied.]</para>
166         /// </devdoc>
167         public string TempDir {
168             get { return tempDir == null ? string.Empty : tempDir; }
169         }
170 
171         /// <devdoc>
172         ///    <para>[To be supplied.]</para>
173         /// </devdoc>
174         public string BasePath {
175             get {
176                 EnsureTempNameCreated();
177                 return basePath;
178             }
179         }
180 
181         [ResourceExposure(ResourceScope.None)]
182         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
EnsureTempNameCreated()183         void EnsureTempNameCreated() {
184             if (basePath == null) {
185 
186                 string tempFileName = null;
187                 FileStream tempFileStream;
188                 bool uniqueFile = false;
189                 int retryCount = 5000;
190                 do {
191                     try {
192                         basePath = GetTempFileName(TempDir);
193 
194                         string full = Path.GetFullPath(basePath);
195 
196                         new FileIOPermission(FileIOPermissionAccess.AllAccess, full).Demand();
197 
198                         // make sure the filename is unique.
199                         tempFileName = basePath + ".tmp";
200                         using (tempFileStream = new FileStream(tempFileName, FileMode.CreateNew, FileAccess.Write)) { }
201                         uniqueFile = true;
202                     }
203                     catch (IOException e) {
204                         retryCount--;
205 
206                         uint HR_ERROR_FILE_EXISTS = unchecked(((uint)0x80070000) | NativeMethods.ERROR_FILE_EXISTS);
207                         if (retryCount == 0 || Marshal.GetHRForException(e) != HR_ERROR_FILE_EXISTS)
208                             throw;
209 
210                         uniqueFile = false;
211                     }
212                 }while (!uniqueFile);
213                 files.Add(tempFileName, keepFiles);
214 
215             }
216         }
217 
218         /// <devdoc>
219         ///    <para>[To be supplied.]</para>
220         /// </devdoc>
221         public bool KeepFiles {
222             get { return keepFiles; }
223             set { keepFiles = value; }
224         }
225 
KeepFile(string fileName)226         bool KeepFile(string fileName) {
227             object keep = files[fileName];
228             if (keep == null) return false;
229             return (bool)keep;
230         }
231 
232         /// <devdoc>
233         ///    <para>[To be supplied.]</para>
234         /// </devdoc>
235         [ResourceExposure(ResourceScope.None)]
236         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
Delete()237         public void Delete() {
238             if (files != null && files.Count > 0) {
239                 string[] fileNames = new string[files.Count];
240                 files.Keys.CopyTo(fileNames, 0);
241                 foreach (string fileName in fileNames) {
242                     if (!KeepFile(fileName)) {
243                         Delete(fileName);
244                         files.Remove(fileName);
245                     }
246                 }
247             }
248         }
249 
250         // This function deletes files after reverting impersonation.
251         [ResourceExposure(ResourceScope.None)]
252         [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
SafeDelete()253         internal void SafeDelete() {
254 #if !FEATURE_PAL
255             WindowsImpersonationContext impersonation = Executor.RevertImpersonation();
256 #endif
257             try{
258                 Delete();
259             }
260             finally {
261 #if !FEATURE_PAL
262                 Executor.ReImpersonate(impersonation);
263 #endif
264             }
265         }
266 
267         [ResourceExposure(ResourceScope.Machine)]
268         [ResourceConsumption(ResourceScope.Machine)]
Delete(string fileName)269         void Delete(string fileName) {
270             try {
271                 File.Delete(fileName);
272             }
273             catch {
274                 // Ignore all exceptions
275             }
276         }
277 
278         [ResourceExposure(ResourceScope.Machine)]
279         [ResourceConsumption(ResourceScope.Machine)]
GetTempFileName(string tempDir)280         static string GetTempFileName(string tempDir) {
281             string fileName;
282             if (String.IsNullOrEmpty(tempDir)) tempDir = Path.GetTempPath();
283 
284             string randomFileName = Path.GetFileNameWithoutExtension(Path.GetRandomFileName());
285 
286             if (tempDir.EndsWith("\\", StringComparison.Ordinal))
287                 fileName = tempDir + randomFileName;
288             else
289                 fileName = tempDir + "\\" + randomFileName;
290 
291             return fileName;
292         }
293     }
294 }
295