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