aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesFromTransforms.cs
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2020-05-30 14:53:05 -0700
committerRob Mensching <rob@firegiant.com>2020-05-30 15:07:21 -0700
commitd529525a1e331f3ef9ec2707791c99bd78fdd82f (patch)
tree1d9fe1f0c0ee9850371c916802eb03ec9dc37a87 /src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesFromTransforms.cs
parent9c54d2fce80983bbee5f0f113b5aa30f22bc8a23 (diff)
downloadwix-d529525a1e331f3ef9ec2707791c99bd78fdd82f.tar.gz
wix-d529525a1e331f3ef9ec2707791c99bd78fdd82f.tar.bz2
wix-d529525a1e331f3ef9ec2707791c99bd78fdd82f.zip
Basic patching support
Diffstat (limited to 'src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesFromTransforms.cs')
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesFromTransforms.cs576
1 files changed, 80 insertions, 496 deletions
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesFromTransforms.cs b/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesFromTransforms.cs
index 9818f01a..99bf7101 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesFromTransforms.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesFromTransforms.cs
@@ -4,12 +4,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using System.IO;
9 using System.Linq; 7 using System.Linq;
10 using WixToolset.Core.Bind; 8 using WixToolset.Core.Bind;
11 using WixToolset.Data; 9 using WixToolset.Data;
12 using WixToolset.Data.Tuples;
13 using WixToolset.Data.WindowsInstaller; 10 using WixToolset.Data.WindowsInstaller;
14 using WixToolset.Data.WindowsInstaller.Rows; 11 using WixToolset.Data.WindowsInstaller.Rows;
15 using WixToolset.Extensibility; 12 using WixToolset.Extensibility;
@@ -17,90 +14,39 @@ namespace WixToolset.Core.WindowsInstaller.Bind
17 14
18 internal class GetFileFacadesFromTransforms 15 internal class GetFileFacadesFromTransforms
19 { 16 {
20 public GetFileFacadesFromTransforms(IMessaging messaging, IEnumerable<SubStorage> subStorages, TableDefinitionCollection tableDefinitions) 17 public GetFileFacadesFromTransforms(IMessaging messaging, FileSystemManager fileSystemManager, IEnumerable<SubStorage> subStorages)
21 { 18 {
22 this.Messaging = messaging; 19 this.Messaging = messaging;
20 this.FileSystemManager = fileSystemManager;
23 this.SubStorages = subStorages; 21 this.SubStorages = subStorages;
24 this.TableDefinitions = tableDefinitions;
25 } 22 }
26 23
27 public IEnumerable<IFileSystemExtension> Extensions { get; }
28
29 private IMessaging Messaging { get; } 24 private IMessaging Messaging { get; }
30 25
31 public IEnumerable<SubStorage> SubStorages { get; } 26 private FileSystemManager FileSystemManager { get; }
32 27
33 private TableDefinitionCollection TableDefinitions { get; } 28 private IEnumerable<SubStorage> SubStorages { get; }
34 29
35 public IEnumerable<FileFacade> FileFacades { get; private set; } 30 public IEnumerable<FileFacade> FileFacades { get; private set; }
36 31
37 public void Execute() 32 public void Execute()
38 { 33 {
39 var allFileRows = new List<FileFacade>(); 34 var allFileRows = new List<FileFacade>();
40 var copyToPatch = (allFileRows != null);
41#if TODO_PATCHING
42 var patchMediaRows = new RowDictionary<MediaRow>();
43 35
44 var patchMediaFileRows = new Dictionary<int, RowDictionary<WixFileRow>>(); 36 var patchMediaRows = new RowDictionary<MediaRow>();
45 37
46 var patchActualFileTable = this.Output.EnsureTable(this.TableDefinitions["File"]); 38 var patchMediaFileRows = new Dictionary<int, RowDictionary<FileRow>>();
47 var patchFileTable = this.Output.EnsureTable(this.TableDefinitions["WixFile"]);
48 39
49 if (copyFromPatch) 40 //var patchActualFileTable = this.Output.EnsureTable(this.TableDefinitions["File"]);
50 {
51 // index patch files by diskId+fileId
52 foreach (WixFileRow patchFileRow in patchFileTable.Rows)
53 {
54 int diskId = patchFileRow.DiskId;
55 if (!patchMediaFileRows.TryGetValue(diskId, out var mediaFileRows))
56 {
57 mediaFileRows = new RowDictionary<WixFileRow>();
58 patchMediaFileRows.Add(diskId, mediaFileRows);
59 }
60
61 mediaFileRows.Add(patchFileRow);
62 }
63
64 var patchMediaTable = this.Output.EnsureTable(this.TableDefinitions["Media"]);
65 patchMediaRows = new RowDictionary<MediaRow>(patchMediaTable);
66 }
67 41
68 // Index paired transforms by name without their "#" prefix. 42 // Index paired transforms by name without their "#" prefix.
69 var pairedTransforms = this.SubStorages.Where(s => s.Name.StartsWith("#")).ToDictionary(s => s.Name.Substring(1), s => s.Data); 43 var pairedTransforms = this.SubStorages.Where(s => s.Name.StartsWith("#")).ToDictionary(s => s.Name, s => s.Data);
70 44
71 foreach (var substorage in this.SubStorages) 45 // Enumerate through main transforms.
46 foreach (var substorage in this.SubStorages.Where(s => !s.Name.StartsWith("#")))
72 { 47 {
73 if (substorage.Name.StartsWith("#"))
74 {
75 // Skip paired transforms.
76 continue;
77 }
78
79 var mainTransform = substorage.Data; 48 var mainTransform = substorage.Data;
80 var mainWixFiles = new RowDictionary<WixFileRow>(mainTransform.Tables["WixFile"]);
81 var mainMsiFileHashIndex = new RowDictionary<Row>(mainTransform.Tables["MsiFileHash"]);
82
83 var mainFileTable = mainTransform.Tables["File"]; 49 var mainFileTable = mainTransform.Tables["File"];
84 var pairedTransform = pairedTransforms[substorage.Name];
85
86 // copy Media.LastSequence and index the MsiFileHash table if it exists.
87 if (copyFromPatch)
88 {
89 var pairedMediaTable = pairedTransform.Tables["Media"];
90 foreach (MediaRow pairedMediaRow in pairedMediaTable.Rows)
91 {
92 var patchMediaRow = patchMediaRows.Get(pairedMediaRow.DiskId);
93 pairedMediaRow.Fields[1] = patchMediaRow.Fields[1];
94 }
95
96 if (null != mainMsiFileHashTable)
97 {
98 mainMsiFileHashIndex = new RowDictionary<Row>(mainMsiFileHashTable);
99 }
100
101 // Validate file row changes for keypath-related issues
102 this.ValidateFileRowChanges(mainTransform);
103 }
104 50
105 if (null == mainFileTable) 51 if (null == mainFileTable)
106 { 52 {
@@ -108,74 +54,21 @@ namespace WixToolset.Core.WindowsInstaller.Bind
108 } 54 }
109 55
110 // Index File table of pairedTransform 56 // Index File table of pairedTransform
57 var pairedTransform = pairedTransforms["#" + substorage.Name];
111 var pairedFileRows = new RowDictionary<FileRow>(pairedTransform.Tables["File"]); 58 var pairedFileRows = new RowDictionary<FileRow>(pairedTransform.Tables["File"]);
112 59
113 foreach (FileRow mainFileRow in mainFileTable.Rows) 60 foreach (FileRow mainFileRow in mainFileTable.Rows.Where(f => f.Operation != RowOperation.Delete))
114 { 61 {
115 if (RowOperation.Delete == mainFileRow.Operation) 62 var mainFileId = mainFileRow.File;
116 {
117 continue;
118 }
119 else if (RowOperation.None == mainFileRow.Operation)
120 {
121 continue;
122 }
123 63
124 var mainWixFileRow = mainWixFiles.Get(mainFileRow.File); 64 // We need compare the underlying files and include all file changes.
65 var objectField = (ObjectField)mainFileRow.Fields[9];
66 var pairedFileRow = pairedFileRows.Get(mainFileId);
125 67
126 if (copyToPatch) // when copying to the patch, we need compare the underlying files and include all file changes. 68 // If the file is new, we always need to add it to the patch.
69 if (mainFileRow.Operation == RowOperation.Add)
127 { 70 {
128 var objectField = (ObjectField)mainWixFileRow.Fields[6]; 71 if (null != pairedFileRow) // RowOperation.Add
129 var pairedFileRow = pairedFileRows.Get(mainFileRow.File);
130
131 // If the file is new, we always need to add it to the patch.
132 if (mainFileRow.Operation != RowOperation.Add)
133 {
134 // If PreviousData doesn't exist, target and upgrade layout point to the same location. No need to compare.
135 if (null == objectField.PreviousData)
136 {
137 if (mainFileRow.Operation == RowOperation.None)
138 {
139 continue;
140 }
141 }
142 else
143 {
144 // TODO: should this entire condition be placed in the binder file manager?
145 if ((0 == (PatchAttributeType.Ignore & mainWixFileRow.PatchAttributes)) &&
146 !this.CompareFiles(objectField.PreviousData.ToString(), objectField.Data.ToString()))
147 {
148 // If the file is different, we need to mark the mainFileRow and pairedFileRow as modified.
149 mainFileRow.Operation = RowOperation.Modify;
150 if (null != pairedFileRow)
151 {
152 // Always patch-added, but never non-compressed.
153 pairedFileRow.Attributes |= WindowsInstallerConstants.MsidbFileAttributesPatchAdded;
154 pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesNoncompressed;
155 pairedFileRow.Fields[6].Modified = true;
156 pairedFileRow.Operation = RowOperation.Modify;
157 }
158 }
159 else
160 {
161 // The File is same. We need mark all the attributes as unchanged.
162 mainFileRow.Operation = RowOperation.None;
163 foreach (var field in mainFileRow.Fields)
164 {
165 field.Modified = false;
166 }
167
168 if (null != pairedFileRow)
169 {
170 pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesPatchAdded;
171 pairedFileRow.Fields[6].Modified = false;
172 pairedFileRow.Operation = RowOperation.None;
173 }
174 continue;
175 }
176 }
177 }
178 else if (null != pairedFileRow) // RowOperation.Add
179 { 72 {
180 // Always patch-added, but never non-compressed. 73 // Always patch-added, but never non-compressed.
181 pairedFileRow.Attributes |= WindowsInstallerConstants.MsidbFileAttributesPatchAdded; 74 pairedFileRow.Attributes |= WindowsInstallerConstants.MsidbFileAttributesPatchAdded;
@@ -184,402 +77,93 @@ namespace WixToolset.Core.WindowsInstaller.Bind
184 pairedFileRow.Operation = RowOperation.Add; 77 pairedFileRow.Operation = RowOperation.Add;
185 } 78 }
186 } 79 }
187 80 else
188 // index patch files by diskId+fileId
189 int diskId = mainWixFileRow.DiskId;
190
191 if (!patchMediaFileRows.TryGetValue(diskId, out var mediaFileRows))
192 {
193 mediaFileRows = new RowDictionary<WixFileRow>();
194 patchMediaFileRows.Add(diskId, mediaFileRows);
195 }
196
197 var fileId = mainFileRow.File;
198 var patchFileRow = mediaFileRows.Get(fileId);
199 if (copyToPatch)
200 { 81 {
201 if (null == patchFileRow) 82 // If PreviousData doesn't exist, target and upgrade layout point to the same location. No need to compare.
202 { 83 if (null == objectField.PreviousData)
203 var patchActualFileRow = (FileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers);
204 patchActualFileRow.CopyFrom(mainFileRow);
205
206 patchFileRow = (WixFileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers);
207 patchFileRow.CopyFrom(mainWixFileRow);
208
209 mediaFileRows.Add(patchFileRow);
210
211 allFileRows.Add(new FileFacade(patchActualFileRow, patchFileRow, null)); // TODO: should we be passing along delta information? Probably, right?
212 }
213 else
214 { 84 {
215 // TODO: confirm the rest of data is identical? 85 if (mainFileRow.Operation == RowOperation.None)
216
217 // make sure Source is same. Otherwise we are silently ignoring a file.
218 if (0 != String.Compare(patchFileRow.Source, mainWixFileRow.Source, StringComparison.OrdinalIgnoreCase))
219 { 86 {
220 this.Messaging.Write(ErrorMessages.SameFileIdDifferentSource(mainFileRow.SourceLineNumbers, fileId, patchFileRow.Source, mainWixFileRow.Source)); 87 continue;
221 } 88 }
222
223 // capture the previous file versions (and associated data) from this targeted instance of the baseline into the current filerow.
224 patchFileRow.AppendPreviousDataFrom(mainWixFileRow);
225 }
226 }
227 //else
228 //{
229 // // copy data from the patch back to the transform
230 // if (null != patchFileRow)
231 // {
232 // var pairedFileRow = pairedFileRows.Get(fileId);
233 // for (var i = 0; i < patchFileRow.Fields.Length; i++)
234 // {
235 // var patchValue = patchFileRow[i] == null ? String.Empty : patchFileRow.FieldAsString(i);
236 // var mainValue = mainFileRow[i] == null ? String.Empty : mainFileRow.FieldAsString(i);
237
238 // if (1 == i)
239 // {
240 // // File.Component_ changes should not come from the shared file rows
241 // // that contain the file information as each individual transform might
242 // // have different changes (or no changes at all).
243 // }
244 // // File.Attributes should not changed for binary deltas
245 // else if (6 == i)
246 // {
247 // if (null != patchFileRow.Patch)
248 // {
249 // // File.Attribute should not change for binary deltas
250 // pairedFileRow.Attributes = mainFileRow.Attributes;
251 // mainFileRow.Fields[i].Modified = false;
252 // }
253 // }
254 // // File.Sequence is updated in pairedTransform, not mainTransform
255 // else if (7 == i)
256 // {
257 // // file sequence is updated in Patch table instead of File table for delta patches
258 // if (null != patchFileRow.Patch)
259 // {
260 // pairedFileRow.Fields[i].Modified = false;
261 // }
262 // else
263 // {
264 // pairedFileRow[i] = patchFileRow[i];
265 // pairedFileRow.Fields[i].Modified = true;
266 // }
267 // mainFileRow.Fields[i].Modified = false;
268 // }
269 // else if (patchValue != mainValue)
270 // {
271 // mainFileRow[i] = patchFileRow[i];
272 // mainFileRow.Fields[i].Modified = true;
273 // if (mainFileRow.Operation == RowOperation.None)
274 // {
275 // mainFileRow.Operation = RowOperation.Modify;
276 // }
277 // }
278 // }
279
280 // // copy MsiFileHash row for this File
281 // if (!mainMsiFileHashIndex.TryGetValue(patchFileRow.File, out var patchHashRow))
282 // {
283 // patchHashRow = patchFileRow.Hash;
284 // }
285
286 // if (null != patchHashRow)
287 // {
288 // var mainHashTable = mainTransform.EnsureTable(this.TableDefinitions["MsiFileHash"]);
289 // var mainHashRow = mainHashTable.CreateRow(mainFileRow.SourceLineNumbers);
290 // for (var i = 0; i < patchHashRow.Fields.Length; i++)
291 // {
292 // mainHashRow[i] = patchHashRow[i];
293 // if (i > 1)
294 // {
295 // // assume all hash fields have been modified
296 // mainHashRow.Fields[i].Modified = true;
297 // }
298 // }
299
300 // // assume the MsiFileHash operation follows the File one
301 // mainHashRow.Operation = mainFileRow.Operation;
302 // }
303
304 // // copy MsiAssemblyName rows for this File
305 // List<Row> patchAssemblyNameRows = patchFileRow.AssemblyNames;
306 // if (null != patchAssemblyNameRows)
307 // {
308 // var mainAssemblyNameTable = mainTransform.EnsureTable(this.TableDefinitions["MsiAssemblyName"]);
309 // foreach (var patchAssemblyNameRow in patchAssemblyNameRows)
310 // {
311 // // Copy if there isn't an identical modified/added row already in the transform.
312 // var foundMatchingModifiedRow = false;
313 // foreach (var mainAssemblyNameRow in mainAssemblyNameTable.Rows)
314 // {
315 // if (RowOperation.None != mainAssemblyNameRow.Operation && mainAssemblyNameRow.GetPrimaryKey('/').Equals(patchAssemblyNameRow.GetPrimaryKey('/')))
316 // {
317 // foundMatchingModifiedRow = true;
318 // break;
319 // }
320 // }
321
322 // if (!foundMatchingModifiedRow)
323 // {
324 // var mainAssemblyNameRow = mainAssemblyNameTable.CreateRow(mainFileRow.SourceLineNumbers);
325 // for (var i = 0; i < patchAssemblyNameRow.Fields.Length; i++)
326 // {
327 // mainAssemblyNameRow[i] = patchAssemblyNameRow[i];
328 // }
329
330 // // assume value field has been modified
331 // mainAssemblyNameRow.Fields[2].Modified = true;
332 // mainAssemblyNameRow.Operation = mainFileRow.Operation;
333 // }
334 // }
335 // }
336
337 // // Add patch header for this file
338 // if (null != patchFileRow.Patch)
339 // {
340 // // Add the PatchFiles action automatically to the AdminExecuteSequence and InstallExecuteSequence tables.
341 // this.AddPatchFilesActionToSequenceTable(SequenceTable.AdminExecuteSequence, mainTransform, pairedTransform, mainFileRow);
342 // this.AddPatchFilesActionToSequenceTable(SequenceTable.InstallExecuteSequence, mainTransform, pairedTransform, mainFileRow);
343
344 // // Add to Patch table
345 // var patchTable = pairedTransform.EnsureTable(this.TableDefinitions["Patch"]);
346 // if (0 == patchTable.Rows.Count)
347 // {
348 // patchTable.Operation = TableOperation.Add;
349 // }
350
351 // var patchRow = patchTable.CreateRow(mainFileRow.SourceLineNumbers);
352 // patchRow[0] = patchFileRow.File;
353 // patchRow[1] = patchFileRow.Sequence;
354
355 // var patchFile = new FileInfo(patchFileRow.Source);
356 // patchRow[2] = (int)patchFile.Length;
357 // patchRow[3] = 0 == (PatchAttributeType.AllowIgnoreOnError & patchFileRow.PatchAttributes) ? 0 : 1;
358
359 // var streamName = patchTable.Name + "." + patchRow[0] + "." + patchRow[1];
360 // if (Msi.MsiInterop.MsiMaxStreamNameLength < streamName.Length)
361 // {
362 // streamName = "_" + Guid.NewGuid().ToString("D").ToUpperInvariant().Replace('-', '_');
363
364 // var patchHeadersTable = pairedTransform.EnsureTable(this.TableDefinitions["MsiPatchHeaders"]);
365 // if (0 == patchHeadersTable.Rows.Count)
366 // {
367 // patchHeadersTable.Operation = TableOperation.Add;
368 // }
369
370 // var patchHeadersRow = patchHeadersTable.CreateRow(mainFileRow.SourceLineNumbers);
371 // patchHeadersRow[0] = streamName;
372 // patchHeadersRow[1] = patchFileRow.Patch;
373 // patchRow[5] = streamName;
374 // patchHeadersRow.Operation = RowOperation.Add;
375 // }
376 // else
377 // {
378 // patchRow[4] = patchFileRow.Patch;
379 // }
380 // patchRow.Operation = RowOperation.Add;
381 // }
382 // }
383 // else
384 // {
385 // // TODO: throw because all transform rows should have made it into the patch
386 // }
387 //}
388 }
389 }
390#endif
391 this.FileFacades = allFileRows;
392 }
393
394 /// <summary>
395 /// Adds the PatchFiles action to the sequence table if it does not already exist.
396 /// </summary>
397 /// <param name="table">The sequence table to check or modify.</param>
398 /// <param name="mainTransform">The primary authoring transform.</param>
399 /// <param name="pairedTransform">The secondary patch transform.</param>
400 /// <param name="mainFileRow">The file row that contains information about the patched file.</param>
401 private void AddPatchFilesActionToSequenceTable(SequenceTable table, WindowsInstallerData mainTransform, WindowsInstallerData pairedTransform, Row mainFileRow)
402 {
403 var tableName = table.ToString();
404
405 // Find/add PatchFiles action (also determine sequence for it).
406 // Search mainTransform first, then pairedTransform (pairedTransform overrides).
407 var hasPatchFilesAction = false;
408 var installFilesSequence = 0;
409 var duplicateFilesSequence = 0;
410
411 TestSequenceTableForPatchFilesAction(
412 mainTransform.Tables[tableName],
413 ref hasPatchFilesAction,
414 ref installFilesSequence,
415 ref duplicateFilesSequence);
416 TestSequenceTableForPatchFilesAction(
417 pairedTransform.Tables[tableName],
418 ref hasPatchFilesAction,
419 ref installFilesSequence,
420 ref duplicateFilesSequence);
421 if (!hasPatchFilesAction)
422 {
423 WindowsInstallerStandard.TryGetStandardAction(tableName, "PatchFiles", out var patchFilesActionTuple);
424
425 var sequence = patchFilesActionTuple.Sequence;
426
427 // Test for default sequence value's appropriateness
428 if (installFilesSequence >= sequence || (0 != duplicateFilesSequence && duplicateFilesSequence <= sequence))
429 {
430 if (0 != duplicateFilesSequence)
431 {
432 if (duplicateFilesSequence < installFilesSequence)
433 {
434 throw new WixException(ErrorMessages.InsertInvalidSequenceActionOrder(mainFileRow.SourceLineNumbers, tableName, "InstallFiles", "DuplicateFiles", patchFilesActionTuple.Action));
435 } 89 }
436 else 90 else
437 { 91 {
438 sequence = (duplicateFilesSequence + installFilesSequence) / 2; 92 // TODO: should this entire condition be placed in the binder file manager?
439 if (installFilesSequence == sequence || duplicateFilesSequence == sequence) 93 if (/*(0 == (PatchAttributeType.Ignore & mainWixFileRow.PatchAttributes)) &&*/
94 !this.FileSystemManager.CompareFiles(objectField.PreviousData.ToString(), objectField.Data.ToString()))
440 { 95 {
441 throw new WixException(ErrorMessages.InsertSequenceNoSpace(mainFileRow.SourceLineNumbers, tableName, "InstallFiles", "DuplicateFiles", patchFilesActionTuple.Action)); 96 // If the file is different, we need to mark the mainFileRow and pairedFileRow as modified.
97 mainFileRow.Operation = RowOperation.Modify;
98 if (null != pairedFileRow)
99 {
100 // Always patch-added, but never non-compressed.
101 pairedFileRow.Attributes |= WindowsInstallerConstants.MsidbFileAttributesPatchAdded;
102 pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesNoncompressed;
103 pairedFileRow.Fields[6].Modified = true;
104 pairedFileRow.Operation = RowOperation.Modify;
105 }
106 }
107 else
108 {
109 // The File is same. We need mark all the attributes as unchanged.
110 mainFileRow.Operation = RowOperation.None;
111 foreach (var field in mainFileRow.Fields)
112 {
113 field.Modified = false;
114 }
115
116 if (null != pairedFileRow)
117 {
118 pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesPatchAdded;
119 pairedFileRow.Fields[6].Modified = false;
120 pairedFileRow.Operation = RowOperation.None;
121 }
122 continue;
442 } 123 }
443 } 124 }
444 } 125 }
445 else
446 {
447 sequence = installFilesSequence + 1;
448 }
449 }
450 126
451 var sequenceTable = pairedTransform.EnsureTable(this.TableDefinitions[tableName]); 127 // index patch files by diskId+fileId
452 if (0 == sequenceTable.Rows.Count) 128 var diskId = mainFileRow.DiskId;
453 {
454 sequenceTable.Operation = TableOperation.Add;
455 }
456
457 var patchAction = sequenceTable.CreateRow(null);
458 patchAction[0] = patchFilesActionTuple.Action;
459 patchAction[1] = patchFilesActionTuple.Condition;
460 patchAction[2] = sequence;
461 patchAction.Operation = RowOperation.Add;
462 }
463 }
464 129
465 /// <summary> 130 if (!patchMediaFileRows.TryGetValue(diskId, out var mediaFileRows))
466 /// Tests sequence table for PatchFiles and associated actions
467 /// </summary>
468 /// <param name="sequenceTable">The table to test.</param>
469 /// <param name="hasPatchFilesAction">Set to true if PatchFiles action is found. Left unchanged otherwise.</param>
470 /// <param name="installFilesSequence">Set to sequence value of InstallFiles action if found. Left unchanged otherwise.</param>
471 /// <param name="duplicateFilesSequence">Set to sequence value of DuplicateFiles action if found. Left unchanged otherwise.</param>
472 private static void TestSequenceTableForPatchFilesAction(Table sequenceTable, ref bool hasPatchFilesAction, ref int installFilesSequence, ref int duplicateFilesSequence)
473 {
474 if (null != sequenceTable)
475 {
476 foreach (var row in sequenceTable.Rows)
477 {
478 var actionName = row.FieldAsString(0);
479 switch (actionName)
480 { 131 {
481 case "PatchFiles": 132 mediaFileRows = new RowDictionary<FileRow>();
482 hasPatchFilesAction = true; 133 patchMediaFileRows.Add(diskId, mediaFileRows);
483 break;
484
485 case "InstallFiles":
486 installFilesSequence = row.FieldAsInteger(2);
487 break;
488
489 case "DuplicateFiles":
490 duplicateFilesSequence = row.FieldAsInteger(2);
491 break;
492 } 134 }
493 }
494 }
495 }
496
497 /// <summary>
498 /// Signal a warning if a non-keypath file was changed in a patch without also changing the keypath file of the component.
499 /// </summary>
500 /// <param name="output">The output to validate.</param>
501 private void ValidateFileRowChanges(WindowsInstallerData transform)
502 {
503 var componentTable = transform.Tables["Component"];
504 var fileTable = transform.Tables["File"];
505
506 // There's no sense validating keypaths if the transform has no component or file table
507 if (componentTable == null || fileTable == null)
508 {
509 return;
510 }
511 135
512 var componentKeyPath = new Dictionary<string, string>(componentTable.Rows.Count); 136 var patchFileRow = mediaFileRows.Get(mainFileId);
513
514 // Index the Component table for non-directory & non-registry key paths.
515 foreach (var row in componentTable.Rows)
516 {
517 var keyPath = row.FieldAsString(5);
518 if (keyPath != null && 0 != (row.FieldAsInteger(3) & WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath))
519 {
520 componentKeyPath.Add(row.FieldAsString(0), keyPath);
521 }
522 }
523 137
524 var componentWithChangedKeyPath = new Dictionary<string, string>(); 138 if (null == patchFileRow)
525 var componentWithNonKeyPathChanged = new Dictionary<string, string>(); 139 {
526 // Verify changes in the file table, now that file diffing has occurred 140 //patchFileRow = (FileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers);
527 foreach (FileRow row in fileTable.Rows) 141 patchFileRow = (FileRow)mainFileRow.TableDefinition.CreateRow(mainFileRow.SourceLineNumbers);
528 { 142 mainFileRow.CopyTo(patchFileRow);
529 if (RowOperation.Modify != row.Operation)
530 {
531 continue;
532 }
533 143
534 var fileId = row.FieldAsString(0); 144 mediaFileRows.Add(patchFileRow);
535 var componentId = row.FieldAsString(1);
536 145
537 // If this file is the keypath of a component 146 allFileRows.Add(new FileFacade(patchFileRow)); // TODO: should we be passing along delta information? Probably, right?
538 if (componentKeyPath.ContainsValue(fileId))
539 {
540 if (!componentWithChangedKeyPath.ContainsKey(componentId))
541 {
542 componentWithChangedKeyPath.Add(componentId, fileId);
543 } 147 }
544 } 148 else
545 else
546 {
547 if (!componentWithNonKeyPathChanged.ContainsKey(componentId))
548 { 149 {
549 componentWithNonKeyPathChanged.Add(componentId, fileId); 150 // TODO: confirm the rest of data is identical?
550 }
551 }
552 }
553
554 foreach (var componentFile in componentWithNonKeyPathChanged)
555 {
556 // Make sure all changes to non keypath files also had a change in the keypath.
557 if (!componentWithChangedKeyPath.ContainsKey(componentFile.Key) && componentKeyPath.TryGetValue(componentFile.Key, out var keyPath))
558 {
559 this.Messaging.Write(WarningMessages.UpdateOfNonKeyPathFile(componentFile.Value, componentFile.Key, keyPath));
560 }
561 }
562 }
563 151
564 private bool CompareFiles(string targetFile, string updatedFile) 152 // make sure Source is same. Otherwise we are silently ignoring a file.
565 { 153 if (0 != String.Compare(patchFileRow.Source, mainFileRow.Source, StringComparison.OrdinalIgnoreCase))
566 bool? compared = null; 154 {
567 foreach (var extension in this.Extensions) 155 this.Messaging.Write(ErrorMessages.SameFileIdDifferentSource(mainFileRow.SourceLineNumbers, mainFileId, patchFileRow.Source, mainFileRow.Source));
568 { 156 }
569 compared = extension.CompareFiles(targetFile, updatedFile);
570 157
571 if (compared.HasValue) 158#if TODO_PATCHING_DELTA
572 { 159 // capture the previous file versions (and associated data) from this targeted instance of the baseline into the current filerow.
573 break; 160 patchFileRow.AppendPreviousDataFrom(mainFileRow);
161#endif
162 }
574 } 163 }
575 } 164 }
576 165
577 if (!compared.HasValue) 166 this.FileFacades = allFileRows;
578 {
579 throw new InvalidOperationException(); // TODO: something needs to be said here that none of the binder file managers returned a result.
580 }
581
582 return compared.Value;
583 } 167 }
584 } 168 }
585} 169}