1 //------------------------------------------------------------------------------
2 // <copyright file="PreservationFileReader.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6 
7 
8 
9 namespace System.Web.Compilation {
10 
11 using System;
12 using System.IO;
13 using System.Collections;
14 using System.Diagnostics.CodeAnalysis;
15 using System.Globalization;
16 using System.Xml;
17 using System.Security;
18 using System.Web.Configuration;
19 using System.Web.Util;
20 using System.Web.UI;
21 
22 internal class PreservationFileReader {
23 
24     private XmlNode _root;
25     private bool _precompilationMode;
26     private DiskBuildResultCache _diskCache;
27 
28     private ArrayList _sourceDependencies;
29 
PreservationFileReader(DiskBuildResultCache diskCache, bool precompilationMode)30     internal PreservationFileReader(DiskBuildResultCache diskCache, bool precompilationMode) {
31         _diskCache = diskCache;
32         _precompilationMode = precompilationMode;
33     }
34 
ReadBuildResultFromFile(VirtualPath virtualPath, string preservationFile, long hashCode, bool ensureIsUpToDate)35     internal BuildResult ReadBuildResultFromFile(VirtualPath virtualPath, string preservationFile, long hashCode, bool ensureIsUpToDate) {
36 
37         // Ignore if the preservation file doesn't exist
38         if (!FileUtil.FileExists(preservationFile)) {
39             Debug.Trace("PreservationFileReader", "Can't find preservation file " + Path.GetFileName(preservationFile));
40             return null;
41         }
42 
43         BuildResult result = null;
44         try {
45             result = ReadFileInternal(virtualPath, preservationFile, hashCode, ensureIsUpToDate);
46         }
47         catch (SecurityException) {
48             // We eat all exceptions, except for SecurityException's, because they
49             // are ususally a sign that something is not set up correctly, and we
50             // don't want to lose the stack (VSWhidbey 269566)
51             throw;
52         }
53         catch {
54             if (!_precompilationMode) {
55                 // The preservation file can't be used, so get rid of it
56                 Util.RemoveOrRenameFile(preservationFile);
57             }
58         }
59 
60         return result;
61     }
62 
63     [SuppressMessage("Microsoft.Security", "MSEC1207:UseXmlReaderForLoad", Justification = "Xml file is created by us and only accessible to admins.")]
64     [SuppressMessage("Microsoft.Security.Xml", "CA3056:UseXmlReaderForLoad", Justification = "Xml file is created by us and only accessible to admins.")]
ReadFileInternal(VirtualPath virtualPath, string preservationFile, long hashCode, bool ensureIsUpToDate)65     private BuildResult ReadFileInternal(VirtualPath virtualPath, string preservationFile, long hashCode, bool ensureIsUpToDate) {
66 
67         XmlDocument doc = new XmlDocument();
68 
69         doc.Load(preservationFile);
70 
71         // Get the root element, and make sure it's what we expect
72         _root = doc.DocumentElement;
73         Debug.Assert(_root != null && _root.Name == "preserve", "_root != null && _root.Name == \"preserve\"");
74         if (_root == null || _root.Name != "preserve")
75             return null;
76 
77         // Get the type of the BuildResult preserved in this file
78         string resultTypeCodeString = GetAttribute("resultType");
79         BuildResultTypeCode resultTypeCode = (BuildResultTypeCode)Int32.Parse(
80             resultTypeCodeString, CultureInfo.InvariantCulture);
81 
82         // Get the config path that affects this BuildResult if one wasn't passed in.
83         // Note that the passed in path may be different with Sharepoint-like ghosting (VSWhidbey 343230)
84         if (virtualPath == null)
85             virtualPath = VirtualPath.Create(GetAttribute("virtualPath"));
86 
87         long savedHash = 0;
88         string savedFileHash = null;
89 
90         // Ignore dependencies in precompilation mode
91         if (!_precompilationMode) {
92             // Read the saved hash from the preservation file
93             string hashString = GetAttribute("hash");
94             Debug.Assert(hashString != null, "hashString != null");
95             if (hashString == null)
96                 return null;
97 
98             // Parse the saved hash string as an hex int
99             savedHash = Int64.Parse(hashString, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
100 
101             // Read the saved file hash from the preservation file.  This is the hash the represents
102             // the state of all the virtual files that the build result depends on.
103             savedFileHash = GetAttribute("filehash");
104         }
105 
106         // Create the BuildResult accordingly
107         BuildResult result = BuildResult.CreateBuildResultFromCode(resultTypeCode, virtualPath);
108 
109         // Ignore dependencies in precompilation mode
110         if (!_precompilationMode) {
111 
112             ReadDependencies();
113             if (_sourceDependencies != null)
114                 result.SetVirtualPathDependencies(_sourceDependencies);
115 
116             result.VirtualPathDependenciesHash = savedFileHash;
117 
118             // Check if the build result is up to date
119             bool outOfDate = false;
120             if (!result.IsUpToDate(virtualPath, ensureIsUpToDate)) {
121                 Debug.Trace("PreservationFileReader", Path.GetFileName(preservationFile) +
122                     " is out of date (IsUpToDate==false)");
123 
124                 outOfDate = true;
125             }
126             else {
127 
128                 // The virtual paths hash code was up to date, so check the
129                 // other hash code.
130 
131                 // Get the current hash code
132                 long currentHash = result.ComputeHashCode(hashCode);
133 
134                 // If the hash doesn't match, the preserved data is out of date
135                 if (currentHash == 0 || currentHash != savedHash) {
136                     outOfDate = true;
137                     Debug.Trace("PreservationFileReader", Path.GetFileName(preservationFile) +
138                         " is out of date (ComputeHashCode)");
139                 }
140             }
141 
142             if (outOfDate) {
143                 bool gotLock = false;
144                 try {
145                     // We need to delete the preservation file together with the assemblies/pdbs
146                     // under the same lock so to avoid bad interleaving where one process
147                     // deletes the .compiled file that another process just created, orphaning
148                     // the files generated by the other process.
149                     // (Dev10 bug 791299)
150                     CompilationLock.GetLock(ref gotLock);
151 
152                     // Give the BuildResult a chance to do some cleanup
153                     result.RemoveOutOfDateResources(this);
154 
155                     // The preservation file is not useable, so delete it
156                     File.Delete(preservationFile);
157                 }
158                 finally {
159                     // Always release the mutex if we had taken it
160                     if (gotLock) {
161                         CompilationLock.ReleaseLock();
162                     }
163                 }
164                 return null;
165             }
166         }
167 
168         // Ask the BuildResult to read the data it needs
169         result.GetPreservedAttributes(this);
170 
171         return result;
172     }
173 
ReadDependencies()174     private void ReadDependencies() {
175 
176         IEnumerator childEnumerator = _root.ChildNodes.GetEnumerator();
177         while (childEnumerator.MoveNext()) {
178             XmlNode dependenciesNode = (XmlNode)childEnumerator.Current;
179             if (dependenciesNode.NodeType != XmlNodeType.Element)
180                 continue;
181 
182             // verify no unrecognized attributes
183             Debug.Assert(dependenciesNode.Attributes.Count == 0);
184 
185             switch (dependenciesNode.Name) {
186             case PreservationFileWriter.fileDependenciesTagName:
187                 Debug.Assert(_sourceDependencies == null);
188                 _sourceDependencies = ReadDependencies(dependenciesNode,
189                     PreservationFileWriter.fileDependencyTagName);
190                 break;
191 
192             default:
193                 Debug.Assert(false, dependenciesNode.Name);
194                 break;
195             }
196         }
197     }
198 
ReadDependencies(XmlNode parent, string tagName)199     private ArrayList ReadDependencies(XmlNode parent, string tagName) {
200 
201         ArrayList dependencies = new ArrayList();
202 
203         IEnumerator childEnumerator = parent.ChildNodes.GetEnumerator();
204         while (childEnumerator.MoveNext()) {
205             XmlNode dependencyNode = (XmlNode)childEnumerator.Current;
206             if (dependencyNode.NodeType != XmlNodeType.Element)
207                 continue;
208 
209             Debug.Assert(dependencyNode.Name.Equals(tagName));
210             if (!dependencyNode.Name.Equals(tagName))
211                 break;
212 
213             string fileName = HandlerBase.RemoveAttribute(dependencyNode, "name");
214 
215             Debug.Assert(fileName != null, "fileName != null");
216 
217             // verify no unrecognized attributes
218             Debug.Assert(dependencyNode.Attributes.Count == 0);
219 
220             if (fileName == null)
221                 return null;
222 
223             dependencies.Add(fileName);
224         }
225 
226         return dependencies;
227     }
228 
GetAttribute(string name)229     internal string GetAttribute(string name) {
230         return HandlerBase.RemoveAttribute(_root, name);
231     }
232 
233     internal DiskBuildResultCache DiskCache {
234         get { return _diskCache; }
235     }
236 }
237 
238 }
239