Provide useful error message when file system operations fail
Fixes 5417
Rob Mensching committed
Nov 4, 2022 at 01:05 UTC
f8749f8e438b150884449ad3e2e13aed750de679
21 files changed
+152
-61
src/api/wix/WixToolset.Extensibility/Services/IFileSystem.cs
+9
-4
@@ -4,6 +4,7 @@ namespace WixToolset.Extensibility.Services
4
{
5
using System;
6
using System.IO;
7
+ using WixToolset.Data;
8
9
/// <summary>
10
/// Abstracts basic file system operations.
@@ -13,34 +14,38 @@ namespace WixToolset.Extensibility.Services
14
/// <summary>
15
/// Copies a file.
16
/// </summary>
17
+ /// <param name="sourceLineNumbers">Optional source line number requiring the copy.</param>
18
/// <param name="source">The file to copy.</param>
19
/// <param name="destination">The destination file.</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);
22
23
/// <summary>
24
/// Deletes a file.
25
/// </summary>
26
+ /// <param name="sourceLineNumbers">Optional source line number requiring the delete.</param>
27
/// <param name="source">The file to delete.</param>
28
/// <param name="throwOnError">Indicates the file must be deleted. Default is a best effort delete.</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);
31
32
/// <summary>
33
/// Moves a file.
34
/// </summary>
35
+ /// <param name="sourceLineNumbers">Optional source line number requiring the move.</param>
36
/// <param name="source">The file to move.</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);
39
40
/// <summary>
41
/// Opens a file.
42
/// </summary>
43
+ /// <param name="sourceLineNumbers">Optional source line number requiring the file.</param>
44
/// <param name="path">The file to open.</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>
46
/// <param name="access">A System.IO.FileAccess value that specifies the operations that can be performed on 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);
49
50
/// <summary>
51
/// Executes an action and retries on any exception a few times with short pause
src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs
+6
-6
@@ -56,7 +56,7 @@ namespace WixToolset.Core.Burn.Bundles
56
/// <returns>Burn reader.</returns>
57
public static BurnReader Open(IMessaging messaging, IFileSystem fileSystem, string fileExe)
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)
61
{
62
binaryReader = binaryReader,
@@ -92,7 +92,7 @@ namespace WixToolset.Core.Burn.Bundles
92
var uxContainerSlot = this.AttachedContainers[0];
93
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
{
97
BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)uxContainerSlot.Size);
98
}
@@ -100,7 +100,7 @@ namespace WixToolset.Core.Burn.Bundles
100
var cabinet = new Cabinet(tempCabPath);
101
cabinet.Extract(outputDirectory);
102
103
- this.fileSystem.MoveFile(manifestOriginalPath, manifestPath);
103
+ this.fileSystem.MoveFile(null, manifestOriginalPath, manifestPath);
104
105
var document = new XmlDocument();
106
document.Load(manifestPath);
@@ -117,7 +117,7 @@ namespace WixToolset.Core.Burn.Bundles
117
var sourcePath = Path.Combine(outputDirectory, sourcePathNode.Value);
118
var destinationPath = Path.Combine(outputDirectory, filePathNode.Value);
119
120
- this.fileSystem.MoveFile(sourcePath, destinationPath);
120
+ this.fileSystem.MoveFile(null, sourcePath, destinationPath);
121
}
122
123
foreach (XmlNode payload in payloads)
@@ -169,7 +169,7 @@ namespace WixToolset.Core.Burn.Bundles
169
var tempCabPath = Path.Combine(tempDirectory, $"a{i}.cab");
170
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
{
174
BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)cntnr.Size);
175
}
@@ -185,7 +185,7 @@ namespace WixToolset.Core.Burn.Bundles
185
var sourcePath = Path.Combine(outputDirectory, (string)entry.Key);
186
var destinationPath = Path.Combine(outputDirectory, (string)entry.Value);
187
188
- this.fileSystem.MoveFile(sourcePath, destinationPath);
188
+ this.fileSystem.MoveFile(null, sourcePath, destinationPath);
189
}
190
191
return true;
src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs
+5
-4
@@ -43,14 +43,14 @@ namespace WixToolset.Core.Burn.Bundles
43
{
44
var writer = new BurnWriter(messaging, fileSystem, fileExe);
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
{
48
writer.Initialize(binaryReader);
49
}
50
51
if (!writer.Invalid)
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
}
55
56
return writer;
@@ -102,12 +102,13 @@ namespace WixToolset.Core.Burn.Bundles
102
/// <summary>
103
/// Appends a UX or Attached container to the exe and updates the ".wixburn" section data to point to it.
104
/// </summary>
105
+ /// <param name="sourceLineNumbers">Source line numbers for the container.</param>
106
/// <param name="fileContainer">File path to append to the current exe.</param>
107
/// <param name="container">Container section represented by the fileContainer.</param>
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)
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))
112
{
113
return this.AppendContainer(reader, reader.Length, container);
114
}
src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs
+3
-3
@@ -71,7 +71,7 @@ namespace WixToolset.Core.Burn.Bundles
71
72
this.Transfer = this.BackendHelper.CreateFileTransfer(bundleTempPath, this.OutputPath, true, this.BundleSymbol.SourceLineNumbers);
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);
76
77
var fourPartVersion = this.GetFourPartVersion(this.BundleSymbol);
@@ -88,7 +88,7 @@ namespace WixToolset.Core.Burn.Bundles
88
writer.InitializeBundleSectionData(burnStubFile.Length, this.BundleSymbol.BundleId);
89
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
93
// Now append all other attached 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.
99
if (!String.IsNullOrEmpty(container.WorkingPath) && BurnConstants.BurnUXContainerName != container.Id.Id)
100
{
101
- writer.AppendContainer(container.WorkingPath, BurnWriter.Container.Attached);
101
+ writer.AppendContainer(container.SourceLineNumbers, container.WorkingPath, BurnWriter.Container.Attached);
102
}
103
}
104
}
src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs
+2
-2
@@ -38,7 +38,7 @@ namespace WixToolset.Core.Burn.Inscribe
38
39
using (var reader = BurnReader.Open(this.Messaging, this.FileSystem, this.InputFilePath))
40
{
41
- this.FileSystem.CopyFile(this.SignedEngineFile, tempFile, allowHardlink: false);
41
+ this.FileSystem.CopyFile(null, this.SignedEngineFile, tempFile, allowHardlink: false);
42
43
using (var writer = BurnWriter.Open(this.Messaging, this.FileSystem, tempFile))
44
{
@@ -46,7 +46,7 @@ namespace WixToolset.Core.Burn.Inscribe
46
}
47
}
48
49
- this.FileSystem.MoveFile(tempFile, this.OutputFile);
49
+ this.FileSystem.MoveFile(null, tempFile, this.OutputFile);
50
51
return inscribed;
52
}
src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleEngineCommand.cs
+2
-2
@@ -33,7 +33,7 @@ namespace WixToolset.Core.Burn.Inscribe
33
var tempFile = Path.Combine(this.IntermediateFolder, "bundle_engine_unsigned.exe");
34
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
{
38
reader.Stream.Seek(0, SeekOrigin.Begin);
39
@@ -58,7 +58,7 @@ namespace WixToolset.Core.Burn.Inscribe
58
// TODO: update writer with detached container signatures.
59
}
60
61
- this.FileSystem.MoveFile(tempFile, this.OutputFile);
61
+ this.FileSystem.MoveFile(null, tempFile, this.OutputFile);
62
}
63
}
64
}
src/wix/WixToolset.Core.WindowsInstaller/Bind/AssemblyNameReader.cs
+1
-1
@@ -19,7 +19,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
19
{
20
try
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))
24
{
25
var reader = peReader.GetMetadataReader();
src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
+1
-1
@@ -212,7 +212,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
212
213
if (softwareTags.Any())
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();
217
218
trackedFiles.AddRange(command.TrackedFiles);
src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
+1
-1
@@ -401,7 +401,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
401
{
402
if (!String.IsNullOrEmpty(emptyFile))
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
{
406
}
407
}
src/wix/WixToolset.Core.WindowsInstaller/Bind/FileSystemManager.cs
+2
-3
@@ -5,7 +5,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind
5
using System;
6
using System.Collections.Generic;
7
using System.IO;
8
- using System.Runtime.CompilerServices;
8
using WixToolset.Extensibility;
9
using WixToolset.Extensibility.Services;
10
@@ -42,8 +41,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
41
return true;
42
}
43
45
- using (var firstStream = this.FileSystem.OpenFile(firstPath, FileMode.Open, FileAccess.Read, FileShare.Read))
46
- using (var secondStream = this.FileSystem.OpenFile(secondPath, FileMode.Open, FileAccess.Read, FileShare.Read))
44
+ using (var firstStream = this.FileSystem.OpenFile(null, firstPath, FileMode.Open, FileAccess.Read, FileShare.Read))
45
+ using (var secondStream = this.FileSystem.OpenFile(null, secondPath, FileMode.Open, FileAccess.Read, FileShare.Read))
46
{
47
if (firstStream.Length != secondStream.Length)
48
{
src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessPackageSoftwareTagsCommand.cs
+5
-2
@@ -15,10 +15,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
15
16
internal class ProcessPackageSoftwareTagsCommand
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
{
20
this.Section = section;
21
this.BackendHelper = backendHelper;
22
+ this.FileSystem = fileSystem;
23
this.SoftwareTags = softwareTags;
24
this.IntermediateFolder = intermediateFolder;
25
}
@@ -29,6 +30,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
30
31
private IBackendHelper BackendHelper { get; }
32
33
+ private IFileSystem FileSystem { get; }
34
+
35
private IEnumerable<WixPackageTagSymbol> SoftwareTags { get; }
36
37
public IReadOnlyCollection<ITrackedFile> TrackedFiles { get; private set; }
@@ -61,7 +64,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
64
65
trackedFiles.Add(this.BackendHelper.TrackFile(fileSymbol.Source.Path, TrackedFileType.Intermediate, tagRow.SourceLineNumbers));
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))
68
{
69
CreateTagFile(fs, uniqueId, packageSymbol.Name, packageSymbol.Version, tagRow.Regid, packageSymbol.Manufacturer, persistentId);
70
}
src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs
+1
-1
@@ -84,7 +84,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
84
return;
85
}
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
{
89
if (Int32.MaxValue < fileStream.Length)
90
{
src/wix/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs
+5
-5
@@ -51,7 +51,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
51
52
if (!String.Equals(this.InputPath, this.OutputPath, StringComparison.OrdinalIgnoreCase))
53
{
54
- this.FileSystem.CopyFile(this.InputPath, this.OutputPath, allowHardlink: false);
54
+ this.FileSystem.CopyFile(null, this.InputPath, this.OutputPath, allowHardlink: false);
55
}
56
57
var attributes = File.GetAttributes(databasePath);
@@ -98,7 +98,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
98
Directory.CreateDirectory(hashPath);
99
hashPath = Path.Combine(hashPath, hashFileName);
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
{
103
int bytesRead;
104
var buffer = new byte[1024 * 4];
@@ -129,7 +129,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
129
Directory.CreateDirectory(certPath);
130
certPath = Path.Combine(certPath, String.Concat(certificateId, ".cer"));
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
{
134
int bytesRead;
135
var buffer = new byte[1024 * 4];
@@ -223,9 +223,9 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
223
var certPath = Path.Combine(this.IntermediateFolder, "MsiDigitalCertificate");
224
Directory.CreateDirectory(certPath);
225
certPath = Path.Combine(certPath, String.Concat(cert2.Thumbprint, ".cer"));
226
- this.FileSystem.DeleteFile(certPath, true);
226
+ this.FileSystem.DeleteFile(null, certPath, true);
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
{
230
writer.Write(cert2.RawData);
231
writer.Close();
src/wix/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs
+3
-2
@@ -97,6 +97,8 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
97
{
98
if (null != record)
99
{
100
+ embeddedCabinetRowsByDiskId.TryGetValue(diskId, out var cabinetMediaRow);
101
+
102
// since the cabinets are stored in case-sensitive streams inside the msi, but the file system is not (typically) case-sensitive,
103
// embedded cabinets must be extracted to a canonical file name (like their diskid) to ensure extraction will always work
104
var cabinetPath = Path.Combine(this.IntermediateFolder, "Media", diskId.ToString(CultureInfo.InvariantCulture), ".cab");
@@ -104,7 +106,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
106
// ensure the parent directory exists
107
Directory.CreateDirectory(Path.GetDirectoryName(cabinetPath));
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))
110
{
111
int bytesRead;
112
var buffer = new byte[4096];
@@ -115,7 +117,6 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
117
}
118
}
119
118
- embeddedCabinetRowsByDiskId.TryGetValue(diskId, out var cabinetMediaRow);
120
cabinetPathsWithMediaRow.Add(cabinetPath, cabinetMediaRow);
121
}
122
else
src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
+1
-1
@@ -206,7 +206,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
206
207
Directory.CreateDirectory(Path.Combine(this.ExportBasePath, tableName));
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
{
211
int bytesRead;
212
var buffer = new byte[4096];
src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs
+1
-1
@@ -327,7 +327,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
327
if (null == this.EmptyFile)
328
{
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
{
332
}
333
}
src/wix/WixToolset.Core.WindowsInstaller/Validate/ValidateDatabaseCommand.cs
+2
-2
@@ -71,7 +71,7 @@ namespace WixToolset.Core.WindowsInstaller.Validate
71
var workingDatabasePath = Path.Combine(this.IntermediateFolder, workingDatabaseFilename);
72
try
73
{
74
- this.FileSystem.CopyFile(this.DatabasePath, workingDatabasePath, allowHardlink: false);
74
+ this.FileSystem.CopyFile(null, this.DatabasePath, workingDatabasePath, allowHardlink: false);
75
76
var attributes = File.GetAttributes(workingDatabasePath);
77
File.SetAttributes(workingDatabasePath, attributes & ~FileAttributes.ReadOnly);
@@ -81,7 +81,7 @@ namespace WixToolset.Core.WindowsInstaller.Validate
81
}
82
finally
83
{
84
- this.FileSystem.DeleteFile(workingDatabasePath);
84
+ this.FileSystem.DeleteFile(null, workingDatabasePath);
85
}
86
87
stopwatch.Stop();
src/wix/WixToolset.Core/Bind/TransferFilesCommand.cs
+6
-6
@@ -53,12 +53,12 @@ namespace WixToolset.Core.Bind
53
if (fileTransfer.Move)
54
{
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
}
58
else
59
{
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
}
63
64
retry = false;
@@ -170,7 +170,7 @@ namespace WixToolset.Core.Bind
170
}
171
}
172
173
- private void CopyFile(string source, string destination)
173
+ private void CopyFile(SourceLineNumber sourceLineNumbers, string source, string destination)
174
{
175
foreach (var extension in this.Extensions)
176
{
@@ -180,10 +180,10 @@ namespace WixToolset.Core.Bind
180
}
181
}
182
183
- this.FileSystem.CopyFile(source, destination, allowHardlink: true);
183
+ this.FileSystem.CopyFile(sourceLineNumbers, source, destination, allowHardlink: true);
184
}
185
186
- private void MoveFile(string source, string destination)
186
+ private void MoveFile(SourceLineNumber sourceLineNumbers, string source, string destination)
187
{
188
foreach (var extension in this.Extensions)
189
{
@@ -193,7 +193,7 @@ namespace WixToolset.Core.Bind
193
}
194
}
195
196
- this.FileSystem.MoveFile(source, destination);
196
+ this.FileSystem.MoveFile(sourceLineNumbers, source, destination);
197
}
198
199
private void AclReset(IEnumerable<string> files)
src/wix/WixToolset.Core/CoreErrors.cs
new
+42
@@ -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
+
3
+namespace 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
+}
src/wix/WixToolset.Core/ExtensibilityServices/FileSystem.cs
+53
-13
@@ -7,19 +7,34 @@ namespace WixToolset.Core.ExtensibilityServices
7
using System.IO;
8
using System.Runtime.InteropServices;
9
using System.Threading;
10
+ using WixToolset.Data;
11
using WixToolset.Extensibility.Services;
12
13
internal class FileSystem : IFileSystem
14
{
14
- public void CopyFile(string source, string destination, bool allowHardlink)
15
+ public void CopyFile(SourceLineNumber sourceLineNumbers, string source, string destination, bool allowHardlink)
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
+ }
25
26
var hardlinked = false;
27
28
if (allowHardlink)
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
+ }
38
}
39
40
if (!hardlinked)
@@ -28,30 +43,48 @@ namespace WixToolset.Core.ExtensibilityServices
43
var er = Marshal.GetLastWin32Error();
44
#endif
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
+ }
54
}
55
}
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)
58
{
59
try
60
{
61
this.ExecuteWithRetries(() => File.Delete(source), maxRetries);
62
}
41
- catch when (!throwOnError)
63
+ catch (Exception e)
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.
70
}
71
}
72
47
- public void MoveFile(string source, string destination)
73
+ public void MoveFile(SourceLineNumber sourceLineNumbers, string source, string destination)
74
{
49
- this.EnsureDirectoryWithoutFile(destination);
75
+ try
76
+ {
77
+ this.EnsureDirectoryWithoutFile(destination);
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
+ }
85
}
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)
88
{
89
const int maxRetries = 4;
90
@@ -61,9 +94,16 @@ namespace WixToolset.Core.ExtensibilityServices
94
{
95
return File.Open(path, mode, access, share);
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)
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
+ }
107
}
108
}
109
src/wix/WixToolset.Core/LayoutCreator.cs
+1
-1
@@ -114,7 +114,7 @@ namespace WixToolset.Core
114
{
115
this.SplitUniqueFolders(intermediateFolder, tempPath, uniqueFolders);
116
117
- this.FileSystem.DeleteFile(tempPath);
117
+ this.FileSystem.DeleteFile(null, tempPath);
118
}
119
120
// Clean up empty temp folders.