Eric Beckmann | c152744 | 2017-07-20 21:42:04 +0000 | [diff] [blame] | 1 | //===-- WindowsManifestMerger.cpp ------------------------------*- C++ -*-===// |
| 2 | // |
| 3 | // The LLVM Compiler Infrastructure |
| 4 | // |
| 5 | // This file is distributed under the University of Illinois Open Source |
| 6 | // License. See LICENSE.TXT for details. |
| 7 | // |
| 8 | //===---------------------------------------------------------------------===// |
| 9 | // |
| 10 | // This file implements the .manifest merger class. |
| 11 | // |
| 12 | //===---------------------------------------------------------------------===// |
| 13 | |
Eric Beckmann | f74bed2 | 2017-07-26 01:21:55 +0000 | [diff] [blame] | 14 | #include "llvm/WindowsManifest/WindowsManifestMerger.h" |
Eric Beckmann | fa58033 | 2017-09-14 23:01:13 +0000 | [diff] [blame] | 15 | #include "llvm/Config/config.h" |
Eric Beckmann | c152744 | 2017-07-20 21:42:04 +0000 | [diff] [blame] | 16 | #include "llvm/Support/MemoryBuffer.h" |
| 17 | |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 18 | #include <map> |
| 19 | |
| 20 | #if LLVM_LIBXML2_ENABLED |
| 21 | #include <libxml/xmlreader.h> |
| 22 | #endif |
Eric Beckmann | c152744 | 2017-07-20 21:42:04 +0000 | [diff] [blame] | 23 | |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 24 | #define TO_XML_CHAR(X) reinterpret_cast<const unsigned char *>(X) |
| 25 | #define FROM_XML_CHAR(X) reinterpret_cast<const char *>(X) |
| 26 | |
| 27 | using namespace llvm; |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 28 | using namespace windows_manifest; |
Eric Beckmann | c152744 | 2017-07-20 21:42:04 +0000 | [diff] [blame] | 29 | |
| 30 | char WindowsManifestError::ID = 0; |
| 31 | |
| 32 | WindowsManifestError::WindowsManifestError(const Twine &Msg) : Msg(Msg.str()) {} |
| 33 | |
| 34 | void WindowsManifestError::log(raw_ostream &OS) const { OS << Msg; } |
| 35 | |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 36 | class WindowsManifestMerger::WindowsManifestMergerImpl { |
| 37 | public: |
| 38 | ~WindowsManifestMergerImpl(); |
| 39 | Error merge(const MemoryBuffer &Manifest); |
| 40 | std::unique_ptr<MemoryBuffer> getMergedManifest(); |
| 41 | |
| 42 | private: |
| 43 | static void errorCallback(void *Ctx, const char *Format, ...); |
| 44 | Error getParseError(); |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 45 | #if LLVM_LIBXML2_ENABLED |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 46 | xmlDocPtr CombinedDoc = nullptr; |
| 47 | std::vector<xmlDocPtr> MergedDocs; |
Vitaly Buka | 074e282 | 2017-09-02 03:15:13 +0000 | [diff] [blame] | 48 | |
| 49 | bool Merged = false; |
| 50 | struct XmlDeleter { |
| 51 | void operator()(xmlChar *Ptr) { xmlFree(Ptr); } |
| 52 | void operator()(xmlDoc *Ptr) { xmlFreeDoc(Ptr); } |
| 53 | }; |
| 54 | int BufferSize = 0; |
| 55 | std::unique_ptr<xmlChar, XmlDeleter> Buffer; |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 56 | #endif |
| 57 | bool ParseErrorOccurred = false; |
| 58 | }; |
| 59 | |
| 60 | #if LLVM_LIBXML2_ENABLED |
| 61 | |
| 62 | static const std::pair<StringRef, StringRef> MtNsHrefsPrefixes[] = { |
| 63 | {"urn:schemas-microsoft-com:asm.v1", "ms_asmv1"}, |
| 64 | {"urn:schemas-microsoft-com:asm.v2", "ms_asmv2"}, |
| 65 | {"urn:schemas-microsoft-com:asm.v3", "ms_asmv3"}, |
| 66 | {"http://schemas.microsoft.com/SMI/2005/WindowsSettings", |
| 67 | "ms_windowsSettings"}, |
| 68 | {"urn:schemas-microsoft-com:compatibility.v1", "ms_compatibilityv1"}}; |
| 69 | |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 70 | static bool xmlStringsEqual(const unsigned char *A, const unsigned char *B) { |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 71 | // Handle null pointers. Comparison of 2 null pointers returns true because |
| 72 | // this indicates the prefix of a default namespace. |
| 73 | if (!A || !B) |
| 74 | return A == B; |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 75 | return strcmp(FROM_XML_CHAR(A), FROM_XML_CHAR(B)) == 0; |
| 76 | } |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 77 | |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 78 | static bool isMergeableElement(const unsigned char *ElementName) { |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 79 | for (StringRef S : {"application", "assembly", "assemblyIdentity", |
| 80 | "compatibility", "noInherit", "requestedExecutionLevel", |
| 81 | "requestedPrivileges", "security", "trustInfo"}) { |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 82 | if (S == FROM_XML_CHAR(ElementName)) { |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 83 | return true; |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 84 | } |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 85 | } |
| 86 | return false; |
| 87 | } |
| 88 | |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 89 | static xmlNodePtr getChildWithName(xmlNodePtr Parent, |
| 90 | const unsigned char *ElementName) { |
| 91 | for (xmlNodePtr Child = Parent->children; Child; Child = Child->next) { |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 92 | if (xmlStringsEqual(Child->name, ElementName)) { |
| 93 | return Child; |
| 94 | } |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 95 | } |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 96 | return nullptr; |
| 97 | } |
| 98 | |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 99 | static xmlAttrPtr getAttribute(xmlNodePtr Node, |
| 100 | const unsigned char *AttributeName) { |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 101 | for (xmlAttrPtr Attribute = Node->properties; Attribute != nullptr; |
| 102 | Attribute = Attribute->next) { |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 103 | if (xmlStringsEqual(Attribute->name, AttributeName)) { |
| 104 | return Attribute; |
| 105 | } |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 106 | } |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 107 | return nullptr; |
| 108 | } |
| 109 | |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 110 | // Check if namespace specified by HRef1 overrides that of HRef2. |
| 111 | static bool namespaceOverrides(const unsigned char *HRef1, |
| 112 | const unsigned char *HRef2) { |
| 113 | auto HRef1Position = llvm::find_if( |
| 114 | MtNsHrefsPrefixes, [=](const std::pair<StringRef, StringRef> &Element) { |
| 115 | return xmlStringsEqual(HRef1, TO_XML_CHAR(Element.first.data())); |
| 116 | }); |
| 117 | auto HRef2Position = llvm::find_if( |
| 118 | MtNsHrefsPrefixes, [=](const std::pair<StringRef, StringRef> &Element) { |
| 119 | return xmlStringsEqual(HRef2, TO_XML_CHAR(Element.first.data())); |
| 120 | }); |
| 121 | return HRef1Position < HRef2Position; |
| 122 | } |
| 123 | |
| 124 | // Search for prefix-defined namespace specified by HRef, starting on Node and |
| 125 | // continuing recursively upwards. Returns the namespace or nullptr if not |
| 126 | // found. |
| 127 | static xmlNsPtr search(const unsigned char *HRef, xmlNodePtr Node) { |
| 128 | for (xmlNsPtr Def = Node->nsDef; Def; Def = Def->next) { |
| 129 | if (Def->prefix && xmlStringsEqual(Def->href, HRef)) { |
| 130 | return Def; |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 131 | } |
| 132 | } |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 133 | if (Node->parent) { |
| 134 | return search(HRef, Node->parent); |
| 135 | } |
| 136 | return nullptr; |
| 137 | } |
| 138 | |
| 139 | // Return the prefix that corresponds to the HRef. If HRef is not a recognized |
| 140 | // URI, then just return the HRef itself to use as the prefix. |
| 141 | static const unsigned char *getPrefixForHref(const unsigned char *HRef) { |
| 142 | for (auto &Ns : MtNsHrefsPrefixes) { |
| 143 | if (xmlStringsEqual(HRef, TO_XML_CHAR(Ns.first.data()))) { |
| 144 | return TO_XML_CHAR(Ns.second.data()); |
| 145 | } |
| 146 | } |
| 147 | return HRef; |
| 148 | } |
| 149 | |
| 150 | // Search for prefix-defined namespace specified by HRef, starting on Node and |
| 151 | // continuing recursively upwards. If it is found, then return it. If it is |
| 152 | // not found, then prefix-define that namespace on the node and return a |
| 153 | // reference to it. |
| 154 | static Expected<xmlNsPtr> searchOrDefine(const unsigned char *HRef, |
| 155 | xmlNodePtr Node) { |
| 156 | if (xmlNsPtr Def = search(HRef, Node)) |
| 157 | return Def; |
| 158 | if (xmlNsPtr Def = xmlNewNs(Node, HRef, getPrefixForHref(HRef))) |
| 159 | return Def; |
| 160 | return make_error<WindowsManifestError>("failed to create new namespace"); |
| 161 | } |
| 162 | |
| 163 | // Set the namespace of OrigionalAttribute on OriginalNode to be that of |
| 164 | // AdditionalAttribute's. |
| 165 | static Error copyAttributeNamespace(xmlAttrPtr OriginalAttribute, |
| 166 | xmlNodePtr OriginalNode, |
| 167 | xmlAttrPtr AdditionalAttribute) { |
| 168 | |
| 169 | Expected<xmlNsPtr> ExplicitOrError = |
| 170 | searchOrDefine(AdditionalAttribute->ns->href, OriginalNode); |
| 171 | if (!ExplicitOrError) |
| 172 | return ExplicitOrError.takeError(); |
| 173 | OriginalAttribute->ns = std::move(ExplicitOrError.get()); |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 174 | return Error::success(); |
| 175 | } |
| 176 | |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 177 | // Return the corresponding namespace definition for the prefix, defined on the |
| 178 | // given Node. Returns nullptr if there is no such definition. |
| 179 | static xmlNsPtr getNamespaceWithPrefix(const unsigned char *Prefix, |
| 180 | xmlNodePtr Node) { |
| 181 | if (Node == nullptr) |
| 182 | return nullptr; |
| 183 | for (xmlNsPtr Def = Node->nsDef; Def; Def = Def->next) { |
| 184 | if (xmlStringsEqual(Def->prefix, Prefix)) { |
| 185 | return Def; |
| 186 | } |
| 187 | } |
| 188 | return nullptr; |
| 189 | } |
| 190 | |
| 191 | // Search for the closest inheritable default namespace, starting on (and |
| 192 | // including) the Node and traveling upwards through parent nodes. Returns |
| 193 | // nullptr if there are no inheritable default namespaces. |
| 194 | static xmlNsPtr getClosestDefault(xmlNodePtr Node) { |
| 195 | if (xmlNsPtr Ret = getNamespaceWithPrefix(nullptr, Node)) |
| 196 | return Ret; |
| 197 | if (Node->parent == nullptr) |
| 198 | return nullptr; |
| 199 | return getClosestDefault(Node->parent); |
| 200 | } |
| 201 | |
| 202 | // Merge the attributes of AdditionalNode into OriginalNode. If attributes |
| 203 | // with identical types are present, they are not duplicated but rather if |
| 204 | // their values are not consistent and error is thrown. In addition, the |
| 205 | // higher priority namespace is used for each attribute, EXCEPT in the case |
| 206 | // of merging two default namespaces and the lower priority namespace |
| 207 | // definition occurs closer than the higher priority one. |
| 208 | static Error mergeAttributes(xmlNodePtr OriginalNode, |
| 209 | xmlNodePtr AdditionalNode) { |
| 210 | xmlNsPtr ClosestDefault = getClosestDefault(OriginalNode); |
| 211 | for (xmlAttrPtr Attribute = AdditionalNode->properties; Attribute; |
| 212 | Attribute = Attribute->next) { |
| 213 | if (xmlAttrPtr OriginalAttribute = |
| 214 | getAttribute(OriginalNode, Attribute->name)) { |
| 215 | if (!xmlStringsEqual(OriginalAttribute->children->content, |
| 216 | Attribute->children->content)) { |
| 217 | return make_error<WindowsManifestError>( |
| 218 | Twine("conflicting attributes for ") + |
| 219 | FROM_XML_CHAR(OriginalNode->name)); |
| 220 | } |
| 221 | if (!Attribute->ns) { |
| 222 | continue; |
| 223 | } |
| 224 | if (!OriginalAttribute->ns) { |
| 225 | if (auto E = copyAttributeNamespace(OriginalAttribute, OriginalNode, |
| 226 | Attribute)) { |
| 227 | return E; |
| 228 | } |
| 229 | continue; |
| 230 | } |
| 231 | if (namespaceOverrides(OriginalAttribute->ns->href, |
| 232 | Attribute->ns->href)) { |
| 233 | // In this case, the original attribute has a higher priority namespace |
| 234 | // than the incomiing attribute, however the namespace definition of |
| 235 | // the lower priority namespace occurs first traveling upwards in the |
| 236 | // tree. Therefore the lower priority namespace is applied. |
| 237 | if (!OriginalAttribute->ns->prefix && !Attribute->ns->prefix && |
| 238 | ClosestDefault && |
| 239 | xmlStringsEqual(Attribute->ns->href, ClosestDefault->href)) { |
| 240 | if (auto E = copyAttributeNamespace(OriginalAttribute, OriginalNode, |
| 241 | Attribute)) { |
| 242 | return E; |
| 243 | } |
| 244 | continue; |
| 245 | } |
| 246 | continue; |
| 247 | // This covers the case where the incoming attribute has the higher |
| 248 | // priority. The higher priority namespace is applied in all cases |
| 249 | // EXCEPT when both of the namespaces are default inherited, and the |
| 250 | // closest inherited default is the lower priority one. |
| 251 | } |
| 252 | if (Attribute->ns->prefix || OriginalAttribute->ns->prefix || |
| 253 | (ClosestDefault && !xmlStringsEqual(OriginalAttribute->ns->href, |
| 254 | ClosestDefault->href))) { |
| 255 | if (auto E = copyAttributeNamespace(OriginalAttribute, OriginalNode, |
| 256 | Attribute)) { |
| 257 | return E; |
| 258 | } |
| 259 | continue; |
| 260 | } |
| 261 | continue; |
| 262 | } |
| 263 | // If the incoming attribute is not already found on the node, append it |
| 264 | // to the end of the properties list. Also explicitly apply its |
| 265 | // namespace as a prefix because it might be contained in a separate |
| 266 | // namespace that doesn't use the attribute. |
| 267 | xmlAttrPtr NewProp = |
| 268 | xmlNewProp(OriginalNode, Attribute->name, Attribute->children->content); |
| 269 | Expected<xmlNsPtr> ExplicitOrError = |
| 270 | searchOrDefine(Attribute->ns->href, OriginalNode); |
| 271 | if (!ExplicitOrError) |
| 272 | return ExplicitOrError.takeError(); |
| 273 | NewProp->ns = std::move(ExplicitOrError.get()); |
| 274 | } |
| 275 | return Error::success(); |
| 276 | } |
| 277 | |
| 278 | // Given two nodes, return the one with the higher priority namespace. |
| 279 | static xmlNodePtr getDominantNode(xmlNodePtr Node1, xmlNodePtr Node2) { |
| 280 | |
| 281 | if (!Node1 || !Node1->ns) |
| 282 | return Node2; |
| 283 | if (!Node2 || !Node2->ns) |
| 284 | return Node1; |
| 285 | if (namespaceOverrides(Node1->ns->href, Node2->ns->href)) |
| 286 | return Node1; |
| 287 | return Node2; |
| 288 | } |
| 289 | |
| 290 | // Checks if this Node's namespace is inherited or one it defined itself. |
| 291 | static bool hasInheritedNs(xmlNodePtr Node) { |
| 292 | return Node->ns && Node->ns != getNamespaceWithPrefix(Node->ns->prefix, Node); |
| 293 | } |
| 294 | |
| 295 | // Check if this Node's namespace is a default namespace that it inherited, as |
| 296 | // opposed to defining itself. |
| 297 | static bool hasInheritedDefaultNs(xmlNodePtr Node) { |
| 298 | return hasInheritedNs(Node) && Node->ns->prefix == nullptr; |
| 299 | } |
| 300 | |
| 301 | // Check if this Node's namespace is a default namespace it defined itself. |
| 302 | static bool hasDefinedDefaultNamespace(xmlNodePtr Node) { |
| 303 | return Node->ns && (Node->ns == getNamespaceWithPrefix(nullptr, Node)); |
| 304 | } |
| 305 | |
| 306 | // For the given explicit prefix-definition of a namespace, travel downwards |
| 307 | // from a node recursively, and for every implicit, inherited default usage of |
| 308 | // that namespace replace it with that explicit prefix use. This is important |
| 309 | // when namespace overriding occurs when merging, so that elements unique to a |
| 310 | // namespace will still stay in that namespace. |
| 311 | static void explicateNamespace(xmlNsPtr PrefixDef, xmlNodePtr Node) { |
| 312 | // If a node as its own default namespace definition it clearly cannot have |
| 313 | // inherited the given default namespace, and neither will any of its |
| 314 | // children. |
| 315 | if (hasDefinedDefaultNamespace(Node)) |
| 316 | return; |
| 317 | if (Node->ns && xmlStringsEqual(Node->ns->href, PrefixDef->href) && |
| 318 | hasInheritedDefaultNs(Node)) |
| 319 | Node->ns = PrefixDef; |
| 320 | for (xmlAttrPtr Attribute = Node->properties; Attribute; |
| 321 | Attribute = Attribute->next) { |
| 322 | if (Attribute->ns && |
| 323 | xmlStringsEqual(Attribute->ns->href, PrefixDef->href)) { |
| 324 | Attribute->ns = PrefixDef; |
| 325 | } |
| 326 | } |
| 327 | for (xmlNodePtr Child = Node->children; Child; Child = Child->next) { |
| 328 | explicateNamespace(PrefixDef, Child); |
| 329 | } |
| 330 | } |
| 331 | |
| 332 | // Perform the namespace merge between two nodes. |
| 333 | static Error mergeNamespaces(xmlNodePtr OriginalNode, |
| 334 | xmlNodePtr AdditionalNode) { |
| 335 | // Save the original default namespace definition in case the incoming node |
| 336 | // overrides it. |
| 337 | const unsigned char *OriginalDefinedDefaultHref = nullptr; |
| 338 | if (xmlNsPtr OriginalDefinedDefaultNs = |
| 339 | getNamespaceWithPrefix(nullptr, OriginalNode)) { |
| 340 | OriginalDefinedDefaultHref = xmlStrdup(OriginalDefinedDefaultNs->href); |
| 341 | } |
| 342 | const unsigned char *NewDefinedDefaultHref = nullptr; |
| 343 | // Copy all namespace definitions. There can only be one default namespace |
| 344 | // definition per node, so the higher priority one takes precedence in the |
| 345 | // case of collision. |
| 346 | for (xmlNsPtr Def = AdditionalNode->nsDef; Def; Def = Def->next) { |
| 347 | if (xmlNsPtr OriginalNsDef = |
| 348 | getNamespaceWithPrefix(Def->prefix, OriginalNode)) { |
| 349 | if (!Def->prefix) { |
| 350 | if (namespaceOverrides(Def->href, OriginalNsDef->href)) { |
| 351 | NewDefinedDefaultHref = TO_XML_CHAR(strdup(FROM_XML_CHAR(Def->href))); |
| 352 | } |
| 353 | } else if (!xmlStringsEqual(OriginalNsDef->href, Def->href)) { |
| 354 | return make_error<WindowsManifestError>( |
| 355 | Twine("conflicting namespace definitions for ") + |
| 356 | FROM_XML_CHAR(Def->prefix)); |
| 357 | } |
| 358 | } else { |
| 359 | xmlNsPtr NewDef = xmlCopyNamespace(Def); |
| 360 | NewDef->next = OriginalNode->nsDef; |
| 361 | OriginalNode->nsDef = NewDef; |
| 362 | } |
| 363 | } |
| 364 | |
| 365 | // Check whether the original node or the incoming node has the higher |
| 366 | // priority namespace. Depending on which one is dominant, we will have |
| 367 | // to recursively apply namespace changes down to children of the original |
| 368 | // node. |
| 369 | xmlNodePtr DominantNode = getDominantNode(OriginalNode, AdditionalNode); |
| 370 | xmlNodePtr NonDominantNode = |
| 371 | DominantNode == OriginalNode ? AdditionalNode : OriginalNode; |
| 372 | if (DominantNode == OriginalNode) { |
| 373 | if (OriginalDefinedDefaultHref) { |
| 374 | xmlNsPtr NonDominantDefinedDefault = |
| 375 | getNamespaceWithPrefix(nullptr, NonDominantNode); |
| 376 | // In this case, both the nodes defined a default namespace. However |
| 377 | // the lower priority node ended up having a higher priority default |
| 378 | // definition. This can occur if the higher priority node is prefix |
| 379 | // namespace defined. In this case we have to define an explicit |
| 380 | // prefix for the overridden definition and apply it to all children |
| 381 | // who relied on that definition. |
| 382 | if (NonDominantDefinedDefault && |
| 383 | namespaceOverrides(NonDominantDefinedDefault->href, |
| 384 | OriginalDefinedDefaultHref)) { |
| 385 | Expected<xmlNsPtr> EC = |
| 386 | searchOrDefine(OriginalDefinedDefaultHref, DominantNode); |
| 387 | if (!EC) { |
| 388 | return EC.takeError(); |
| 389 | } |
| 390 | xmlNsPtr PrefixDominantDefinedDefault = std::move(EC.get()); |
| 391 | explicateNamespace(PrefixDominantDefinedDefault, DominantNode); |
| 392 | } |
| 393 | // In this case the node with a higher priority namespace did not have a |
| 394 | // default namespace definition, but the lower priority node did. In this |
| 395 | // case the new default namespace definition is copied. A side effect of |
| 396 | // this is that all children will suddenly find themselves in a different |
| 397 | // default namespace. To maintain correctness we need to ensure that all |
| 398 | // children now explicitly refer to the namespace that they had previously |
| 399 | // implicitly inherited. |
| 400 | } else if (getNamespaceWithPrefix(nullptr, NonDominantNode)) { |
| 401 | if (DominantNode->parent) { |
| 402 | xmlNsPtr ClosestDefault = getClosestDefault(DominantNode->parent); |
| 403 | Expected<xmlNsPtr> EC = |
| 404 | searchOrDefine(ClosestDefault->href, DominantNode); |
| 405 | if (!EC) { |
| 406 | return EC.takeError(); |
| 407 | } |
| 408 | xmlNsPtr ExplicitDefault = std::move(EC.get()); |
| 409 | explicateNamespace(ExplicitDefault, DominantNode); |
| 410 | } |
| 411 | } |
| 412 | } else { |
| 413 | // Covers case where the incoming node has a default namespace definition |
| 414 | // that overrides the original node's namespace. This always leads to |
| 415 | // the original node receiving that new default namespace. |
| 416 | if (hasDefinedDefaultNamespace(DominantNode)) { |
| 417 | NonDominantNode->ns = getNamespaceWithPrefix(nullptr, NonDominantNode); |
| 418 | } else { |
| 419 | // This covers the case where the incoming node either has a prefix |
| 420 | // namespace, or an inherited default namespace. Since the namespace |
| 421 | // may not yet be defined in the original tree we do a searchOrDefine |
| 422 | // for it, and then set the namespace equal to it. |
| 423 | Expected<xmlNsPtr> EC = |
| 424 | searchOrDefine(DominantNode->ns->href, NonDominantNode); |
| 425 | if (!EC) { |
| 426 | return EC.takeError(); |
| 427 | } |
| 428 | xmlNsPtr Explicit = std::move(EC.get()); |
| 429 | NonDominantNode->ns = Explicit; |
| 430 | } |
| 431 | // This covers cases where the incoming dominant node HAS a default |
| 432 | // namespace definition, but MIGHT NOT NECESSARILY be in that namespace. |
| 433 | if (xmlNsPtr DominantDefaultDefined = |
| 434 | getNamespaceWithPrefix(nullptr, DominantNode)) { |
| 435 | if (OriginalDefinedDefaultHref) { |
| 436 | if (namespaceOverrides(DominantDefaultDefined->href, |
| 437 | OriginalDefinedDefaultHref)) { |
| 438 | // In this case, the incoming node's default definition overrides |
| 439 | // the original default definition, all children who relied on that |
| 440 | // definition must be updated accordingly. |
| 441 | Expected<xmlNsPtr> EC = |
| 442 | searchOrDefine(OriginalDefinedDefaultHref, NonDominantNode); |
| 443 | if (!EC) { |
| 444 | return EC.takeError(); |
| 445 | } |
| 446 | xmlNsPtr ExplicitDefault = std::move(EC.get()); |
| 447 | explicateNamespace(ExplicitDefault, NonDominantNode); |
| 448 | } |
| 449 | } else { |
| 450 | // The original did not define a default definition, however the new |
| 451 | // default definition still applies to all children, so they must be |
| 452 | // updated to explicitly refer to the namespace they had previously |
| 453 | // been inheriting implicitly. |
| 454 | xmlNsPtr ClosestDefault = getClosestDefault(NonDominantNode); |
| 455 | Expected<xmlNsPtr> EC = |
| 456 | searchOrDefine(ClosestDefault->href, NonDominantNode); |
| 457 | if (!EC) { |
| 458 | return EC.takeError(); |
| 459 | } |
| 460 | xmlNsPtr ExplicitDefault = std::move(EC.get()); |
| 461 | explicateNamespace(ExplicitDefault, NonDominantNode); |
| 462 | } |
| 463 | } |
| 464 | } |
| 465 | if (NewDefinedDefaultHref) { |
| 466 | xmlNsPtr OriginalNsDef = getNamespaceWithPrefix(nullptr, OriginalNode); |
| 467 | xmlFree(const_cast<unsigned char *>(OriginalNsDef->href)); |
| 468 | OriginalNsDef->href = NewDefinedDefaultHref; |
| 469 | } |
| 470 | xmlFree(const_cast<unsigned char *>(OriginalDefinedDefaultHref)); |
| 471 | return Error::success(); |
| 472 | } |
| 473 | |
| 474 | static bool isRecognizedNamespace(const unsigned char *NsHref) { |
| 475 | for (auto &Ns : MtNsHrefsPrefixes) { |
| 476 | if (xmlStringsEqual(NsHref, TO_XML_CHAR(Ns.first.data()))) { |
| 477 | return true; |
| 478 | } |
| 479 | } |
| 480 | return false; |
| 481 | } |
| 482 | |
| 483 | static bool hasRecognizedNamespace(xmlNodePtr Node) { |
| 484 | return isRecognizedNamespace(Node->ns->href); |
| 485 | } |
| 486 | |
| 487 | // Ensure a node's inherited namespace is actually defined in the tree it |
| 488 | // resides in. |
| 489 | static Error reconcileNamespaces(xmlNodePtr Node) { |
| 490 | if (!Node) { |
| 491 | return Error::success(); |
| 492 | } |
| 493 | if (hasInheritedNs(Node)) { |
| 494 | Expected<xmlNsPtr> ExplicitOrError = searchOrDefine(Node->ns->href, Node); |
| 495 | if (!ExplicitOrError) { |
| 496 | return ExplicitOrError.takeError(); |
| 497 | } |
| 498 | xmlNsPtr Explicit = std::move(ExplicitOrError.get()); |
| 499 | Node->ns = Explicit; |
| 500 | } |
| 501 | for (xmlNodePtr Child = Node->children; Child; Child = Child->next) { |
| 502 | if (auto E = reconcileNamespaces(Child)) { |
| 503 | return E; |
| 504 | } |
| 505 | } |
| 506 | return Error::success(); |
| 507 | } |
| 508 | |
| 509 | // Recursively merge the two given manifest trees, depending on which elements |
| 510 | // are of a mergeable type, and choose namespaces according to which have |
| 511 | // higher priority. |
| 512 | static Error treeMerge(xmlNodePtr OriginalRoot, xmlNodePtr AdditionalRoot) { |
| 513 | if (auto E = mergeAttributes(OriginalRoot, AdditionalRoot)) |
| 514 | return E; |
| 515 | if (auto E = mergeNamespaces(OriginalRoot, AdditionalRoot)) |
| 516 | return E; |
| 517 | xmlNodePtr AdditionalFirstChild = AdditionalRoot->children; |
Eric Beckmann | 6caf087 | 2017-07-26 18:33:21 +0000 | [diff] [blame] | 518 | xmlNode StoreNext; |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 519 | for (xmlNodePtr Child = AdditionalFirstChild; Child; Child = Child->next) { |
| 520 | xmlNodePtr OriginalChildWithName; |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 521 | if (!isMergeableElement(Child->name) || |
| 522 | !(OriginalChildWithName = |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 523 | getChildWithName(OriginalRoot, Child->name)) || |
| 524 | !hasRecognizedNamespace(Child)) { |
Eric Beckmann | 6caf087 | 2017-07-26 18:33:21 +0000 | [diff] [blame] | 525 | StoreNext.next = Child->next; |
| 526 | xmlUnlinkNode(Child); |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 527 | if (!xmlAddChild(OriginalRoot, Child)) { |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 528 | return make_error<WindowsManifestError>(Twine("could not merge ") + |
Eric Beckmann | 6caf087 | 2017-07-26 18:33:21 +0000 | [diff] [blame] | 529 | FROM_XML_CHAR(Child->name)); |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 530 | } |
| 531 | if (auto E = reconcileNamespaces(Child)) { |
| 532 | return E; |
| 533 | } |
Eric Beckmann | 6caf087 | 2017-07-26 18:33:21 +0000 | [diff] [blame] | 534 | Child = &StoreNext; |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 535 | } else if (auto E = treeMerge(OriginalChildWithName, Child)) { |
| 536 | return E; |
| 537 | } |
| 538 | } |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 539 | return Error::success(); |
| 540 | } |
| 541 | |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 542 | static void stripComments(xmlNodePtr Root) { |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 543 | xmlNode StoreNext; |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 544 | for (xmlNodePtr Child = Root->children; Child; Child = Child->next) { |
| 545 | if (!xmlStringsEqual(Child->name, TO_XML_CHAR("comment"))) { |
| 546 | stripComments(Child); |
| 547 | continue; |
| 548 | } |
| 549 | StoreNext.next = Child->next; |
| 550 | xmlNodePtr Remove = Child; |
| 551 | Child = &StoreNext; |
| 552 | xmlUnlinkNode(Remove); |
| 553 | xmlFreeNode(Remove); |
| 554 | } |
| 555 | } |
| 556 | |
| 557 | // libxml2 assumes that attributes do not inherit default namespaces, whereas |
| 558 | // the original mt.exe does make this assumption. This function reconciles |
| 559 | // this by setting all attributes to have the inherited default namespace. |
| 560 | static void setAttributeNamespaces(xmlNodePtr Node) { |
| 561 | for (xmlAttrPtr Attribute = Node->properties; Attribute; |
| 562 | Attribute = Attribute->next) { |
| 563 | if (!Attribute->ns) { |
| 564 | Attribute->ns = getClosestDefault(Node); |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 565 | } |
| 566 | } |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 567 | for (xmlNodePtr Child = Node->children; Child; Child = Child->next) { |
| 568 | setAttributeNamespaces(Child); |
| 569 | } |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 570 | } |
| 571 | |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 572 | // The merging process may create too many prefix defined namespaces. This |
| 573 | // function removes all unnecessary ones from the tree. |
| 574 | static void checkAndStripPrefixes(xmlNodePtr Node, |
| 575 | std::vector<xmlNsPtr> &RequiredPrefixes) { |
| 576 | for (xmlNodePtr Child = Node->children; Child; Child = Child->next) { |
| 577 | checkAndStripPrefixes(Child, RequiredPrefixes); |
| 578 | } |
| 579 | if (Node->ns && Node->ns->prefix != nullptr) { |
| 580 | xmlNsPtr ClosestDefault = getClosestDefault(Node); |
| 581 | if (ClosestDefault && |
| 582 | xmlStringsEqual(ClosestDefault->href, Node->ns->href)) { |
| 583 | Node->ns = ClosestDefault; |
| 584 | } else if (!llvm::is_contained(RequiredPrefixes, Node->ns)) { |
| 585 | RequiredPrefixes.push_back(Node->ns); |
| 586 | } |
| 587 | } |
| 588 | for (xmlAttrPtr Attribute = Node->properties; Attribute; |
| 589 | Attribute = Attribute->next) { |
| 590 | if (Attribute->ns && Attribute->ns->prefix != nullptr) { |
| 591 | xmlNsPtr ClosestDefault = getClosestDefault(Node); |
| 592 | if (ClosestDefault && |
| 593 | xmlStringsEqual(ClosestDefault->href, Attribute->ns->href)) { |
| 594 | Attribute->ns = ClosestDefault; |
| 595 | } else if (!llvm::is_contained(RequiredPrefixes, Node->ns)) { |
| 596 | RequiredPrefixes.push_back(Attribute->ns); |
| 597 | } |
| 598 | } |
| 599 | } |
| 600 | xmlNsPtr Prev; |
| 601 | xmlNs Temp; |
| 602 | for (xmlNsPtr Def = Node->nsDef; Def; Def = Def->next) { |
| 603 | if (!Def->prefix || llvm::is_contained(RequiredPrefixes, Def)) { |
| 604 | Prev = Def; |
| 605 | continue; |
| 606 | } |
| 607 | if (Def == Node->nsDef) { |
| 608 | Node->nsDef = Def->next; |
| 609 | } else { |
| 610 | Prev->next = Def->next; |
| 611 | } |
| 612 | Temp.next = Def->next; |
| 613 | xmlFreeNs(Def); |
| 614 | Def = &Temp; |
| 615 | } |
| 616 | } |
| 617 | |
| 618 | WindowsManifestMerger::WindowsManifestMergerImpl::~WindowsManifestMergerImpl() { |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 619 | for (auto &Doc : MergedDocs) |
| 620 | xmlFreeDoc(Doc); |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 621 | } |
| 622 | |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 623 | Error WindowsManifestMerger::WindowsManifestMergerImpl::merge( |
| 624 | const MemoryBuffer &Manifest) { |
Vitaly Buka | 074e282 | 2017-09-02 03:15:13 +0000 | [diff] [blame] | 625 | if (Merged) |
| 626 | return make_error<WindowsManifestError>( |
| 627 | "merge after getMergedManifest is not supported"); |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 628 | if (Manifest.getBufferSize() == 0) |
| 629 | return make_error<WindowsManifestError>( |
| 630 | "attempted to merge empty manifest"); |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 631 | xmlSetGenericErrorFunc((void *)this, |
| 632 | WindowsManifestMergerImpl::errorCallback); |
Vitaly Buka | 074e282 | 2017-09-02 03:15:13 +0000 | [diff] [blame] | 633 | xmlDocPtr ManifestXML = xmlReadMemory( |
| 634 | Manifest.getBufferStart(), Manifest.getBufferSize(), "manifest.xml", |
| 635 | nullptr, XML_PARSE_NOBLANKS | XML_PARSE_NODICT); |
Eric Beckmann | c152744 | 2017-07-20 21:42:04 +0000 | [diff] [blame] | 636 | xmlSetGenericErrorFunc(nullptr, nullptr); |
| 637 | if (auto E = getParseError()) |
| 638 | return E; |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 639 | xmlNodePtr AdditionalRoot = xmlDocGetRootElement(ManifestXML); |
| 640 | stripComments(AdditionalRoot); |
| 641 | setAttributeNamespaces(AdditionalRoot); |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 642 | if (CombinedDoc == nullptr) { |
| 643 | CombinedDoc = ManifestXML; |
| 644 | } else { |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 645 | xmlNodePtr CombinedRoot = xmlDocGetRootElement(CombinedDoc); |
| 646 | if (!xmlStringsEqual(CombinedRoot->name, AdditionalRoot->name) || |
| 647 | !isMergeableElement(AdditionalRoot->name) || |
| 648 | !hasRecognizedNamespace(AdditionalRoot)) { |
Eric Beckmann | 6caf087 | 2017-07-26 18:33:21 +0000 | [diff] [blame] | 649 | return make_error<WindowsManifestError>("multiple root nodes"); |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 650 | } |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 651 | if (auto E = treeMerge(CombinedRoot, AdditionalRoot)) { |
| 652 | return E; |
| 653 | } |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 654 | } |
| 655 | MergedDocs.push_back(ManifestXML); |
Eric Beckmann | c152744 | 2017-07-20 21:42:04 +0000 | [diff] [blame] | 656 | return Error::success(); |
| 657 | } |
| 658 | |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 659 | std::unique_ptr<MemoryBuffer> |
| 660 | WindowsManifestMerger::WindowsManifestMergerImpl::getMergedManifest() { |
Vitaly Buka | 074e282 | 2017-09-02 03:15:13 +0000 | [diff] [blame] | 661 | if (!Merged) { |
| 662 | Merged = true; |
| 663 | |
| 664 | if (!CombinedDoc) |
| 665 | return nullptr; |
| 666 | |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 667 | xmlNodePtr CombinedRoot = xmlDocGetRootElement(CombinedDoc); |
| 668 | std::vector<xmlNsPtr> RequiredPrefixes; |
| 669 | checkAndStripPrefixes(CombinedRoot, RequiredPrefixes); |
Vitaly Buka | 074e282 | 2017-09-02 03:15:13 +0000 | [diff] [blame] | 670 | std::unique_ptr<xmlDoc, XmlDeleter> OutputDoc( |
| 671 | xmlNewDoc((const unsigned char *)"1.0")); |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 672 | xmlDocSetRootElement(OutputDoc.get(), CombinedRoot); |
Vitaly Buka | 074e282 | 2017-09-02 03:15:13 +0000 | [diff] [blame] | 673 | assert(0 == xmlDocGetRootElement(CombinedDoc)); |
| 674 | |
Eric Beckmann | ec76cbb | 2017-07-26 00:25:12 +0000 | [diff] [blame] | 675 | xmlKeepBlanksDefault(0); |
Vitaly Buka | 074e282 | 2017-09-02 03:15:13 +0000 | [diff] [blame] | 676 | xmlChar *Buff = nullptr; |
| 677 | xmlDocDumpFormatMemoryEnc(OutputDoc.get(), &Buff, &BufferSize, "UTF-8", 1); |
| 678 | Buffer.reset(Buff); |
Eric Beckmann | c152744 | 2017-07-20 21:42:04 +0000 | [diff] [blame] | 679 | } |
Vitaly Buka | 074e282 | 2017-09-02 03:15:13 +0000 | [diff] [blame] | 680 | |
Vitaly Buka | 2ce7ffd | 2017-09-02 05:14:55 +0000 | [diff] [blame] | 681 | return BufferSize ? MemoryBuffer::getMemBufferCopy(StringRef( |
Vitaly Buka | 074e282 | 2017-09-02 03:15:13 +0000 | [diff] [blame] | 682 | FROM_XML_CHAR(Buffer.get()), (size_t)BufferSize)) |
| 683 | : nullptr; |
Eric Beckmann | c152744 | 2017-07-20 21:42:04 +0000 | [diff] [blame] | 684 | } |
| 685 | |
Eric Beckmann | e8aea29 | 2017-09-06 01:50:36 +0000 | [diff] [blame] | 686 | bool windows_manifest::isAvailable() { return true; } |
| 687 | |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 688 | #else |
| 689 | |
| 690 | WindowsManifestMerger::WindowsManifestMergerImpl::~WindowsManifestMergerImpl() { |
| 691 | } |
| 692 | |
| 693 | Error WindowsManifestMerger::WindowsManifestMergerImpl::merge( |
| 694 | const MemoryBuffer &Manifest) { |
Eric Beckmann | 3c6e126 | 2017-08-22 03:15:28 +0000 | [diff] [blame] | 695 | return make_error<WindowsManifestError>("no libxml2"); |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 696 | } |
| 697 | |
| 698 | std::unique_ptr<MemoryBuffer> |
| 699 | WindowsManifestMerger::WindowsManifestMergerImpl::getMergedManifest() { |
| 700 | return nullptr; |
| 701 | } |
| 702 | |
Eric Beckmann | e8aea29 | 2017-09-06 01:50:36 +0000 | [diff] [blame] | 703 | bool windows_manifest::isAvailable() { return false; } |
| 704 | |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 705 | #endif |
| 706 | |
| 707 | WindowsManifestMerger::WindowsManifestMerger() |
| 708 | : Impl(make_unique<WindowsManifestMergerImpl>()) {} |
| 709 | |
| 710 | WindowsManifestMerger::~WindowsManifestMerger() {} |
| 711 | |
| 712 | Error WindowsManifestMerger::merge(const MemoryBuffer &Manifest) { |
| 713 | return Impl->merge(Manifest); |
| 714 | } |
| 715 | |
| 716 | std::unique_ptr<MemoryBuffer> WindowsManifestMerger::getMergedManifest() { |
| 717 | return Impl->getMergedManifest(); |
| 718 | } |
| 719 | |
| 720 | void WindowsManifestMerger::WindowsManifestMergerImpl::errorCallback( |
| 721 | void *Ctx, const char *Format, ...) { |
| 722 | auto *Merger = (WindowsManifestMergerImpl *)Ctx; |
Eric Beckmann | c152744 | 2017-07-20 21:42:04 +0000 | [diff] [blame] | 723 | Merger->ParseErrorOccurred = true; |
| 724 | } |
| 725 | |
Eric Beckmann | 79fe536 | 2017-08-19 00:37:41 +0000 | [diff] [blame] | 726 | Error WindowsManifestMerger::WindowsManifestMergerImpl::getParseError() { |
Eric Beckmann | c152744 | 2017-07-20 21:42:04 +0000 | [diff] [blame] | 727 | if (!ParseErrorOccurred) |
| 728 | return Error::success(); |
| 729 | return make_error<WindowsManifestError>("invalid xml document"); |
| 730 | } |