main
cs 211 lines 8.2 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.Bind
4 {
5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8 using System.Security.AccessControl;
9 using WixToolset.Data;
10 using WixToolset.Extensibility;
11 using WixToolset.Extensibility.Data;
12 using WixToolset.Extensibility.Services;
13
14 internal class TransferFilesCommand
15 {
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; }
32
33 private bool ResetAcls { get; }
34
35 public void Execute()
36 {
37 var destinationFiles = new List<string>();
38
39 foreach (var fileTransfer in this.FileTransfers)
40 {
41 // If the source and destination are identical, then there's nothing to do here
42 if (0 == String.Compare(fileTransfer.Source, fileTransfer.Destination, StringComparison.OrdinalIgnoreCase))
43 {
44 fileTransfer.Redundant = true;
45 continue;
46 }
47
48 var retry = false;
49 do
50 {
51 try
52 {
53 if (fileTransfer.Move)
54 {
55 this.Messaging.Write(VerboseMessages.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.SourceLineNumbers, fileTransfer.Source, fileTransfer.Destination);
62 }
63
64 retry = false;
65 destinationFiles.Add(fileTransfer.Destination);
66 }
67 catch (FileNotFoundException e)
68 {
69 throw new WixException(ErrorMessages.FileNotFound(fileTransfer.SourceLineNumbers, e.FileName));
70 }
71 catch (DirectoryNotFoundException)
72 {
73 // if we already retried, give up
74 if (retry)
75 {
76 throw;
77 }
78
79 var directory = Path.GetDirectoryName(fileTransfer.Destination);
80 this.Messaging.Write(VerboseMessages.CreateDirectory(directory));
81 Directory.CreateDirectory(directory);
82 retry = true;
83 }
84 catch (UnauthorizedAccessException)
85 {
86 // if we already retried, give up
87 if (retry)
88 {
89 throw;
90 }
91
92 if (File.Exists(fileTransfer.Destination))
93 {
94 this.Messaging.Write(VerboseMessages.RemoveDestinationFile(fileTransfer.Destination));
95
96 // try to ensure the file is not read-only
97 var attributes = File.GetAttributes(fileTransfer.Destination);
98 try
99 {
100 File.SetAttributes(fileTransfer.Destination, attributes & ~FileAttributes.ReadOnly);
101 }
102 catch (ArgumentException) // thrown for unauthorized access errors
103 {
104 throw new WixException(ErrorMessages.UnauthorizedAccess(fileTransfer.Destination));
105 }
106
107 // try to delete the file
108 try
109 {
110 File.Delete(fileTransfer.Destination);
111 }
112 catch (IOException)
113 {
114 throw new WixException(ErrorMessages.FileInUse(null, fileTransfer.Destination));
115 }
116
117 retry = true;
118 }
119 else // no idea what just happened, bail
120 {
121 throw;
122 }
123 }
124 catch (IOException)
125 {
126 // if we already retried, give up
127 if (retry)
128 {
129 throw;
130 }
131
132 if (File.Exists(fileTransfer.Destination))
133 {
134 this.Messaging.Write(VerboseMessages.RemoveDestinationFile(fileTransfer.Destination));
135
136 // ensure the file is not read-only, then delete it
137 var attributes = File.GetAttributes(fileTransfer.Destination);
138 File.SetAttributes(fileTransfer.Destination, attributes & ~FileAttributes.ReadOnly);
139 try
140 {
141 File.Delete(fileTransfer.Destination);
142 }
143 catch (IOException)
144 {
145 throw new WixException(ErrorMessages.FileInUse(null, fileTransfer.Destination));
146 }
147
148 retry = true;
149 }
150 else // no idea what just happened, bail
151 {
152 throw;
153 }
154 }
155 } while (retry);
156 }
157
158 // Finally, if directed then reset remove ACLs that may may have been picked up
159 // during the file transfer process.
160 if (this.ResetAcls && 0 < destinationFiles.Count)
161 {
162 try
163 {
164 this.AclReset(destinationFiles);
165 }
166 catch (Exception e)
167 {
168 this.Messaging.Write(WarningMessages.UnableToResetAcls(e.Message));
169 }
170 }
171 }
172
173 private void CopyFile(SourceLineNumber sourceLineNumbers, string source, string destination)
174 {
175 foreach (var extension in this.Extensions)
176 {
177 if (extension.CopyFile(source, destination))
178 {
179 return;
180 }
181 }
182
183 this.FileSystem.CopyFile(sourceLineNumbers, source, destination, allowHardlink: true);
184 }
185
186 private void MoveFile(SourceLineNumber sourceLineNumbers, string source, string destination)
187 {
188 foreach (var extension in this.Extensions)
189 {
190 if (extension.MoveFile(source, destination))
191 {
192 return;
193 }
194 }
195
196 this.FileSystem.MoveFile(sourceLineNumbers, 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 this.FileSystem.ExecuteWithRetries(() => fileInfo.SetAccessControl(aclReset));
208 }
209 }
210 }
211 }