main
cs 144 lines 4.82 KB
Raw
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.ExtensibilityServices
4 {
5 using System;
6 using System.ComponentModel;
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 {
15 public void CopyFile(SourceLineNumber sourceLineNumbers, string source, string destination, bool allowHardlink)
16 {
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 {
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)
41 {
42 #if DEBUG
43 var er = Marshal.GetLastWin32Error();
44 #endif
45
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
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 }
63 catch (Exception e)
64 {
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
73 public void MoveFile(SourceLineNumber sourceLineNumbers, string source, string destination)
74 {
75 try
76 {
77 this.EnsureDirectoryWithoutFile(destination);
78
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
87 public FileStream OpenFile(SourceLineNumber sourceLineNumbers, string path, FileMode mode, FileAccess access, FileShare share)
88 {
89 const int maxRetries = 4;
90
91 for (var attempt = 1; attempt <= maxRetries; ++attempt)
92 {
93 try
94 {
95 return File.Open(path, mode, access, share);
96 }
97 catch (Exception e) when (e is IOException || e is SystemException || e is Win32Exception)
98 {
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
110 throw new InvalidOperationException("Cannot reach this code");
111 }
112
113 public void ExecuteWithRetries(Action action, int maxRetries = 4)
114 {
115 for (var attempt = 1; attempt <= maxRetries; ++attempt)
116 {
117 try
118 {
119 action();
120 break;
121 }
122 catch (Exception e) when (attempt < maxRetries && (e is IOException || e is SystemException || e is Win32Exception))
123 {
124 Thread.Sleep(250);
125 }
126 }
127 }
128
129 private void EnsureDirectoryWithoutFile(string path)
130 {
131 var directory = Path.GetDirectoryName(path);
132
133 if (!String.IsNullOrEmpty(directory))
134 {
135 this.ExecuteWithRetries(() => Directory.CreateDirectory(directory));
136 }
137
138 this.ExecuteWithRetries(() => File.Delete(path));
139 }
140
141 [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
142 private static extern bool CreateHardLink(string lpFileName, string lpExistingFileName, IntPtr lpSecurityAttributes);
143 }
144 }