aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2022-11-04 01:05:28 -0700
committerRob Mensching <rob@firegiant.com>2022-11-04 14:11:42 -0700
commitf8749f8e438b150884449ad3e2e13aed750de679 (patch)
tree7cc2f8c403d0f1bbea04153ed618897f14d688a1 /src
parent8f67ce3ce8a248b7421e14ec3dd6f65e9b53ea08 (diff)
downloadwix-f8749f8e438b150884449ad3e2e13aed750de679.tar.gz
wix-f8749f8e438b150884449ad3e2e13aed750de679.tar.bz2
wix-f8749f8e438b150884449ad3e2e13aed750de679.zip
Provide useful error message when file system operations fail
Fixes 5417
Diffstat (limited to 'src')
-rw-r--r--src/api/wix/WixToolset.Extensibility/Services/IFileSystem.cs13
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs12
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs9
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs6
-rw-r--r--src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs4
-rw-r--r--src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleEngineCommand.cs4
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Bind/AssemblyNameReader.cs2
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs2
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs2
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Bind/FileSystemManager.cs5
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessPackageSoftwareTagsCommand.cs7
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs2
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs10
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs5
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs2
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs2
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Validate/ValidateDatabaseCommand.cs4
-rw-r--r--src/wix/WixToolset.Core/Bind/TransferFilesCommand.cs12
-rw-r--r--src/wix/WixToolset.Core/CoreErrors.cs42
-rw-r--r--src/wix/WixToolset.Core/ExtensibilityServices/FileSystem.cs66
-rw-r--r--src/wix/WixToolset.Core/LayoutCreator.cs2
21 files changed, 152 insertions, 61 deletions
diff --git a/src/api/wix/WixToolset.Extensibility/Services/IFileSystem.cs b/src/api/wix/WixToolset.Extensibility/Services/IFileSystem.cs
index cd987555..83a44400 100644
--- a/src/api/wix/WixToolset.Extensibility/Services/IFileSystem.cs
+++ b/src/api/wix/WixToolset.Extensibility/Services/IFileSystem.cs
@@ -4,6 +4,7 @@ namespace WixToolset.Extensibility.Services
4{ 4{
5 using System; 5 using System;
6 using System.IO; 6 using System.IO;
7 using WixToolset.Data;
7 8
8 /// <summary> 9 /// <summary>
9 /// Abstracts basic file system operations. 10 /// Abstracts basic file system operations.
@@ -13,34 +14,38 @@ namespace WixToolset.Extensibility.Services
13 /// <summary> 14 /// <summary>
14 /// Copies a file. 15 /// Copies a file.
15 /// </summary> 16 /// </summary>
17 /// <param name="sourceLineNumbers">Optional source line number requiring the copy.</param>
16 /// <param name="source">The file to copy.</param> 18 /// <param name="source">The file to copy.</param>
17 /// <param name="destination">The destination file.</param> 19 /// <param name="destination">The destination file.</param>
18 /// <param name="allowHardlink">Allow hardlinks.</param> 20 /// <param name="allowHardlink">Allow hardlinks.</param>
19 void CopyFile(string source, string destination, bool allowHardlink); 21 void CopyFile(SourceLineNumber sourceLineNumbers, string source, string destination, bool allowHardlink);
20 22
21 /// <summary> 23 /// <summary>
22 /// Deletes a file. 24 /// Deletes a file.
23 /// </summary> 25 /// </summary>
26 /// <param name="sourceLineNumbers">Optional source line number requiring the delete.</param>
24 /// <param name="source">The file to delete.</param> 27 /// <param name="source">The file to delete.</param>
25 /// <param name="throwOnError">Indicates the file must be deleted. Default is a best effort delete.</param> 28 /// <param name="throwOnError">Indicates the file must be deleted. Default is a best effort delete.</param>
26 /// <param name="maxRetries">Maximum retry attempts. Default is 4.</param> 29 /// <param name="maxRetries">Maximum retry attempts. Default is 4.</param>
27 void DeleteFile(string source, bool throwOnError = false, int maxRetries = 4); 30 void DeleteFile(SourceLineNumber sourceLineNumbers, string source, bool throwOnError = false, int maxRetries = 4);
28 31
29 /// <summary> 32 /// <summary>
30 /// Moves a file. 33 /// Moves a file.
31 /// </summary> 34 /// </summary>
35 /// <param name="sourceLineNumbers">Optional source line number requiring the move.</param>
32 /// <param name="source">The file to move.</param> 36 /// <param name="source">The file to move.</param>
33 /// <param name="destination">The destination file.</param> 37 /// <param name="destination">The destination file.</param>
34 void MoveFile(string source, string destination); 38 void MoveFile(SourceLineNumber sourceLineNumbers, string source, string destination);
35 39
36 /// <summary> 40 /// <summary>
37 /// Opens a file. 41 /// Opens a file.
38 /// </summary> 42 /// </summary>
43 /// <param name="sourceLineNumbers">Optional source line number requiring the file.</param>
39 /// <param name="path">The file to open.</param> 44 /// <param name="path">The file to open.</param>
40 /// <param name="mode">A System.IO.FileMode value that specifies whether a file is created if one does not exist, and determines whether the contents of existing files are retained or overwritten.</param> 45 /// <param name="mode">A System.IO.FileMode value that specifies whether a file is created if one does not exist, and determines whether the contents of existing files are retained or overwritten.</param>
41 /// <param name="access">A System.IO.FileAccess value that specifies the operations that can be performed on the file.</param> 46 /// <param name="access">A System.IO.FileAccess value that specifies the operations that can be performed on the file.</param>
42 /// <param name="share">A System.IO.FileShare value specifying the type of access other threads have to the file.</param> 47 /// <param name="share">A System.IO.FileShare value specifying the type of access other threads have to the file.</param>
43 FileStream OpenFile(string path, FileMode mode, FileAccess access, FileShare share); 48 FileStream OpenFile(SourceLineNumber sourceLineNumbers, string path, FileMode mode, FileAccess access, FileShare share);
44 49
45 /// <summary> 50 /// <summary>
46 /// Executes an action and retries on any exception a few times with short pause 51 /// Executes an action and retries on any exception a few times with short pause
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs b/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs
index acc76a3b..d8e49397 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs
@@ -56,7 +56,7 @@ namespace WixToolset.Core.Burn.Bundles
56 /// <returns>Burn reader.</returns> 56 /// <returns>Burn reader.</returns>
57 public static BurnReader Open(IMessaging messaging, IFileSystem fileSystem, string fileExe) 57 public static BurnReader Open(IMessaging messaging, IFileSystem fileSystem, string fileExe)
58 { 58 {
59 var binaryReader = new BinaryReader(fileSystem.OpenFile(fileExe, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)); 59 var binaryReader = new BinaryReader(fileSystem.OpenFile(null, fileExe, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete));
60 var reader = new BurnReader(messaging, fileSystem, fileExe) 60 var reader = new BurnReader(messaging, fileSystem, fileExe)
61 { 61 {
62 binaryReader = binaryReader, 62 binaryReader = binaryReader,
@@ -92,7 +92,7 @@ namespace WixToolset.Core.Burn.Bundles
92 var uxContainerSlot = this.AttachedContainers[0]; 92 var uxContainerSlot = this.AttachedContainers[0];
93 93
94 this.binaryReader.BaseStream.Seek(this.UXAddress, SeekOrigin.Begin); 94 this.binaryReader.BaseStream.Seek(this.UXAddress, SeekOrigin.Begin);
95 using (Stream tempCab = this.fileSystem.OpenFile(tempCabPath, FileMode.Create, FileAccess.Write, FileShare.Read)) 95 using (Stream tempCab = this.fileSystem.OpenFile(null, tempCabPath, FileMode.Create, FileAccess.Write, FileShare.Read))
96 { 96 {
97 BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)uxContainerSlot.Size); 97 BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)uxContainerSlot.Size);
98 } 98 }
@@ -100,7 +100,7 @@ namespace WixToolset.Core.Burn.Bundles
100 var cabinet = new Cabinet(tempCabPath); 100 var cabinet = new Cabinet(tempCabPath);
101 cabinet.Extract(outputDirectory); 101 cabinet.Extract(outputDirectory);
102 102
103 this.fileSystem.MoveFile(manifestOriginalPath, manifestPath); 103 this.fileSystem.MoveFile(null, manifestOriginalPath, manifestPath);
104 104
105 var document = new XmlDocument(); 105 var document = new XmlDocument();
106 document.Load(manifestPath); 106 document.Load(manifestPath);
@@ -117,7 +117,7 @@ namespace WixToolset.Core.Burn.Bundles
117 var sourcePath = Path.Combine(outputDirectory, sourcePathNode.Value); 117 var sourcePath = Path.Combine(outputDirectory, sourcePathNode.Value);
118 var destinationPath = Path.Combine(outputDirectory, filePathNode.Value); 118 var destinationPath = Path.Combine(outputDirectory, filePathNode.Value);
119 119
120 this.fileSystem.MoveFile(sourcePath, destinationPath); 120 this.fileSystem.MoveFile(null, sourcePath, destinationPath);
121 } 121 }
122 122
123 foreach (XmlNode payload in payloads) 123 foreach (XmlNode payload in payloads)
@@ -169,7 +169,7 @@ namespace WixToolset.Core.Burn.Bundles
169 var tempCabPath = Path.Combine(tempDirectory, $"a{i}.cab"); 169 var tempCabPath = Path.Combine(tempDirectory, $"a{i}.cab");
170 170
171 this.binaryReader.BaseStream.Seek(nextAddress, SeekOrigin.Begin); 171 this.binaryReader.BaseStream.Seek(nextAddress, SeekOrigin.Begin);
172 using (Stream tempCab = this.fileSystem.OpenFile(tempCabPath, FileMode.Create, FileAccess.Write, FileShare.Read)) 172 using (Stream tempCab = this.fileSystem.OpenFile(null, tempCabPath, FileMode.Create, FileAccess.Write, FileShare.Read))
173 { 173 {
174 BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)cntnr.Size); 174 BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)cntnr.Size);
175 } 175 }
@@ -185,7 +185,7 @@ namespace WixToolset.Core.Burn.Bundles
185 var sourcePath = Path.Combine(outputDirectory, (string)entry.Key); 185 var sourcePath = Path.Combine(outputDirectory, (string)entry.Key);
186 var destinationPath = Path.Combine(outputDirectory, (string)entry.Value); 186 var destinationPath = Path.Combine(outputDirectory, (string)entry.Value);
187 187
188 this.fileSystem.MoveFile(sourcePath, destinationPath); 188 this.fileSystem.MoveFile(null, sourcePath, destinationPath);
189 } 189 }
190 190
191 return true; 191 return true;
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs b/src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs
index f6282443..a61713f1 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs
@@ -43,14 +43,14 @@ namespace WixToolset.Core.Burn.Bundles
43 { 43 {
44 var writer = new BurnWriter(messaging, fileSystem, fileExe); 44 var writer = new BurnWriter(messaging, fileSystem, fileExe);
45 45
46 using (var binaryReader = new BinaryReader(fileSystem.OpenFile(fileExe, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete))) 46 using (var binaryReader = new BinaryReader(fileSystem.OpenFile(null, fileExe, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)))
47 { 47 {
48 writer.Initialize(binaryReader); 48 writer.Initialize(binaryReader);
49 } 49 }
50 50
51 if (!writer.Invalid) 51 if (!writer.Invalid)
52 { 52 {
53 writer.binaryWriter = new BinaryWriter(fileSystem.OpenFile(fileExe, FileMode.Open, FileAccess.ReadWrite, FileShare.Read | FileShare.Delete)); 53 writer.binaryWriter = new BinaryWriter(fileSystem.OpenFile(null, fileExe, FileMode.Open, FileAccess.ReadWrite, FileShare.Read | FileShare.Delete));
54 } 54 }
55 55
56 return writer; 56 return writer;
@@ -102,12 +102,13 @@ namespace WixToolset.Core.Burn.Bundles
102 /// <summary> 102 /// <summary>
103 /// Appends a UX or Attached container to the exe and updates the ".wixburn" section data to point to it. 103 /// Appends a UX or Attached container to the exe and updates the ".wixburn" section data to point to it.
104 /// </summary> 104 /// </summary>
105 /// <param name="sourceLineNumbers">Source line numbers for the container.</param>
105 /// <param name="fileContainer">File path to append to the current exe.</param> 106 /// <param name="fileContainer">File path to append to the current exe.</param>
106 /// <param name="container">Container section represented by the fileContainer.</param> 107 /// <param name="container">Container section represented by the fileContainer.</param>
107 /// <returns>true if the container data is successfully appended; false otherwise</returns> 108 /// <returns>true if the container data is successfully appended; false otherwise</returns>
108 public bool AppendContainer(string fileContainer, BurnCommon.Container container) 109 public bool AppendContainer(SourceLineNumber sourceLineNumbers, string fileContainer, BurnCommon.Container container)
109 { 110 {
110 using (var reader = this.fileSystem.OpenFile(fileContainer, FileMode.Open, FileAccess.Read, FileShare.Read)) 111 using (var reader = this.fileSystem.OpenFile(sourceLineNumbers, fileContainer, FileMode.Open, FileAccess.Read, FileShare.Read))
111 { 112 {
112 return this.AppendContainer(reader, reader.Length, container); 113 return this.AppendContainer(reader, reader.Length, container);
113 } 114 }
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs
index 64e74bbe..f4b36ac2 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs
@@ -71,7 +71,7 @@ namespace WixToolset.Core.Burn.Bundles
71 71
72 this.Transfer = this.BackendHelper.CreateFileTransfer(bundleTempPath, this.OutputPath, true, this.BundleSymbol.SourceLineNumbers); 72 this.Transfer = this.BackendHelper.CreateFileTransfer(bundleTempPath, this.OutputPath, true, this.BundleSymbol.SourceLineNumbers);
73 73
74 this.FileSystem.CopyFile(stubFile, bundleTempPath, allowHardlink: false); 74 this.FileSystem.CopyFile(this.BundleSymbol.SourceLineNumbers, stubFile, bundleTempPath, allowHardlink: false);
75 File.SetAttributes(bundleTempPath, FileAttributes.Normal); 75 File.SetAttributes(bundleTempPath, FileAttributes.Normal);
76 76
77 var fourPartVersion = this.GetFourPartVersion(this.BundleSymbol); 77 var fourPartVersion = this.GetFourPartVersion(this.BundleSymbol);
@@ -88,7 +88,7 @@ namespace WixToolset.Core.Burn.Bundles
88 writer.InitializeBundleSectionData(burnStubFile.Length, this.BundleSymbol.BundleId); 88 writer.InitializeBundleSectionData(burnStubFile.Length, this.BundleSymbol.BundleId);
89 89
90 // Always attach the UX container first 90 // Always attach the UX container first
91 writer.AppendContainer(this.UXContainer.WorkingPath, BurnWriter.Container.UX); 91 writer.AppendContainer(this.UXContainer.SourceLineNumbers, this.UXContainer.WorkingPath, BurnWriter.Container.UX);
92 92
93 // Now append all other attached containers 93 // Now append all other attached containers
94 foreach (var container in this.Containers) 94 foreach (var container in this.Containers)
@@ -98,7 +98,7 @@ namespace WixToolset.Core.Burn.Bundles
98 // The container was only created if it had payloads. 98 // The container was only created if it had payloads.
99 if (!String.IsNullOrEmpty(container.WorkingPath) && BurnConstants.BurnUXContainerName != container.Id.Id) 99 if (!String.IsNullOrEmpty(container.WorkingPath) && BurnConstants.BurnUXContainerName != container.Id.Id)
100 { 100 {
101 writer.AppendContainer(container.WorkingPath, BurnWriter.Container.Attached); 101 writer.AppendContainer(container.SourceLineNumbers, container.WorkingPath, BurnWriter.Container.Attached);
102 } 102 }
103 } 103 }
104 } 104 }
diff --git a/src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs b/src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs
index 422ad329..2ebc9c47 100644
--- a/src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs
@@ -38,7 +38,7 @@ namespace WixToolset.Core.Burn.Inscribe
38 38
39 using (var reader = BurnReader.Open(this.Messaging, this.FileSystem, this.InputFilePath)) 39 using (var reader = BurnReader.Open(this.Messaging, this.FileSystem, this.InputFilePath))
40 { 40 {
41 this.FileSystem.CopyFile(this.SignedEngineFile, tempFile, allowHardlink: false); 41 this.FileSystem.CopyFile(null, this.SignedEngineFile, tempFile, allowHardlink: false);
42 42
43 using (var writer = BurnWriter.Open(this.Messaging, this.FileSystem, tempFile)) 43 using (var writer = BurnWriter.Open(this.Messaging, this.FileSystem, tempFile))
44 { 44 {
@@ -46,7 +46,7 @@ namespace WixToolset.Core.Burn.Inscribe
46 } 46 }
47 } 47 }
48 48
49 this.FileSystem.MoveFile(tempFile, this.OutputFile); 49 this.FileSystem.MoveFile(null, tempFile, this.OutputFile);
50 50
51 return inscribed; 51 return inscribed;
52 } 52 }
diff --git a/src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleEngineCommand.cs b/src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleEngineCommand.cs
index 3fde859a..5eeb7061 100644
--- a/src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleEngineCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleEngineCommand.cs
@@ -33,7 +33,7 @@ namespace WixToolset.Core.Burn.Inscribe
33 var tempFile = Path.Combine(this.IntermediateFolder, "bundle_engine_unsigned.exe"); 33 var tempFile = Path.Combine(this.IntermediateFolder, "bundle_engine_unsigned.exe");
34 34
35 using (var reader = BurnReader.Open(this.Messaging, this.FileSystem, this.InputFilePath)) 35 using (var reader = BurnReader.Open(this.Messaging, this.FileSystem, this.InputFilePath))
36 using (var writer = this.FileSystem.OpenFile(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read | FileShare.Delete)) 36 using (var writer = this.FileSystem.OpenFile(null, tempFile, FileMode.Create, FileAccess.Write, FileShare.Read | FileShare.Delete))
37 { 37 {
38 reader.Stream.Seek(0, SeekOrigin.Begin); 38 reader.Stream.Seek(0, SeekOrigin.Begin);
39 39
@@ -58,7 +58,7 @@ namespace WixToolset.Core.Burn.Inscribe
58 // TODO: update writer with detached container signatures. 58 // TODO: update writer with detached container signatures.
59 } 59 }
60 60
61 this.FileSystem.MoveFile(tempFile, this.OutputFile); 61 this.FileSystem.MoveFile(null, tempFile, this.OutputFile);
62 } 62 }
63 } 63 }
64} 64}
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/AssemblyNameReader.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/AssemblyNameReader.cs
index 0bb9c389..838a6fe1 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/AssemblyNameReader.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/AssemblyNameReader.cs
@@ -19,7 +19,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
19 { 19 {
20 try 20 try
21 { 21 {
22 using (var stream = fileSystem.OpenFile(assemblyPath, FileMode.Open, FileAccess.Read, FileShare.Read)) 22 using (var stream = fileSystem.OpenFile(sourceLineNumbers, assemblyPath, FileMode.Open, FileAccess.Read, FileShare.Read))
23 using (var peReader = new PEReader(stream)) 23 using (var peReader = new PEReader(stream))
24 { 24 {
25 var reader = peReader.GetMetadataReader(); 25 var reader = peReader.GetMetadataReader();
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
index e3d70b70..b3008d6e 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
@@ -212,7 +212,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
212 212
213 if (softwareTags.Any()) 213 if (softwareTags.Any())
214 { 214 {
215 var command = new ProcessPackageSoftwareTagsCommand(section, this.WindowsInstallerBackendHelper, softwareTags, this.IntermediateFolder); 215 var command = new ProcessPackageSoftwareTagsCommand(section, this.WindowsInstallerBackendHelper, this.FileSystem, softwareTags, this.IntermediateFolder);
216 command.Execute(); 216 command.Execute();
217 217
218 trackedFiles.AddRange(command.TrackedFiles); 218 trackedFiles.AddRange(command.TrackedFiles);
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
index e6ab3a28..b61247a5 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
@@ -401,7 +401,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
401 { 401 {
402 if (!String.IsNullOrEmpty(emptyFile)) 402 if (!String.IsNullOrEmpty(emptyFile))
403 { 403 {
404 using (var fileStream = this.FileSystem.OpenFile(emptyFile, FileMode.Create, FileAccess.Write, FileShare.None)) 404 using (var fileStream = this.FileSystem.OpenFile(null, emptyFile, FileMode.Create, FileAccess.Write, FileShare.None))
405 { 405 {
406 } 406 }
407 } 407 }
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/FileSystemManager.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/FileSystemManager.cs
index 8707d5f1..e4fa279c 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/FileSystemManager.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/FileSystemManager.cs
@@ -5,7 +5,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.IO; 7 using System.IO;
8 using System.Runtime.CompilerServices;
9 using WixToolset.Extensibility; 8 using WixToolset.Extensibility;
10 using WixToolset.Extensibility.Services; 9 using WixToolset.Extensibility.Services;
11 10
@@ -42,8 +41,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
42 return true; 41 return true;
43 } 42 }
44 43
45 using (var firstStream = this.FileSystem.OpenFile(firstPath, FileMode.Open, FileAccess.Read, FileShare.Read)) 44 using (var firstStream = this.FileSystem.OpenFile(null, firstPath, FileMode.Open, FileAccess.Read, FileShare.Read))
46 using (var secondStream = this.FileSystem.OpenFile(secondPath, FileMode.Open, FileAccess.Read, FileShare.Read)) 45 using (var secondStream = this.FileSystem.OpenFile(null, secondPath, FileMode.Open, FileAccess.Read, FileShare.Read))
47 { 46 {
48 if (firstStream.Length != secondStream.Length) 47 if (firstStream.Length != secondStream.Length)
49 { 48 {
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessPackageSoftwareTagsCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessPackageSoftwareTagsCommand.cs
index 1348f163..9fb42fbc 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessPackageSoftwareTagsCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessPackageSoftwareTagsCommand.cs
@@ -15,10 +15,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
15 15
16 internal class ProcessPackageSoftwareTagsCommand 16 internal class ProcessPackageSoftwareTagsCommand
17 { 17 {
18 public ProcessPackageSoftwareTagsCommand(IntermediateSection section, IBackendHelper backendHelper, IEnumerable<WixPackageTagSymbol> softwareTags, string intermediateFolder) 18 public ProcessPackageSoftwareTagsCommand(IntermediateSection section, IBackendHelper backendHelper, IFileSystem fileSystem, IEnumerable<WixPackageTagSymbol> softwareTags, string intermediateFolder)
19 { 19 {
20 this.Section = section; 20 this.Section = section;
21 this.BackendHelper = backendHelper; 21 this.BackendHelper = backendHelper;
22 this.FileSystem = fileSystem;
22 this.SoftwareTags = softwareTags; 23 this.SoftwareTags = softwareTags;
23 this.IntermediateFolder = intermediateFolder; 24 this.IntermediateFolder = intermediateFolder;
24 } 25 }
@@ -29,6 +30,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
29 30
30 private IBackendHelper BackendHelper { get; } 31 private IBackendHelper BackendHelper { get; }
31 32
33 private IFileSystem FileSystem { get; }
34
32 private IEnumerable<WixPackageTagSymbol> SoftwareTags { get; } 35 private IEnumerable<WixPackageTagSymbol> SoftwareTags { get; }
33 36
34 public IReadOnlyCollection<ITrackedFile> TrackedFiles { get; private set; } 37 public IReadOnlyCollection<ITrackedFile> TrackedFiles { get; private set; }
@@ -61,7 +64,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
61 64
62 trackedFiles.Add(this.BackendHelper.TrackFile(fileSymbol.Source.Path, TrackedFileType.Intermediate, tagRow.SourceLineNumbers)); 65 trackedFiles.Add(this.BackendHelper.TrackFile(fileSymbol.Source.Path, TrackedFileType.Intermediate, tagRow.SourceLineNumbers));
63 66
64 using (var fs = new FileStream(fileSymbol.Source.Path, FileMode.Create)) 67 using (var fs = this.FileSystem.OpenFile(fileSymbol.SourceLineNumbers, fileSymbol.Source.Path, FileMode.Create, FileAccess.ReadWrite, FileShare.Read))
65 { 68 {
66 CreateTagFile(fs, uniqueId, packageSymbol.Name, packageSymbol.Version, tagRow.Regid, packageSymbol.Manufacturer, persistentId); 69 CreateTagFile(fs, uniqueId, packageSymbol.Name, packageSymbol.Version, tagRow.Regid, packageSymbol.Manufacturer, persistentId);
67 } 70 }
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs
index 7f764f49..b51e3a0f 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs
@@ -84,7 +84,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
84 return; 84 return;
85 } 85 }
86 86
87 using (var fileStream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.Read)) 87 using (var fileStream = this.FileSystem.OpenFile(facade.SourceLineNumber, fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.Read))
88 { 88 {
89 if (Int32.MaxValue < fileStream.Length) 89 if (Int32.MaxValue < fileStream.Length)
90 { 90 {
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs
index 3f0d0c51..455057f6 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs
@@ -51,7 +51,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
51 51
52 if (!String.Equals(this.InputPath, this.OutputPath, StringComparison.OrdinalIgnoreCase)) 52 if (!String.Equals(this.InputPath, this.OutputPath, StringComparison.OrdinalIgnoreCase))
53 { 53 {
54 this.FileSystem.CopyFile(this.InputPath, this.OutputPath, allowHardlink: false); 54 this.FileSystem.CopyFile(null, this.InputPath, this.OutputPath, allowHardlink: false);
55 } 55 }
56 56
57 var attributes = File.GetAttributes(databasePath); 57 var attributes = File.GetAttributes(databasePath);
@@ -98,7 +98,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
98 Directory.CreateDirectory(hashPath); 98 Directory.CreateDirectory(hashPath);
99 hashPath = Path.Combine(hashPath, hashFileName); 99 hashPath = Path.Combine(hashPath, hashFileName);
100 100
101 using (var fs = this.FileSystem.OpenFile(hashPath, FileMode.Create, FileAccess.Write, FileShare.None)) 101 using (var fs = this.FileSystem.OpenFile(null, hashPath, FileMode.Create, FileAccess.Write, FileShare.None))
102 { 102 {
103 int bytesRead; 103 int bytesRead;
104 var buffer = new byte[1024 * 4]; 104 var buffer = new byte[1024 * 4];
@@ -129,7 +129,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
129 Directory.CreateDirectory(certPath); 129 Directory.CreateDirectory(certPath);
130 certPath = Path.Combine(certPath, String.Concat(certificateId, ".cer")); 130 certPath = Path.Combine(certPath, String.Concat(certificateId, ".cer"));
131 131
132 using (var fs = this.FileSystem.OpenFile(certPath, FileMode.Create, FileAccess.Write, FileShare.None)) 132 using (var fs = this.FileSystem.OpenFile(null, certPath, FileMode.Create, FileAccess.Write, FileShare.None))
133 { 133 {
134 int bytesRead; 134 int bytesRead;
135 var buffer = new byte[1024 * 4]; 135 var buffer = new byte[1024 * 4];
@@ -223,9 +223,9 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
223 var certPath = Path.Combine(this.IntermediateFolder, "MsiDigitalCertificate"); 223 var certPath = Path.Combine(this.IntermediateFolder, "MsiDigitalCertificate");
224 Directory.CreateDirectory(certPath); 224 Directory.CreateDirectory(certPath);
225 certPath = Path.Combine(certPath, String.Concat(cert2.Thumbprint, ".cer")); 225 certPath = Path.Combine(certPath, String.Concat(cert2.Thumbprint, ".cer"));
226 this.FileSystem.DeleteFile(certPath, true); 226 this.FileSystem.DeleteFile(null, certPath, true);
227 227
228 using (var writer = new BinaryWriter(this.FileSystem.OpenFile(certPath, FileMode.Create, FileAccess.Write, FileShare.Read))) 228 using (var writer = new BinaryWriter(this.FileSystem.OpenFile(null, certPath, FileMode.Create, FileAccess.Write, FileShare.Read)))
229 { 229 {
230 writer.Write(cert2.RawData); 230 writer.Write(cert2.RawData);
231 writer.Close(); 231 writer.Close();
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs
index c2981b08..bfbe0339 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs
@@ -97,6 +97,8 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
97 { 97 {
98 if (null != record) 98 if (null != record)
99 { 99 {
100 embeddedCabinetRowsByDiskId.TryGetValue(diskId, out var cabinetMediaRow);
101
100 // since the cabinets are stored in case-sensitive streams inside the msi, but the file system is not (typically) case-sensitive, 102 // since the cabinets are stored in case-sensitive streams inside the msi, but the file system is not (typically) case-sensitive,
101 // embedded cabinets must be extracted to a canonical file name (like their diskid) to ensure extraction will always work 103 // embedded cabinets must be extracted to a canonical file name (like their diskid) to ensure extraction will always work
102 var cabinetPath = Path.Combine(this.IntermediateFolder, "Media", diskId.ToString(CultureInfo.InvariantCulture), ".cab"); 104 var cabinetPath = Path.Combine(this.IntermediateFolder, "Media", diskId.ToString(CultureInfo.InvariantCulture), ".cab");
@@ -104,7 +106,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
104 // ensure the parent directory exists 106 // ensure the parent directory exists
105 Directory.CreateDirectory(Path.GetDirectoryName(cabinetPath)); 107 Directory.CreateDirectory(Path.GetDirectoryName(cabinetPath));
106 108
107 using (var fs = this.FileSystem.OpenFile(cabinetPath, FileMode.Create, FileAccess.Write, FileShare.None)) 109 using (var fs = this.FileSystem.OpenFile(cabinetMediaRow.SourceLineNumbers, cabinetPath, FileMode.Create, FileAccess.Write, FileShare.None))
108 { 110 {
109 int bytesRead; 111 int bytesRead;
110 var buffer = new byte[4096]; 112 var buffer = new byte[4096];
@@ -115,7 +117,6 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
115 } 117 }
116 } 118 }
117 119
118 embeddedCabinetRowsByDiskId.TryGetValue(diskId, out var cabinetMediaRow);
119 cabinetPathsWithMediaRow.Add(cabinetPath, cabinetMediaRow); 120 cabinetPathsWithMediaRow.Add(cabinetPath, cabinetMediaRow);
120 } 121 }
121 else 122 else
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
index 54534e50..9ad936e4 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
@@ -206,7 +206,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
206 206
207 Directory.CreateDirectory(Path.Combine(this.ExportBasePath, tableName)); 207 Directory.CreateDirectory(Path.Combine(this.ExportBasePath, tableName));
208 208
209 using (var fs = this.FileSystem.OpenFile(source, FileMode.Create, FileAccess.Write, FileShare.None)) 209 using (var fs = this.FileSystem.OpenFile(null, source, FileMode.Create, FileAccess.Write, FileShare.None))
210 { 210 {
211 int bytesRead; 211 int bytesRead;
212 var buffer = new byte[4096]; 212 var buffer = new byte[4096];
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs
index bb4da1ed..49759d2c 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs
@@ -327,7 +327,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
327 if (null == this.EmptyFile) 327 if (null == this.EmptyFile)
328 { 328 {
329 this.EmptyFile = Path.Combine(this.IntermediateFolder, ".empty"); 329 this.EmptyFile = Path.Combine(this.IntermediateFolder, ".empty");
330 using (var fileStream = this.FileSystem.OpenFile(this.EmptyFile, FileMode.Create, FileAccess.Write, FileShare.None)) 330 using (var fileStream = this.FileSystem.OpenFile(null, this.EmptyFile, FileMode.Create, FileAccess.Write, FileShare.None))
331 { 331 {
332 } 332 }
333 } 333 }
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Validate/ValidateDatabaseCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Validate/ValidateDatabaseCommand.cs
index 24dd9927..6996dc44 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Validate/ValidateDatabaseCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Validate/ValidateDatabaseCommand.cs
@@ -71,7 +71,7 @@ namespace WixToolset.Core.WindowsInstaller.Validate
71 var workingDatabasePath = Path.Combine(this.IntermediateFolder, workingDatabaseFilename); 71 var workingDatabasePath = Path.Combine(this.IntermediateFolder, workingDatabaseFilename);
72 try 72 try
73 { 73 {
74 this.FileSystem.CopyFile(this.DatabasePath, workingDatabasePath, allowHardlink: false); 74 this.FileSystem.CopyFile(null, this.DatabasePath, workingDatabasePath, allowHardlink: false);
75 75
76 var attributes = File.GetAttributes(workingDatabasePath); 76 var attributes = File.GetAttributes(workingDatabasePath);
77 File.SetAttributes(workingDatabasePath, attributes & ~FileAttributes.ReadOnly); 77 File.SetAttributes(workingDatabasePath, attributes & ~FileAttributes.ReadOnly);
@@ -81,7 +81,7 @@ namespace WixToolset.Core.WindowsInstaller.Validate
81 } 81 }
82 finally 82 finally
83 { 83 {
84 this.FileSystem.DeleteFile(workingDatabasePath); 84 this.FileSystem.DeleteFile(null, workingDatabasePath);
85 } 85 }
86 86
87 stopwatch.Stop(); 87 stopwatch.Stop();
diff --git a/src/wix/WixToolset.Core/Bind/TransferFilesCommand.cs b/src/wix/WixToolset.Core/Bind/TransferFilesCommand.cs
index 4a0329bc..28ca6b49 100644
--- a/src/wix/WixToolset.Core/Bind/TransferFilesCommand.cs
+++ b/src/wix/WixToolset.Core/Bind/TransferFilesCommand.cs
@@ -53,12 +53,12 @@ namespace WixToolset.Core.Bind
53 if (fileTransfer.Move) 53 if (fileTransfer.Move)
54 { 54 {
55 this.Messaging.Write(VerboseMessages.MoveFile(fileTransfer.Source, fileTransfer.Destination)); 55 this.Messaging.Write(VerboseMessages.MoveFile(fileTransfer.Source, fileTransfer.Destination));
56 this.MoveFile(fileTransfer.Source, fileTransfer.Destination); 56 this.MoveFile(fileTransfer.SourceLineNumbers, fileTransfer.Source, fileTransfer.Destination);
57 } 57 }
58 else 58 else
59 { 59 {
60 this.Messaging.Write(VerboseMessages.CopyFile(fileTransfer.Source, fileTransfer.Destination)); 60 this.Messaging.Write(VerboseMessages.CopyFile(fileTransfer.Source, fileTransfer.Destination));
61 this.CopyFile(fileTransfer.Source, fileTransfer.Destination); 61 this.CopyFile(fileTransfer.SourceLineNumbers, fileTransfer.Source, fileTransfer.Destination);
62 } 62 }
63 63
64 retry = false; 64 retry = false;
@@ -170,7 +170,7 @@ namespace WixToolset.Core.Bind
170 } 170 }
171 } 171 }
172 172
173 private void CopyFile(string source, string destination) 173 private void CopyFile(SourceLineNumber sourceLineNumbers, string source, string destination)
174 { 174 {
175 foreach (var extension in this.Extensions) 175 foreach (var extension in this.Extensions)
176 { 176 {
@@ -180,10 +180,10 @@ namespace WixToolset.Core.Bind
180 } 180 }
181 } 181 }
182 182
183 this.FileSystem.CopyFile(source, destination, allowHardlink: true); 183 this.FileSystem.CopyFile(sourceLineNumbers, source, destination, allowHardlink: true);
184 } 184 }
185 185
186 private void MoveFile(string source, string destination) 186 private void MoveFile(SourceLineNumber sourceLineNumbers, string source, string destination)
187 { 187 {
188 foreach (var extension in this.Extensions) 188 foreach (var extension in this.Extensions)
189 { 189 {
@@ -193,7 +193,7 @@ namespace WixToolset.Core.Bind
193 } 193 }
194 } 194 }
195 195
196 this.FileSystem.MoveFile(source, destination); 196 this.FileSystem.MoveFile(sourceLineNumbers, source, destination);
197 } 197 }
198 198
199 private void AclReset(IEnumerable<string> files) 199 private void AclReset(IEnumerable<string> files)
diff --git a/src/wix/WixToolset.Core/CoreErrors.cs b/src/wix/WixToolset.Core/CoreErrors.cs
new file mode 100644
index 00000000..6dbd6c88
--- /dev/null
+++ b/src/wix/WixToolset.Core/CoreErrors.cs
@@ -0,0 +1,42 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Core
4{
5 using WixToolset.Data;
6
7 internal static class CoreErrors
8 {
9 public static Message UnableToCopyFile(SourceLineNumber sourceLineNumbers, string source, string destination, string detail)
10 {
11 return Message(sourceLineNumbers, Ids.UnableToCopyFile, "Unable to copy file from: {0}, to: {1}. Error detail: {2}", source, destination, detail);
12 }
13
14 public static Message UnableToDeleteFile(SourceLineNumber sourceLineNumbers, string path, string detail)
15 {
16 return Message(sourceLineNumbers, Ids.UnableToDeleteFile, "Unable to delete file: {0}. Error detail: {1}", path, detail);
17 }
18
19 public static Message UnableToMoveFile(SourceLineNumber sourceLineNumbers, string source, string destination, string detail)
20 {
21 return Message(sourceLineNumbers, Ids.UnableToMoveFile, "Unable to move file from: {0}, to: {1}. Error detail: {2}", source, destination, detail);
22 }
23
24 public static Message UnableToOpenFile(SourceLineNumber sourceLineNumbers, string path, string detail)
25 {
26 return Message(sourceLineNumbers, Ids.UnableToOpenFile, "Unable to open file: {0}. Error detail: {1}", path, detail);
27 }
28
29 private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args)
30 {
31 return new Message(sourceLineNumber, MessageLevel.Error, (int)id, format, args);
32 }
33
34 public enum Ids
35 {
36 UnableToCopyFile = 7010,
37 UnableToDeleteFile = 7011,
38 UnableToMoveFile = 7012,
39 UnableToOpenFile = 7013,
40 } // last available is 7099. 7100 is WindowsInstallerBackendWarnings.
41 }
42}
diff --git a/src/wix/WixToolset.Core/ExtensibilityServices/FileSystem.cs b/src/wix/WixToolset.Core/ExtensibilityServices/FileSystem.cs
index 8cf7a872..8baee3d8 100644
--- a/src/wix/WixToolset.Core/ExtensibilityServices/FileSystem.cs
+++ b/src/wix/WixToolset.Core/ExtensibilityServices/FileSystem.cs
@@ -7,19 +7,34 @@ namespace WixToolset.Core.ExtensibilityServices
7 using System.IO; 7 using System.IO;
8 using System.Runtime.InteropServices; 8 using System.Runtime.InteropServices;
9 using System.Threading; 9 using System.Threading;
10 using WixToolset.Data;
10 using WixToolset.Extensibility.Services; 11 using WixToolset.Extensibility.Services;
11 12
12 internal class FileSystem : IFileSystem 13 internal class FileSystem : IFileSystem
13 { 14 {
14 public void CopyFile(string source, string destination, bool allowHardlink) 15 public void CopyFile(SourceLineNumber sourceLineNumbers, string source, string destination, bool allowHardlink)
15 { 16 {
16 this.EnsureDirectoryWithoutFile(destination); 17 try
18 {
19 this.EnsureDirectoryWithoutFile(destination);
20 }
21 catch (Exception e)
22 {
23 throw new WixException(CoreErrors.UnableToCopyFile(sourceLineNumbers, source, destination, e.Message), e);
24 }
17 25
18 var hardlinked = false; 26 var hardlinked = false;
19 27
20 if (allowHardlink) 28 if (allowHardlink)
21 { 29 {
22 this.ExecuteWithRetries(() => hardlinked = CreateHardLink(destination, source, IntPtr.Zero)); 30 try
31 {
32 this.ExecuteWithRetries(() => hardlinked = CreateHardLink(destination, source, IntPtr.Zero));
33 }
34 catch
35 {
36 // Catch hard-link failures and fall back to copy file.
37 }
23 } 38 }
24 39
25 if (!hardlinked) 40 if (!hardlinked)
@@ -28,30 +43,48 @@ namespace WixToolset.Core.ExtensibilityServices
28 var er = Marshal.GetLastWin32Error(); 43 var er = Marshal.GetLastWin32Error();
29#endif 44#endif
30 45
31 this.ExecuteWithRetries(() => File.Copy(source, destination, overwrite: true)); 46 try
47 {
48 this.ExecuteWithRetries(() => File.Copy(source, destination, overwrite: true));
49 }
50 catch (Exception e)
51 {
52 throw new WixException(CoreErrors.UnableToCopyFile(sourceLineNumbers, source, destination, e.Message), e);
53 }
32 } 54 }
33 } 55 }
34 56
35 public void DeleteFile(string source, bool throwOnError = false, int maxRetries = 4) 57 public void DeleteFile(SourceLineNumber sourceLineNumbers, string source, bool throwOnError = false, int maxRetries = 4)
36 { 58 {
37 try 59 try
38 { 60 {
39 this.ExecuteWithRetries(() => File.Delete(source), maxRetries); 61 this.ExecuteWithRetries(() => File.Delete(source), maxRetries);
40 } 62 }
41 catch when (!throwOnError) 63 catch (Exception e)
42 { 64 {
43 // Do nothing on best-effort deletes. 65 if (throwOnError)
66 {
67 throw new WixException(CoreErrors.UnableToDeleteFile(sourceLineNumbers, source, e.Message), e);
68 }
69 // else do nothing on best-effort deletes.
44 } 70 }
45 } 71 }
46 72
47 public void MoveFile(string source, string destination) 73 public void MoveFile(SourceLineNumber sourceLineNumbers, string source, string destination)
48 { 74 {
49 this.EnsureDirectoryWithoutFile(destination); 75 try
76 {
77 this.EnsureDirectoryWithoutFile(destination);
50 78
51 this.ExecuteWithRetries(() => File.Move(source, destination)); 79 this.ExecuteWithRetries(() => File.Move(source, destination));
80 }
81 catch (Exception e)
82 {
83 throw new WixException(CoreErrors.UnableToMoveFile(sourceLineNumbers, source, destination, e.Message), e);
84 }
52 } 85 }
53 86
54 public FileStream OpenFile(string path, FileMode mode, FileAccess access, FileShare share) 87 public FileStream OpenFile(SourceLineNumber sourceLineNumbers, string path, FileMode mode, FileAccess access, FileShare share)
55 { 88 {
56 const int maxRetries = 4; 89 const int maxRetries = 4;
57 90
@@ -61,9 +94,16 @@ namespace WixToolset.Core.ExtensibilityServices
61 { 94 {
62 return File.Open(path, mode, access, share); 95 return File.Open(path, mode, access, share);
63 } 96 }
64 catch (Exception e) when (attempt < maxRetries && (e is IOException || e is SystemException || e is Win32Exception)) 97 catch (Exception e) when (e is IOException || e is SystemException || e is Win32Exception)
65 { 98 {
66 Thread.Sleep(250); 99 if (attempt < maxRetries)
100 {
101 Thread.Sleep(250);
102 }
103 else
104 {
105 throw new WixException(CoreErrors.UnableToOpenFile(sourceLineNumbers, path, e.Message), e);
106 }
67 } 107 }
68 } 108 }
69 109
diff --git a/src/wix/WixToolset.Core/LayoutCreator.cs b/src/wix/WixToolset.Core/LayoutCreator.cs
index c361f78a..f26b1127 100644
--- a/src/wix/WixToolset.Core/LayoutCreator.cs
+++ b/src/wix/WixToolset.Core/LayoutCreator.cs
@@ -114,7 +114,7 @@ namespace WixToolset.Core
114 { 114 {
115 this.SplitUniqueFolders(intermediateFolder, tempPath, uniqueFolders); 115 this.SplitUniqueFolders(intermediateFolder, tempPath, uniqueFolders);
116 116
117 this.FileSystem.DeleteFile(tempPath); 117 this.FileSystem.DeleteFile(null, tempPath);
118 } 118 }
119 119
120 // Clean up empty temp folders. 120 // Clean up empty temp folders.