@joebigelow / wix / commits / 024e03c1

Abstract file system to remove Core to Core.Native dependency

The only dependency Core had on Core.Native was for FileSystem which would be better served as an extensibility service everywhere anyway. This moves FileSystem to Core and exposes it via IFileSystem from Extensibility for use everywhere. Core now carries no native code dependencies.

Rob Mensching committed Jul 26, 2022 at 17:20 UTC 024e03c1ae9d956834b9ccc4d4f91a991507b27c
19 files changed +143 -93
src/api/wix/WixToolset.Extensibility/Services/IFileSystem.cs new
+25
@@ -0,0 +1,25 @@
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.Extensibility.Services
4 +{
5 + /// <summary>
6 + /// Abstracts basic file system operations.
7 + /// </summary>
8 + public interface IFileSystem
9 + {
10 + /// <summary>
11 + /// Copies a file.
12 + /// </summary>
13 + /// <param name="source">The file to copy.</param>
14 + /// <param name="destination">The destination file.</param>
15 + /// <param name="allowHardlink">Allow hardlinks.</param>
16 + void CopyFile(string source, string destination, bool allowHardlink);
17 +
18 + /// <summary>
19 + /// Moves a file.
20 + /// </summary>
21 + /// <param name="source">The file to move.</param>
22 + /// <param name="destination">The destination file.</param>
23 + void MoveFile(string source, string destination);
24 + }
25 +}
src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
+4 -1
@@ -28,6 +28,7 @@ namespace WixToolset.Core.Burn
28 this.ServiceProvider = context.ServiceProvider;
29
30 this.Messaging = context.ServiceProvider.GetService<IMessaging>();
31 + this.FileSystem = context.ServiceProvider.GetService<IFileSystem>();
32
33 this.BackendHelper = context.ServiceProvider.GetService<IBackendHelper>();
34 this.InternalBurnBackendHelper = context.ServiceProvider.GetService<IInternalBurnBackendHelper>();
@@ -49,6 +50,8 @@ namespace WixToolset.Core.Burn
50
51 private IMessaging Messaging { get; }
52
53 + private IFileSystem FileSystem { get; }
54 +
55 private IBackendHelper BackendHelper { get; }
56
57 private IInternalBurnBackendHelper InternalBurnBackendHelper { get; }
@@ -507,7 +510,7 @@ namespace WixToolset.Core.Burn
510 }
511
512 {
510 - var command = new CreateBundleExeCommand(this.Messaging, this.BackendHelper, this.IntermediateFolder, this.OutputPath, bundleApplicationDllSymbol, bundleSymbol, uxContainer, containers.Values);
513 + var command = new CreateBundleExeCommand(this.Messaging, this.FileSystem, this.BackendHelper, this.IntermediateFolder, this.OutputPath, bundleApplicationDllSymbol, bundleSymbol, uxContainer, containers.Values);
514 command.Execute();
515
516 fileTransfers.Add(command.Transfer);
src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs
+12 -11
@@ -27,16 +27,19 @@ namespace WixToolset.Core.Burn.Bundles
27
28 private BinaryReader binaryReader;
29 private readonly List<DictionaryEntry> attachedContainerPayloadNames;
30 + private readonly IFileSystem fileSystem;
31
32 /// <summary>
33 /// Creates a BurnReader for reading a PE file.
34 /// </summary>
34 - /// <param name="messaging"></param>
35 + /// <param name="messaging">Messaging.</param>
36 + /// <param name="fileSystem">File system.</param>
37 /// <param name="fileExe">File to read.</param>
36 - private BurnReader(IMessaging messaging, string fileExe)
38 + private BurnReader(IMessaging messaging, IFileSystem fileSystem, string fileExe)
39 : base(messaging, fileExe)
40 {
41 this.attachedContainerPayloadNames = new List<DictionaryEntry>();
42 + this.fileSystem = fileSystem;
43 }
44
45 /// <summary>
@@ -47,13 +50,14 @@ namespace WixToolset.Core.Burn.Bundles
50 /// <summary>
51 /// Opens a Burn reader.
52 /// </summary>
50 - /// <param name="messaging"></param>
53 + /// <param name="messaging">Messaging.</param>
54 + /// <param name="fileSystem">File system.</param>
55 /// <param name="fileExe">Path to file.</param>
56 /// <returns>Burn reader.</returns>
53 - public static BurnReader Open(IMessaging messaging, string fileExe)
57 + public static BurnReader Open(IMessaging messaging, IFileSystem fileSystem, string fileExe)
58 {
59 var binaryReader = new BinaryReader(File.Open(fileExe, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete));
56 - var reader = new BurnReader(messaging, fileExe)
60 + var reader = new BurnReader(messaging, fileSystem, fileExe)
61 {
62 binaryReader = binaryReader,
63 };
@@ -96,8 +100,7 @@ namespace WixToolset.Core.Burn.Bundles
100 var cabinet = new Cabinet(tempCabPath);
101 cabinet.Extract(outputDirectory);
102
99 - Directory.CreateDirectory(Path.GetDirectoryName(manifestPath));
100 - FileSystem.MoveFile(manifestOriginalPath, manifestPath);
103 + this.fileSystem.MoveFile(manifestOriginalPath, manifestPath);
104
105 var document = new XmlDocument();
106 document.Load(manifestPath);
@@ -114,8 +117,7 @@ namespace WixToolset.Core.Burn.Bundles
117 var sourcePath = Path.Combine(outputDirectory, sourcePathNode.Value);
118 var destinationPath = Path.Combine(outputDirectory, filePathNode.Value);
119
117 - Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
118 - FileSystem.MoveFile(sourcePath, destinationPath);
120 + this.fileSystem.MoveFile(sourcePath, destinationPath);
121 }
122
123 foreach (XmlNode payload in payloads)
@@ -183,8 +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
186 - Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
187 - FileSystem.MoveFile(sourcePath, destinationPath);
188 + this.fileSystem.MoveFile(sourcePath, destinationPath);
189 }
190
191 return true;
src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs
+5 -3
@@ -9,7 +9,6 @@ namespace WixToolset.Core.Burn.Bundles
9 using System.Runtime.InteropServices;
10 using System.Text;
11 using System.Xml;
12 - using WixToolset.Core.Native;
12 using WixToolset.Data;
13 using WixToolset.Data.Burn;
14 using WixToolset.Data.Symbols;
@@ -19,9 +18,10 @@ namespace WixToolset.Core.Burn.Bundles
18
19 internal class CreateBundleExeCommand
20 {
22 - public CreateBundleExeCommand(IMessaging messaging, IBackendHelper backendHelper, string intermediateFolder, string outputPath, WixBootstrapperApplicationDllSymbol bootstrapperApplicationDllSymbol, WixBundleSymbol bundleSymbol, WixBundleContainerSymbol uxContainer, IEnumerable<WixBundleContainerSymbol> containers)
21 + public CreateBundleExeCommand(IMessaging messaging, IFileSystem fileSystem, IBackendHelper backendHelper, string intermediateFolder, string outputPath, WixBootstrapperApplicationDllSymbol bootstrapperApplicationDllSymbol, WixBundleSymbol bundleSymbol, WixBundleContainerSymbol uxContainer, IEnumerable<WixBundleContainerSymbol> containers)
22 {
23 this.Messaging = messaging;
24 + this.FileSystem = fileSystem;
25 this.BackendHelper = backendHelper;
26 this.IntermediateFolder = intermediateFolder;
27 this.OutputPath = outputPath;
@@ -35,6 +35,8 @@ namespace WixToolset.Core.Burn.Bundles
35
36 private IMessaging Messaging { get; }
37
38 + private IFileSystem FileSystem { get; }
39 +
40 private IBackendHelper BackendHelper { get; }
41
42 private string IntermediateFolder { get; }
@@ -68,7 +70,7 @@ namespace WixToolset.Core.Burn.Bundles
70
71 this.Transfer = this.BackendHelper.CreateFileTransfer(bundleTempPath, this.OutputPath, true, this.BundleSymbol.SourceLineNumbers);
72
71 - FileSystem.CopyFile(stubFile, bundleTempPath, allowHardlink: false);
73 + this.FileSystem.CopyFile(stubFile, bundleTempPath, allowHardlink: false);
74 File.SetAttributes(bundleTempPath, FileAttributes.Normal);
75
76 var fourPartVersion = this.GetFourPartVersion(this.BundleSymbol);
src/wix/WixToolset.Core.Burn/Bundles/HarvestBundlePackageCommand.cs
+4 -1
@@ -18,6 +18,7 @@ namespace WixToolset.Core.Burn.Bundles
18 public HarvestBundlePackageCommand(IServiceProvider serviceProvider, IEnumerable<IBurnBackendBinderExtension> backendExtensions, string intermediateFolder, WixBundlePayloadSymbol payloadSymbol, WixBundleBundlePackagePayloadSymbol packagePayloadSymbol, Dictionary<string, WixBundlePayloadSymbol> packagePayloadsById)
19 {
20 this.Messaging = serviceProvider.GetService<IMessaging>();
21 + this.FileSystem = serviceProvider.GetService<IFileSystem>();
22 this.BackendHelper = serviceProvider.GetService<IBackendHelper>();
23 this.BackendExtensions = backendExtensions;
24 this.IntermediateFolder = intermediateFolder;
@@ -29,6 +30,8 @@ namespace WixToolset.Core.Burn.Bundles
30
31 private IMessaging Messaging { get; }
32
33 + private IFileSystem FileSystem { get; }
34 +
35 private IBackendHelper BackendHelper { get; }
36
37 private IEnumerable<IBurnBackendBinderExtension> BackendExtensions { get; }
@@ -66,7 +69,7 @@ namespace WixToolset.Core.Burn.Bundles
69 var sourcePath = this.PackagePayload.SourceFile.Path;
70 var sourceLineNumbers = this.PackagePayload.SourceLineNumbers;
71
69 - using (var burnReader = BurnReader.Open(this.Messaging, sourcePath))
72 + using (var burnReader = BurnReader.Open(this.Messaging, this.FileSystem, sourcePath))
73 {
74 if (burnReader.Invalid)
75 {
src/wix/WixToolset.Core.Burn/CommandLine/ExtractSubcommand.cs
+4 -5
@@ -7,21 +7,20 @@ namespace WixToolset.Core.Burn.CommandLine
7 using System.Threading;
8 using System.Threading.Tasks;
9 using WixToolset.Core.Burn.Bundles;
10 - using WixToolset.Core.Burn.Inscribe;
10 using WixToolset.Extensibility.Services;
11
12 internal class ExtractSubcommand : BurnSubcommandBase
13 {
14 public ExtractSubcommand(IServiceProvider serviceProvider)
15 {
17 - this.ServiceProvider = serviceProvider;
16 this.Messaging = serviceProvider.GetService<IMessaging>();
17 + this.FileSystem = serviceProvider.GetService<IFileSystem>();
18 }
19
21 - private IServiceProvider ServiceProvider { get; }
22 -
20 private IMessaging Messaging { get; }
21
22 + private IFileSystem FileSystem { get; }
23 +
24 private string InputPath { get; set; }
25
26 private string IntermediateFolder { get; set; }
@@ -49,7 +48,7 @@ namespace WixToolset.Core.Burn.CommandLine
48
49 var uxExtractPath = Path.Combine(this.ExtractPath, "BA");
50
52 - using (var reader = BurnReader.Open(this.Messaging, this.InputPath))
51 + using (var reader = BurnReader.Open(this.Messaging, this.FileSystem, this.InputPath))
52 {
53 reader.ExtractUXContainer(uxExtractPath, this.IntermediateFolder);
54
src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs
+6 -6
@@ -5,7 +5,6 @@ namespace WixToolset.Core.Burn.Inscribe
5 using System;
6 using System.IO;
7 using WixToolset.Core.Burn.Bundles;
8 - using WixToolset.Core.Native;
8 using WixToolset.Extensibility.Services;
9
10 internal class InscribeBundleCommand
@@ -13,6 +12,7 @@ namespace WixToolset.Core.Burn.Inscribe
12 public InscribeBundleCommand(IServiceProvider serviceProvider, string inputPath, string signedEngineFile, string outputPath, string intermediateFolder)
13 {
14 this.Messaging = serviceProvider.GetService<IMessaging>();
15 + this.FileSystem = serviceProvider.GetService<IFileSystem>();
16 this.IntermediateFolder = intermediateFolder;
17 this.InputFilePath = inputPath;
18 this.SignedEngineFile = signedEngineFile;
@@ -21,6 +21,8 @@ namespace WixToolset.Core.Burn.Inscribe
21
22 private IMessaging Messaging { get; }
23
24 + private IFileSystem FileSystem { get; }
25 +
26 private string IntermediateFolder { get; }
27
28 private string InputFilePath { get; }
@@ -34,9 +36,9 @@ namespace WixToolset.Core.Burn.Inscribe
36 var inscribed = false;
37 var tempFile = Path.Combine(this.IntermediateFolder, "~bundle_engine_signed.exe");
38
37 - using (var reader = BurnReader.Open(this.Messaging, this.InputFilePath))
39 + using (var reader = BurnReader.Open(this.Messaging, this.FileSystem, this.InputFilePath))
40 {
39 - FileSystem.CopyFile(this.SignedEngineFile, tempFile, allowHardlink: false);
41 + this.FileSystem.CopyFile(this.SignedEngineFile, tempFile, allowHardlink: false);
42
43 using (var writer = BurnWriter.Open(this.Messaging, tempFile))
44 {
@@ -44,9 +46,7 @@ namespace WixToolset.Core.Burn.Inscribe
46 }
47 }
48
47 - Directory.CreateDirectory(Path.GetDirectoryName(this.OutputFile));
48 -
49 - FileSystem.MoveFile(tempFile, this.OutputFile);
49 + this.FileSystem.MoveFile(tempFile, this.OutputFile);
50
51 return inscribed;
52 }
src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleEngineCommand.cs
+5 -5
@@ -5,7 +5,6 @@ namespace WixToolset.Core.Burn.Inscribe
5 using System;
6 using System.IO;
7 using WixToolset.Core.Burn.Bundles;
8 - using WixToolset.Core.Native;
8 using WixToolset.Extensibility.Services;
9
10 internal class InscribeBundleEngineCommand
@@ -13,6 +12,7 @@ namespace WixToolset.Core.Burn.Inscribe
12 public InscribeBundleEngineCommand(IServiceProvider serviceProvider, string inputPath, string outputPath, string intermediateFolder)
13 {
14 this.Messaging = serviceProvider.GetService<IMessaging>();
15 + this.FileSystem = serviceProvider.GetService<IFileSystem>();
16 this.IntermediateFolder = intermediateFolder;
17 this.InputFilePath = inputPath;
18 this.OutputFile = outputPath;
@@ -20,6 +20,8 @@ namespace WixToolset.Core.Burn.Inscribe
20
21 private IMessaging Messaging { get; }
22
23 + private IFileSystem FileSystem { get; }
24 +
25 private string IntermediateFolder { get; }
26
27 private string InputFilePath { get; }
@@ -30,7 +32,7 @@ namespace WixToolset.Core.Burn.Inscribe
32 {
33 var tempFile = Path.Combine(this.IntermediateFolder, "bundle_engine_unsigned.exe");
34
33 - using (var reader = BurnReader.Open(this.Messaging, this.InputFilePath))
35 + using (var reader = BurnReader.Open(this.Messaging, this.FileSystem, this.InputFilePath))
36 using (var writer = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read | FileShare.Delete))
37 {
38 reader.Stream.Seek(0, SeekOrigin.Begin);
@@ -56,9 +58,7 @@ namespace WixToolset.Core.Burn.Inscribe
58 // TODO: update writer with detached container signatures.
59 }
60
59 - Directory.CreateDirectory(Path.GetDirectoryName(this.OutputFile));
60 -
61 - FileSystem.MoveFile(tempFile, this.OutputFile);
61 + this.FileSystem.MoveFile(tempFile, this.OutputFile);
62 }
63 }
64 }
src/wix/WixToolset.Core.Native/DateTimeInterop.cs
+2 -2
@@ -20,8 +20,8 @@ namespace WixToolset.Core.Native
20 {
21 // dateTime.ToLocalTime() does not match FileTimeToLocalFileTime() for some reason.
22 // so we need to call FileTimeToLocalFileTime() from kernel32.dll.
23 - long filetime = dateTime.ToFileTime();
24 - long localTime = 0;
23 + var filetime = dateTime.ToFileTime();
24 + var localTime = 0L;
25 FileTimeToLocalFileTime(ref filetime, ref localTime);
26 FileTimeToDosDateTime(ref localTime, out cabDate, out cabTime);
27 }
src/wix/WixToolset.Core.Native/WixToolset.Core.Native.csproj
-1
@@ -24,6 +24,5 @@
24
25 <ItemGroup>
26 <PackageReference Include="WixToolset.Data" />
27 - <PackageReference Include="System.IO.FileSystem.AccessControl" />
27 </ItemGroup>
28 </Project>
src/wix/WixToolset.Core.TestPackage/BundleExtractor.cs
+16 -1
@@ -39,7 +39,7 @@ namespace WixToolset.Core.TestPackage
39 {
40 var result = new ExtractBAContainerResult();
41 Directory.CreateDirectory(tempFolderPath);
42 - using (var burnReader = BurnReader.Open(messaging, bundleFilePath))
42 + using (var burnReader = BurnReader.Open(messaging, new TestFileSystem(), bundleFilePath))
43 {
44 result.Success = burnReader.ExtractUXContainer(baFolderPath, tempFolderPath);
45
@@ -138,5 +138,20 @@ namespace WixToolset.Core.TestPackage
138 document.Load(Path.Combine(baFolderPath, "manifest.xml"));
139 return document;
140 }
141 +
142 + private class TestFileSystem : IFileSystem
143 + {
144 + public void CopyFile(string source, string destination, bool allowHardlink)
145 + {
146 + Directory.CreateDirectory(Path.GetDirectoryName(destination));
147 + File.Copy(source, destination);
148 + }
149 +
150 + public void MoveFile(string source, string destination)
151 + {
152 + Directory.CreateDirectory(Path.GetDirectoryName(destination));
153 + File.Move(source, destination);
154 + }
155 + }
156 }
157 }
src/wix/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs
+19 -3
@@ -71,10 +71,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind
71 {
72 merge = MsmInterop.GetMsmMerge();
73
74 - FileSystem.ActionWithRetries(() => merge.OpenLog(logPath));
74 + ActionWithRetries(() => merge.OpenLog(logPath));
75 logOpen = true;
76
77 - FileSystem.ActionWithRetries(() => merge.OpenDatabase(this.OutputPath));
77 + ActionWithRetries(() => merge.OpenDatabase(this.OutputPath));
78 databaseOpen = true;
79
80 var featureModulesByMergeId = this.Section.Symbols.OfType<WixFeatureModulesSymbol>().GroupBy(t => t.WixMergeRef).ToDictionary(g => g.Key);
@@ -99,7 +99,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
99 }
100
101 this.Messaging.Write(VerboseMessages.OpeningMergeModule(wixMergeRow.SourceFile, mergeLanguage));
102 - FileSystem.ActionWithRetries(() => merge.OpenModule(wixMergeRow.SourceFile, mergeLanguage));
102 + ActionWithRetries(() => merge.OpenModule(wixMergeRow.SourceFile, mergeLanguage));
103 moduleOpen = true;
104
105 trackedFiles.Add(this.BackendHelper.TrackFile(wixMergeRow.SourceFile, TrackedFileType.Input, wixMergeRow.SourceLineNumbers));
@@ -340,5 +340,21 @@ namespace WixToolset.Core.WindowsInstaller.Bind
340
341 this.TrackedFiles = trackedFiles;
342 }
343 +
344 + internal static void ActionWithRetries(Action action, int maxRetries = 3)
345 + {
346 + for (var attempt = 1; attempt <= maxRetries; ++attempt)
347 + {
348 + try
349 + {
350 + action();
351 + break;
352 + }
353 + catch when (attempt < maxRetries)
354 + {
355 + Thread.Sleep(250);
356 + }
357 + }
358 + }
359 }
360 }
src/wix/WixToolset.Core.WindowsInstaller/CommandLine/ValidateSubcommand.cs
+4 -1
@@ -16,10 +16,13 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
16 public ValidateSubcommand(IServiceProvider serviceProvider)
17 {
18 this.Messaging = serviceProvider.GetService<IMessaging>();
19 + this.FileSystem = serviceProvider.GetService<IFileSystem>();
20 }
21
22 private IMessaging Messaging { get; }
23
24 + private IFileSystem FileSystem { get; }
25 +
26 private string DatabasePath { get; set; }
27
28 private string WixpdbPath { get; set; }
@@ -76,7 +79,7 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
79 data = WindowsInstallerData.Load(this.WixpdbPath);
80 }
81
79 - var command = new ValidateDatabaseCommand(this.Messaging, this.IntermediateFolder, this.DatabasePath, data, this.CubeFiles, this.Ices, this.SuppressIces);
82 + var command = new ValidateDatabaseCommand(this.Messaging, this.FileSystem, this.IntermediateFolder, this.DatabasePath, data, this.CubeFiles, this.Ices, this.SuppressIces);
83 command.Execute();
84
85 return Task.FromResult(this.Messaging.EncounteredError ? 1 : 0);
src/wix/WixToolset.Core.WindowsInstaller/Validate/ValidateDatabaseCommand.cs
+5 -2
@@ -18,9 +18,10 @@ namespace WixToolset.Core.WindowsInstaller.Validate
18 // Set of ICEs that have equivalent-or-better checks in WiX.
19 private static readonly string[] WellKnownSuppressedIces = new[] { "ICE08", "ICE33", "ICE47", "ICE66" };
20
21 - public ValidateDatabaseCommand(IMessaging messaging, string intermediateFolder, string databasePath, WindowsInstallerData data, IEnumerable<string> cubeFiles, IEnumerable<string> ices, IEnumerable<string> suppressedIces)
21 + public ValidateDatabaseCommand(IMessaging messaging, IFileSystem fileSystem, string intermediateFolder, string databasePath, WindowsInstallerData data, IEnumerable<string> cubeFiles, IEnumerable<string> ices, IEnumerable<string> suppressedIces)
22 {
23 this.Messaging = messaging;
24 + this.FileSystem = fileSystem;
25 this.Data = data;
26 this.DatabasePath = databasePath;
27 this.CubeFiles = cubeFiles;
@@ -40,6 +41,8 @@ namespace WixToolset.Core.WindowsInstaller.Validate
41
42 private IMessaging Messaging { get; }
43
44 + private IFileSystem FileSystem { get; }
45 +
46 private WindowsInstallerData Data { get; }
47
48 private string DatabasePath { get; }
@@ -71,7 +74,7 @@ namespace WixToolset.Core.WindowsInstaller.Validate
74 var workingDatabasePath = Path.Combine(this.IntermediateFolder, workingDatabaseFilename);
75 try
76 {
74 - FileSystem.CopyFile(this.DatabasePath, workingDatabasePath, allowHardlink: false);
77 + this.FileSystem.CopyFile(this.DatabasePath, workingDatabasePath, allowHardlink: false);
78
79 var attributes = File.GetAttributes(workingDatabasePath);
80 File.SetAttributes(workingDatabasePath, attributes & ~FileAttributes.ReadOnly);
src/wix/WixToolset.Core/Bind/TransferFilesCommand.cs
+20 -5
@@ -5,7 +5,7 @@ namespace WixToolset.Core.Bind
5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8 - using WixToolset.Core.Native;
8 + using System.Security.AccessControl;
9 using WixToolset.Data;
10 using WixToolset.Extensibility;
11 using WixToolset.Extensibility.Data;
@@ -13,16 +13,19 @@ namespace WixToolset.Core.Bind
13
14 internal class TransferFilesCommand
15 {
16 - public TransferFilesCommand(IMessaging messaging, IEnumerable<ILayoutExtension> extensions, IEnumerable<IFileTransfer> fileTransfers, bool resetAcls)
16 + public TransferFilesCommand(IMessaging messaging, IFileSystem fileSystem, IEnumerable<ILayoutExtension> extensions, IEnumerable<IFileTransfer> fileTransfers, bool resetAcls)
17 {
18 this.Extensions = extensions;
19 this.Messaging = messaging;
20 + this.FileSystem = fileSystem;
21 this.FileTransfers = fileTransfers;
22 this.ResetAcls = resetAcls;
23 }
24
25 private IMessaging Messaging { get; }
26
27 + private IFileSystem FileSystem { get; }
28 +
29 private IEnumerable<ILayoutExtension> Extensions { get; }
30
31 private IEnumerable<IFileTransfer> FileTransfers { get; }
@@ -158,7 +161,7 @@ namespace WixToolset.Core.Bind
161 {
162 try
163 {
161 - FileSystem.ResetAcls(destinationFiles);
164 + this.AclReset(destinationFiles);
165 }
166 catch (Exception e)
167 {
@@ -177,7 +180,7 @@ namespace WixToolset.Core.Bind
180 }
181 }
182
180 - FileSystem.CopyFile(source, destination, allowHardlink: true);
183 + this.FileSystem.CopyFile(source, destination, allowHardlink: true);
184 }
185
186 private void MoveFile(string source, string destination)
@@ -190,7 +193,19 @@ namespace WixToolset.Core.Bind
193 }
194 }
195
193 - FileSystem.MoveFile(source, destination);
196 + this.FileSystem.MoveFile(source, destination);
197 + }
198 +
199 + private void AclReset(IEnumerable<string> files)
200 + {
201 + var aclReset = new FileSecurity();
202 + aclReset.SetAccessRuleProtection(false, false);
203 +
204 + foreach (var file in files)
205 + {
206 + var fileInfo = new FileInfo(file);
207 + ExtensibilityServices.FileSystem.ActionWithRetries(() => fileInfo.SetAccessControl(aclReset));
208 + }
209 }
210 }
211 }
src/wix/WixToolset.Core/ExtensibilityServices/FileSystem.cs renamed
+6 -37
@@ -1,26 +1,16 @@
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.Native
3 +namespace WixToolset.Core.ExtensibilityServices
4 {
5 using System;
6 - using System.Collections.Generic;
6 using System.IO;
7 using System.Runtime.InteropServices;
9 - using System.Security.AccessControl;
8 using System.Threading;
9 + using WixToolset.Extensibility.Services;
10
12 - /// <summary>
13 - /// File system helpers.
14 - /// </summary>
15 - public static class FileSystem
11 + internal class FileSystem : IFileSystem
12 {
17 - /// <summary>
18 - /// Copies a file.
19 - /// </summary>
20 - /// <param name="source">The file to copy.</param>
21 - /// <param name="destination">The destination file.</param>
22 - /// <param name="allowHardlink">Allow hardlinks.</param>
23 - public static void CopyFile(string source, string destination, bool allowHardlink)
13 + public void CopyFile(string source, string destination, bool allowHardlink)
14 {
15 EnsureDirectoryWithoutFile(destination);
16
@@ -41,34 +31,13 @@ namespace WixToolset.Core.Native
31 }
32 }
33
44 - /// <summary>
45 - /// Moves a file.
46 - /// </summary>
47 - /// <param name="source">The file to move.</param>
48 - /// <param name="destination">The destination file.</param>
49 - public static void MoveFile(string source, string destination)
34 + public void MoveFile(string source, string destination)
35 {
36 EnsureDirectoryWithoutFile(destination);
37
38 ActionWithRetries(() => File.Move(source, destination));
39 }
40
56 - /// <summary>
57 - /// Reset the ACLs on a set of files.
58 - /// </summary>
59 - /// <param name="files">The list of file paths to set ACLs.</param>
60 - public static void ResetAcls(IEnumerable<string> files)
61 - {
62 - var aclReset = new FileSecurity();
63 - aclReset.SetAccessRuleProtection(false, false);
64 -
65 - foreach (var file in files)
66 - {
67 - var fileInfo = new FileInfo(file);
68 - ActionWithRetries(() => fileInfo.SetAccessControl(aclReset));
69 - }
70 - }
71 -
41 /// <summary>
42 /// Executes an action and retries on any exception up to a few times. Primarily
43 /// intended for use with file system operations that might get interrupted by
@@ -76,7 +45,7 @@ namespace WixToolset.Core.Native
45 /// </summary>
46 /// <param name="action">Action to execute.</param>
47 /// <param name="maxRetries">Maximum retry attempts.</param>
79 - public static void ActionWithRetries(Action action, int maxRetries = 3)
48 + internal static void ActionWithRetries(Action action, int maxRetries = 3)
49 {
50 for (var attempt = 1; attempt <= maxRetries; ++attempt)
51 {
src/wix/WixToolset.Core/LayoutCreator.cs
+4 -1
@@ -21,10 +21,13 @@ namespace WixToolset.Core
21 internal LayoutCreator(IServiceProvider serviceProvider)
22 {
23 this.Messaging = serviceProvider.GetService<IMessaging>();
24 + this.FileSystem = serviceProvider.GetService<IFileSystem>();
25 }
26
27 private IMessaging Messaging { get; }
28
29 + private IFileSystem FileSystem { get; }
30 +
31 public void Layout(ILayoutContext context)
32 {
33 // Pre-layout.
@@ -42,7 +45,7 @@ namespace WixToolset.Core
45 {
46 this.Messaging.Write(VerboseMessages.LayingOutMedia());
47
45 - var command = new TransferFilesCommand(this.Messaging, context.Extensions, context.FileTransfers, context.ResetAcls);
48 + var command = new TransferFilesCommand(this.Messaging, this.FileSystem, context.Extensions, context.FileTransfers, context.ResetAcls);
49 command.Execute();
50 }
51
src/wix/WixToolset.Core/WixToolset.Core.csproj
+1 -8
@@ -14,20 +14,13 @@
14 <GitThisAssembly>true</GitThisAssembly>
15 </PropertyGroup>
16
17 - <ItemGroup>
18 - <ProjectReference Include="..\WixToolset.Core.Native\WixToolset.Core.Native.csproj" />
19 - </ItemGroup>
20 -
17 <ItemGroup>
18 <PackageReference Include="WixToolset.Data" />
19 <PackageReference Include="WixToolset.Extensibility" />
20 </ItemGroup>
21
26 - <!--
27 - These package references are duplicated in WixToolset.Core.TestPackage.csproj. If
28 - you update these here, be sure to update them there.
29 - -->
22 <ItemGroup>
23 + <PackageReference Include="System.IO.FileSystem.AccessControl" />
24 <PackageReference Include="System.Text.Encoding.CodePages" />
25 <PackageReference Include="NuGet.Versioning" />
26 </ItemGroup>
src/wix/WixToolset.Core/WixToolsetServiceProvider.cs
+1
@@ -27,6 +27,7 @@ namespace WixToolset.Core
27 this.AddService((provider, singletons) => AddSingleton<ILayoutServices>(singletons, new LayoutServices(provider)));
28 this.AddService((provider, singletons) => AddSingleton<IBackendHelper>(singletons, new BackendHelper(provider)));
29 this.AddService((provider, singletons) => AddSingleton<IPathResolver>(singletons, new PathResolver()));
30 + this.AddService((provider, singletons) => AddSingleton<IFileSystem>(singletons, new FileSystem()));
31 this.AddService((provider, singletons) => AddSingleton<IWixBranding>(singletons, new WixBranding()));
32
33 // Transients.