aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2018-12-29 22:12:08 -0600
committerSean Hall <r.sean.hall@gmail.com>2018-12-29 22:12:08 -0600
commit61847dddd4fd497057c780658e383c4627de19ec (patch)
treef85a845182922538ab9aa6ee85b0db3ab40c1f6e
parent8295f5f8fd28042e1a0a172d5afbba79178064c2 (diff)
downloadwix-61847dddd4fd497057c780658e383c4627de19ec.tar.gz
wix-61847dddd4fd497057c780658e383c4627de19ec.tar.bz2
wix-61847dddd4fd497057c780658e383c4627de19ec.zip
Import code from old v4 repo
-rw-r--r--src/Samples/ManagedBundleRunner/BundleErrorEventArgs.cs33
-rw-r--r--src/Samples/ManagedBundleRunner/BundleProgressEventArgs.cs23
-rw-r--r--src/Samples/ManagedBundleRunner/BundleResult.cs24
-rw-r--r--src/Samples/ManagedBundleRunner/BundleRunner.cs212
-rw-r--r--src/Samples/runbundle/AssemblyInfo.cs12
-rw-r--r--src/Samples/runbundle/Program.cs47
-rw-r--r--src/engine/EngineForApplication.cpp894
-rw-r--r--src/engine/EngineForApplication.h44
-rw-r--r--src/engine/apply.cpp2516
-rw-r--r--src/engine/apply.h106
-rw-r--r--src/engine/approvedexe.cpp262
-rw-r--r--src/engine/approvedexe.h67
-rw-r--r--src/engine/bitsengine.cpp505
-rw-r--r--src/engine/bitsengine.h23
-rw-r--r--src/engine/cabextract.cpp974
-rw-r--r--src/engine/cabextract.h40
-rw-r--r--src/engine/cache.cpp2026
-rw-r--r--src/engine/cache.h150
-rw-r--r--src/engine/catalog.cpp180
-rw-r--r--src/engine/catalog.h56
-rw-r--r--src/engine/condition.cpp1030
-rw-r--r--src/engine/condition.h39
-rw-r--r--src/engine/container.cpp386
-rw-r--r--src/engine/container.h183
-rw-r--r--src/engine/core.cpp1705
-rw-r--r--src/engine/core.h211
-rw-r--r--src/engine/dependency.cpp1203
-rw-r--r--src/engine/dependency.h176
-rw-r--r--src/engine/detect.cpp431
-rw-r--r--src/engine/detect.h44
-rw-r--r--src/engine/elevation.cpp2814
-rw-r--r--src/engine/elevation.h178
-rw-r--r--src/engine/embedded.cpp197
-rw-r--r--src/engine/embedded.h27
-rw-r--r--src/engine/engine.cpp889
-rw-r--r--src/engine/engine.mc901
-rw-r--r--src/engine/exeengine.cpp820
-rw-r--r--src/engine/exeengine.h48
-rw-r--r--src/engine/inc/engine.h27
-rw-r--r--src/engine/logging.cpp683
-rw-r--r--src/engine/logging.h144
-rw-r--r--src/engine/manifest.cpp125
-rw-r--r--src/engine/manifest.h23
-rw-r--r--src/engine/msiengine.cpp1910
-rw-r--r--src/engine/msiengine.h73
-rw-r--r--src/engine/mspengine.cpp980
-rw-r--r--src/engine/mspengine.h67
-rw-r--r--src/engine/msuengine.cpp513
-rw-r--r--src/engine/msuengine.h48
-rw-r--r--src/engine/netfxchainer.cpp418
-rw-r--r--src/engine/netfxchainer.h98
-rw-r--r--src/engine/package.cpp678
-rw-r--r--src/engine/package.h336
-rw-r--r--src/engine/payload.cpp367
-rw-r--r--src/engine/payload.h93
-rw-r--r--src/engine/pipe.cpp873
-rw-r--r--src/engine/pipe.h113
-rw-r--r--src/engine/plan.cpp3169
-rw-r--r--src/engine/plan.h543
-rw-r--r--src/engine/platform.cpp16
-rw-r--r--src/engine/platform.h34
-rw-r--r--src/engine/precomp.h100
-rw-r--r--src/engine/pseudobundle.cpp271
-rw-r--r--src/engine/pseudobundle.h39
-rw-r--r--src/engine/registration.cpp1599
-rw-r--r--src/engine/registration.h214
-rw-r--r--src/engine/relatedbundle.cpp457
-rw-r--r--src/engine/relatedbundle.h20
-rw-r--r--src/engine/search.cpp1195
-rw-r--r--src/engine/search.h152
-rw-r--r--src/engine/section.cpp399
-rw-r--r--src/engine/section.h54
-rw-r--r--src/engine/splashscreen.cpp316
-rw-r--r--src/engine/splashscreen.h31
-rw-r--r--src/engine/uithread.cpp220
-rw-r--r--src/engine/uithread.h23
-rw-r--r--src/engine/update.cpp44
-rw-r--r--src/engine/update.h33
-rw-r--r--src/engine/userexperience.cpp2122
-rw-r--r--src/engine/userexperience.h439
-rw-r--r--src/engine/variable.cpp2345
-rw-r--r--src/engine/variable.h190
-rw-r--r--src/engine/variant.cpp601
-rw-r--r--src/engine/variant.h102
-rw-r--r--src/stub/StubSection.cpp23
-rw-r--r--src/stub/precomp.h13
-rw-r--r--src/stub/stub.cpp64
-rw-r--r--src/stub/stub.icobin0 -> 2238 bytes
-rw-r--r--src/stub/stub.manifest18
-rw-r--r--src/stub/stub.rc14
90 files changed, 41905 insertions, 0 deletions
diff --git a/src/Samples/ManagedBundleRunner/BundleErrorEventArgs.cs b/src/Samples/ManagedBundleRunner/BundleErrorEventArgs.cs
new file mode 100644
index 00000000..2c377326
--- /dev/null
+++ b/src/Samples/ManagedBundleRunner/BundleErrorEventArgs.cs
@@ -0,0 +1,33 @@
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
3namespace Wix.Samples
4{
5 using System;
6
7 /// <summary>
8 /// Arguments provided when bundle encounters an error.
9 /// </summary>
10 [Serializable]
11 public class BundleErrorEventArgs : EventArgs
12 {
13 /// <summary>
14 /// Gets the error code.
15 /// </summary>
16 public int Code { get; set; }
17
18 /// <summary>
19 /// Gets the error message.
20 /// </summary>
21 public string Message { get; set; }
22
23 /// <summary>
24 /// Gets the recommended display flags for an error dialog.
25 /// </summary>
26 public int UIHint { get; set; }
27
28 /// <summary>
29 /// Gets or sets the <see cref="Result"/> of the operation. This is passed back to the bundle.
30 /// </summary>
31 public BundleResult Result { get; set; }
32 }
33}
diff --git a/src/Samples/ManagedBundleRunner/BundleProgressEventArgs.cs b/src/Samples/ManagedBundleRunner/BundleProgressEventArgs.cs
new file mode 100644
index 00000000..ed42b5b1
--- /dev/null
+++ b/src/Samples/ManagedBundleRunner/BundleProgressEventArgs.cs
@@ -0,0 +1,23 @@
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
3namespace Wix.Samples
4{
5 using System;
6
7 /// <summary>
8 /// Arguments provided when bundle progress is updated.
9 /// </summary>
10 [Serializable]
11 public class BundleProgressEventArgs : EventArgs
12 {
13 /// <summary>
14 /// Gets the percentage from 0 to 100 completed for a bundle.
15 /// </summary>
16 public int Progress { get; set; }
17
18 /// <summary>
19 /// Gets or sets the <see cref="Result"/> of the operation. This is passed back to the bundle.
20 /// </summary>
21 public BundleResult Result { get; set; }
22 }
23}
diff --git a/src/Samples/ManagedBundleRunner/BundleResult.cs b/src/Samples/ManagedBundleRunner/BundleResult.cs
new file mode 100644
index 00000000..c32644f4
--- /dev/null
+++ b/src/Samples/ManagedBundleRunner/BundleResult.cs
@@ -0,0 +1,24 @@
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
3namespace Wix.Samples
4{
5 /// <summary>
6 /// Result codes.
7 /// </summary>
8 public enum BundleResult
9 {
10 Error = -1,
11 None,
12 Ok,
13 Cancel,
14 Abort,
15 Retry,
16 Ignore,
17 Yes,
18 No,
19 Close,
20 Help,
21 TryAgain,
22 Continue,
23 }
24}
diff --git a/src/Samples/ManagedBundleRunner/BundleRunner.cs b/src/Samples/ManagedBundleRunner/BundleRunner.cs
new file mode 100644
index 00000000..e2089787
--- /dev/null
+++ b/src/Samples/ManagedBundleRunner/BundleRunner.cs
@@ -0,0 +1,212 @@
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
3namespace Wix.Samples
4{
5 using System;
6 using System.Diagnostics;
7 using System.IO.Pipes;
8 using System.Text;
9 using System.Threading;
10
11 /// <summary>
12 /// Runs a bundle with provided command-line.
13 /// </summary>
14 public class BundleRunner
15 {
16 /// <summary>
17 /// Creates a runner for the provided bundle.
18 /// </summary>
19 /// <param name="bundle">Path to the bundle to run.</param>
20 public BundleRunner(string bundle)
21 {
22 this.Path = bundle;
23 }
24
25 /// <summary>
26 /// Fired when the bundle encounters an error.
27 /// </summary>
28 public event EventHandler<BundleErrorEventArgs> Error;
29
30 /// <summary>
31 /// Fired when the bundle progress is udpated.
32 /// </summary>
33 public event EventHandler<BundleProgressEventArgs> Progress;
34
35 /// <summary>
36 /// Gets the path to the bundle to run.
37 /// </summary>
38 public string Path { get; private set; }
39
40 /// <summary>
41 /// Runs the bundle with the provided command-line.
42 /// </summary>
43 /// <param name="commandLine">Optional command-line to pass to the bundle.</param>
44 /// <returns>Exit code from the bundle.</returns>
45 public int Run(string commandLine = null)
46 {
47 WaitHandle[] waits = new WaitHandle[] { new ManualResetEvent(false), new ManualResetEvent(false) };
48 int returnCode = 0;
49 int pid = Process.GetCurrentProcess().Id;
50 string pipeName = String.Concat("bpe_", pid);
51 string pipeSecret = Guid.NewGuid().ToString("N");
52
53 using (NamedPipeServerStream pipe = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1))
54 {
55 using (Process bundleProcess = new Process())
56 {
57 bundleProcess.StartInfo.FileName = this.Path;
58 bundleProcess.StartInfo.Arguments = String.Format("{0} -burn.embedded {1} {2} {3}", commandLine ?? String.Empty, pipeName, pipeSecret, pid);
59 bundleProcess.StartInfo.UseShellExecute = false;
60 bundleProcess.StartInfo.CreateNoWindow = true;
61 bundleProcess.Start();
62
63 Connect(pipe, pipeSecret, pid, bundleProcess.Id);
64
65 PumpMessages(pipe);
66
67 bundleProcess.WaitForExit();
68 returnCode = bundleProcess.ExitCode;
69 }
70 }
71
72 return returnCode;
73 }
74
75 /// <summary>
76 /// Called when bundle encounters an error.
77 /// </summary>
78 /// <param name="e">Additional arguments for this event.</param>
79 protected virtual void OnError(BundleErrorEventArgs e)
80 {
81 EventHandler<BundleErrorEventArgs> handler = this.Error;
82 if (handler != null)
83 {
84 handler(this, e);
85 }
86 }
87
88 /// <summary>
89 /// Called when bundle progress is updated.
90 /// </summary>
91 /// <param name="e">Additional arguments for this event.</param>
92 protected virtual void OnProgress(BundleProgressEventArgs e)
93 {
94 EventHandler<BundleProgressEventArgs> handler = this.Progress;
95 if (handler != null)
96 {
97 handler(this, e);
98 }
99 }
100
101 private void Connect(NamedPipeServerStream pipe, string pipeSecret, int pid, int childPid)
102 {
103 pipe.WaitForConnection();
104
105 WriteSecretToPipe(pipe, pipeSecret);
106
107 WriteNumberToPipe(pipe, (uint)pid);
108
109 uint ack = ReadNumberFromPipe(pipe);
110 // This is not true when bundle is run under a debugger
111 //if (ack != childPid)
112 //{
113 // throw new ApplicationException("Incorrect child process.");
114 //}
115 }
116
117 private void PumpMessages(NamedPipeServerStream pipe)
118 {
119 uint messageId;
120 while (TryReadNumberFromPipe(pipe, out messageId))
121 {
122 uint messageSize = ReadNumberFromPipe(pipe);
123
124 BundleResult result = BundleResult.None;
125 switch (messageId)
126 {
127 case 1: //error
128 result = ProcessErrorMessage(pipe);
129 break;
130
131 case 2: // progress
132 result = ProcessProgressMessage(pipe);
133 break;
134
135 default: // unknown message, do nothing.
136 break;
137 }
138
139 CompleteMessage(pipe, result);
140 }
141 }
142
143 private BundleResult ProcessErrorMessage(NamedPipeServerStream pipe)
144 {
145 BundleErrorEventArgs e = new BundleErrorEventArgs();
146 e.Code = (int)ReadNumberFromPipe(pipe);
147 e.Message = ReadStringFromPipe(pipe);
148 e.UIHint = (int)ReadNumberFromPipe(pipe);
149
150 this.OnError(e);
151
152 return e.Result;
153 }
154
155 private BundleResult ProcessProgressMessage(NamedPipeServerStream pipe)
156 {
157 ReadNumberFromPipe(pipe); // eat the first progress number because it is always zero.
158
159 BundleProgressEventArgs e = new BundleProgressEventArgs();
160 e.Progress = (int)ReadNumberFromPipe(pipe);
161
162 this.OnProgress(e);
163
164 return e.Result;
165 }
166
167 private void CompleteMessage(NamedPipeServerStream pipe, BundleResult result)
168 {
169 uint complete = 0xF0000002;
170 WriteNumberToPipe(pipe, complete);
171 WriteNumberToPipe(pipe, 4); // size of message data
172 WriteNumberToPipe(pipe, (uint)result);
173 }
174
175 private uint ReadNumberFromPipe(NamedPipeServerStream pipe)
176 {
177 byte[] buffer = new byte[4];
178 pipe.Read(buffer, 0, buffer.Length);
179 return BitConverter.ToUInt32(buffer, 0);
180 }
181
182 private string ReadStringFromPipe(NamedPipeServerStream pipe)
183 {
184 uint length = ReadNumberFromPipe(pipe);
185
186 byte[] buffer = new byte[length * 2];
187 pipe.Read(buffer, 0, buffer.Length);
188
189 return Encoding.Unicode.GetString(buffer);
190 }
191
192 private bool TryReadNumberFromPipe(NamedPipeServerStream pipe, out uint value)
193 {
194 value = ReadNumberFromPipe(pipe); // reading will not block and return zero if pipe is not connected.
195 return pipe.IsConnected;
196 }
197
198 private void WriteNumberToPipe(NamedPipeServerStream pipe, uint value)
199 {
200 byte[] buffer = BitConverter.GetBytes(value);
201 pipe.Write(buffer, 0, buffer.Length);
202 }
203
204 private void WriteSecretToPipe(NamedPipeServerStream pipe, string secret)
205 {
206 byte[] buffer = Encoding.Unicode.GetBytes(secret);
207
208 WriteNumberToPipe(pipe, (uint)buffer.Length);
209 pipe.Write(buffer, 0, buffer.Length);
210 }
211 }
212}
diff --git a/src/Samples/runbundle/AssemblyInfo.cs b/src/Samples/runbundle/AssemblyInfo.cs
new file mode 100644
index 00000000..3a66d5e3
--- /dev/null
+++ b/src/Samples/runbundle/AssemblyInfo.cs
@@ -0,0 +1,12 @@
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
3using System;
4using System.Reflection;
5using System.Runtime.CompilerServices;
6using System.Runtime.InteropServices;
7
8[assembly: AssemblyTitle("Executable to demonstrate Bundle Runner Sample")]
9[assembly: AssemblyDescription("")]
10[assembly: AssemblyCulture("")]
11[assembly: CLSCompliant(true)]
12[assembly: ComVisible(false)]
diff --git a/src/Samples/runbundle/Program.cs b/src/Samples/runbundle/Program.cs
new file mode 100644
index 00000000..8edca5dc
--- /dev/null
+++ b/src/Samples/runbundle/Program.cs
@@ -0,0 +1,47 @@
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
3namespace Wix.Samples
4{
5 using System;
6 using System.Linq;
7 using Wix.Samples;
8
9 /// <summary>
10 /// Example executable that installs then immediately uninstalls a bundle showing progress.
11 /// </summary>
12 class Program
13 {
14 static int Main(string[] args)
15 {
16 if (args.Length == 0)
17 {
18 Console.WriteLine("Must provide the path to the bundle to install then uninstall.");
19 return -1;
20 }
21
22 BundleRunner runner = new BundleRunner(args[0]);
23 runner.Error += Program.OnError;
24 runner.Progress += Program.OnProgress;
25
26 Console.WriteLine("Installing: {0}", runner.Path);
27 int exitCode = runner.Run(String.Join(" ", args.Skip(1).ToArray()));
28 if (0 == exitCode)
29 {
30 Console.WriteLine("\r\nUninstalling: {0}", runner.Path);
31 exitCode = runner.Run("-uninstall");
32 }
33
34 return exitCode;
35 }
36
37 static void OnError(object sender, BundleErrorEventArgs e)
38 {
39 Console.WriteLine("error: {0}, uiHint: {1}, message: {2}", e.Code, e.UIHint, e.Message);
40 }
41
42 static void OnProgress(object sender, BundleProgressEventArgs e)
43 {
44 Console.WriteLine("progresss: {0}%", e.Progress);
45 }
46 }
47}
diff --git a/src/engine/EngineForApplication.cpp b/src/engine/EngineForApplication.cpp
new file mode 100644
index 00000000..eda5fc64
--- /dev/null
+++ b/src/engine/EngineForApplication.cpp
@@ -0,0 +1,894 @@
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#include "precomp.h"
4
5static HRESULT BAEngineGetPackageCount(
6 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
7 __in BAENGINE_GETPACKAGECOUNT_ARGS* /*pArgs*/,
8 __in BAENGINE_GETPACKAGECOUNT_RESULTS* pResults
9 )
10{
11 HRESULT hr = S_OK;
12 DWORD* pcPackages = &pResults->cPackages;
13
14 *pcPackages = pContext->pEngineState->packages.cPackages;
15
16 return hr;
17}
18
19static HRESULT BAEngineGetVariableNumeric(
20 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
21 __in BAENGINE_GETVARIABLENUMERIC_ARGS* pArgs,
22 __in BAENGINE_GETVARIABLENUMERIC_RESULTS* pResults
23 )
24{
25 HRESULT hr = S_OK;
26 LPCWSTR wzVariable = pArgs->wzVariable;
27 LONGLONG* pllValue = &pResults->llValue;
28
29 if (wzVariable && *wzVariable)
30 {
31 hr = VariableGetNumeric(&pContext->pEngineState->variables, wzVariable, pllValue);
32 }
33 else
34 {
35 hr = E_INVALIDARG;
36 }
37
38 return hr;
39}
40
41static HRESULT BAEngineGetVariableString(
42 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
43 __in BAENGINE_GETVARIABLESTRING_ARGS* pArgs,
44 __in BAENGINE_GETVARIABLESTRING_RESULTS* pResults
45 )
46{
47 HRESULT hr = S_OK;
48 LPWSTR sczValue = NULL;
49 size_t cchRemaining = 0;
50 LPCWSTR wzVariable = pArgs->wzVariable;
51 LPWSTR wzValue = pResults->wzValue;
52 DWORD* pcchValue = &pResults->cchValue;
53
54 if (wzVariable && *wzVariable)
55 {
56 hr = VariableGetString(&pContext->pEngineState->variables, wzVariable, &sczValue);
57 if (SUCCEEDED(hr))
58 {
59 if (wzValue)
60 {
61 hr = ::StringCchCopyExW(wzValue, *pcchValue, sczValue, NULL, &cchRemaining, STRSAFE_FILL_BEHIND_NULL);
62 if (STRSAFE_E_INSUFFICIENT_BUFFER == hr)
63 {
64 hr = E_MOREDATA;
65
66 ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining);
67 *pcchValue = cchRemaining + 1;
68 }
69 }
70 else
71 {
72 hr = E_MOREDATA;
73
74 ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining);
75 *pcchValue = cchRemaining + 1;
76 }
77 }
78 }
79 else
80 {
81 hr = E_INVALIDARG;
82 }
83
84 StrSecureZeroFreeString(sczValue);
85 return hr;
86}
87
88static HRESULT BAEngineGetVariableVersion(
89 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
90 __in BAENGINE_GETVARIABLEVERSION_ARGS* pArgs,
91 __in BAENGINE_GETVARIABLEVERSION_RESULTS* pResults
92 )
93{
94 HRESULT hr = S_OK;
95 LPCWSTR wzVariable = pArgs->wzVariable;
96 DWORD64* pqwValue = &pResults->qwValue;
97
98 if (wzVariable && *wzVariable)
99 {
100 hr = VariableGetVersion(&pContext->pEngineState->variables, wzVariable, pqwValue);
101 }
102 else
103 {
104 hr = E_INVALIDARG;
105 }
106
107 return hr;
108}
109
110static HRESULT BAEngineFormatString(
111 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
112 __in BAENGINE_FORMATSTRING_ARGS* pArgs,
113 __in BAENGINE_FORMATSTRING_RESULTS* pResults
114 )
115{
116 HRESULT hr = S_OK;
117 LPWSTR sczValue = NULL;
118 DWORD cchValue = 0;
119 LPCWSTR wzIn = pArgs->wzIn;
120 LPWSTR wzOut = pResults->wzOut;
121 DWORD* pcchOut = &pResults->cchOut;
122
123 if (wzIn && *wzIn)
124 {
125 hr = VariableFormatString(&pContext->pEngineState->variables, wzIn, &sczValue, &cchValue);
126 if (SUCCEEDED(hr))
127 {
128 if (wzOut)
129 {
130 hr = ::StringCchCopyExW(wzOut, *pcchOut, sczValue, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
131 if (FAILED(hr))
132 {
133 *pcchOut = cchValue;
134 if (STRSAFE_E_INSUFFICIENT_BUFFER == hr)
135 {
136 hr = E_MOREDATA;
137 }
138 }
139 }
140 else
141 {
142 hr = E_MOREDATA;
143 *pcchOut = cchValue;
144 }
145 }
146 }
147 else
148 {
149 hr = E_INVALIDARG;
150 }
151
152 StrSecureZeroFreeString(sczValue);
153 return hr;
154}
155
156static HRESULT BAEngineEscapeString(
157 __in BOOTSTRAPPER_ENGINE_CONTEXT* /*pContext*/,
158 __in BAENGINE_ESCAPESTRING_ARGS* pArgs,
159 __in BAENGINE_ESCAPESTRING_RESULTS* pResults
160 )
161{
162 HRESULT hr = S_OK;
163 LPWSTR sczValue = NULL;
164 size_t cchRemaining = 0;
165 LPCWSTR wzIn = pArgs->wzIn;
166 LPWSTR wzOut = pResults->wzOut;
167 DWORD* pcchOut = &pResults->cchOut;
168
169 if (wzIn && *wzIn)
170 {
171 hr = VariableEscapeString(wzIn, &sczValue);
172 if (SUCCEEDED(hr))
173 {
174 if (wzOut)
175 {
176 hr = ::StringCchCopyExW(wzOut, *pcchOut, sczValue, NULL, &cchRemaining, STRSAFE_FILL_BEHIND_NULL);
177 if (STRSAFE_E_INSUFFICIENT_BUFFER == hr)
178 {
179 hr = E_MOREDATA;
180 ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining);
181 *pcchOut = cchRemaining;
182 }
183 }
184 else
185 {
186 ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, &cchRemaining);
187 *pcchOut = cchRemaining;
188 }
189 }
190 }
191 else
192 {
193 hr = E_INVALIDARG;
194 }
195
196 StrSecureZeroFreeString(sczValue);
197 return hr;
198}
199
200static HRESULT BAEngineEvaluateCondition(
201 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
202 __in BAENGINE_EVALUATECONDITION_ARGS* pArgs,
203 __in BAENGINE_EVALUATECONDITION_RESULTS* pResults
204 )
205{
206 HRESULT hr = S_OK;
207 LPCWSTR wzCondition = pArgs->wzCondition;
208 BOOL* pf = &pResults->f;
209
210 if (wzCondition && *wzCondition)
211 {
212 hr = ConditionEvaluate(&pContext->pEngineState->variables, wzCondition, pf);
213 }
214 else
215 {
216 hr = E_INVALIDARG;
217 }
218
219 return hr;
220}
221
222static HRESULT BAEngineLog(
223 __in BOOTSTRAPPER_ENGINE_CONTEXT* /*pContext*/,
224 __in BAENGINE_LOG_ARGS* pArgs,
225 __in BAENGINE_LOG_RESULTS* /*pResults*/
226 )
227{
228 HRESULT hr = S_OK;
229 REPORT_LEVEL rl = REPORT_NONE;
230 BOOTSTRAPPER_LOG_LEVEL level = pArgs->level;
231 LPCWSTR wzMessage = pArgs->wzMessage;
232
233 switch (level)
234 {
235 case BOOTSTRAPPER_LOG_LEVEL_STANDARD:
236 rl = REPORT_STANDARD;
237 break;
238
239 case BOOTSTRAPPER_LOG_LEVEL_VERBOSE:
240 rl = REPORT_VERBOSE;
241 break;
242
243 case BOOTSTRAPPER_LOG_LEVEL_DEBUG:
244 rl = REPORT_DEBUG;
245 break;
246
247 case BOOTSTRAPPER_LOG_LEVEL_ERROR:
248 rl = REPORT_ERROR;
249 break;
250
251 default:
252 ExitFunction1(hr = E_INVALIDARG);
253 }
254
255 hr = LogStringLine(rl, "%ls", wzMessage);
256 ExitOnFailure(hr, "Failed to log BA message.");
257
258LExit:
259 return hr;
260}
261
262static HRESULT BAEngineSendEmbeddedError(
263 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
264 __in BAENGINE_SENDEMBEDDEDERROR_ARGS* pArgs,
265 __in BAENGINE_SENDEMBEDDEDERROR_RESULTS* pResults
266 )
267{
268 HRESULT hr = S_OK;
269 BYTE* pbData = NULL;
270 DWORD cbData = 0;
271 DWORD dwResult = 0;
272 DWORD dwErrorCode = pArgs->dwErrorCode;
273 LPCWSTR wzMessage = pArgs->wzMessage;
274 DWORD dwUIHint = pArgs->dwUIHint;
275 int* pnResult = &pResults->nResult;
276
277 if (BURN_MODE_EMBEDDED != pContext->pEngineState->mode)
278 {
279 hr = HRESULT_FROM_WIN32(ERROR_INVALID_STATE);
280 ExitOnRootFailure(hr, "BA requested to send embedded message when not in embedded mode.");
281 }
282
283 hr = BuffWriteNumber(&pbData, &cbData, dwErrorCode);
284 ExitOnFailure(hr, "Failed to write error code to message buffer.");
285
286 hr = BuffWriteString(&pbData, &cbData, wzMessage ? wzMessage : L"");
287 ExitOnFailure(hr, "Failed to write message string to message buffer.");
288
289 hr = BuffWriteNumber(&pbData, &cbData, dwUIHint);
290 ExitOnFailure(hr, "Failed to write UI hint to message buffer.");
291
292 hr = PipeSendMessage(pContext->pEngineState->embeddedConnection.hPipe, BURN_EMBEDDED_MESSAGE_TYPE_ERROR, pbData, cbData, NULL, NULL, &dwResult);
293 ExitOnFailure(hr, "Failed to send embedded message over pipe.");
294
295 *pnResult = static_cast<int>(dwResult);
296
297LExit:
298 ReleaseBuffer(pbData);
299 return hr;
300}
301
302static HRESULT BAEngineSendEmbeddedProgress(
303 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
304 __in BAENGINE_SENDEMBEDDEDPROGRESS_ARGS* pArgs,
305 __in BAENGINE_SENDEMBEDDEDPROGRESS_RESULTS* pResults
306 )
307{
308 HRESULT hr = S_OK;
309 BYTE* pbData = NULL;
310 DWORD cbData = 0;
311 DWORD dwResult = 0;
312 DWORD dwProgressPercentage = pArgs->dwProgressPercentage;
313 DWORD dwOverallProgressPercentage = pArgs->dwOverallProgressPercentage;
314 int* pnResult = &pResults->nResult;
315
316 if (BURN_MODE_EMBEDDED != pContext->pEngineState->mode)
317 {
318 hr = HRESULT_FROM_WIN32(ERROR_INVALID_STATE);
319 ExitOnRootFailure(hr, "BA requested to send embedded progress message when not in embedded mode.");
320 }
321
322 hr = BuffWriteNumber(&pbData, &cbData, dwProgressPercentage);
323 ExitOnFailure(hr, "Failed to write progress percentage to message buffer.");
324
325 hr = BuffWriteNumber(&pbData, &cbData, dwOverallProgressPercentage);
326 ExitOnFailure(hr, "Failed to write overall progress percentage to message buffer.");
327
328 hr = PipeSendMessage(pContext->pEngineState->embeddedConnection.hPipe, BURN_EMBEDDED_MESSAGE_TYPE_PROGRESS, pbData, cbData, NULL, NULL, &dwResult);
329 ExitOnFailure(hr, "Failed to send embedded progress message over pipe.");
330
331 *pnResult = static_cast<int>(dwResult);
332
333LExit:
334 ReleaseBuffer(pbData);
335 return hr;
336}
337
338static HRESULT BAEngineSetUpdate(
339 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
340 __in const BAENGINE_SETUPDATE_ARGS* pArgs,
341 __in BAENGINE_SETUPDATE_RESULTS* /*pResults*/
342 )
343{
344 HRESULT hr = S_OK;
345 LPCWSTR sczId = NULL;
346 LPWSTR sczLocalSource = NULL;
347 LPWSTR sczCommandline = NULL;
348 UUID guid = { };
349 WCHAR wzGuid[39];
350 RPC_STATUS rs = RPC_S_OK;
351 LPCWSTR wzLocalSource = pArgs->wzLocalSource;
352 LPCWSTR wzDownloadSource = pArgs->wzDownloadSource;
353 DWORD64 qwSize = pArgs->qwSize;
354 BOOTSTRAPPER_UPDATE_HASH_TYPE hashType = pArgs->hashType;
355 BYTE* rgbHash = pArgs->rgbHash;
356 DWORD cbHash = pArgs->cbHash;
357
358 ::EnterCriticalSection(&pContext->pEngineState->csActive);
359
360 if ((!wzLocalSource || !*wzLocalSource) && (!wzDownloadSource || !*wzDownloadSource))
361 {
362 UpdateUninitialize(&pContext->pEngineState->update);
363 }
364 else if (BOOTSTRAPPER_UPDATE_HASH_TYPE_NONE == hashType && (0 != cbHash || rgbHash))
365 {
366 hr = E_INVALIDARG;
367 }
368 else if (BOOTSTRAPPER_UPDATE_HASH_TYPE_SHA1 == hashType && (SHA1_HASH_LEN != cbHash || !rgbHash))
369 {
370 hr = E_INVALIDARG;
371 }
372 else
373 {
374 UpdateUninitialize(&pContext->pEngineState->update);
375
376 if (!wzLocalSource || !*wzLocalSource)
377 {
378 hr = StrAllocFormatted(&sczLocalSource, L"update\\%ls", pContext->pEngineState->registration.sczExecutableName);
379 ExitOnFailure(hr, "Failed to default local update source");
380 }
381
382 hr = CoreRecreateCommandLine(&sczCommandline, BOOTSTRAPPER_ACTION_INSTALL, pContext->pEngineState->command.display, pContext->pEngineState->command.restart, BOOTSTRAPPER_RELATION_NONE, FALSE, pContext->pEngineState->registration.sczActiveParent, pContext->pEngineState->registration.sczAncestors, NULL, pContext->pEngineState->command.wzCommandLine);
383 ExitOnFailure(hr, "Failed to recreate command-line for update bundle.");
384
385 // Per-user bundles would fail to use the downloaded update bundle, as the existing install would already be cached
386 // at the registration id's location. Here I am generating a random guid, but in the future it would be nice if the
387 // feed would provide the ID of the update.
388 if (!pContext->pEngineState->registration.fPerMachine)
389 {
390 rs = ::UuidCreate(&guid);
391 hr = HRESULT_FROM_RPC(rs);
392 ExitOnFailure(hr, "Failed to create bundle update guid.");
393
394 if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid)))
395 {
396 hr = E_OUTOFMEMORY;
397 ExitOnRootFailure(hr, "Failed to convert bundle update guid into string.");
398 }
399
400 sczId = wzGuid;
401 }
402 else
403 {
404 sczId = pContext->pEngineState->registration.sczId;
405 }
406
407 hr = PseudoBundleInitialize(FILEMAKEVERSION(rmj, rmm, rup, 0), &pContext->pEngineState->update.package, FALSE, sczId, BOOTSTRAPPER_RELATION_UPDATE, BOOTSTRAPPER_PACKAGE_STATE_ABSENT, pContext->pEngineState->registration.sczExecutableName, sczLocalSource ? sczLocalSource : wzLocalSource, wzDownloadSource, qwSize, TRUE, sczCommandline, NULL, NULL, NULL, rgbHash, cbHash);
408 ExitOnFailure(hr, "Failed to set update bundle.");
409
410 pContext->pEngineState->update.fUpdateAvailable = TRUE;
411 }
412
413LExit:
414 ::LeaveCriticalSection(&pContext->pEngineState->csActive);
415
416 ReleaseStr(sczCommandline);
417 ReleaseStr(sczLocalSource);
418 return hr;
419}
420
421static HRESULT BAEngineSetLocalSource(
422 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
423 __in BAENGINE_SETLOCALSOURCE_ARGS* pArgs,
424 __in BAENGINE_SETLOCALSOURCE_RESULTS* /*pResults*/
425 )
426{
427 HRESULT hr = S_OK;
428 BURN_CONTAINER* pContainer = NULL;
429 BURN_PAYLOAD* pPayload = NULL;
430 LPCWSTR wzPackageOrContainerId = pArgs->wzPackageOrContainerId;
431 LPCWSTR wzPayloadId = pArgs->wzPayloadId;
432 LPCWSTR wzPath = pArgs->wzPath;
433
434 ::EnterCriticalSection(&pContext->pEngineState->csActive);
435 hr = UserExperienceEnsureEngineInactive(&pContext->pEngineState->userExperience);
436 ExitOnFailure(hr, "Engine is active, cannot change engine state.");
437
438 if (!wzPath || !*wzPath)
439 {
440 hr = E_INVALIDARG;
441 }
442 else if (wzPayloadId && * wzPayloadId)
443 {
444 hr = PayloadFindById(&pContext->pEngineState->payloads, wzPayloadId, &pPayload);
445 ExitOnFailure(hr, "BA requested unknown payload with id: %ls", wzPayloadId);
446
447 if (BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging)
448 {
449 hr = HRESULT_FROM_WIN32(ERROR_INVALID_OPERATION);
450 ExitOnFailure(hr, "BA denied while trying to set source on embedded payload: %ls", wzPayloadId);
451 }
452
453 hr = StrAllocString(&pPayload->sczSourcePath, wzPath, 0);
454 ExitOnFailure(hr, "Failed to set source path for payload.");
455 }
456 else if (wzPackageOrContainerId && *wzPackageOrContainerId)
457 {
458 hr = ContainerFindById(&pContext->pEngineState->containers, wzPackageOrContainerId, &pContainer);
459 ExitOnFailure(hr, "BA requested unknown container with id: %ls", wzPackageOrContainerId);
460
461 hr = StrAllocString(&pContainer->sczSourcePath, wzPath, 0);
462 ExitOnFailure(hr, "Failed to set source path for container.");
463 }
464 else
465 {
466 hr = E_INVALIDARG;
467 }
468
469LExit:
470 ::LeaveCriticalSection(&pContext->pEngineState->csActive);
471 return hr;
472}
473
474static HRESULT BAEngineSetDownloadSource(
475 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
476 __in BAENGINE_SETDOWNLOADSOURCE_ARGS* pArgs,
477 __in BAENGINE_SETDOWNLOADSOURCE_RESULTS* /*pResults*/
478 )
479{
480 HRESULT hr = S_OK;
481 BURN_CONTAINER* pContainer = NULL;
482 BURN_PAYLOAD* pPayload = NULL;
483 DOWNLOAD_SOURCE* pDownloadSource = NULL;
484 LPCWSTR wzPackageOrContainerId = pArgs->wzPackageOrContainerId;
485 LPCWSTR wzPayloadId = pArgs->wzPayloadId;
486 LPCWSTR wzUrl = pArgs->wzUrl;
487 LPCWSTR wzUser = pArgs->wzUser;
488 LPCWSTR wzPassword = pArgs->wzPassword;
489
490 ::EnterCriticalSection(&pContext->pEngineState->csActive);
491 hr = UserExperienceEnsureEngineInactive(&pContext->pEngineState->userExperience);
492 ExitOnFailure(hr, "Engine is active, cannot change engine state.");
493
494 if (wzPayloadId && *wzPayloadId)
495 {
496 hr = PayloadFindById(&pContext->pEngineState->payloads, wzPayloadId, &pPayload);
497 ExitOnFailure(hr, "BA requested unknown payload with id: %ls", wzPayloadId);
498
499 if (BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging)
500 {
501 hr = HRESULT_FROM_WIN32(ERROR_INVALID_OPERATION);
502 ExitOnFailure(hr, "BA denied while trying to set download URL on embedded payload: %ls", wzPayloadId);
503 }
504
505 pDownloadSource = &pPayload->downloadSource;
506 }
507 else if (wzPackageOrContainerId && *wzPackageOrContainerId)
508 {
509 hr = ContainerFindById(&pContext->pEngineState->containers, wzPackageOrContainerId, &pContainer);
510 ExitOnFailure(hr, "BA requested unknown container with id: %ls", wzPackageOrContainerId);
511
512 pDownloadSource = &pContainer->downloadSource;
513 }
514 else
515 {
516 hr = E_INVALIDARG;
517 ExitOnFailure(hr, "BA did not provide container or payload id.");
518 }
519
520 if (wzUrl && *wzUrl)
521 {
522 hr = StrAllocString(&pDownloadSource->sczUrl, wzUrl, 0);
523 ExitOnFailure(hr, "Failed to set download URL.");
524
525 if (wzUser && *wzUser)
526 {
527 hr = StrAllocString(&pDownloadSource->sczUser, wzUser, 0);
528 ExitOnFailure(hr, "Failed to set download user.");
529
530 if (wzPassword && *wzPassword)
531 {
532 hr = StrAllocString(&pDownloadSource->sczPassword, wzPassword, 0);
533 ExitOnFailure(hr, "Failed to set download password.");
534 }
535 else // no password.
536 {
537 ReleaseNullStr(pDownloadSource->sczPassword);
538 }
539 }
540 else // no user means no password either.
541 {
542 ReleaseNullStr(pDownloadSource->sczUser);
543 ReleaseNullStr(pDownloadSource->sczPassword);
544 }
545 }
546 else // no URL provided means clear out the whole download source.
547 {
548 ReleaseNullStr(pDownloadSource->sczUrl);
549 ReleaseNullStr(pDownloadSource->sczUser);
550 ReleaseNullStr(pDownloadSource->sczPassword);
551 }
552
553LExit:
554 ::LeaveCriticalSection(&pContext->pEngineState->csActive);
555 return hr;
556}
557
558static HRESULT BAEngineSetVariableNumeric(
559 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
560 __in const BAENGINE_SETVARIABLENUMERIC_ARGS* pArgs,
561 __in BAENGINE_SETVARIABLENUMERIC_RESULTS* /*pResults*/
562 )
563{
564 HRESULT hr = S_OK;
565 LPCWSTR wzVariable = pArgs->wzVariable;
566 LONGLONG llValue = pArgs->llValue;
567
568 if (wzVariable && *wzVariable)
569 {
570 hr = VariableSetNumeric(&pContext->pEngineState->variables, wzVariable, llValue, FALSE);
571 ExitOnFailure(hr, "Failed to set numeric variable.");
572 }
573 else
574 {
575 hr = E_INVALIDARG;
576 ExitOnFailure(hr, "BA did not provide variable name.");
577 }
578
579LExit:
580 return hr;
581}
582
583static HRESULT BAEngineSetVariableString(
584 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
585 __in const BAENGINE_SETVARIABLESTRING_ARGS* pArgs,
586 __in BAENGINE_SETVARIABLESTRING_RESULTS* /*pResults*/
587 )
588{
589 HRESULT hr = S_OK;
590 LPCWSTR wzVariable = pArgs->wzVariable;
591 LPCWSTR wzValue = pArgs->wzValue;
592
593 if (wzVariable && *wzVariable)
594 {
595 hr = VariableSetString(&pContext->pEngineState->variables, wzVariable, wzValue, FALSE);
596 ExitOnFailure(hr, "Failed to set numeric variable.");
597 }
598 else
599 {
600 hr = E_INVALIDARG;
601 ExitOnFailure(hr, "BA did not provide variable name.");
602 }
603
604LExit:
605 return hr;
606}
607
608static HRESULT BAEngineSetVariableVersion(
609 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
610 __in const BAENGINE_SETVARIABLEVERSION_ARGS* pArgs,
611 __in BAENGINE_SETVARIABLEVERSION_RESULTS* /*pResults*/
612 )
613{
614 HRESULT hr = S_OK;
615 LPCWSTR wzVariable = pArgs->wzVariable;
616 DWORD64 qwValue = pArgs->qwValue;
617
618 if (wzVariable && *wzVariable)
619 {
620 hr = VariableSetVersion(&pContext->pEngineState->variables, wzVariable, qwValue, FALSE);
621 ExitOnFailure(hr, "Failed to set version variable.");
622 }
623 else
624 {
625 hr = E_INVALIDARG;
626 ExitOnFailure(hr, "BA did not provide variable name.");
627 }
628
629LExit:
630 return hr;
631}
632
633static HRESULT BAEngineCloseSplashScreen(
634 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
635 __in const BAENGINE_CLOSESPLASHSCREEN_ARGS* /*pArgs*/,
636 __in BAENGINE_CLOSESPLASHSCREEN_RESULTS* /*pResults*/
637 )
638{
639 // If the splash screen is still around, close it.
640 if (::IsWindow(pContext->pEngineState->command.hwndSplashScreen))
641 {
642 ::PostMessageW(pContext->pEngineState->command.hwndSplashScreen, WM_CLOSE, 0, 0);
643 }
644
645 return S_OK;
646}
647
648static HRESULT BAEngineDetect(
649 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
650 __in BAENGINE_DETECT_ARGS* pArgs,
651 __in BAENGINE_DETECT_RESULTS* /*pResults*/
652 )
653{
654 HRESULT hr = S_OK;
655
656 if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_DETECT, 0, reinterpret_cast<LPARAM>(pArgs->hwndParent)))
657 {
658 ExitWithLastError(hr, "Failed to post detect message.");
659 }
660
661LExit:
662 return hr;
663}
664
665static HRESULT BAEnginePlan(
666 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
667 __in const BAENGINE_PLAN_ARGS* pArgs,
668 __in BAENGINE_PLAN_RESULTS* /*pResults*/
669 )
670{
671 HRESULT hr = S_OK;
672 BOOTSTRAPPER_ACTION action = pArgs->action;
673
674 if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_PLAN, 0, action))
675 {
676 ExitWithLastError(hr, "Failed to post plan message.");
677 }
678
679LExit:
680 return hr;
681}
682
683static HRESULT BAEngineElevate(
684 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
685 __in const BAENGINE_ELEVATE_ARGS* pArgs,
686 __in BAENGINE_ELEVATE_RESULTS* /*pResults*/
687 )
688{
689 HRESULT hr = S_OK;
690
691 if (INVALID_HANDLE_VALUE != pContext->pEngineState->companionConnection.hPipe)
692 {
693 hr = HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED);
694 }
695 else if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_ELEVATE, 0, reinterpret_cast<LPARAM>(pArgs->hwndParent)))
696 {
697 ExitWithLastError(hr, "Failed to post elevate message.");
698 }
699
700LExit:
701 return hr;
702}
703
704static HRESULT BAEngineApply(
705 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
706 __in const BAENGINE_APPLY_ARGS* pArgs,
707 __in BAENGINE_APPLY_RESULTS* /*pResults*/
708 )
709{
710 HRESULT hr = S_OK;
711
712 if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_APPLY, 0, reinterpret_cast<LPARAM>(pArgs->hwndParent)))
713 {
714 ExitWithLastError(hr, "Failed to post apply message.");
715 }
716
717LExit:
718 return hr;
719}
720
721static HRESULT BAEngineQuit(
722 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
723 __in const BAENGINE_QUIT_ARGS* pArgs,
724 __in BAENGINE_QUIT_RESULTS* /*pResults*/
725 )
726{
727 HRESULT hr = S_OK;
728
729 if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_QUIT, static_cast<WPARAM>(pArgs->dwExitCode), 0))
730 {
731 ExitWithLastError(hr, "Failed to post shutdown message.");
732 }
733
734LExit:
735 return hr;
736}
737
738static HRESULT BAEngineLaunchApprovedExe(
739 __in BOOTSTRAPPER_ENGINE_CONTEXT* pContext,
740 __in const BAENGINE_LAUNCHAPPROVEDEXE_ARGS* pArgs,
741 __in BAENGINE_LAUNCHAPPROVEDEXE_RESULTS* /*pResults*/
742 )
743{
744 HRESULT hr = S_OK;
745 BURN_APPROVED_EXE* pApprovedExe = NULL;
746 BOOL fLeaveCriticalSection = FALSE;
747 BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe = (BURN_LAUNCH_APPROVED_EXE*)MemAlloc(sizeof(BURN_LAUNCH_APPROVED_EXE), TRUE);
748 HWND hwndParent = pArgs->hwndParent;
749 LPCWSTR wzApprovedExeForElevationId = pArgs->wzApprovedExeForElevationId;
750 LPCWSTR wzArguments = pArgs->wzArguments;
751 DWORD dwWaitForInputIdleTimeout = pArgs->dwWaitForInputIdleTimeout;
752
753 ::EnterCriticalSection(&pContext->pEngineState->csActive);
754 fLeaveCriticalSection = TRUE;
755 hr = UserExperienceEnsureEngineInactive(&pContext->pEngineState->userExperience);
756 ExitOnFailure(hr, "Engine is active, cannot change engine state.");
757
758 if (!wzApprovedExeForElevationId || !*wzApprovedExeForElevationId)
759 {
760 ExitFunction1(hr = E_INVALIDARG);
761 }
762
763 hr = ApprovedExesFindById(&pContext->pEngineState->approvedExes, wzApprovedExeForElevationId, &pApprovedExe);
764 ExitOnFailure(hr, "BA requested unknown approved exe with id: %ls", wzApprovedExeForElevationId);
765
766 ::LeaveCriticalSection(&pContext->pEngineState->csActive);
767 fLeaveCriticalSection = FALSE;
768
769 hr = StrAllocString(&pLaunchApprovedExe->sczId, wzApprovedExeForElevationId, NULL);
770 ExitOnFailure(hr, "Failed to copy the id.");
771
772 if (wzArguments)
773 {
774 hr = StrAllocString(&pLaunchApprovedExe->sczArguments, wzArguments, NULL);
775 ExitOnFailure(hr, "Failed to copy the arguments.");
776 }
777
778 pLaunchApprovedExe->dwWaitForInputIdleTimeout = dwWaitForInputIdleTimeout;
779
780 pLaunchApprovedExe->hwndParent = hwndParent;
781
782 if (!::PostThreadMessageW(pContext->dwThreadId, WM_BURN_LAUNCH_APPROVED_EXE, 0, reinterpret_cast<LPARAM>(pLaunchApprovedExe)))
783 {
784 ExitWithLastError(hr, "Failed to post launch approved exe message.");
785 }
786
787LExit:
788 if (fLeaveCriticalSection)
789 {
790 ::LeaveCriticalSection(&pContext->pEngineState->csActive);
791 }
792
793 if (FAILED(hr))
794 {
795 ApprovedExesUninitializeLaunch(pLaunchApprovedExe);
796 }
797
798 return hr;
799}
800
801HRESULT WINAPI EngineForApplicationProc(
802 __in BOOTSTRAPPER_ENGINE_MESSAGE message,
803 __in const LPVOID pvArgs,
804 __inout LPVOID pvResults,
805 __in_opt LPVOID pvContext
806 )
807{
808 HRESULT hr = S_OK;
809 BOOTSTRAPPER_ENGINE_CONTEXT* pContext = reinterpret_cast<BOOTSTRAPPER_ENGINE_CONTEXT*>(pvContext);
810
811 if (!pContext || !pvArgs || !pvResults)
812 {
813 ExitFunction1(hr = E_INVALIDARG);
814 }
815
816 switch (message)
817 {
818 case BOOTSTRAPPER_ENGINE_MESSAGE_GETPACKAGECOUNT:
819 hr = BAEngineGetPackageCount(pContext, reinterpret_cast<BAENGINE_GETPACKAGECOUNT_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_GETPACKAGECOUNT_RESULTS*>(pvResults));
820 break;
821 case BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLENUMERIC:
822 hr = BAEngineGetVariableNumeric(pContext, reinterpret_cast<BAENGINE_GETVARIABLENUMERIC_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_GETVARIABLENUMERIC_RESULTS*>(pvResults));
823 break;
824 case BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLESTRING:
825 hr = BAEngineGetVariableString(pContext, reinterpret_cast<BAENGINE_GETVARIABLESTRING_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_GETVARIABLESTRING_RESULTS*>(pvResults));
826 break;
827 case BOOTSTRAPPER_ENGINE_MESSAGE_GETVARIABLEVERSION:
828 hr = BAEngineGetVariableVersion(pContext, reinterpret_cast<BAENGINE_GETVARIABLEVERSION_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_GETVARIABLEVERSION_RESULTS*>(pvResults));
829 break;
830 case BOOTSTRAPPER_ENGINE_MESSAGE_FORMATSTRING:
831 hr = BAEngineFormatString(pContext, reinterpret_cast<BAENGINE_FORMATSTRING_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_FORMATSTRING_RESULTS*>(pvResults));
832 break;
833 case BOOTSTRAPPER_ENGINE_MESSAGE_ESCAPESTRING:
834 hr = BAEngineEscapeString(pContext, reinterpret_cast<BAENGINE_ESCAPESTRING_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_ESCAPESTRING_RESULTS*>(pvResults));
835 break;
836 case BOOTSTRAPPER_ENGINE_MESSAGE_EVALUATECONDITION:
837 hr = BAEngineEvaluateCondition(pContext, reinterpret_cast<BAENGINE_EVALUATECONDITION_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_EVALUATECONDITION_RESULTS*>(pvResults));
838 break;
839 case BOOTSTRAPPER_ENGINE_MESSAGE_LOG:
840 hr = BAEngineLog(pContext, reinterpret_cast<BAENGINE_LOG_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_LOG_RESULTS*>(pvResults));
841 break;
842 case BOOTSTRAPPER_ENGINE_MESSAGE_SENDEMBEDDEDERROR:
843 hr = BAEngineSendEmbeddedError(pContext, reinterpret_cast<BAENGINE_SENDEMBEDDEDERROR_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_SENDEMBEDDEDERROR_RESULTS*>(pvResults));
844 break;
845 case BOOTSTRAPPER_ENGINE_MESSAGE_SENDEMBEDDEDPROGRESS:
846 hr = BAEngineSendEmbeddedProgress(pContext, reinterpret_cast<BAENGINE_SENDEMBEDDEDPROGRESS_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_SENDEMBEDDEDPROGRESS_RESULTS*>(pvResults));
847 break;
848 case BOOTSTRAPPER_ENGINE_MESSAGE_SETUPDATE:
849 hr = BAEngineSetUpdate(pContext, reinterpret_cast<BAENGINE_SETUPDATE_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_SETUPDATE_RESULTS*>(pvResults));
850 break;
851 case BOOTSTRAPPER_ENGINE_MESSAGE_SETLOCALSOURCE:
852 hr = BAEngineSetLocalSource(pContext, reinterpret_cast<BAENGINE_SETLOCALSOURCE_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_SETLOCALSOURCE_RESULTS*>(pvResults));
853 break;
854 case BOOTSTRAPPER_ENGINE_MESSAGE_SETDOWNLOADSOURCE:
855 hr = BAEngineSetDownloadSource(pContext, reinterpret_cast<BAENGINE_SETDOWNLOADSOURCE_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_SETDOWNLOADSOURCE_RESULTS*>(pvResults));
856 break;
857 case BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLENUMERIC:
858 hr = BAEngineSetVariableNumeric(pContext, reinterpret_cast<BAENGINE_SETVARIABLENUMERIC_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_SETVARIABLENUMERIC_RESULTS*>(pvResults));
859 break;
860 case BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLESTRING:
861 hr = BAEngineSetVariableString(pContext, reinterpret_cast<BAENGINE_SETVARIABLESTRING_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_SETVARIABLESTRING_RESULTS*>(pvResults));
862 break;
863 case BOOTSTRAPPER_ENGINE_MESSAGE_SETVARIABLEVERSION:
864 hr = BAEngineSetVariableVersion(pContext, reinterpret_cast<BAENGINE_SETVARIABLEVERSION_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_SETVARIABLEVERSION_RESULTS*>(pvResults));
865 break;
866 case BOOTSTRAPPER_ENGINE_MESSAGE_CLOSESPLASHSCREEN:
867 hr = BAEngineCloseSplashScreen(pContext, reinterpret_cast<BAENGINE_CLOSESPLASHSCREEN_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_CLOSESPLASHSCREEN_RESULTS*>(pvResults));
868 break;
869 case BOOTSTRAPPER_ENGINE_MESSAGE_DETECT:
870 hr = BAEngineDetect(pContext, reinterpret_cast<BAENGINE_DETECT_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_DETECT_RESULTS*>(pvResults));
871 break;
872 case BOOTSTRAPPER_ENGINE_MESSAGE_PLAN:
873 hr = BAEnginePlan(pContext, reinterpret_cast<BAENGINE_PLAN_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_PLAN_RESULTS*>(pvResults));
874 break;
875 case BOOTSTRAPPER_ENGINE_MESSAGE_ELEVATE:
876 hr = BAEngineElevate(pContext, reinterpret_cast<BAENGINE_ELEVATE_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_ELEVATE_RESULTS*>(pvResults));
877 break;
878 case BOOTSTRAPPER_ENGINE_MESSAGE_APPLY:
879 hr = BAEngineApply(pContext, reinterpret_cast<BAENGINE_APPLY_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_APPLY_RESULTS*>(pvResults));
880 break;
881 case BOOTSTRAPPER_ENGINE_MESSAGE_QUIT:
882 hr = BAEngineQuit(pContext, reinterpret_cast<BAENGINE_QUIT_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_QUIT_RESULTS*>(pvResults));
883 break;
884 case BOOTSTRAPPER_ENGINE_MESSAGE_LAUNCHAPPROVEDEXE:
885 hr = BAEngineLaunchApprovedExe(pContext, reinterpret_cast<BAENGINE_LAUNCHAPPROVEDEXE_ARGS*>(pvArgs), reinterpret_cast<BAENGINE_LAUNCHAPPROVEDEXE_RESULTS*>(pvResults));
886 break;
887 default:
888 hr = E_NOTIMPL;
889 break;
890 }
891
892LExit:
893 return hr;
894}
diff --git a/src/engine/EngineForApplication.h b/src/engine/EngineForApplication.h
new file mode 100644
index 00000000..1b755acc
--- /dev/null
+++ b/src/engine/EngineForApplication.h
@@ -0,0 +1,44 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9// constants
10
11enum WM_BURN
12{
13 WM_BURN_FIRST = WM_APP + 0xFFF, // this enum value must always be first.
14
15 WM_BURN_DETECT,
16 WM_BURN_PLAN,
17 WM_BURN_ELEVATE,
18 WM_BURN_APPLY,
19 WM_BURN_LAUNCH_APPROVED_EXE,
20 WM_BURN_QUIT,
21
22 WM_BURN_LAST, // this enum value must always be last.
23};
24
25// structs
26
27struct BOOTSTRAPPER_ENGINE_CONTEXT
28{
29 BURN_ENGINE_STATE* pEngineState;
30 DWORD dwThreadId;
31};
32
33// function declarations
34
35HRESULT WINAPI EngineForApplicationProc(
36 __in BOOTSTRAPPER_ENGINE_MESSAGE message,
37 __in const LPVOID pvArgs,
38 __inout LPVOID pvResults,
39 __in_opt LPVOID pvContext
40 );
41
42#if defined(__cplusplus)
43}
44#endif
diff --git a/src/engine/apply.cpp b/src/engine/apply.cpp
new file mode 100644
index 00000000..0cef9ac8
--- /dev/null
+++ b/src/engine/apply.cpp
@@ -0,0 +1,2516 @@
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#include "precomp.h"
4
5
6const DWORD BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS = 2;
7
8// structs
9
10struct BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT
11{
12 BURN_USER_EXPERIENCE* pUX;
13 BURN_CONTAINER* pContainer;
14 BURN_PACKAGE* pPackage;
15 BURN_PAYLOAD* pPayload;
16 DWORD64 qwCacheProgress;
17 DWORD64 qwTotalCacheSize;
18
19 BOOL fCancel;
20 BOOL fError;
21};
22
23typedef struct _BURN_EXECUTE_CONTEXT
24{
25 BURN_USER_EXPERIENCE* pUX;
26 BOOL fRollback;
27 BURN_PACKAGE* pExecutingPackage;
28 DWORD cExecutedPackages;
29 DWORD cExecutePackagesTotal;
30 DWORD* pcOverallProgressTicks;
31} BURN_EXECUTE_CONTEXT;
32
33
34// internal function declarations
35static HRESULT WINAPI AuthenticationRequired(
36 __in LPVOID pData,
37 __in HINTERNET hUrl,
38 __in long lHttpCode,
39 __out BOOL* pfRetrySend,
40 __out BOOL* pfRetry
41 );
42
43static HRESULT ExecuteDependentRegistrationActions(
44 __in HANDLE hPipe,
45 __in const BURN_REGISTRATION* pRegistration,
46 __in_ecount(cActions) const BURN_DEPENDENT_REGISTRATION_ACTION* rgActions,
47 __in DWORD cActions
48 );
49static HRESULT ExtractContainer(
50 __in HANDLE hSourceEngineFile,
51 __in BURN_CONTAINER* pContainer,
52 __in_z LPCWSTR wzContainerPath,
53 __in_ecount(cExtractPayloads) BURN_EXTRACT_PAYLOAD* rgExtractPayloads,
54 __in DWORD cExtractPayloads
55 );
56static void UpdateCacheSuccessProgress(
57 __in BURN_PLAN* pPlan,
58 __in BURN_CACHE_ACTION* pCacheAction,
59 __inout DWORD64* pqwSuccessfulCachedProgress
60 );
61static HRESULT LayoutBundle(
62 __in BURN_USER_EXPERIENCE* pUX,
63 __in BURN_VARIABLES* pVariables,
64 __in HANDLE hPipe,
65 __in_z LPCWSTR wzExecutableName,
66 __in_z LPCWSTR wzLayoutDirectory,
67 __in_z LPCWSTR wzUnverifiedPath,
68 __in DWORD64 qwSuccessfulCacheProgress,
69 __in DWORD64 qwTotalCacheSize
70 );
71static HRESULT AcquireContainerOrPayload(
72 __in BURN_USER_EXPERIENCE* pUX,
73 __in BURN_VARIABLES* pVariables,
74 __in_opt BURN_CONTAINER* pContainer,
75 __in_opt BURN_PACKAGE* pPackage,
76 __in_opt BURN_PAYLOAD* pPayload,
77 __in LPCWSTR wzDestinationPath,
78 __in DWORD64 qwSuccessfulCacheProgress,
79 __in DWORD64 qwTotalCacheSize
80 );
81static HRESULT LayoutOrCacheContainerOrPayload(
82 __in BURN_USER_EXPERIENCE* pUX,
83 __in HANDLE hPipe,
84 __in_opt BURN_CONTAINER* pContainer,
85 __in_opt BURN_PACKAGE* pPackage,
86 __in_opt BURN_PAYLOAD* pPayload,
87 __in BOOL fAlreadyProvidedProgress,
88 __in DWORD64 qwSuccessfullyCacheProgress,
89 __in DWORD64 qwTotalCacheSize,
90 __in_z_opt LPCWSTR wzLayoutDirectory,
91 __in_z LPCWSTR wzUnverifiedPath,
92 __in BOOL fMove,
93 __in DWORD cTryAgainAttempts,
94 __out BOOL* pfRetry
95 );
96static HRESULT PromptForSource(
97 __in BURN_USER_EXPERIENCE* pUX,
98 __in_z LPCWSTR wzPackageOrContainerId,
99 __in_z_opt LPCWSTR wzPayloadId,
100 __in_z LPCWSTR wzLocalSource,
101 __in_z_opt LPCWSTR wzDownloadSource,
102 __out BOOL* pfRetry,
103 __out BOOL* pfDownload
104 );
105static HRESULT CopyPayload(
106 __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress,
107 __in_z LPCWSTR wzSourcePath,
108 __in_z LPCWSTR wzDestinationPath
109 );
110static HRESULT DownloadPayload(
111 __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress,
112 __in_z LPCWSTR wzDestinationPath
113 );
114static DWORD CALLBACK CacheProgressRoutine(
115 __in LARGE_INTEGER TotalFileSize,
116 __in LARGE_INTEGER TotalBytesTransferred,
117 __in LARGE_INTEGER StreamSize,
118 __in LARGE_INTEGER StreamBytesTransferred,
119 __in DWORD dwStreamNumber,
120 __in DWORD dwCallbackReason,
121 __in HANDLE hSourceFile,
122 __in HANDLE hDestinationFile,
123 __in_opt LPVOID lpData
124 );
125static void DoRollbackCache(
126 __in BURN_USER_EXPERIENCE* pUX,
127 __in BURN_PLAN* pPlan,
128 __in HANDLE hPipe,
129 __in DWORD dwCheckpoint
130 );
131static HRESULT DoExecuteAction(
132 __in BURN_ENGINE_STATE* pEngineState,
133 __in BURN_EXECUTE_ACTION* pExecuteAction,
134 __in_opt HANDLE hCacheThread,
135 __in BURN_EXECUTE_CONTEXT* pContext,
136 __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary,
137 __out DWORD* pdwCheckpoint,
138 __out BOOL* pfKeepRegistration,
139 __out BOOL* pfSuspend,
140 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
141 );
142static HRESULT DoRollbackActions(
143 __in BURN_ENGINE_STATE* pEngineState,
144 __in BURN_EXECUTE_CONTEXT* pContext,
145 __in DWORD dwCheckpoint,
146 __in BOOL fInTransaction,
147 __out BOOL* pfKeepRegistration,
148 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
149 );
150static HRESULT ExecuteExePackage(
151 __in BURN_ENGINE_STATE* pEngineState,
152 __in BURN_EXECUTE_ACTION* pExecuteAction,
153 __in BURN_EXECUTE_CONTEXT* pContext,
154 __in BOOL fRollback,
155 __out BOOL* pfRetry,
156 __out BOOL* pfSuspend,
157 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
158 );
159static HRESULT ExecuteMsiPackage(
160 __in BURN_ENGINE_STATE* pEngineState,
161 __in BURN_EXECUTE_ACTION* pExecuteAction,
162 __in BURN_EXECUTE_CONTEXT* pContext,
163 __in BOOL fRollback,
164 __out BOOL* pfRetry,
165 __out BOOL* pfSuspend,
166 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
167 );
168static HRESULT ExecuteMspPackage(
169 __in BURN_ENGINE_STATE* pEngineState,
170 __in BURN_EXECUTE_ACTION* pExecuteAction,
171 __in BURN_EXECUTE_CONTEXT* pContext,
172 __in BOOL fRollback,
173 __out BOOL* pfRetry,
174 __out BOOL* pfSuspend,
175 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
176 );
177static HRESULT ExecuteMsuPackage(
178 __in BURN_ENGINE_STATE* pEngineState,
179 __in BURN_EXECUTE_ACTION* pExecuteAction,
180 __in BURN_EXECUTE_CONTEXT* pContext,
181 __in BOOL fRollback,
182 __in BOOL fStopWusaService,
183 __out BOOL* pfRetry,
184 __out BOOL* pfSuspend,
185 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
186 );
187static HRESULT ExecutePackageProviderAction(
188 __in BURN_ENGINE_STATE* pEngineState,
189 __in BURN_EXECUTE_ACTION* pAction,
190 __in BURN_EXECUTE_CONTEXT* pContext
191 );
192static HRESULT ExecuteDependencyAction(
193 __in BURN_ENGINE_STATE* pEngineState,
194 __in BURN_EXECUTE_ACTION* pAction,
195 __in BURN_EXECUTE_CONTEXT* pContext
196 );
197static HRESULT ExecuteCompatiblePackageAction(
198 __in BURN_ENGINE_STATE* pEngineState,
199 __in BURN_EXECUTE_ACTION* pAction
200 );
201static HRESULT CleanPackage(
202 __in HANDLE hElevatedPipe,
203 __in BURN_PACKAGE* pPackage
204 );
205static int GenericExecuteMessageHandler(
206 __in GENERIC_EXECUTE_MESSAGE* pMessage,
207 __in LPVOID pvContext
208 );
209static int MsiExecuteMessageHandler(
210 __in WIU_MSI_EXECUTE_MESSAGE* pMessage,
211 __in_opt LPVOID pvContext
212 );
213static HRESULT ReportOverallProgressTicks(
214 __in BURN_USER_EXPERIENCE* pUX,
215 __in BOOL fRollback,
216 __in DWORD cOverallProgressTicksTotal,
217 __in DWORD cOverallProgressTicks
218 );
219static HRESULT ExecutePackageComplete(
220 __in BURN_USER_EXPERIENCE* pUX,
221 __in BURN_VARIABLES* pVariables,
222 __in BURN_PACKAGE* pPackage,
223 __in HRESULT hrOverall,
224 __in HRESULT hrExecute,
225 __in BOOL fRollback,
226 __out BOOTSTRAPPER_APPLY_RESTART* pRestart,
227 __out BOOL* pfRetry,
228 __out BOOL* pfSuspend
229 );
230
231static HRESULT DoMsiBeginTransaction(
232 __in BURN_EXECUTE_CONTEXT *context
233 , __in BURN_ENGINE_STATE* pEngineState
234);
235static HRESULT DoMsiCommitTransaction(
236 __in BURN_EXECUTE_CONTEXT *context
237 , __in BURN_ENGINE_STATE* pEngineState
238);
239static HRESULT DoMsiRollbackTransaction(
240 __in BURN_EXECUTE_CONTEXT *context
241 , __in BURN_ENGINE_STATE* pEngineState
242);
243static HRESULT ExecuteMsiBeginTransaction(
244 __in BURN_EXECUTE_CONTEXT* pContext
245 , __in BURN_ENGINE_STATE* pEngineState
246);
247static HRESULT ExecuteMsiCommitTransaction(
248 __in BURN_EXECUTE_CONTEXT* pContext
249 , __in BURN_ENGINE_STATE* pEngineState
250);
251static HRESULT ExecuteMsiRollbackTransaction(
252 __in BURN_EXECUTE_CONTEXT* pContext
253 , __in BURN_ENGINE_STATE* pEngineState
254);
255
256// function definitions
257
258extern "C" void ApplyInitialize()
259{
260 // Prevent the system from sleeping.
261 ::SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
262}
263
264extern "C" void ApplyUninitialize()
265{
266 ::SetThreadExecutionState(ES_CONTINUOUS);
267}
268
269extern "C" HRESULT ApplySetVariables(
270 __in BURN_VARIABLES* pVariables
271 )
272{
273 HRESULT hr = S_OK;
274
275 hr = VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, NULL, TRUE);
276 ExitOnFailure(hr, "Failed to set the bundle forced restart package built-in variable.");
277
278LExit:
279 return hr;
280}
281
282extern "C" void ApplyReset(
283 __in BURN_USER_EXPERIENCE* pUX,
284 __in BURN_PACKAGES* pPackages
285 )
286{
287 UserExperienceExecuteReset(pUX);
288
289 for (DWORD i = 0; i < pPackages->cPackages; ++i)
290 {
291 BURN_PACKAGE* pPackage = pPackages->rgPackages + i;
292 pPackage->hrCacheResult = S_OK;
293 }
294}
295
296extern "C" HRESULT ApplyLock(
297 __in BOOL /*fPerMachine*/,
298 __out HANDLE* /*phLock*/
299 )
300{
301 HRESULT hr = S_OK;
302#if 0 // eventually figure out the correct way to support this. In its current form, embedded bundles (including related bundles) are hosed.
303 DWORD er = ERROR_SUCCESS;
304 HANDLE hLock = NULL;
305
306 hLock = ::CreateMutexW(NULL, TRUE, fPerMachine ? L"Global\\WixBurnExecutionLock" : L"Local\\WixBurnExecutionLock");
307 ExitOnNullWithLastError(hLock, hr, "Failed to create lock.");
308
309 er = ::GetLastError();
310 if (ERROR_ALREADY_EXISTS == er)
311 {
312 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_INSTALL_ALREADY_RUNNING));
313 }
314
315 *phLock = hLock;
316 hLock = NULL;
317
318LExit:
319 ReleaseHandle(hLock);
320#endif
321 return hr;
322}
323
324extern "C" HRESULT ApplyRegister(
325 __in BURN_ENGINE_STATE* pEngineState
326 )
327{
328 HRESULT hr = S_OK;
329 LPWSTR sczEngineWorkingPath = NULL;
330
331 hr = UserExperienceOnRegisterBegin(&pEngineState->userExperience);
332 ExitOnRootFailure(hr, "BA aborted register begin.");
333
334 // If we have a resume mode that suggests the bundle is on the machine.
335 if (BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING < pEngineState->command.resumeType)
336 {
337 // resume previous session
338 if (pEngineState->registration.fPerMachine)
339 {
340 hr = ElevationSessionResume(pEngineState->companionConnection.hPipe, pEngineState->registration.sczResumeCommandLine, pEngineState->registration.fDisableResume, &pEngineState->variables);
341 ExitOnFailure(hr, "Failed to resume registration session in per-machine process.");
342 }
343 else
344 {
345 hr = RegistrationSessionResume(&pEngineState->registration, &pEngineState->variables);
346 ExitOnFailure(hr, "Failed to resume registration session.");
347 }
348 }
349 else // need to complete registration on the machine.
350 {
351 hr = CacheCalculateBundleWorkingPath(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &sczEngineWorkingPath);
352 ExitOnFailure(hr, "Failed to calculate working path for engine.");
353
354 // begin new session
355 if (pEngineState->registration.fPerMachine)
356 {
357 hr = ElevationSessionBegin(pEngineState->companionConnection.hPipe, sczEngineWorkingPath, pEngineState->registration.sczResumeCommandLine, pEngineState->registration.fDisableResume, &pEngineState->variables, pEngineState->plan.dwRegistrationOperations, pEngineState->plan.dependencyRegistrationAction, pEngineState->plan.qwEstimatedSize);
358 ExitOnFailure(hr, "Failed to begin registration session in per-machine process.");
359 }
360 else
361 {
362 hr = RegistrationSessionBegin(sczEngineWorkingPath, &pEngineState->registration, &pEngineState->variables, &pEngineState->userExperience, pEngineState->plan.dwRegistrationOperations, pEngineState->plan.dependencyRegistrationAction, pEngineState->plan.qwEstimatedSize);
363 ExitOnFailure(hr, "Failed to begin registration session.");
364 }
365 }
366
367 // Apply any registration actions.
368 HRESULT hrExecuteRegistration = ExecuteDependentRegistrationActions(pEngineState->companionConnection.hPipe, &pEngineState->registration, pEngineState->plan.rgRegistrationActions, pEngineState->plan.cRegistrationActions);
369 UNREFERENCED_PARAMETER(hrExecuteRegistration);
370
371 // Try to save engine state.
372 hr = CoreSaveEngineState(pEngineState);
373 if (FAILED(hr))
374 {
375 LogErrorId(hr, MSG_STATE_NOT_SAVED, NULL, NULL, NULL);
376 hr = S_OK;
377 }
378
379LExit:
380 UserExperienceOnRegisterComplete(&pEngineState->userExperience, hr);
381 ReleaseStr(sczEngineWorkingPath);
382
383 return hr;
384}
385
386extern "C" HRESULT ApplyUnregister(
387 __in BURN_ENGINE_STATE* pEngineState,
388 __in BOOL fFailedOrRollback,
389 __in BOOL fKeepRegistration,
390 __in BOOL fSuspend,
391 __in BOOTSTRAPPER_APPLY_RESTART restart
392 )
393{
394 HRESULT hr = S_OK;
395 BURN_RESUME_MODE resumeMode = BURN_RESUME_MODE_NONE;
396
397 hr = UserExperienceOnUnregisterBegin(&pEngineState->userExperience);
398 ExitOnRootFailure(hr, "BA aborted unregister begin.");
399
400 // Calculate the correct resume mode. If a restart has been initiated, that trumps all other
401 // modes. If the user chose to suspend the install then we'll use that as the resume mode.
402 // Barring those special cases, if it was determined that we should keep the registration then
403 // do that, otherwise the resume mode was initialized to none and registration will be removed.
404 if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart)
405 {
406 resumeMode = BURN_RESUME_MODE_REBOOT_PENDING;
407 }
408 else if (fSuspend)
409 {
410 resumeMode = BURN_RESUME_MODE_SUSPEND;
411 }
412 else if (fKeepRegistration)
413 {
414 resumeMode = BURN_RESUME_MODE_ARP;
415 }
416
417 // If apply failed in any way and we're going to be keeping the bundle registered then
418 // execute any rollback dependency registration actions.
419 if (fFailedOrRollback && fKeepRegistration)
420 {
421 // Execute any rollback registration actions.
422 HRESULT hrRegistrationRollback = ExecuteDependentRegistrationActions(pEngineState->companionConnection.hPipe, &pEngineState->registration, pEngineState->plan.rgRollbackRegistrationActions, pEngineState->plan.cRollbackRegistrationActions);
423 UNREFERENCED_PARAMETER(hrRegistrationRollback);
424 }
425
426 if (pEngineState->registration.fPerMachine)
427 {
428 hr = ElevationSessionEnd(pEngineState->companionConnection.hPipe, resumeMode, restart, pEngineState->plan.dependencyRegistrationAction);
429 ExitOnFailure(hr, "Failed to end session in per-machine process.");
430 }
431 else
432 {
433 hr = RegistrationSessionEnd(&pEngineState->registration, resumeMode, restart, pEngineState->plan.dependencyRegistrationAction);
434 ExitOnFailure(hr, "Failed to end session in per-user process.");
435 }
436
437 pEngineState->resumeMode = resumeMode;
438
439LExit:
440 UserExperienceOnUnregisterComplete(&pEngineState->userExperience, hr);
441
442 return hr;
443}
444
445extern "C" HRESULT ApplyCache(
446 __in HANDLE hSourceEngineFile,
447 __in BURN_USER_EXPERIENCE* pUX,
448 __in BURN_VARIABLES* pVariables,
449 __in BURN_PLAN* pPlan,
450 __in HANDLE hPipe,
451 __inout DWORD* pcOverallProgressTicks,
452 __out BOOL* pfRollback
453 )
454{
455 HRESULT hr = S_OK;
456 DWORD dwCheckpoint = 0;
457 BOOL fRetry = FALSE;
458 DWORD iRetryAction = BURN_PLAN_INVALID_ACTION_INDEX;
459 BURN_PACKAGE* pStartedPackage = NULL;
460 DWORD64 qwSuccessfulCachedProgress = 0;
461
462 // Allow us to retry and skip packages.
463 DWORD iPackageStartAction = BURN_PLAN_INVALID_ACTION_INDEX;
464 DWORD iPackageCompleteAction = BURN_PLAN_INVALID_ACTION_INDEX;
465
466 hr = UserExperienceOnCacheBegin(pUX);
467 ExitOnRootFailure(hr, "BA aborted cache.");
468
469 do
470 {
471 hr = S_OK;
472 fRetry = FALSE;
473
474 // Allow us to retry just a container or payload.
475 LPCWSTR wzRetryId = NULL;
476 DWORD iRetryContainerOrPayloadAction = BURN_PLAN_INVALID_ACTION_INDEX;
477 BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION cachePackageCompleteAction = BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE;
478
479 // cache actions
480 for (DWORD i = (BURN_PLAN_INVALID_ACTION_INDEX == iRetryAction) ? 0 : iRetryAction; SUCCEEDED(hr) && i < pPlan->cCacheActions; ++i)
481 {
482 BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + i;
483 BOOL fRetryContainerOrPayload = FALSE;
484 cachePackageCompleteAction = BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE;
485
486 if (pCacheAction->fSkipUntilRetried)
487 {
488 // If this action was retried, let's make sure it will not be skipped any longer.
489 if (iRetryAction == i)
490 {
491 pCacheAction->fSkipUntilRetried = FALSE;
492 }
493 else // skip the action.
494 {
495 continue;
496 }
497 }
498
499 switch (pCacheAction->type)
500 {
501 case BURN_CACHE_ACTION_TYPE_CHECKPOINT:
502 dwCheckpoint = pCacheAction->checkpoint.dwId;
503 break;
504
505 case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE:
506 hr = LayoutBundle(pUX, pVariables, hPipe, pCacheAction->bundleLayout.sczExecutableName, pCacheAction->bundleLayout.sczLayoutDirectory, pCacheAction->bundleLayout.sczUnverifiedPath, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal);
507 if (SUCCEEDED(hr))
508 {
509 UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress);
510 ++(*pcOverallProgressTicks);
511
512 hr = ReportOverallProgressTicks(pUX, FALSE, pPlan->cOverallProgressTicksTotal, *pcOverallProgressTicks);
513 if (FAILED(hr))
514 {
515 LogErrorId(hr, MSG_USER_CANCELED, L"layout bundle", NULL, NULL);
516 }
517 }
518 break;
519
520 case BURN_CACHE_ACTION_TYPE_PACKAGE_START:
521 iPackageStartAction = i; // if we retry this package, we'll start here in the plan.
522 iPackageCompleteAction = pCacheAction->packageStart.iPackageCompleteAction; // if we ignore this package, we'll start after the complete action in the plan.
523 pStartedPackage = pCacheAction->packageStart.pPackage;
524
525 hr = UserExperienceOnCachePackageBegin(pUX, pStartedPackage->sczId, pCacheAction->packageStart.cCachePayloads, pCacheAction->packageStart.qwCachePayloadSizeTotal);
526 if (FAILED(hr))
527 {
528 LogErrorId(hr, MSG_USER_CANCELED, L"begin cache package", pStartedPackage->sczId, NULL);
529 }
530 break;
531
532 case BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER:
533 hr = AcquireContainerOrPayload(pUX, pVariables, pCacheAction->resolveContainer.pContainer, NULL, NULL, pCacheAction->resolveContainer.sczUnverifiedPath, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal);
534 if (SUCCEEDED(hr))
535 {
536 UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress);
537 }
538 else
539 {
540 LogErrorId(hr, MSG_FAILED_ACQUIRE_CONTAINER, pCacheAction->resolveContainer.pContainer->sczId, pCacheAction->resolveContainer.sczUnverifiedPath, NULL);
541 }
542 break;
543
544 case BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER:
545 // If this action is to be skipped until the acquire action is not skipped and the other
546 // action is still being skipped then skip this action.
547 if (BURN_PLAN_INVALID_ACTION_INDEX != pCacheAction->extractContainer.iSkipUntilAcquiredByAction && pPlan->rgCacheActions[pCacheAction->extractContainer.iSkipUntilAcquiredByAction].fSkipUntilRetried)
548 {
549 break;
550 }
551
552 hr = ExtractContainer(hSourceEngineFile, pCacheAction->extractContainer.pContainer, pCacheAction->extractContainer.sczContainerUnverifiedPath, pCacheAction->extractContainer.rgPayloads, pCacheAction->extractContainer.cPayloads);
553 if (FAILED(hr))
554 {
555 LogErrorId(hr, MSG_FAILED_EXTRACT_CONTAINER, pCacheAction->extractContainer.pContainer->sczId, pCacheAction->extractContainer.sczContainerUnverifiedPath, NULL);
556 }
557 break;
558
559 case BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER:
560 hr = LayoutOrCacheContainerOrPayload(pUX, hPipe, pCacheAction->layoutContainer.pContainer, pCacheAction->layoutContainer.pPackage, NULL, pPlan->rgContainerProgress[pCacheAction->layoutContainer.iProgress].fCachedDuringApply, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal, pCacheAction->layoutContainer.sczLayoutDirectory, pCacheAction->layoutContainer.sczUnverifiedPath, pCacheAction->layoutContainer.fMove, pCacheAction->layoutContainer.cTryAgainAttempts, &fRetryContainerOrPayload);
561 if (SUCCEEDED(hr))
562 {
563 UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress);
564 }
565 else
566 {
567 LogErrorId(hr, MSG_FAILED_LAYOUT_CONTAINER, pCacheAction->layoutContainer.pContainer->sczId, pCacheAction->layoutContainer.sczLayoutDirectory, pCacheAction->layoutContainer.sczUnverifiedPath);
568
569 if (fRetryContainerOrPayload)
570 {
571 wzRetryId = pCacheAction->layoutContainer.pContainer->sczId;
572 iRetryContainerOrPayloadAction = pCacheAction->layoutContainer.iTryAgainAction;
573
574 ++pCacheAction->layoutContainer.cTryAgainAttempts;
575 }
576 }
577 break;
578
579 case BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD:
580 hr = AcquireContainerOrPayload(pUX, pVariables, NULL, pCacheAction->resolvePayload.pPackage, pCacheAction->resolvePayload.pPayload, pCacheAction->resolvePayload.sczUnverifiedPath, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal);
581 if (SUCCEEDED(hr))
582 {
583 UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress);
584 }
585 else
586 {
587 LogErrorId(hr, MSG_FAILED_ACQUIRE_PAYLOAD, pCacheAction->resolvePayload.pPayload->sczKey, pCacheAction->resolvePayload.sczUnverifiedPath, NULL);
588 }
589 break;
590
591 case BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD:
592 hr = LayoutOrCacheContainerOrPayload(pUX, pCacheAction->cachePayload.pPackage->fPerMachine ? hPipe : INVALID_HANDLE_VALUE, NULL, pCacheAction->cachePayload.pPackage, pCacheAction->cachePayload.pPayload, pPlan->rgPayloadProgress[pCacheAction->cachePayload.iProgress].fCachedDuringApply, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal, NULL, pCacheAction->cachePayload.sczUnverifiedPath, pCacheAction->cachePayload.fMove, pCacheAction->cachePayload.cTryAgainAttempts, &fRetryContainerOrPayload);
593 if (SUCCEEDED(hr))
594 {
595 UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress);
596 }
597 else
598 {
599 LogErrorId(hr, MSG_FAILED_CACHE_PAYLOAD, pCacheAction->cachePayload.pPayload->sczKey, pCacheAction->cachePayload.sczUnverifiedPath, NULL);
600
601 if (fRetryContainerOrPayload)
602 {
603 wzRetryId = pCacheAction->cachePayload.pPayload->sczKey;
604 iRetryContainerOrPayloadAction = pCacheAction->cachePayload.iTryAgainAction;
605
606 ++pCacheAction->cachePayload.cTryAgainAttempts;
607 }
608 }
609 break;
610
611 case BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD:
612 hr = LayoutOrCacheContainerOrPayload(pUX, hPipe, NULL, pCacheAction->layoutPayload.pPackage, pCacheAction->layoutPayload.pPayload, pPlan->rgPayloadProgress[pCacheAction->layoutPayload.iProgress].fCachedDuringApply, qwSuccessfulCachedProgress, pPlan->qwCacheSizeTotal, pCacheAction->layoutPayload.sczLayoutDirectory, pCacheAction->layoutPayload.sczUnverifiedPath, pCacheAction->layoutPayload.fMove, pCacheAction->layoutPayload.cTryAgainAttempts, &fRetryContainerOrPayload);
613 if (SUCCEEDED(hr))
614 {
615 UpdateCacheSuccessProgress(pPlan, pCacheAction, &qwSuccessfulCachedProgress);
616 }
617 else
618 {
619 LogErrorId(hr, MSG_FAILED_LAYOUT_PAYLOAD, pCacheAction->layoutPayload.pPayload->sczKey, pCacheAction->layoutPayload.sczLayoutDirectory, pCacheAction->layoutPayload.sczUnverifiedPath);
620
621 if (fRetryContainerOrPayload)
622 {
623 wzRetryId = pCacheAction->layoutPayload.pPayload->sczKey;
624 iRetryContainerOrPayloadAction = pCacheAction->layoutPayload.iTryAgainAction;
625
626 ++pCacheAction->layoutPayload.cTryAgainAttempts;
627 }
628 }
629 break;
630
631 case BURN_CACHE_ACTION_TYPE_PACKAGE_STOP:
632 AssertSz(pStartedPackage == pCacheAction->packageStop.pPackage, "Expected package started cached to be the same as the package checkpointed.");
633
634 hr = ReportOverallProgressTicks(pUX, FALSE, pPlan->cOverallProgressTicksTotal, *pcOverallProgressTicks + 1);
635 if (FAILED(hr))
636 {
637 LogErrorId(hr, MSG_USER_CANCELED, L"end cache package", NULL, NULL);
638 }
639 else
640 {
641 ++(*pcOverallProgressTicks);
642
643 UserExperienceOnCachePackageComplete(pUX, pStartedPackage->sczId, hr, &cachePackageCompleteAction);
644
645 pStartedPackage->hrCacheResult = hr;
646
647 iPackageStartAction = BURN_PLAN_INVALID_ACTION_INDEX;
648 iPackageCompleteAction = BURN_PLAN_INVALID_ACTION_INDEX;
649 pStartedPackage = NULL;
650 }
651 break;
652
653 case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT:
654 if (!::SetEvent(pCacheAction->syncpoint.hEvent))
655 {
656 ExitWithLastError(hr, "Failed to set syncpoint event.");
657 }
658 break;
659
660 default:
661 AssertSz(FALSE, "Unknown cache action.");
662 break;
663 }
664 }
665
666 if (BURN_PLAN_INVALID_ACTION_INDEX != iRetryContainerOrPayloadAction)
667 {
668 Assert(wzRetryId);
669
670 LogErrorId(hr, MSG_APPLY_RETRYING_PAYLOAD, wzRetryId, NULL, NULL);
671
672 iRetryAction = iRetryContainerOrPayloadAction;
673 fRetry = TRUE;
674 }
675 else if (pStartedPackage)
676 {
677 Assert(BURN_PLAN_INVALID_ACTION_INDEX != iPackageStartAction);
678 Assert(BURN_PLAN_INVALID_ACTION_INDEX != iPackageCompleteAction);
679
680 cachePackageCompleteAction = SUCCEEDED(hr) || pStartedPackage->fVital ? BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_NONE : BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE;
681 UserExperienceOnCachePackageComplete(pUX, pStartedPackage->sczId, hr, &cachePackageCompleteAction);
682 if (BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_RETRY == cachePackageCompleteAction)
683 {
684 LogErrorId(hr, MSG_APPLY_RETRYING_PACKAGE, pStartedPackage->sczId, NULL, NULL);
685
686 iRetryAction = iPackageStartAction;
687 fRetry = TRUE;
688 }
689 else if (BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION_IGNORE == cachePackageCompleteAction && !pStartedPackage->fVital) // ignore non-vital download failures.
690 {
691 LogId(REPORT_STANDARD, MSG_APPLY_CONTINUING_NONVITAL_PACKAGE, pStartedPackage->sczId, hr);
692
693 ++(*pcOverallProgressTicks); // add progress even though we didn't fully cache the package.
694
695 iRetryAction = iPackageCompleteAction + 1;
696 fRetry = TRUE;
697 }
698
699 pStartedPackage->hrCacheResult = hr;
700
701 iPackageStartAction = BURN_PLAN_INVALID_ACTION_INDEX;
702 iPackageCompleteAction = BURN_PLAN_INVALID_ACTION_INDEX;
703 pStartedPackage = NULL;
704 }
705 else
706 {
707 Assert(BURN_PLAN_INVALID_ACTION_INDEX == iPackageStartAction);
708 Assert(BURN_PLAN_INVALID_ACTION_INDEX == iPackageCompleteAction);
709 }
710 } while (fRetry);
711
712LExit:
713 Assert(NULL == pStartedPackage);
714 Assert(BURN_PLAN_INVALID_ACTION_INDEX == iPackageStartAction);
715 Assert(BURN_PLAN_INVALID_ACTION_INDEX == iPackageCompleteAction);
716
717 if (FAILED(hr))
718 {
719 DoRollbackCache(pUX, pPlan, hPipe, dwCheckpoint);
720 *pfRollback = TRUE;
721 }
722
723 // Clean up any remanents in the cache.
724 if (INVALID_HANDLE_VALUE != hPipe)
725 {
726 ElevationCacheCleanup(hPipe);
727 }
728
729 CacheCleanup(FALSE, pPlan->wzBundleId);
730
731 UserExperienceOnCacheComplete(pUX, hr);
732 return hr;
733}
734
735extern "C" HRESULT ApplyExecute(
736 __in BURN_ENGINE_STATE* pEngineState,
737 __in_opt HANDLE hCacheThread,
738 __inout DWORD* pcOverallProgressTicks,
739 __out BOOL* pfKeepRegistration,
740 __out BOOL* pfRollback,
741 __out BOOL* pfSuspend,
742 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
743 )
744{
745 HRESULT hr = S_OK;
746 DWORD dwCheckpoint = 0;
747 BURN_EXECUTE_CONTEXT context = { };
748 BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL;
749 BOOL fSeekNextRollbackBoundary = FALSE;
750 BOOL fInTransaction = FALSE;
751
752 context.pUX = &pEngineState->userExperience;
753 context.cExecutePackagesTotal = pEngineState->plan.cExecutePackagesTotal;
754 context.pcOverallProgressTicks = pcOverallProgressTicks;
755
756 // Send execute begin to BA.
757 hr = UserExperienceOnExecuteBegin(&pEngineState->userExperience, pEngineState->plan.cExecutePackagesTotal);
758 ExitOnRootFailure(hr, "BA aborted execute begin.");
759
760 // Do execute actions.
761 for (DWORD i = 0; i < pEngineState->plan.cExecuteActions; ++i)
762 {
763 BURN_EXECUTE_ACTION* pExecuteAction = &pEngineState->plan.rgExecuteActions[i];
764 if (pExecuteAction->fDeleted)
765 {
766 continue;
767 }
768
769 // Transaction end/start
770 if (BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY == pExecuteAction->type)
771 {
772 // End previous transaction
773 if (fInTransaction)
774 {
775 LogString(REPORT_STANDARD, "Committing MSI transaction\n");
776 hr = DoMsiCommitTransaction(&context, pEngineState);
777 ExitOnFailure(hr, "Failed committing an MSI transaction");
778 fInTransaction = FALSE;
779 }
780
781 // Start New transaction
782 if (!fInTransaction && pExecuteAction->rollbackBoundary.pRollbackBoundary && pExecuteAction->rollbackBoundary.pRollbackBoundary->fTransaction)
783 {
784 // Transactions don't go together with DisableRollback.
785 if (pEngineState->fDisableRollback)
786 {
787 LogString(REPORT_STANDARD, "Ignoring Transaction flag due to DisableRollback flag\n");
788 }
789 else
790 {
791 LogString(REPORT_STANDARD, "Starting a new MSI transaction\n");
792 hr = DoMsiBeginTransaction(&context, pEngineState);
793 ExitOnFailure(hr, "Failed beginning an MSI transaction");
794 fInTransaction = TRUE;
795 }
796 }
797 }
798
799 // If we are seeking the next rollback boundary, skip if this action wasn't it.
800 if (fSeekNextRollbackBoundary)
801 {
802 if (BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY == pExecuteAction->type)
803 {
804 continue;
805 }
806 else
807 {
808 fSeekNextRollbackBoundary = FALSE;
809 }
810 }
811
812 // Execute the action.
813 hr = DoExecuteAction(pEngineState, pExecuteAction, hCacheThread, &context, &pRollbackBoundary, &dwCheckpoint, pfKeepRegistration, pfSuspend, pRestart);
814
815 if (*pfSuspend || BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart)
816 {
817 if (fInTransaction)
818 {
819 hr = E_INVALIDSTATE;
820 LogString(REPORT_ERROR, "Ilegal state: Reboot requested within an MSI transaction. Transaction will rollback.");
821 }
822 else
823 {
824 ExitFunction();
825 }
826 }
827
828 if (FAILED(hr))
829 {
830 // If we failed, but rollback is disabled just bail with our error code.
831 if (pEngineState->fDisableRollback)
832 {
833 *pfRollback = TRUE;
834 break;
835 }
836 else // the action failed, roll back to previous rollback boundary.
837 {
838 HRESULT hrRollback = DoRollbackActions(pEngineState, &context, dwCheckpoint, fInTransaction, pfKeepRegistration, pRestart);
839 UNREFERENCED_PARAMETER(hrRollback);
840 fInTransaction = FALSE;
841
842 // If the rollback boundary is vital, end execution here.
843 if (pRollbackBoundary && pRollbackBoundary->fVital)
844 {
845 *pfRollback = TRUE;
846 break;
847 }
848
849 // Move forward to next rollback boundary.
850 fSeekNextRollbackBoundary = TRUE;
851 }
852 }
853 }
854
855 if (fInTransaction)
856 {
857 LogString(REPORT_STANDARD, "Committing an MSI transaction\n");
858 hr = DoMsiCommitTransaction(&context, pEngineState);
859 ExitOnFailure(hr, "Failed committing an MSI transaction");
860 fInTransaction = FALSE;
861 }
862
863LExit:
864 // Send execute complete to BA.
865 UserExperienceOnExecuteComplete(&pEngineState->userExperience, hr);
866
867 return hr;
868}
869
870extern "C" void ApplyClean(
871 __in BURN_USER_EXPERIENCE* /*pUX*/,
872 __in BURN_PLAN* pPlan,
873 __in HANDLE hPipe
874 )
875{
876 HRESULT hr = S_OK;
877
878 for (DWORD i = 0; i < pPlan->cCleanActions; ++i)
879 {
880 BURN_CLEAN_ACTION* pCleanAction = pPlan->rgCleanActions + i;
881
882 hr = CleanPackage(hPipe, pCleanAction->pPackage);
883 }
884}
885
886
887// internal helper functions
888
889static HRESULT ExecuteDependentRegistrationActions(
890 __in HANDLE hPipe,
891 __in const BURN_REGISTRATION* pRegistration,
892 __in_ecount(cActions) const BURN_DEPENDENT_REGISTRATION_ACTION* rgActions,
893 __in DWORD cActions
894 )
895{
896 HRESULT hr = S_OK;
897
898 for (DWORD iAction = 0; iAction < cActions; ++iAction)
899 {
900 const BURN_DEPENDENT_REGISTRATION_ACTION* pAction = rgActions + iAction;
901
902 if (pRegistration->fPerMachine)
903 {
904 hr = ElevationProcessDependentRegistration(hPipe, pAction);
905 ExitOnFailure(hr, "Failed to execute dependent registration action.");
906 }
907 else
908 {
909 hr = DependencyProcessDependentRegistration(pRegistration, pAction);
910 ExitOnFailure(hr, "Failed to process dependency registration action.");
911 }
912 }
913
914LExit:
915 return hr;
916}
917
918static HRESULT ExtractContainer(
919 __in HANDLE hSourceEngineFile,
920 __in BURN_CONTAINER* pContainer,
921 __in_z LPCWSTR wzContainerPath,
922 __in_ecount(cExtractPayloads) BURN_EXTRACT_PAYLOAD* rgExtractPayloads,
923 __in DWORD cExtractPayloads
924 )
925{
926 HRESULT hr = S_OK;
927 BURN_CONTAINER_CONTEXT context = { };
928 HANDLE hContainerHandle = INVALID_HANDLE_VALUE;
929 LPWSTR sczExtractPayloadId = NULL;
930
931 // If the container is actually attached, then it was planned to be acquired through hSourceEngineFile.
932 if (pContainer->fActuallyAttached)
933 {
934 hContainerHandle = hSourceEngineFile;
935 }
936
937 hr = ContainerOpen(&context, pContainer, hContainerHandle, wzContainerPath);
938 ExitOnFailure(hr, "Failed to open container: %ls.", pContainer->sczId);
939
940 while (S_OK == (hr = ContainerNextStream(&context, &sczExtractPayloadId)))
941 {
942 BOOL fExtracted = FALSE;
943
944 for (DWORD iExtract = 0; iExtract < cExtractPayloads; ++iExtract)
945 {
946 BURN_EXTRACT_PAYLOAD* pExtract = rgExtractPayloads + iExtract;
947 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczExtractPayloadId, -1, pExtract->pPayload->sczSourcePath, -1))
948 {
949 // TODO: Send progress when extracting stream to file.
950 hr = ContainerStreamToFile(&context, pExtract->sczUnverifiedPath);
951 ExitOnFailure(hr, "Failed to extract payload: %ls from container: %ls", sczExtractPayloadId, pContainer->sczId);
952
953 fExtracted = TRUE;
954 break;
955 }
956 }
957
958 if (!fExtracted)
959 {
960 hr = ContainerSkipStream(&context);
961 ExitOnFailure(hr, "Failed to skip the extraction of payload: %ls from container: %ls", sczExtractPayloadId, pContainer->sczId);
962 }
963 }
964
965 if (E_NOMOREITEMS == hr)
966 {
967 hr = S_OK;
968 }
969 ExitOnFailure(hr, "Failed to extract all payloads from container: %ls", pContainer->sczId);
970
971LExit:
972 ReleaseStr(sczExtractPayloadId);
973 ContainerClose(&context);
974
975 return hr;
976}
977
978static void UpdateCacheSuccessProgress(
979 __in BURN_PLAN* pPlan,
980 __in BURN_CACHE_ACTION* pCacheAction,
981 __inout DWORD64* pqwSuccessfulCachedProgress
982 )
983{
984 switch (pCacheAction->type)
985 {
986 case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE:
987 *pqwSuccessfulCachedProgress += pCacheAction->bundleLayout.qwBundleSize;
988 break;
989
990 case BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER:
991 if (!pPlan->rgContainerProgress[pCacheAction->resolveContainer.iProgress].fCachedDuringApply)
992 {
993 pPlan->rgContainerProgress[pCacheAction->resolveContainer.iProgress].fCachedDuringApply = TRUE;
994 *pqwSuccessfulCachedProgress += pCacheAction->resolveContainer.pContainer->qwFileSize;
995 }
996 break;
997
998 case BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER:
999 if (!pPlan->rgContainerProgress[pCacheAction->layoutContainer.iProgress].fCachedDuringApply)
1000 {
1001 pPlan->rgContainerProgress[pCacheAction->layoutContainer.iProgress].fCachedDuringApply = TRUE;
1002 *pqwSuccessfulCachedProgress += pCacheAction->layoutContainer.pContainer->qwFileSize;
1003 }
1004 break;
1005
1006 case BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD:
1007 if (!pPlan->rgPayloadProgress[pCacheAction->resolvePayload.iProgress].fCachedDuringApply)
1008 {
1009 pPlan->rgPayloadProgress[pCacheAction->resolvePayload.iProgress].fCachedDuringApply = TRUE;
1010 *pqwSuccessfulCachedProgress += pCacheAction->resolvePayload.pPayload->qwFileSize;
1011 }
1012 break;
1013
1014 case BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD:
1015 if (!pPlan->rgPayloadProgress[pCacheAction->cachePayload.iProgress].fCachedDuringApply)
1016 {
1017 pPlan->rgPayloadProgress[pCacheAction->cachePayload.iProgress].fCachedDuringApply = TRUE;
1018 *pqwSuccessfulCachedProgress += pCacheAction->cachePayload.pPayload->qwFileSize;
1019 }
1020 break;
1021
1022 case BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD:
1023 if (!pPlan->rgPayloadProgress[pCacheAction->layoutPayload.iProgress].fCachedDuringApply)
1024 {
1025 pPlan->rgPayloadProgress[pCacheAction->layoutPayload.iProgress].fCachedDuringApply = TRUE;
1026 *pqwSuccessfulCachedProgress += pCacheAction->layoutPayload.pPayload->qwFileSize;
1027 }
1028 break;
1029
1030 default:
1031 AssertSz(FALSE, "Unexpected cache action type.");
1032 break;
1033 }
1034}
1035
1036static HRESULT LayoutBundle(
1037 __in BURN_USER_EXPERIENCE* pUX,
1038 __in BURN_VARIABLES* pVariables,
1039 __in HANDLE hPipe,
1040 __in_z LPCWSTR wzExecutableName,
1041 __in_z LPCWSTR wzLayoutDirectory,
1042 __in_z LPCWSTR wzUnverifiedPath,
1043 __in DWORD64 qwSuccessfulCacheProgress,
1044 __in DWORD64 qwTotalCacheSize
1045 )
1046{
1047 HRESULT hr = S_OK;
1048 LPWSTR sczBundlePath = NULL;
1049 LPWSTR sczDestinationPath = NULL;
1050 int nEquivalentPaths = 0;
1051 BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { };
1052 BOOL fRetry = FALSE;
1053 BOOL fRetryAcquire = FALSE;
1054
1055 hr = VariableGetString(pVariables, BURN_BUNDLE_SOURCE_PROCESS_PATH, &sczBundlePath);
1056 if (FAILED(hr))
1057 {
1058 if (E_NOTFOUND != hr)
1059 {
1060 ExitOnFailure(hr, "Failed to get path to bundle source process path to layout.");
1061 }
1062
1063 hr = PathForCurrentProcess(&sczBundlePath, NULL);
1064 ExitOnFailure(hr, "Failed to get path to bundle to layout.");
1065 }
1066
1067 hr = PathConcat(wzLayoutDirectory, wzExecutableName, &sczDestinationPath);
1068 ExitOnFailure(hr, "Failed to concat layout path for bundle.");
1069
1070 // If the destination path is the currently running bundle, bail.
1071 hr = PathCompare(sczBundlePath, sczDestinationPath, &nEquivalentPaths);
1072 ExitOnFailure(hr, "Failed to determine if layout bundle path was equivalent with current process path.");
1073
1074 if (CSTR_EQUAL == nEquivalentPaths)
1075 {
1076 ExitFunction1(hr = S_OK);
1077 }
1078
1079 progress.pUX = pUX;
1080 progress.qwCacheProgress = qwSuccessfulCacheProgress;
1081 progress.qwTotalCacheSize = qwTotalCacheSize;
1082
1083 do
1084 {
1085 hr = S_OK;
1086 fRetry = FALSE;
1087
1088 for (;;)
1089 {
1090 fRetryAcquire = FALSE;
1091 progress.fCancel = FALSE;
1092
1093 hr = UserExperienceOnCacheAcquireBegin(pUX, NULL, NULL, BOOTSTRAPPER_CACHE_OPERATION_COPY, sczBundlePath);
1094 ExitOnRootFailure(hr, "BA aborted cache acquire begin.");
1095
1096 hr = CopyPayload(&progress, sczBundlePath, wzUnverifiedPath);
1097 // Error handling happens after sending complete message to BA.
1098
1099 UserExperienceOnCacheAcquireComplete(pUX, NULL, NULL, hr, &fRetryAcquire);
1100 if (fRetryAcquire)
1101 {
1102 continue;
1103 }
1104
1105 ExitOnFailure(hr, "Failed to copy bundle from: '%ls' to: '%ls'", sczBundlePath, wzUnverifiedPath);
1106 break;
1107 }
1108
1109 do
1110 {
1111 hr = UserExperienceOnCacheVerifyBegin(pUX, NULL, NULL);
1112 ExitOnRootFailure(hr, "BA aborted cache verify begin.");
1113
1114 if (INVALID_HANDLE_VALUE != hPipe)
1115 {
1116 hr = ElevationLayoutBundle(hPipe, wzLayoutDirectory, wzUnverifiedPath);
1117 }
1118 else
1119 {
1120 hr = CacheLayoutBundle(wzExecutableName, wzLayoutDirectory, wzUnverifiedPath);
1121 }
1122
1123 BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE;
1124 UserExperienceOnCacheVerifyComplete(pUX, NULL, NULL, hr, &action);
1125 if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYVERIFICATION == action)
1126 {
1127 hr = S_FALSE; // retry verify.
1128 }
1129 else if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION == action)
1130 {
1131 fRetry = TRUE; // go back and retry acquire.
1132 }
1133 } while (S_FALSE == hr);
1134 } while (fRetry);
1135 LogExitOnFailure(hr, MSG_FAILED_LAYOUT_BUNDLE, "Failed to layout bundle: %ls to layout directory: %ls", sczBundlePath, wzLayoutDirectory);
1136
1137LExit:
1138 ReleaseStr(sczDestinationPath);
1139 ReleaseStr(sczBundlePath);
1140
1141 return hr;
1142}
1143
1144static HRESULT AcquireContainerOrPayload(
1145 __in BURN_USER_EXPERIENCE* pUX,
1146 __in BURN_VARIABLES* pVariables,
1147 __in_opt BURN_CONTAINER* pContainer,
1148 __in_opt BURN_PACKAGE* pPackage,
1149 __in_opt BURN_PAYLOAD* pPayload,
1150 __in LPCWSTR wzDestinationPath,
1151 __in DWORD64 qwSuccessfulCacheProgress,
1152 __in DWORD64 qwTotalCacheSize
1153 )
1154{
1155 AssertSz(pContainer || pPayload, "Must provide a container or a payload.");
1156
1157 HRESULT hr = S_OK;
1158 int nEquivalentPaths = 0;
1159 LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : NULL;
1160 LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : NULL;
1161 LPCWSTR wzRelativePath = pContainer ? pContainer->sczFilePath : pPayload->sczFilePath;
1162 LPWSTR sczSourceFullPath = NULL;
1163 BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { };
1164 BOOL fRetry = FALSE;
1165
1166 progress.pContainer = pContainer;
1167 progress.pPackage = pPackage;
1168 progress.pPayload = pPayload;
1169 progress.pUX = pUX;
1170 progress.qwCacheProgress = qwSuccessfulCacheProgress;
1171 progress.qwTotalCacheSize = qwTotalCacheSize;
1172
1173 do
1174 {
1175 LPCWSTR wzDownloadUrl = pContainer ? pContainer->downloadSource.sczUrl : pPayload->downloadSource.sczUrl;
1176 LPCWSTR wzSourcePath = pContainer ? pContainer->sczSourcePath : pPayload->sczSourcePath;
1177
1178 BOOL fFoundLocal = FALSE;
1179 BOOL fCopy = FALSE;
1180 BOOL fDownload = FALSE;
1181
1182 fRetry = FALSE;
1183 progress.fCancel = FALSE;
1184
1185 hr = CacheFindLocalSource(wzSourcePath, pVariables, &fFoundLocal, &sczSourceFullPath);
1186 ExitOnFailure(hr, "Failed to search local source.");
1187
1188 if (fFoundLocal) // the file exists locally, so copy it.
1189 {
1190 // If the source path and destination path are different, do the copy (otherwise there's no point).
1191 hr = PathCompare(sczSourceFullPath, wzDestinationPath, &nEquivalentPaths);
1192 ExitOnFailure(hr, "Failed to determine if payload source path was equivalent to the destination path.");
1193
1194 fCopy = (CSTR_EQUAL != nEquivalentPaths);
1195 }
1196 else // can't find the file locally, so prompt for source.
1197 {
1198 DWORD dwLogId = pContainer ? (wzPayloadId ? MSG_PROMPT_CONTAINER_PAYLOAD_SOURCE : MSG_PROMPT_CONTAINER_SOURCE) : pPackage ? MSG_PROMPT_PACKAGE_PAYLOAD_SOURCE : MSG_PROMPT_BUNDLE_PAYLOAD_SOURCE;
1199 LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId ? wzPackageOrContainerId : L"", wzPayloadId ? wzPayloadId : L"", sczSourceFullPath);
1200
1201 hr = PromptForSource(pUX, wzPackageOrContainerId, wzPayloadId, sczSourceFullPath, wzDownloadUrl, &fRetry, &fDownload);
1202
1203 // If the BA requested download then ensure a download url is available (it may have been set
1204 // during PromptForSource so we need to check again).
1205 if (fDownload)
1206 {
1207 wzDownloadUrl = pContainer ? pContainer->downloadSource.sczUrl : pPayload->downloadSource.sczUrl;
1208 if (!wzDownloadUrl || !*wzDownloadUrl)
1209 {
1210 hr = E_INVALIDARG;
1211 }
1212 }
1213
1214 // Log the error
1215 LogExitOnFailure(hr, MSG_PAYLOAD_FILE_NOT_PRESENT, "Failed while prompting for source (original path '%ls').", sczSourceFullPath);
1216 }
1217
1218 if (fCopy)
1219 {
1220 hr = UserExperienceOnCacheAcquireBegin(pUX, wzPackageOrContainerId, wzPayloadId, BOOTSTRAPPER_CACHE_OPERATION_COPY, sczSourceFullPath);
1221 ExitOnRootFailure(hr, "BA aborted cache acquire begin.");
1222
1223 hr = CopyPayload(&progress, sczSourceFullPath, wzDestinationPath);
1224 // Error handling happens after sending complete message to BA.
1225
1226 // We successfully copied from a source location, set that as the last used source.
1227 if (SUCCEEDED(hr))
1228 {
1229 CacheSetLastUsedSource(pVariables, sczSourceFullPath, wzRelativePath);
1230 }
1231 }
1232 else if (fDownload)
1233 {
1234 hr = UserExperienceOnCacheAcquireBegin(pUX, wzPackageOrContainerId, wzPayloadId, BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD, wzDownloadUrl);
1235 ExitOnRootFailure(hr, "BA aborted cache download payload begin.");
1236
1237 hr = DownloadPayload(&progress, wzDestinationPath);
1238 // Error handling happens after sending complete message to BA.
1239 }
1240
1241 if (fCopy || fDownload)
1242 {
1243 UserExperienceOnCacheAcquireComplete(pUX, wzPackageOrContainerId, wzPayloadId, hr, &fRetry);
1244 if (fRetry)
1245 {
1246 hr = S_OK;
1247 }
1248 }
1249 ExitOnFailure(hr, "Failed to acquire payload from: '%ls' to working path: '%ls'", fCopy ? sczSourceFullPath : wzDownloadUrl, wzDestinationPath);
1250 } while (fRetry);
1251 ExitOnFailure(hr, "Failed to find external payload to cache.");
1252
1253LExit:
1254 ReleaseStr(sczSourceFullPath);
1255
1256 return hr;
1257}
1258
1259static HRESULT LayoutOrCacheContainerOrPayload(
1260 __in BURN_USER_EXPERIENCE* pUX,
1261 __in HANDLE hPipe,
1262 __in_opt BURN_CONTAINER* pContainer,
1263 __in_opt BURN_PACKAGE* pPackage,
1264 __in_opt BURN_PAYLOAD* pPayload,
1265 __in BOOL fAlreadyProvidedProgress,
1266 __in DWORD64 qwSuccessfulCachedProgress,
1267 __in DWORD64 qwTotalCacheSize,
1268 __in_z_opt LPCWSTR wzLayoutDirectory,
1269 __in_z LPCWSTR wzUnverifiedPath,
1270 __in BOOL fMove,
1271 __in DWORD cTryAgainAttempts,
1272 __out BOOL* pfRetry
1273 )
1274{
1275 HRESULT hr = S_OK;
1276 LPCWSTR wzPackageOrContainerId = pContainer ? pContainer->sczId : pPackage ? pPackage->sczId : L"";
1277 LPCWSTR wzPayloadId = pPayload ? pPayload->sczKey : L"";
1278 LARGE_INTEGER liContainerOrPayloadSize = { };
1279 LARGE_INTEGER liZero = { };
1280 BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT progress = { };
1281
1282 liContainerOrPayloadSize.QuadPart = pContainer ? pContainer->qwFileSize : pPayload->qwFileSize;
1283
1284 progress.pContainer = pContainer;
1285 progress.pPackage = pPackage;
1286 progress.pPayload = pPayload;
1287 progress.pUX = pUX;
1288 progress.qwTotalCacheSize = qwTotalCacheSize;
1289 if (fAlreadyProvidedProgress)
1290 {
1291 Assert(qwSuccessfulCachedProgress >= static_cast<DWORD64>(liContainerOrPayloadSize.QuadPart));
1292 progress.qwCacheProgress = qwSuccessfulCachedProgress - liContainerOrPayloadSize.QuadPart; // remove the payload size, since it was marked successful thus included in the successful size already.
1293 }
1294 else
1295 {
1296 progress.qwCacheProgress = qwSuccessfulCachedProgress;
1297 }
1298
1299 *pfRetry = FALSE;
1300
1301 do
1302 {
1303 hr = UserExperienceOnCacheVerifyBegin(pUX, wzPackageOrContainerId, wzPayloadId);
1304 ExitOnRootFailure(hr, "BA aborted cache verify begin.");
1305
1306 if (INVALID_HANDLE_VALUE != hPipe) // pass the decision off to the elevated process.
1307 {
1308 hr = ElevationCacheOrLayoutContainerOrPayload(hPipe, pContainer, pPackage, pPayload, wzLayoutDirectory, wzUnverifiedPath, fMove);
1309 }
1310 else if (wzLayoutDirectory) // layout the container or payload.
1311 {
1312 if (pContainer)
1313 {
1314 hr = CacheLayoutContainer(pContainer, wzLayoutDirectory, wzUnverifiedPath, fMove);
1315 }
1316 else
1317 {
1318 hr = CacheLayoutPayload(pPayload, wzLayoutDirectory, wzUnverifiedPath, fMove);
1319 }
1320 }
1321 else // complete the payload.
1322 {
1323 Assert(!pContainer);
1324 Assert(pPackage);
1325
1326 hr = CacheCompletePayload(pPackage->fPerMachine, pPayload, pPackage->sczCacheId, wzUnverifiedPath, fMove);
1327 }
1328
1329 // If succeeded, send 100% complete here. If the payload was already cached this is the first progress the BA
1330 // will get.
1331 if (SUCCEEDED(hr))
1332 {
1333 CacheProgressRoutine(liContainerOrPayloadSize, liContainerOrPayloadSize, liZero, liZero, 0, 0, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, &progress);
1334 if (progress.fCancel)
1335 {
1336 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1337 }
1338 else if (progress.fError)
1339 {
1340 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE);
1341 }
1342 ExitOnRootFailure(hr, "BA aborted verify of %hs: %ls", pContainer ? "container" : "payload", pContainer ? wzPackageOrContainerId : wzPayloadId);
1343 }
1344
1345 BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION action = FAILED(hr) && cTryAgainAttempts < BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS ? BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION : BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE;
1346 UserExperienceOnCacheVerifyComplete(pUX, wzPackageOrContainerId, wzPayloadId, hr, &action);
1347 if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYVERIFICATION == action)
1348 {
1349 hr = S_FALSE; // retry verify.
1350 }
1351 else if (BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_RETRYACQUISITION == action)
1352 {
1353 *pfRetry = TRUE; // go back and retry acquire.
1354 }
1355 } while (S_FALSE == hr);
1356
1357LExit:
1358 return hr;
1359}
1360
1361static HRESULT PromptForSource(
1362 __in BURN_USER_EXPERIENCE* pUX,
1363 __in_z LPCWSTR wzPackageOrContainerId,
1364 __in_z_opt LPCWSTR wzPayloadId,
1365 __in_z LPCWSTR wzLocalSource,
1366 __in_z_opt LPCWSTR wzDownloadSource,
1367 __inout BOOL* pfRetry,
1368 __inout BOOL* pfDownload
1369 )
1370{
1371 HRESULT hr = S_OK;
1372 BOOTSTRAPPER_RESOLVESOURCE_ACTION action = BOOTSTRAPPER_RESOLVESOURCE_ACTION_NONE;
1373
1374 UserExperienceDeactivateEngine(pUX);
1375
1376 hr = UserExperienceOnResolveSource(pUX, wzPackageOrContainerId, wzPayloadId, wzLocalSource, wzDownloadSource, &action);
1377 if (FAILED(hr))
1378 {
1379 ExitFunction();
1380 }
1381
1382 switch (action)
1383 {
1384 case BOOTSTRAPPER_RESOLVESOURCE_ACTION_NONE:
1385 hr = E_FILENOTFOUND;
1386 break;
1387
1388 case BOOTSTRAPPER_RESOLVESOURCE_ACTION_RETRY:
1389 *pfRetry = TRUE;
1390 break;
1391
1392 case BOOTSTRAPPER_RESOLVESOURCE_ACTION_DOWNLOAD:
1393 *pfDownload = TRUE;
1394 break;
1395
1396 default:
1397 hr = E_INVALIDARG;
1398 break;
1399 }
1400
1401LExit:
1402 UserExperienceActivateEngine(pUX, NULL);
1403 return hr;
1404}
1405
1406static HRESULT CopyPayload(
1407 __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress,
1408 __in_z LPCWSTR wzSourcePath,
1409 __in_z LPCWSTR wzDestinationPath
1410 )
1411{
1412 HRESULT hr = S_OK;
1413 DWORD dwFileAttributes = 0;
1414 LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : L"";
1415 LPCWSTR wzPayloadId = pProgress->pPayload ? pProgress->pPayload->sczKey : L"";
1416
1417 DWORD dwLogId = pProgress->pContainer ? (pProgress->pPayload ? MSG_ACQUIRE_CONTAINER_PAYLOAD : MSG_ACQUIRE_CONTAINER) : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD;
1418 LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "copy", wzSourcePath);
1419
1420 // If the destination file already exists, clear the readonly bit to avoid E_ACCESSDENIED.
1421 if (FileExistsEx(wzDestinationPath, &dwFileAttributes))
1422 {
1423 if (FILE_ATTRIBUTE_READONLY & dwFileAttributes)
1424 {
1425 dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY;
1426 if (!::SetFileAttributes(wzDestinationPath, dwFileAttributes))
1427 {
1428 ExitWithLastError(hr, "Failed to clear readonly bit on payload destination path: %ls", wzDestinationPath);
1429 }
1430 }
1431 }
1432
1433 if (!::CopyFileExW(wzSourcePath, wzDestinationPath, CacheProgressRoutine, pProgress, &pProgress->fCancel, 0))
1434 {
1435 if (pProgress->fCancel)
1436 {
1437 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1438 ExitOnRootFailure(hr, "BA aborted copy of payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath);
1439 }
1440 else
1441 {
1442 ExitWithLastError(hr, "Failed attempt to copy payload from: '%ls' to: %ls.", wzSourcePath, wzDestinationPath);
1443 }
1444 }
1445
1446LExit:
1447 return hr;
1448}
1449
1450static HRESULT DownloadPayload(
1451 __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress,
1452 __in_z LPCWSTR wzDestinationPath
1453 )
1454{
1455 HRESULT hr = S_OK;
1456 DWORD dwFileAttributes = 0;
1457 LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : L"";
1458 LPCWSTR wzPayloadId = pProgress->pPayload ? pProgress->pPayload->sczKey : L"";
1459 DOWNLOAD_SOURCE* pDownloadSource = pProgress->pContainer ? &pProgress->pContainer->downloadSource : &pProgress->pPayload->downloadSource;
1460 DWORD64 qwDownloadSize = pProgress->pContainer ? pProgress->pContainer->qwFileSize : pProgress->pPayload->qwFileSize;
1461 DOWNLOAD_CACHE_CALLBACK cacheCallback = { };
1462 DOWNLOAD_AUTHENTICATION_CALLBACK authenticationCallback = { };
1463 APPLY_AUTHENTICATION_REQUIRED_DATA authenticationData = { };
1464
1465 DWORD dwLogId = pProgress->pContainer ? (pProgress->pPayload ? MSG_ACQUIRE_CONTAINER_PAYLOAD : MSG_ACQUIRE_CONTAINER) : pProgress->pPackage ? MSG_ACQUIRE_PACKAGE_PAYLOAD : MSG_ACQUIRE_BUNDLE_PAYLOAD;
1466 LogId(REPORT_STANDARD, dwLogId, wzPackageOrContainerId, wzPayloadId, "download", pDownloadSource->sczUrl);
1467
1468 // If the destination file already exists, clear the readonly bit to avoid E_ACCESSDENIED.
1469 if (FileExistsEx(wzDestinationPath, &dwFileAttributes))
1470 {
1471 if (FILE_ATTRIBUTE_READONLY & dwFileAttributes)
1472 {
1473 dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY;
1474 if (!::SetFileAttributes(wzDestinationPath, dwFileAttributes))
1475 {
1476 ExitWithLastError(hr, "Failed to clear readonly bit on payload destination path: %ls", wzDestinationPath);
1477 }
1478 }
1479 }
1480
1481 cacheCallback.pfnProgress = CacheProgressRoutine;
1482 cacheCallback.pfnCancel = NULL; // TODO: set this
1483 cacheCallback.pv = pProgress;
1484
1485 // If the protocol is specially marked, "bits" let's use that.
1486 if (L'b' == pDownloadSource->sczUrl[0] &&
1487 L'i' == pDownloadSource->sczUrl[1] &&
1488 L't' == pDownloadSource->sczUrl[2] &&
1489 L's' == pDownloadSource->sczUrl[3] &&
1490 (L':' == pDownloadSource->sczUrl[4] || (L's' == pDownloadSource->sczUrl[4] && L':' == pDownloadSource->sczUrl[5]))
1491 )
1492 {
1493 hr = BitsDownloadUrl(&cacheCallback, pDownloadSource, wzDestinationPath);
1494 }
1495 else // wininet handles everything else.
1496 {
1497 authenticationData.pUX = pProgress->pUX;
1498 authenticationData.wzPackageOrContainerId = wzPackageOrContainerId;
1499 authenticationData.wzPayloadId = wzPayloadId;
1500 authenticationCallback.pv = static_cast<LPVOID>(&authenticationData);
1501 authenticationCallback.pfnAuthenticate = &AuthenticationRequired;
1502
1503 hr = DownloadUrl(pDownloadSource, qwDownloadSize, wzDestinationPath, &cacheCallback, &authenticationCallback);
1504 }
1505 ExitOnFailure(hr, "Failed attempt to download URL: '%ls' to: '%ls'", pDownloadSource->sczUrl, wzDestinationPath);
1506
1507LExit:
1508 return hr;
1509}
1510
1511static HRESULT WINAPI AuthenticationRequired(
1512 __in LPVOID pData,
1513 __in HINTERNET hUrl,
1514 __in long lHttpCode,
1515 __out BOOL* pfRetrySend,
1516 __out BOOL* pfRetry
1517 )
1518{
1519 Assert(401 == lHttpCode || 407 == lHttpCode);
1520
1521 HRESULT hr = S_OK;
1522 DWORD er = ERROR_SUCCESS;
1523 BOOTSTRAPPER_ERROR_TYPE errorType = (401 == lHttpCode) ? BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER : BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY;
1524 LPWSTR sczError = NULL;
1525 int nResult = IDNOACTION;
1526
1527 *pfRetrySend = FALSE;
1528 *pfRetry = FALSE;
1529
1530 hr = StrAllocFromError(&sczError, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), NULL);
1531 ExitOnFailure(hr, "Failed to allocation error string.");
1532
1533 APPLY_AUTHENTICATION_REQUIRED_DATA* authenticationData = reinterpret_cast<APPLY_AUTHENTICATION_REQUIRED_DATA*>(pData);
1534
1535 UserExperienceOnError(authenticationData->pUX, errorType, authenticationData->wzPackageOrContainerId, ERROR_ACCESS_DENIED, sczError, MB_RETRYTRYAGAIN, 0, NULL, &nResult); // ignore return value;
1536 nResult = UserExperienceCheckExecuteResult(authenticationData->pUX, FALSE, MB_RETRYTRYAGAIN, nResult);
1537 if (IDTRYAGAIN == nResult && authenticationData->pUX->hwndApply)
1538 {
1539 er = ::InternetErrorDlg(authenticationData->pUX->hwndApply, hUrl, ERROR_INTERNET_INCORRECT_PASSWORD, FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, NULL);
1540 if (ERROR_SUCCESS == er || ERROR_CANCELLED == er)
1541 {
1542 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1543 }
1544 else if (ERROR_INTERNET_FORCE_RETRY == er)
1545 {
1546 *pfRetrySend = TRUE;
1547 hr = S_OK;
1548 }
1549 else
1550 {
1551 hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
1552 }
1553 }
1554 else if (IDRETRY == nResult)
1555 {
1556 *pfRetry = TRUE;
1557 hr = S_OK;
1558 }
1559 else
1560 {
1561 hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
1562 }
1563
1564LExit:
1565 ReleaseStr(sczError);
1566
1567 return hr;
1568}
1569
1570static DWORD CALLBACK CacheProgressRoutine(
1571 __in LARGE_INTEGER TotalFileSize,
1572 __in LARGE_INTEGER TotalBytesTransferred,
1573 __in LARGE_INTEGER /*StreamSize*/,
1574 __in LARGE_INTEGER /*StreamBytesTransferred*/,
1575 __in DWORD /*dwStreamNumber*/,
1576 __in DWORD /*dwCallbackReason*/,
1577 __in HANDLE /*hSourceFile*/,
1578 __in HANDLE /*hDestinationFile*/,
1579 __in_opt LPVOID lpData
1580 )
1581{
1582 HRESULT hr = S_OK;
1583 DWORD dwResult = PROGRESS_CONTINUE;
1584 BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress = static_cast<BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT*>(lpData);
1585 LPCWSTR wzPackageOrContainerId = pProgress->pContainer ? pProgress->pContainer->sczId : pProgress->pPackage ? pProgress->pPackage->sczId : NULL;
1586 LPCWSTR wzPayloadId = pProgress->pPayload ? pProgress->pPayload->sczKey : NULL;
1587 DWORD64 qwCacheProgress = pProgress->qwCacheProgress + TotalBytesTransferred.QuadPart;
1588 if (qwCacheProgress > pProgress->qwTotalCacheSize)
1589 {
1590 AssertSz(FALSE, "Apply has cached more than Plan envisioned.");
1591 qwCacheProgress = pProgress->qwTotalCacheSize;
1592 }
1593 DWORD dwOverallPercentage = pProgress->qwTotalCacheSize ? static_cast<DWORD>(qwCacheProgress * 100 / pProgress->qwTotalCacheSize) : 0;
1594
1595 hr = UserExperienceOnCacheAcquireProgress(pProgress->pUX, wzPackageOrContainerId, wzPayloadId, TotalBytesTransferred.QuadPart, TotalFileSize.QuadPart, dwOverallPercentage);
1596 if (HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hr)
1597 {
1598 dwResult = PROGRESS_CANCEL;
1599 pProgress->fCancel = TRUE;
1600 }
1601 else if (FAILED(hr))
1602 {
1603 dwResult = PROGRESS_CANCEL;
1604 pProgress->fError = TRUE;
1605 }
1606 else
1607 {
1608 dwResult = PROGRESS_CONTINUE;
1609 }
1610
1611 return dwResult;
1612}
1613
1614static void DoRollbackCache(
1615 __in BURN_USER_EXPERIENCE* /*pUX*/,
1616 __in BURN_PLAN* pPlan,
1617 __in HANDLE hPipe,
1618 __in DWORD dwCheckpoint
1619 )
1620{
1621 HRESULT hr = S_OK;
1622 DWORD iCheckpoint = 0;
1623
1624 // Scan to last checkpoint.
1625 for (DWORD i = 0; i < pPlan->cRollbackCacheActions; ++i)
1626 {
1627 BURN_CACHE_ACTION* pRollbackCacheAction = &pPlan->rgRollbackCacheActions[i];
1628
1629 if (BURN_CACHE_ACTION_TYPE_CHECKPOINT == pRollbackCacheAction->type && pRollbackCacheAction->checkpoint.dwId == dwCheckpoint)
1630 {
1631 iCheckpoint = i;
1632 break;
1633 }
1634 }
1635
1636 // Rollback cache actions.
1637 if (iCheckpoint)
1638 {
1639 // i has to be a signed integer so it doesn't get decremented to 0xFFFFFFFF.
1640 for (int i = iCheckpoint - 1; i >= 0; --i)
1641 {
1642 BURN_CACHE_ACTION* pRollbackCacheAction = &pPlan->rgRollbackCacheActions[i];
1643
1644 switch (pRollbackCacheAction->type)
1645 {
1646 case BURN_CACHE_ACTION_TYPE_CHECKPOINT:
1647 break;
1648
1649 case BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE:
1650 hr = CleanPackage(hPipe, pRollbackCacheAction->rollbackPackage.pPackage);
1651 break;
1652
1653 default:
1654 AssertSz(FALSE, "Invalid rollback cache action.");
1655 break;
1656 }
1657 }
1658 }
1659}
1660
1661/* MSI Transactions:
1662 * All MSI/MSP/MSU packages wrapped in MsiBeginTranasaction-MsiEndTransaction pair are installed or uninstalled together.
1663*/
1664static HRESULT ExecuteMsiBeginTransaction(
1665 __in BURN_EXECUTE_CONTEXT* pContext
1666 , __in BURN_ENGINE_STATE* pEngineState
1667)
1668{
1669 HRESULT hr = S_OK;
1670 UINT uResult = ERROR_SUCCESS;
1671
1672 // Per user/machine context
1673 if (pEngineState->plan.fPerMachine)
1674 {
1675 hr = ElevationMsiBeginTransaction(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pContext);
1676 ExitOnFailure(hr, "Failed to begin an MSI transaction.");
1677 }
1678 else
1679 {
1680 MSIHANDLE hMsiTrns = NULL;
1681 HANDLE hMsiTrnsEvent = NULL;
1682 uResult = MsiBeginTransaction(L"WiX", 0, &hMsiTrns, &hMsiTrnsEvent);
1683 ExitOnWin32Error(uResult, hr, "Failed beginning an MSI transaction");
1684 }
1685
1686LExit:
1687 return hr;
1688}
1689
1690static HRESULT ExecuteMsiCommitTransaction(
1691 __in BURN_EXECUTE_CONTEXT* pContext
1692 , __in BURN_ENGINE_STATE* pEngineState
1693)
1694{
1695 HRESULT hr = S_OK;
1696 UINT uResult = ERROR_SUCCESS;
1697
1698 // Per user/machine context
1699 if (pEngineState->plan.fPerMachine)
1700 {
1701 hr = ElevationMsiCommitTransaction(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pContext);
1702 ExitOnFailure(hr, "Failed to commit an MSI transaction.");
1703 }
1704 else
1705 {
1706 uResult = MsiEndTransaction(MSITRANSACTIONSTATE_COMMIT);
1707 ExitOnWin32Error(uResult, hr, "Failed beginning an MSI transaction");
1708 }
1709
1710LExit:
1711 return hr;
1712}
1713
1714static HRESULT ExecuteMsiRollbackTransaction(
1715 __in BURN_EXECUTE_CONTEXT* pContext
1716 , __in BURN_ENGINE_STATE* pEngineState
1717)
1718{
1719 HRESULT hr = S_OK;
1720 UINT uResult = ERROR_SUCCESS;
1721
1722 // Per user/machine context
1723 if (pEngineState->plan.fPerMachine)
1724 {
1725 hr = ElevationMsiRollbackTransaction(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pContext);
1726 ExitOnFailure(hr, "Failed to rollback an MSI transaction.");
1727 }
1728 else
1729 {
1730 uResult = MsiEndTransaction(MSITRANSACTIONSTATE_ROLLBACK);
1731 ExitOnWin32Error(uResult, hr, "Failed beginning an MSI transaction");
1732 }
1733
1734LExit:
1735 return hr;
1736}
1737
1738// Currently, supporting only elevated transactions.
1739static HRESULT DoMsiBeginTransaction(
1740 __in BURN_EXECUTE_CONTEXT *pContext
1741 , __in BURN_ENGINE_STATE* pEngineState
1742)
1743{
1744 HRESULT hr = S_OK;
1745
1746 hr = ExecuteMsiBeginTransaction(pContext, pEngineState);
1747 ExitOnFailure(hr, "Failed to execute EXE package.");
1748
1749LExit:
1750 return hr;
1751}
1752
1753static HRESULT DoMsiCommitTransaction(
1754 __in BURN_EXECUTE_CONTEXT *pContext
1755 , __in BURN_ENGINE_STATE* pEngineState
1756)
1757{
1758 HRESULT hr = S_OK;
1759
1760 hr = ExecuteMsiCommitTransaction(pContext, pEngineState);
1761 ExitOnFailure(hr, "Failed to execute EXE package.");
1762
1763LExit:
1764 return hr;
1765}
1766
1767static HRESULT DoMsiRollbackTransaction(
1768 __in BURN_EXECUTE_CONTEXT *pContext
1769 , __in BURN_ENGINE_STATE* pEngineState
1770)
1771{
1772 HRESULT hr = S_OK;
1773
1774 hr = ExecuteMsiRollbackTransaction(pContext, pEngineState);
1775 ExitOnFailure(hr, "Failed to execute EXE package.");
1776
1777LExit:
1778 return hr;
1779}
1780
1781
1782static HRESULT DoExecuteAction(
1783 __in BURN_ENGINE_STATE* pEngineState,
1784 __in BURN_EXECUTE_ACTION* pExecuteAction,
1785 __in_opt HANDLE hCacheThread,
1786 __in BURN_EXECUTE_CONTEXT* pContext,
1787 __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary,
1788 __out DWORD* pdwCheckpoint,
1789 __out BOOL* pfKeepRegistration,
1790 __out BOOL* pfSuspend,
1791 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
1792 )
1793{
1794 Assert(!pExecuteAction->fDeleted);
1795
1796 HRESULT hr = S_OK;
1797 HANDLE rghWait[2] = { };
1798 BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
1799 BOOL fRetry = FALSE;
1800 BOOL fStopWusaService = FALSE;
1801
1802 pContext->fRollback = FALSE;
1803
1804 do
1805 {
1806 switch (pExecuteAction->type)
1807 {
1808 case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT:
1809 *pdwCheckpoint = pExecuteAction->checkpoint.dwId;
1810 break;
1811
1812 case BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT:
1813 // wait for cache sync-point
1814 rghWait[0] = pExecuteAction->syncpoint.hEvent;
1815 rghWait[1] = hCacheThread;
1816 switch (::WaitForMultipleObjects(rghWait[1] ? 2 : 1, rghWait, FALSE, INFINITE))
1817 {
1818 case WAIT_OBJECT_0:
1819 break;
1820
1821 case WAIT_OBJECT_0 + 1:
1822 if (!::GetExitCodeThread(hCacheThread, (DWORD*)&hr))
1823 {
1824 ExitWithLastError(hr, "Failed to get cache thread exit code.");
1825 }
1826
1827 if (SUCCEEDED(hr))
1828 {
1829 hr = E_UNEXPECTED;
1830 }
1831 ExitOnFailure(hr, "Cache thread exited unexpectedly.");
1832
1833 case WAIT_FAILED: __fallthrough;
1834 default:
1835 ExitWithLastError(hr, "Failed to wait for cache check-point.");
1836 }
1837 break;
1838
1839 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
1840 hr = ExecuteExePackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart);
1841 ExitOnFailure(hr, "Failed to execute EXE package.");
1842 break;
1843
1844 case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE:
1845 hr = ExecuteMsiPackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart);
1846 ExitOnFailure(hr, "Failed to execute MSI package.");
1847 break;
1848
1849 case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET:
1850 hr = ExecuteMspPackage(pEngineState, pExecuteAction, pContext, FALSE, &fRetry, pfSuspend, &restart);
1851 ExitOnFailure(hr, "Failed to execute MSP package.");
1852 break;
1853
1854 case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE:
1855 hr = ExecuteMsuPackage(pEngineState, pExecuteAction, pContext, FALSE, fStopWusaService, &fRetry, pfSuspend, &restart);
1856 fStopWusaService = fRetry;
1857 ExitOnFailure(hr, "Failed to execute MSU package.");
1858 break;
1859
1860 case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER:
1861 hr = ExecutePackageProviderAction(pEngineState, pExecuteAction, pContext);
1862 ExitOnFailure(hr, "Failed to execute package provider registration action.");
1863 break;
1864
1865 case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY:
1866 hr = ExecuteDependencyAction(pEngineState, pExecuteAction, pContext);
1867 ExitOnFailure(hr, "Failed to execute dependency action.");
1868 break;
1869
1870 case BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE:
1871 hr = ExecuteCompatiblePackageAction(pEngineState, pExecuteAction);
1872 ExitOnFailure(hr, "Failed to execute compatible package action.");
1873 break;
1874
1875 case BURN_EXECUTE_ACTION_TYPE_REGISTRATION:
1876 *pfKeepRegistration = pExecuteAction->registration.fKeep;
1877 break;
1878
1879 case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY:
1880 *ppRollbackBoundary = pExecuteAction->rollbackBoundary.pRollbackBoundary;
1881 break;
1882
1883 case BURN_EXECUTE_ACTION_TYPE_SERVICE_STOP: __fallthrough;
1884 case BURN_EXECUTE_ACTION_TYPE_SERVICE_START: __fallthrough;
1885 default:
1886 hr = E_UNEXPECTED;
1887 ExitOnFailure(hr, "Invalid execute action.");
1888 }
1889
1890 if (*pRestart < restart)
1891 {
1892 *pRestart = restart;
1893 }
1894 } while (fRetry && *pRestart < BOOTSTRAPPER_APPLY_RESTART_INITIATED);
1895
1896LExit:
1897 return hr;
1898}
1899
1900static HRESULT DoRollbackActions(
1901 __in BURN_ENGINE_STATE* pEngineState,
1902 __in BURN_EXECUTE_CONTEXT* pContext,
1903 __in DWORD dwCheckpoint,
1904 __in BOOL fInTransaction,
1905 __out BOOL* pfKeepRegistration,
1906 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
1907)
1908{
1909 HRESULT hr = S_OK;
1910 DWORD iCheckpoint = 0;
1911 BOOL fRetryIgnored = FALSE;
1912 BOOL fSuspendIgnored = FALSE;
1913
1914 pContext->fRollback = TRUE;
1915
1916 // Rollback MSI transaction
1917 if (fInTransaction)
1918 {
1919 hr = DoMsiRollbackTransaction(pContext, pEngineState);
1920 ExitOnFailure(hr, "Failed rolling back transaction");
1921 }
1922
1923 // scan to last checkpoint
1924 for (DWORD i = 0; i < pEngineState->plan.cRollbackActions; ++i)
1925 {
1926 BURN_EXECUTE_ACTION* pRollbackAction = &pEngineState->plan.rgRollbackActions[i];
1927 if (pRollbackAction->fDeleted)
1928 {
1929 continue;
1930 }
1931
1932 if (BURN_EXECUTE_ACTION_TYPE_CHECKPOINT == pRollbackAction->type)
1933 {
1934 if (pRollbackAction->checkpoint.dwId == dwCheckpoint)
1935 {
1936 iCheckpoint = i;
1937 break;
1938 }
1939 }
1940 }
1941
1942 // execute rollback actions
1943 if (iCheckpoint)
1944 {
1945 // i has to be a signed integer so it doesn't get decremented to 0xFFFFFFFF.
1946 for (int i = iCheckpoint - 1; i >= 0; --i)
1947 {
1948 BURN_EXECUTE_ACTION* pRollbackAction = &pEngineState->plan.rgRollbackActions[i];
1949 if (pRollbackAction->fDeleted)
1950 {
1951 continue;
1952 }
1953
1954 BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
1955 switch (pRollbackAction->type)
1956 {
1957 case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT:
1958 break;
1959
1960 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
1961 hr = ExecuteExePackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart);
1962 TraceError(hr, "Failed to rollback EXE package.");
1963 hr = S_OK;
1964 break;
1965
1966 case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE:
1967 if (fInTransaction)
1968 {
1969 LogString(REPORT_STANDARD, "Skipping rolling back an MSI package- already done in transaction rollback\n");
1970 break;
1971 }
1972 hr = ExecuteMsiPackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart);
1973 TraceError(hr, "Failed to rollback MSI package.");
1974 hr = S_OK;
1975 break;
1976
1977 case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET:
1978 if (fInTransaction)
1979 {
1980 LogString(REPORT_STANDARD, "Skipping rolling back an MSP package- already done in transaction rollback\n");
1981 break;
1982 }
1983 hr = ExecuteMspPackage(pEngineState, pRollbackAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart);
1984 TraceError(hr, "Failed to rollback MSP package.");
1985 hr = S_OK;
1986 break;
1987
1988 case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE:
1989 if (fInTransaction)
1990 {
1991 LogString(REPORT_STANDARD, "Skipping rolling back an MSU package- already done in transaction rollback\n");
1992 break;
1993 }
1994 hr = ExecuteMsuPackage(pEngineState, pRollbackAction, pContext, TRUE, FALSE, &fRetryIgnored, &fSuspendIgnored, &restart);
1995 TraceError(hr, "Failed to rollback MSU package.");
1996 hr = S_OK;
1997 break;
1998
1999 case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER:
2000 hr = ExecutePackageProviderAction(pEngineState, pRollbackAction, pContext);
2001 TraceError(hr, "Failed to rollback package provider action.");
2002 hr = S_OK;
2003 break;
2004
2005 case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY:
2006 hr = ExecuteDependencyAction(pEngineState, pRollbackAction, pContext);
2007 TraceError(hr, "Failed to rollback dependency action.");
2008 hr = S_OK;
2009 break;
2010
2011 case BURN_EXECUTE_ACTION_TYPE_REGISTRATION:
2012 *pfKeepRegistration = pRollbackAction->registration.fKeep;
2013 break;
2014
2015 case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY:
2016 ExitFunction1(hr = S_OK);
2017
2018 case BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE:
2019 hr = CleanPackage(pEngineState->companionConnection.hPipe, pRollbackAction->uncachePackage.pPackage);
2020 break;
2021
2022 case BURN_EXECUTE_ACTION_TYPE_SERVICE_STOP: __fallthrough;
2023 case BURN_EXECUTE_ACTION_TYPE_SERVICE_START: __fallthrough;
2024 default:
2025 hr = E_UNEXPECTED;
2026 ExitOnFailure(hr, "Invalid rollback action: %d.", pRollbackAction->type);
2027 }
2028
2029 if (*pRestart < restart)
2030 {
2031 *pRestart = restart;
2032 }
2033 }
2034 }
2035
2036LExit:
2037 return hr;
2038}
2039
2040static HRESULT ExecuteExePackage(
2041 __in BURN_ENGINE_STATE* pEngineState,
2042 __in BURN_EXECUTE_ACTION* pExecuteAction,
2043 __in BURN_EXECUTE_CONTEXT* pContext,
2044 __in BOOL fRollback,
2045 __out BOOL* pfRetry,
2046 __out BOOL* pfSuspend,
2047 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
2048 )
2049{
2050 HRESULT hr = S_OK;
2051 HRESULT hrExecute = S_OK;
2052 GENERIC_EXECUTE_MESSAGE message = { };
2053 int nResult = 0;
2054 BOOL fBeginCalled = FALSE;
2055
2056 if (FAILED(pExecuteAction->exePackage.pPackage->hrCacheResult))
2057 {
2058 LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pExecuteAction->exePackage.pPackage->sczId, pExecuteAction->exePackage.pPackage->hrCacheResult);
2059 ExitFunction1(hr = S_OK);
2060 }
2061
2062 Assert(pContext->fRollback == fRollback);
2063 pContext->pExecutingPackage = pExecuteAction->exePackage.pPackage;
2064 fBeginCalled = TRUE;
2065
2066 // Send package execute begin to BA.
2067 hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->exePackage.pPackage->sczId, !fRollback);
2068 ExitOnRootFailure(hr, "BA aborted execute EXE package begin.");
2069
2070 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2071 message.dwAllowedResults = MB_OKCANCEL;
2072 message.progress.dwPercentage = fRollback ? 100 : 0;
2073 nResult = GenericExecuteMessageHandler(&message, pContext);
2074 hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult);
2075 ExitOnRootFailure(hr, "UX aborted EXE progress.");
2076
2077 // Execute package.
2078 if (pExecuteAction->exePackage.pPackage->fPerMachine)
2079 {
2080 hrExecute = ElevationExecuteExePackage(pEngineState->companionConnection.hPipe, pExecuteAction, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart);
2081 ExitOnFailure(hrExecute, "Failed to configure per-machine EXE package.");
2082 }
2083 else
2084 {
2085 hrExecute = ExeEngineExecutePackage(pExecuteAction, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart);
2086 ExitOnFailure(hrExecute, "Failed to configure per-user EXE package.");
2087 }
2088
2089 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2090 message.dwAllowedResults = MB_OKCANCEL;
2091 message.progress.dwPercentage = fRollback ? 0 : 100;
2092 nResult = GenericExecuteMessageHandler(&message, pContext);
2093 hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult);
2094 ExitOnRootFailure(hr, "UX aborted EXE progress.");
2095
2096 pContext->cExecutedPackages += fRollback ? -1 : 1;
2097 (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1;
2098
2099 hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks);
2100 ExitOnRootFailure(hr, "UX aborted EXE package execute progress.");
2101
2102LExit:
2103 if (fBeginCalled)
2104 {
2105 hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pExecuteAction->exePackage.pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2106 }
2107
2108 return hr;
2109}
2110
2111static HRESULT ExecuteMsiPackage(
2112 __in BURN_ENGINE_STATE* pEngineState,
2113 __in BURN_EXECUTE_ACTION* pExecuteAction,
2114 __in BURN_EXECUTE_CONTEXT* pContext,
2115 __in BOOL fRollback,
2116 __out BOOL* pfRetry,
2117 __out BOOL* pfSuspend,
2118 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
2119 )
2120{
2121 HRESULT hr = S_OK;
2122 HRESULT hrExecute = S_OK;
2123 BOOL fBeginCalled = FALSE;
2124
2125 if (FAILED(pExecuteAction->msiPackage.pPackage->hrCacheResult))
2126 {
2127 LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pExecuteAction->msiPackage.pPackage->sczId, pExecuteAction->msiPackage.pPackage->hrCacheResult);
2128 ExitFunction1(hr = S_OK);
2129 }
2130
2131 Assert(pContext->fRollback == fRollback);
2132 pContext->pExecutingPackage = pExecuteAction->msiPackage.pPackage;
2133 fBeginCalled = TRUE;
2134
2135 // Send package execute begin to BA.
2136 hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->msiPackage.pPackage->sczId, !fRollback);
2137 ExitOnRootFailure(hr, "BA aborted execute MSI package begin.");
2138
2139 // execute package
2140 if (pExecuteAction->msiPackage.pPackage->fPerMachine)
2141 {
2142 hrExecute = ElevationExecuteMsiPackage(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart);
2143 ExitOnFailure(hrExecute, "Failed to configure per-machine MSI package.");
2144 }
2145 else
2146 {
2147 hrExecute = MsiEngineExecutePackage(pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart);
2148 ExitOnFailure(hrExecute, "Failed to configure per-user MSI package.");
2149 }
2150
2151 pContext->cExecutedPackages += fRollback ? -1 : 1;
2152 (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1;
2153
2154 hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks);
2155 ExitOnRootFailure(hr, "UX aborted MSI package execute progress.");
2156
2157LExit:
2158 if (fBeginCalled)
2159 {
2160 hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pExecuteAction->msiPackage.pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2161 }
2162
2163 return hr;
2164}
2165
2166static HRESULT ExecuteMspPackage(
2167 __in BURN_ENGINE_STATE* pEngineState,
2168 __in BURN_EXECUTE_ACTION* pExecuteAction,
2169 __in BURN_EXECUTE_CONTEXT* pContext,
2170 __in BOOL fRollback,
2171 __out BOOL* pfRetry,
2172 __out BOOL* pfSuspend,
2173 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
2174 )
2175{
2176 HRESULT hr = S_OK;
2177 HRESULT hrExecute = S_OK;
2178 BOOL fBeginCalled = FALSE;
2179
2180 if (FAILED(pExecuteAction->mspTarget.pPackage->hrCacheResult))
2181 {
2182 LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pExecuteAction->mspTarget.pPackage->sczId, pExecuteAction->mspTarget.pPackage->hrCacheResult);
2183 ExitFunction1(hr = S_OK);
2184 }
2185
2186 Assert(pContext->fRollback == fRollback);
2187 pContext->pExecutingPackage = pExecuteAction->mspTarget.pPackage;
2188 fBeginCalled = TRUE;
2189
2190 // Send package execute begin to BA.
2191 hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->mspTarget.pPackage->sczId, !fRollback);
2192 ExitOnRootFailure(hr, "BA aborted execute MSP package begin.");
2193
2194 // Now send all the patches that target this product code.
2195 for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i)
2196 {
2197 BURN_PACKAGE* pMspPackage = pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage;
2198
2199 hr = UserExperienceOnExecutePatchTarget(&pEngineState->userExperience, pMspPackage->sczId, pExecuteAction->mspTarget.sczTargetProductCode);
2200 ExitOnRootFailure(hr, "BA aborted execute MSP target.");
2201 }
2202
2203 // execute package
2204 if (pExecuteAction->mspTarget.fPerMachineTarget)
2205 {
2206 hrExecute = ElevationExecuteMspPackage(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart);
2207 ExitOnFailure(hrExecute, "Failed to configure per-machine MSP package.");
2208 }
2209 else
2210 {
2211 hrExecute = MspEngineExecutePackage(pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart);
2212 ExitOnFailure(hrExecute, "Failed to configure per-user MSP package.");
2213 }
2214
2215 pContext->cExecutedPackages += fRollback ? -1 : 1;
2216 (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1;
2217
2218 hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks);
2219 ExitOnRootFailure(hr, "UX aborted MSP package execute progress.");
2220
2221LExit:
2222 if (fBeginCalled)
2223 {
2224 hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pExecuteAction->mspTarget.pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2225 }
2226
2227 return hr;
2228}
2229
2230static HRESULT ExecuteMsuPackage(
2231 __in BURN_ENGINE_STATE* pEngineState,
2232 __in BURN_EXECUTE_ACTION* pExecuteAction,
2233 __in BURN_EXECUTE_CONTEXT* pContext,
2234 __in BOOL fRollback,
2235 __in BOOL fStopWusaService,
2236 __out BOOL* pfRetry,
2237 __out BOOL* pfSuspend,
2238 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
2239 )
2240{
2241 HRESULT hr = S_OK;
2242 HRESULT hrExecute = S_OK;
2243 GENERIC_EXECUTE_MESSAGE message = { };
2244 int nResult = 0;
2245 BOOL fBeginCalled = FALSE;
2246
2247 if (FAILED(pExecuteAction->msuPackage.pPackage->hrCacheResult))
2248 {
2249 LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE, pExecuteAction->msuPackage.pPackage->sczId, pExecuteAction->msuPackage.pPackage->hrCacheResult);
2250 ExitFunction1(hr = S_OK);
2251 }
2252
2253 Assert(pContext->fRollback == fRollback);
2254 pContext->pExecutingPackage = pExecuteAction->msuPackage.pPackage;
2255 fBeginCalled = TRUE;
2256
2257 // Send package execute begin to BA.
2258 hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pExecuteAction->msuPackage.pPackage->sczId, !fRollback);
2259 ExitOnRootFailure(hr, "BA aborted execute MSU package begin.");
2260
2261 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2262 message.dwAllowedResults = MB_OKCANCEL;
2263 message.progress.dwPercentage = fRollback ? 100 : 0;
2264 nResult = GenericExecuteMessageHandler(&message, pContext);
2265 hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult);
2266 ExitOnRootFailure(hr, "UX aborted MSU progress.");
2267
2268 // execute package
2269 if (pExecuteAction->msuPackage.pPackage->fPerMachine)
2270 {
2271 hrExecute = ElevationExecuteMsuPackage(pEngineState->companionConnection.hPipe, pExecuteAction, fRollback, fStopWusaService, GenericExecuteMessageHandler, pContext, pRestart);
2272 ExitOnFailure(hrExecute, "Failed to configure per-machine MSU package.");
2273 }
2274 else
2275 {
2276 hrExecute = E_UNEXPECTED;
2277 ExitOnFailure(hr, "MSU packages cannot be per-user.");
2278 }
2279
2280 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
2281 message.dwAllowedResults = MB_OKCANCEL;
2282 message.progress.dwPercentage = fRollback ? 0 : 100;
2283 nResult = GenericExecuteMessageHandler(&message, pContext);
2284 hr = UserExperienceInterpretExecuteResult(&pEngineState->userExperience, fRollback, message.dwAllowedResults, nResult);
2285 ExitOnRootFailure(hr, "UX aborted MSU progress.");
2286
2287 pContext->cExecutedPackages += fRollback ? -1 : 1;
2288 (*pContext->pcOverallProgressTicks) += fRollback ? -1 : 1;
2289
2290 hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, *pContext->pcOverallProgressTicks);
2291 ExitOnRootFailure(hr, "UX aborted MSU package execute progress.");
2292
2293LExit:
2294 if (fBeginCalled)
2295 {
2296 hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pExecuteAction->msuPackage.pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2297 }
2298
2299 return hr;
2300}
2301
2302static HRESULT ExecutePackageProviderAction(
2303 __in BURN_ENGINE_STATE* pEngineState,
2304 __in BURN_EXECUTE_ACTION* pAction,
2305 __in BURN_EXECUTE_CONTEXT* /*pContext*/
2306 )
2307{
2308 HRESULT hr = S_OK;
2309
2310 if (pAction->packageProvider.pPackage->fPerMachine)
2311 {
2312 hr = ElevationExecutePackageProviderAction(pEngineState->companionConnection.hPipe, pAction);
2313 ExitOnFailure(hr, "Failed to register the package provider on per-machine package.");
2314 }
2315 else
2316 {
2317 hr = DependencyExecutePackageProviderAction(pAction);
2318 ExitOnFailure(hr, "Failed to register the package provider on per-user package.");
2319 }
2320
2321LExit:
2322 return hr;
2323}
2324
2325static HRESULT ExecuteDependencyAction(
2326 __in BURN_ENGINE_STATE* pEngineState,
2327 __in BURN_EXECUTE_ACTION* pAction,
2328 __in BURN_EXECUTE_CONTEXT* /*pContext*/
2329 )
2330{
2331 HRESULT hr = S_OK;
2332
2333 if (pAction->packageDependency.pPackage->fPerMachine)
2334 {
2335 hr = ElevationExecutePackageDependencyAction(pEngineState->companionConnection.hPipe, pAction);
2336 ExitOnFailure(hr, "Failed to register the dependency on per-machine package.");
2337 }
2338 else
2339 {
2340 hr = DependencyExecutePackageDependencyAction(FALSE, pAction);
2341 ExitOnFailure(hr, "Failed to register the dependency on per-user package.");
2342 }
2343
2344LExit:
2345 return hr;
2346}
2347
2348static HRESULT ExecuteCompatiblePackageAction(
2349 __in BURN_ENGINE_STATE* pEngineState,
2350 __in BURN_EXECUTE_ACTION* pAction
2351 )
2352{
2353 HRESULT hr = S_OK;
2354
2355 if (pAction->compatiblePackage.pReferencePackage->fPerMachine)
2356 {
2357 hr = ElevationLoadCompatiblePackageAction(pEngineState->companionConnection.hPipe, pAction);
2358 ExitOnFailure(hr, "Failed to load compatible package on per-machine package.");
2359 }
2360
2361 // Compatible package already loaded in this process.
2362
2363LExit:
2364 return hr;
2365}
2366
2367static HRESULT CleanPackage(
2368 __in HANDLE hElevatedPipe,
2369 __in BURN_PACKAGE* pPackage
2370 )
2371{
2372 HRESULT hr = S_OK;
2373
2374 if (pPackage->fPerMachine)
2375 {
2376 hr = ElevationCleanPackage(hElevatedPipe, pPackage);
2377 }
2378 else
2379 {
2380 hr = CacheRemovePackage(FALSE, pPackage->sczId, pPackage->sczCacheId);
2381 }
2382
2383 return hr;
2384}
2385
2386static int GenericExecuteMessageHandler(
2387 __in GENERIC_EXECUTE_MESSAGE* pMessage,
2388 __in LPVOID pvContext
2389 )
2390{
2391 BURN_EXECUTE_CONTEXT* pContext = (BURN_EXECUTE_CONTEXT*)pvContext;
2392 int nResult = IDNOACTION;
2393
2394 switch (pMessage->type)
2395 {
2396 case GENERIC_EXECUTE_MESSAGE_PROGRESS:
2397 {
2398 DWORD dwOverallProgress = pContext->cExecutePackagesTotal ? (pContext->cExecutedPackages * 100 + pMessage->progress.dwPercentage) / (pContext->cExecutePackagesTotal) : 0;
2399 UserExperienceOnExecuteProgress(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->progress.dwPercentage, dwOverallProgress, &nResult); // ignore return value.
2400 }
2401 break;
2402
2403 case GENERIC_EXECUTE_MESSAGE_ERROR:
2404 UserExperienceOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_EXE_PACKAGE, pContext->pExecutingPackage->sczId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwAllowedResults, 0, NULL, &nResult); // ignore return value.
2405 break;
2406
2407 case GENERIC_EXECUTE_MESSAGE_FILES_IN_USE:
2408 UserExperienceOnExecuteFilesInUse(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->filesInUse.cFiles, pMessage->filesInUse.rgwzFiles, &nResult); // ignore return value.
2409 break;
2410 }
2411
2412 nResult = UserExperienceCheckExecuteResult(pContext->pUX, pContext->fRollback, pMessage->dwAllowedResults, nResult);
2413 return nResult;
2414}
2415
2416static int MsiExecuteMessageHandler(
2417 __in WIU_MSI_EXECUTE_MESSAGE* pMessage,
2418 __in_opt LPVOID pvContext
2419 )
2420{
2421 BURN_EXECUTE_CONTEXT* pContext = (BURN_EXECUTE_CONTEXT*)pvContext;
2422 int nResult = IDNOACTION;
2423
2424 switch (pMessage->type)
2425 {
2426 case WIU_MSI_EXECUTE_MESSAGE_PROGRESS:
2427 {
2428 DWORD dwOverallProgress = pContext->cExecutePackagesTotal ? (pContext->cExecutedPackages * 100 + pMessage->progress.dwPercentage) / (pContext->cExecutePackagesTotal) : 0;
2429 UserExperienceOnExecuteProgress(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->progress.dwPercentage, dwOverallProgress, &nResult); // ignore return value.
2430 }
2431 break;
2432
2433 case WIU_MSI_EXECUTE_MESSAGE_ERROR:
2434 nResult = pMessage->nResultRecommendation;
2435 UserExperienceOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_WINDOWS_INSTALLER, pContext->pExecutingPackage->sczId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwAllowedResults, pMessage->cData, pMessage->rgwzData, &nResult); // ignore return value.
2436 break;
2437
2438 case WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE:
2439 nResult = pMessage->nResultRecommendation;
2440 UserExperienceOnExecuteMsiMessage(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->msiMessage.mt, pMessage->dwAllowedResults, pMessage->msiMessage.wzMessage, pMessage->cData, pMessage->rgwzData, &nResult); // ignore return value.
2441 break;
2442
2443 case WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE:
2444 UserExperienceOnExecuteFilesInUse(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->msiFilesInUse.cFiles, pMessage->msiFilesInUse.rgwzFiles, &nResult); // ignore return value.
2445 break;
2446 }
2447
2448 nResult = UserExperienceCheckExecuteResult(pContext->pUX, pContext->fRollback, pMessage->dwAllowedResults, nResult);
2449 return nResult;
2450}
2451
2452static HRESULT ReportOverallProgressTicks(
2453 __in BURN_USER_EXPERIENCE* pUX,
2454 __in BOOL fRollback,
2455 __in DWORD cOverallProgressTicksTotal,
2456 __in DWORD cOverallProgressTicks
2457 )
2458{
2459 HRESULT hr = S_OK;
2460 DWORD dwProgress = cOverallProgressTicksTotal ? (cOverallProgressTicks * 100 / cOverallProgressTicksTotal) : 0;
2461
2462 // TODO: consider sending different progress numbers in the future.
2463 hr = UserExperienceOnProgress(pUX, fRollback, dwProgress, dwProgress);
2464
2465 return hr;
2466}
2467
2468static HRESULT ExecutePackageComplete(
2469 __in BURN_USER_EXPERIENCE* pUX,
2470 __in BURN_VARIABLES* pVariables,
2471 __in BURN_PACKAGE* pPackage,
2472 __in HRESULT hrOverall,
2473 __in HRESULT hrExecute,
2474 __in BOOL fRollback,
2475 __out BOOTSTRAPPER_APPLY_RESTART* pRestart,
2476 __out BOOL* pfRetry,
2477 __out BOOL* pfSuspend
2478 )
2479{
2480 HRESULT hr = FAILED(hrOverall) ? hrOverall : hrExecute; // if the overall function failed use that otherwise use the execution result.
2481 BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION executePackageCompleteAction = FAILED(hrOverall) || SUCCEEDED(hrExecute) || pPackage->fVital ? BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_NONE : BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_IGNORE;
2482
2483 // Send package execute complete to BA.
2484 UserExperienceOnExecutePackageComplete(pUX, pPackage->sczId, hr, *pRestart, &executePackageCompleteAction);
2485 if (BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RESTART == executePackageCompleteAction)
2486 {
2487 *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
2488 }
2489 *pfRetry = (FAILED(hrExecute) && BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RETRY == executePackageCompleteAction); // allow retry only on failures.
2490 *pfSuspend = (BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_SUSPEND == executePackageCompleteAction);
2491
2492 // Remember this package as the package that initiated the forced restart.
2493 if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart)
2494 {
2495 // Best effort to set the forced restart package variable.
2496 VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, pPackage->sczId, TRUE);
2497 }
2498
2499 // If we're retrying, leave a message in the log file and say everything is okay.
2500 if (*pfRetry)
2501 {
2502 LogId(REPORT_STANDARD, MSG_APPLY_RETRYING_PACKAGE, pPackage->sczId, hrExecute);
2503 hr = S_OK;
2504 }
2505 else if (SUCCEEDED(hrOverall) && FAILED(hrExecute) && BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_IGNORE == executePackageCompleteAction && !pPackage->fVital) // If we *only* failed to execute and the BA ignored this *not-vital* package, say everything is okay.
2506 {
2507 LogId(REPORT_STANDARD, MSG_APPLY_CONTINUING_NONVITAL_PACKAGE, pPackage->sczId, hrExecute);
2508 hr = S_OK;
2509 }
2510 else
2511 {
2512 LogId(REPORT_STANDARD, MSG_APPLY_COMPLETED_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, hr, LoggingRestartToString(*pRestart));
2513 }
2514
2515 return hr;
2516}
diff --git a/src/engine/apply.h b/src/engine/apply.h
new file mode 100644
index 00000000..b717251e
--- /dev/null
+++ b/src/engine/apply.h
@@ -0,0 +1,106 @@
1#pragma once
2// 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.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9
10enum GENERIC_EXECUTE_MESSAGE_TYPE
11{
12 GENERIC_EXECUTE_MESSAGE_NONE,
13 GENERIC_EXECUTE_MESSAGE_ERROR,
14 GENERIC_EXECUTE_MESSAGE_PROGRESS,
15 GENERIC_EXECUTE_MESSAGE_FILES_IN_USE,
16};
17
18typedef struct _APPLY_AUTHENTICATION_REQUIRED_DATA
19{
20 BURN_USER_EXPERIENCE* pUX;
21 LPCWSTR wzPackageOrContainerId;
22 LPCWSTR wzPayloadId;
23} APPLY_AUTHENTICATION_REQUIRED_DATA;
24
25typedef struct _GENERIC_EXECUTE_MESSAGE
26{
27 GENERIC_EXECUTE_MESSAGE_TYPE type;
28 DWORD dwAllowedResults;
29
30 union
31 {
32 struct
33 {
34 DWORD dwErrorCode;
35 LPCWSTR wzMessage;
36 } error;
37 struct
38 {
39 DWORD dwPercentage;
40 } progress;
41 struct
42 {
43 DWORD cFiles;
44 LPCWSTR* rgwzFiles;
45 } filesInUse;
46 };
47} GENERIC_EXECUTE_MESSAGE;
48
49
50typedef int (*PFN_GENERICMESSAGEHANDLER)(
51 __in GENERIC_EXECUTE_MESSAGE* pMessage,
52 __in LPVOID pvContext
53 );
54
55
56void ApplyInitialize();
57void ApplyUninitialize();
58HRESULT ApplySetVariables(
59 __in BURN_VARIABLES* pVariables
60 );
61void ApplyReset(
62 __in BURN_USER_EXPERIENCE* pUX,
63 __in BURN_PACKAGES* pPackages
64 );
65HRESULT ApplyLock(
66 __in BOOL fPerMachine,
67 __out HANDLE* phLock
68 );
69HRESULT ApplyRegister(
70 __in BURN_ENGINE_STATE* pEngineState
71 );
72HRESULT ApplyUnregister(
73 __in BURN_ENGINE_STATE* pEngineState,
74 __in BOOL fFailedOrRollback,
75 __in BOOL fRollback,
76 __in BOOL fSuspend,
77 __in BOOTSTRAPPER_APPLY_RESTART restart
78 );
79HRESULT ApplyCache(
80 __in HANDLE hSourceEngineFile,
81 __in BURN_USER_EXPERIENCE* pUX,
82 __in BURN_VARIABLES* pVariables,
83 __in BURN_PLAN* pPlan,
84 __in HANDLE hPipe,
85 __inout DWORD* pcOverallProgressTicks,
86 __out BOOL* pfRollback
87 );
88HRESULT ApplyExecute(
89 __in BURN_ENGINE_STATE* pEngineState,
90 __in_opt HANDLE hCacheThread,
91 __inout DWORD* pcOverallProgressTicks,
92 __out BOOL* pfKeepRegistration,
93 __out BOOL* pfRollback,
94 __out BOOL* pfSuspend,
95 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
96 );
97void ApplyClean(
98 __in BURN_USER_EXPERIENCE* pUX,
99 __in BURN_PLAN* pPlan,
100 __in HANDLE hPipe
101 );
102
103
104#ifdef __cplusplus
105}
106#endif
diff --git a/src/engine/approvedexe.cpp b/src/engine/approvedexe.cpp
new file mode 100644
index 00000000..55518519
--- /dev/null
+++ b/src/engine/approvedexe.cpp
@@ -0,0 +1,262 @@
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#include "precomp.h"
4
5
6// function definitions
7
8extern "C" HRESULT ApprovedExesParseFromXml(
9 __in BURN_APPROVED_EXES* pApprovedExes,
10 __in IXMLDOMNode* pixnBundle
11 )
12{
13 HRESULT hr = S_OK;
14 IXMLDOMNodeList* pixnNodes = NULL;
15 IXMLDOMNode* pixnNode = NULL;
16 DWORD cNodes = 0;
17 LPWSTR scz = NULL;
18
19 // select approved exe nodes
20 hr = XmlSelectNodes(pixnBundle, L"ApprovedExeForElevation", &pixnNodes);
21 ExitOnFailure(hr, "Failed to select approved exe nodes.");
22
23 // get approved exe node count
24 hr = pixnNodes->get_length((long*)&cNodes);
25 ExitOnFailure(hr, "Failed to get approved exe node count.");
26
27 if (!cNodes)
28 {
29 ExitFunction();
30 }
31
32 // allocate memory for approved exes
33 pApprovedExes->rgApprovedExes = (BURN_APPROVED_EXE*)MemAlloc(sizeof(BURN_APPROVED_EXE) * cNodes, TRUE);
34 ExitOnNull(pApprovedExes->rgApprovedExes, hr, E_OUTOFMEMORY, "Failed to allocate memory for approved exe structs.");
35
36 pApprovedExes->cApprovedExes = cNodes;
37
38 // parse approved exe elements
39 for (DWORD i = 0; i < cNodes; ++i)
40 {
41 BURN_APPROVED_EXE* pApprovedExe = &pApprovedExes->rgApprovedExes[i];
42
43 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
44 ExitOnFailure(hr, "Failed to get next node.");
45
46 // @Id
47 hr = XmlGetAttributeEx(pixnNode, L"Id", &pApprovedExe->sczId);
48 ExitOnFailure(hr, "Failed to get @Id.");
49
50 // @Key
51 hr = XmlGetAttributeEx(pixnNode, L"Key", &pApprovedExe->sczKey);
52 ExitOnFailure(hr, "Failed to get @Key.");
53
54 // @ValueName
55 hr = XmlGetAttributeEx(pixnNode, L"ValueName", &pApprovedExe->sczValueName);
56 if (E_NOTFOUND != hr)
57 {
58 ExitOnFailure(hr, "Failed to get @ValueName.");
59 }
60
61 // @Win64
62 hr = XmlGetYesNoAttribute(pixnNode, L"Win64", &pApprovedExe->fWin64);
63 if (E_NOTFOUND != hr)
64 {
65 ExitOnFailure(hr, "Failed to get @Win64.");
66 }
67
68 // prepare next iteration
69 ReleaseNullObject(pixnNode);
70 ReleaseNullStr(scz);
71 }
72
73 hr = S_OK;
74
75LExit:
76 ReleaseObject(pixnNodes);
77 ReleaseObject(pixnNode);
78 ReleaseStr(scz);
79 return hr;
80}
81
82extern "C" void ApprovedExesUninitialize(
83 __in BURN_APPROVED_EXES* pApprovedExes
84 )
85{
86 if (pApprovedExes->rgApprovedExes)
87 {
88 for (DWORD i = 0; i < pApprovedExes->cApprovedExes; ++i)
89 {
90 BURN_APPROVED_EXE* pApprovedExe = &pApprovedExes->rgApprovedExes[i];
91
92 ReleaseStr(pApprovedExe->sczId);
93 ReleaseStr(pApprovedExe->sczKey);
94 ReleaseStr(pApprovedExe->sczValueName);
95 }
96 MemFree(pApprovedExes->rgApprovedExes);
97 }
98}
99
100extern "C" void ApprovedExesUninitializeLaunch(
101 __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe
102 )
103{
104 if (pLaunchApprovedExe)
105 {
106 ReleaseStr(pLaunchApprovedExe->sczArguments);
107 ReleaseStr(pLaunchApprovedExe->sczExecutablePath);
108 ReleaseStr(pLaunchApprovedExe->sczId);
109 MemFree(pLaunchApprovedExe);
110 }
111}
112
113extern "C" HRESULT ApprovedExesFindById(
114 __in BURN_APPROVED_EXES* pApprovedExes,
115 __in_z LPCWSTR wzId,
116 __out BURN_APPROVED_EXE** ppApprovedExe
117 )
118{
119 HRESULT hr = S_OK;
120 BURN_APPROVED_EXE* pApprovedExe = NULL;
121
122 for (DWORD i = 0; i < pApprovedExes->cApprovedExes; ++i)
123 {
124 pApprovedExe = &pApprovedExes->rgApprovedExes[i];
125
126 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pApprovedExe->sczId, -1, wzId, -1))
127 {
128 *ppApprovedExe = pApprovedExe;
129 ExitFunction1(hr = S_OK);
130 }
131 }
132
133 hr = E_NOTFOUND;
134
135LExit:
136 return hr;
137}
138
139extern "C" HRESULT ApprovedExesLaunch(
140 __in BURN_VARIABLES* pVariables,
141 __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe,
142 __out DWORD* pdwProcessId
143 )
144{
145 HRESULT hr = S_OK;
146 LPWSTR sczArgumentsFormatted = NULL;
147 LPWSTR sczArgumentsObfuscated = NULL;
148 LPWSTR sczCommand = NULL;
149 LPWSTR sczCommandObfuscated = NULL;
150 LPWSTR sczExecutableDirectory = NULL;
151 STARTUPINFOW si = { };
152 PROCESS_INFORMATION pi = { };
153
154 // build command
155 if (pLaunchApprovedExe->sczArguments && *pLaunchApprovedExe->sczArguments)
156 {
157 hr = VariableFormatString(pVariables, pLaunchApprovedExe->sczArguments, &sczArgumentsFormatted, NULL);
158 ExitOnFailure(hr, "Failed to format argument string.");
159
160 hr = StrAllocFormattedSecure(&sczCommand, L"\"%ls\" %s", pLaunchApprovedExe->sczExecutablePath, sczArgumentsFormatted);
161 ExitOnFailure(hr, "Failed to create executable command.");
162
163 hr = VariableFormatStringObfuscated(pVariables, pLaunchApprovedExe->sczArguments, &sczArgumentsObfuscated, NULL);
164 ExitOnFailure(hr, "Failed to format obfuscated argument string.");
165
166 hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\" %s", pLaunchApprovedExe->sczExecutablePath, sczArgumentsObfuscated);
167 }
168 else
169 {
170 hr = StrAllocFormatted(&sczCommand, L"\"%ls\"", pLaunchApprovedExe->sczExecutablePath);
171 ExitOnFailure(hr, "Failed to create executable command.");
172
173 hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\"", pLaunchApprovedExe->sczExecutablePath);
174 }
175 ExitOnFailure(hr, "Failed to create obfuscated executable command.");
176
177 // Try to get the directory of the executable so we can set the current directory of the process to help those executables
178 // that expect stuff to be relative to them. Best effort only.
179 hr = PathGetDirectory(pLaunchApprovedExe->sczExecutablePath, &sczExecutableDirectory);
180 if (FAILED(hr))
181 {
182 ReleaseNullStr(sczExecutableDirectory);
183 }
184
185 LogId(REPORT_STANDARD, MSG_LAUNCHING_APPROVED_EXE, pLaunchApprovedExe->sczExecutablePath, sczCommandObfuscated);
186
187 si.cb = sizeof(si);
188 if (!::CreateProcessW(pLaunchApprovedExe->sczExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NEW_PROCESS_GROUP, NULL, sczExecutableDirectory, &si, &pi))
189 {
190 ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", pLaunchApprovedExe->sczExecutablePath);
191 }
192
193 *pdwProcessId = pi.dwProcessId;
194
195 if (pLaunchApprovedExe->dwWaitForInputIdleTimeout)
196 {
197 ::WaitForInputIdle(pi.hProcess, pLaunchApprovedExe->dwWaitForInputIdleTimeout);
198 }
199
200LExit:
201 StrSecureZeroFreeString(sczArgumentsFormatted);
202 ReleaseStr(sczArgumentsObfuscated);
203 StrSecureZeroFreeString(sczCommand);
204 ReleaseStr(sczCommandObfuscated);
205 ReleaseStr(sczExecutableDirectory);
206
207 ReleaseHandle(pi.hThread);
208 ReleaseHandle(pi.hProcess);
209
210 return hr;
211}
212
213extern "C" HRESULT ApprovedExesVerifySecureLocation(
214 __in BURN_VARIABLES* pVariables,
215 __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe
216 )
217{
218 HRESULT hr = S_OK;
219 LPWSTR scz = NULL;
220
221 const LPCWSTR vrgSecureFolderVariables[] = {
222 L"ProgramFiles64Folder",
223 L"ProgramFilesFolder",
224 };
225
226 for (DWORD i = 0; i < countof(vrgSecureFolderVariables); ++i)
227 {
228 LPCWSTR wzSecureFolderVariable = vrgSecureFolderVariables[i];
229
230 hr = VariableGetString(pVariables, wzSecureFolderVariable, &scz);
231 if (SUCCEEDED(hr))
232 {
233 hr = PathDirectoryContainsPath(scz, pLaunchApprovedExe->sczExecutablePath);
234 if (S_OK == hr)
235 {
236 ExitFunction();
237 }
238 }
239 else if (E_NOTFOUND != hr)
240 {
241 ExitOnFailure(hr, "Failed to get the variable: %ls", wzSecureFolderVariable);
242 }
243 }
244
245 // The problem with using a Variable for the root package cache folder is that it might not have been secured yet.
246 // Getting it through CacheGetRootCompletedPath makes sure it has been secured.
247 hr = CacheGetRootCompletedPath(TRUE, TRUE, &scz);
248 ExitOnFailure(hr, "Failed to get the root package cache folder.");
249
250 hr = PathDirectoryContainsPath(scz, pLaunchApprovedExe->sczExecutablePath);
251 if (S_OK == hr)
252 {
253 ExitFunction();
254 }
255
256 hr = S_FALSE;
257
258LExit:
259 ReleaseStr(scz);
260
261 return hr;
262}
diff --git a/src/engine/approvedexe.h b/src/engine/approvedexe.h
new file mode 100644
index 00000000..23f3b1bb
--- /dev/null
+++ b/src/engine/approvedexe.h
@@ -0,0 +1,67 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// structs
11
12typedef struct _BURN_APPROVED_EXE
13{
14 LPWSTR sczId;
15 LPWSTR sczKey;
16 LPWSTR sczValueName;
17 BOOL fWin64;
18} BURN_APPROVED_EXE;
19
20typedef struct _BURN_APPROVED_EXES
21{
22 BURN_APPROVED_EXE* rgApprovedExes;
23 DWORD cApprovedExes;
24} BURN_APPROVED_EXES;
25
26typedef struct _BURN_LAUNCH_APPROVED_EXE
27{
28 HWND hwndParent;
29 LPWSTR sczId;
30 LPWSTR sczExecutablePath;
31 LPWSTR sczArguments;
32 DWORD dwWaitForInputIdleTimeout;
33} BURN_LAUNCH_APPROVED_EXE;
34
35
36// function declarations
37
38HRESULT ApprovedExesParseFromXml(
39 __in BURN_APPROVED_EXES* pApprovedExes,
40 __in IXMLDOMNode* pixnBundle
41 );
42
43void ApprovedExesUninitialize(
44 __in BURN_APPROVED_EXES* pApprovedExes
45 );
46void ApprovedExesUninitializeLaunch(
47 __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe
48 );
49HRESULT ApprovedExesFindById(
50 __in BURN_APPROVED_EXES* pApprovedExes,
51 __in_z LPCWSTR wzId,
52 __out BURN_APPROVED_EXE** ppApprovedExe
53 );
54HRESULT ApprovedExesLaunch(
55 __in BURN_VARIABLES* pVariables,
56 __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe,
57 __out DWORD* pdwProcessId
58 );
59HRESULT ApprovedExesVerifySecureLocation(
60 __in BURN_VARIABLES* pVariables,
61 __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe
62 );
63
64
65#if defined(__cplusplus)
66}
67#endif
diff --git a/src/engine/bitsengine.cpp b/src/engine/bitsengine.cpp
new file mode 100644
index 00000000..b8093b77
--- /dev/null
+++ b/src/engine/bitsengine.cpp
@@ -0,0 +1,505 @@
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#include "precomp.h"
4
5// const
6
7const DWORD BITSENGINE_NO_PROGRESS_TIMEOUT = 2 * 60;
8const DWORD BITSENGINE_MSG_WAIT_TIMEOUT = 1;
9
10// functions
11
12static HRESULT CreateJob(
13 __out IBackgroundCopyJob** ppJob
14 );
15static HRESULT SetCredentials(
16 __in IBackgroundCopyJob* pJob,
17 __in_z_opt LPCWSTR wzUser,
18 __in_z_opt LPCWSTR wzPassword
19 );
20static void SendError(
21 __in DOWNLOAD_CACHE_CALLBACK* pCacheCallback,
22 __in IBackgroundCopyJob* pJob,
23 __in HRESULT hrError,
24 __in BG_ERROR_CONTEXT context,
25 __out_opt BOOL* pfRetry
26 );
27
28
29// class
30
31class CBurnBitsCallback : public IBackgroundCopyCallback
32{
33public: // IUnknown
34 virtual STDMETHODIMP QueryInterface(
35 __in const IID& riid,
36 __out void** ppvObject
37 )
38 {
39 HRESULT hr = S_OK;
40
41 ExitOnNull(ppvObject, hr, E_INVALIDARG, "Invalid argument ppvObject");
42 *ppvObject = NULL;
43
44 if (::IsEqualIID(__uuidof(IBackgroundCopyCallback), riid))
45 {
46 *ppvObject = static_cast<IBackgroundCopyCallback*>(this);
47 }
48 else if (::IsEqualIID(IID_IUnknown, riid))
49 {
50 *ppvObject = reinterpret_cast<IUnknown*>(this);
51 }
52 else // no interface for requested iid
53 {
54 ExitFunction1(hr = E_NOINTERFACE);
55 }
56
57 AddRef();
58
59 LExit:
60 return hr;
61 }
62
63 virtual STDMETHODIMP_(ULONG) AddRef()
64 {
65 return ::InterlockedIncrement(&this->m_cReferences);
66 }
67
68 virtual STDMETHODIMP_(ULONG) Release()
69 {
70 long l = ::InterlockedDecrement(&this->m_cReferences);
71 if (0 < l)
72 {
73 return l;
74 }
75
76 delete this;
77 return 0;
78 }
79
80public: // IBackgroundCopyCallback
81 virtual STDMETHODIMP JobTransferred(
82 __in IBackgroundCopyJob* pJob
83 )
84 {
85 HRESULT hr = S_OK;
86
87 hr = SendProgress(pJob);
88 ExitOnFailure(hr, "Failure while sending progress during BITS job transferred.");
89
90 LExit:
91 if (FAILED(hr))
92 {
93 ProcessResult(BG_ERROR_CONTEXT_NONE, hr);
94 }
95 else
96 {
97 ::SetEvent(m_hComplete);
98 }
99
100 return S_OK; // must return S_OK otherwise BITS just keeps calling back.
101 }
102
103 virtual STDMETHODIMP JobError(
104 __in IBackgroundCopyJob* /*pJob*/,
105 __in IBackgroundCopyError* pError
106 )
107 {
108 HRESULT hr = S_OK;
109 BG_ERROR_CONTEXT context = BG_ERROR_CONTEXT_NONE;
110 HRESULT hrError = S_OK;
111
112 hr = pError->GetError(&context, &hrError);
113 ExitOnFailure(hr, "Failed to get error context.");
114
115 if (SUCCEEDED(hrError))
116 {
117 hr = E_UNEXPECTED;
118 }
119
120 LExit:
121 ProcessResult(context, FAILED(hrError) ? hrError : hr);
122
123 return S_OK; // must return S_OK otherwise BITS just keeps calling back.
124 }
125
126 virtual STDMETHODIMP JobModification(
127 __in IBackgroundCopyJob* pJob,
128 __in DWORD /*dwReserved*/
129 )
130 {
131 HRESULT hr = S_OK;
132 BG_JOB_STATE state = BG_JOB_STATE_ERROR;
133
134 ::EnterCriticalSection(&m_cs);
135
136 hr = pJob->GetState(&state);
137 ExitOnFailure(hr, "Failed to get state during job modification.");
138
139 // If we're actually downloading stuff, let's send progress.
140 if (BG_JOB_STATE_TRANSFERRING == state)
141 {
142 hr = SendProgress(pJob);
143 ExitOnFailure(hr, "Failure while sending progress during BITS job modification.");
144 }
145
146 LExit:
147 ::LeaveCriticalSection(&m_cs);
148
149 ProcessResult(BG_ERROR_CONTEXT_NONE, hr);
150
151 return S_OK; // documentation says to always return S_OK
152 }
153
154public:
155 void Reset()
156 {
157 m_hrError = S_OK;
158 m_contextError = BG_ERROR_CONTEXT_NONE;
159
160 ::ResetEvent(m_hComplete);
161 }
162
163 HRESULT WaitForCompletion(
164 __in IBackgroundCopyJob* pJob
165 )
166 {
167 HRESULT hr = S_OK;
168 HANDLE rghEvents[1] = { m_hComplete };
169 MSG msg = { };
170 BOOL fMessageProcessed = FALSE;
171
172 do
173 {
174 fMessageProcessed = FALSE;
175
176 switch (::MsgWaitForMultipleObjects(countof(rghEvents), rghEvents, FALSE, BITSENGINE_MSG_WAIT_TIMEOUT * 1000, QS_ALLINPUT))
177 {
178 case WAIT_OBJECT_0:
179 break;
180
181 case WAIT_OBJECT_0 + 1:
182 ::PeekMessageW(&msg, NULL, 0, 0, PM_NOREMOVE);
183 fMessageProcessed = TRUE;
184 break;
185
186 case WAIT_TIMEOUT:
187 // Call the progress callback periodically if we are not transferring to ensure that cancelling is responsive
188 // (progress callback is also handles cancelling). Note that if we are transferring, IBackgroundCopyCallback
189 // methods handle progress/cancelling. If we are not transferring, the IBackgroundCopyCallback methods may
190 // not be called until the job times out (minutes for a foreground job, weeks for a background job).
191 SendProgressIfNotTransferring(pJob);
192 fMessageProcessed = TRUE;
193 break;
194
195 default:
196 ExitWithLastError(hr, "Failed while waiting for download.");
197 }
198 } while (fMessageProcessed);
199
200 LExit:
201 return hr;
202 }
203
204 void GetError(
205 __out HRESULT* pHR,
206 __out BG_ERROR_CONTEXT* pContext
207 )
208 {
209 *pHR = m_hrError;
210 *pContext = m_contextError;
211 }
212
213private:
214 HRESULT SendProgress(
215 __in IBackgroundCopyJob* pJob
216 )
217 {
218 HRESULT hr = S_OK;
219 BG_JOB_PROGRESS progress = { };
220
221 if (m_pCallback && m_pCallback->pfnProgress)
222 {
223 hr = pJob->GetProgress(&progress);
224 ExitOnFailure(hr, "Failed to get progress when BITS job was transferred.");
225
226 hr = CacheSendProgressCallback(m_pCallback, progress.BytesTransferred, progress.BytesTotal, INVALID_HANDLE_VALUE);
227 ExitOnFailure(hr, "Failed to send progress from BITS job.");
228 }
229
230 LExit:
231 return hr;
232 }
233
234 void SendProgressIfNotTransferring(
235 __in IBackgroundCopyJob* pJob
236 )
237 {
238 HRESULT hr = S_OK;
239 BG_JOB_STATE state = BG_JOB_STATE_ERROR;
240
241 ::EnterCriticalSection(&m_cs);
242
243 hr = pJob->GetState(&state);
244 ExitOnFailure(hr, "Failed to get BITS job state.");
245
246 if (BG_JOB_STATE_TRANSFERRING != state)
247 {
248 hr = SendProgress(pJob);
249 ExitOnFailure(hr, "Failure while sending progress.");
250 }
251
252 LExit:
253 ::LeaveCriticalSection(&m_cs);
254
255 ProcessResult(BG_ERROR_CONTEXT_NONE, hr);
256 }
257
258 void ProcessResult(
259 __in BG_ERROR_CONTEXT context,
260 __in HRESULT hr
261 )
262 {
263 if (FAILED(hr))
264 {
265 m_contextError = context;
266 m_hrError = hr;
267
268 ::SetEvent(m_hComplete);
269 }
270 }
271
272public:
273 CBurnBitsCallback(
274 __in_opt DOWNLOAD_CACHE_CALLBACK* pCallback,
275 __out HRESULT* pHR
276 )
277 {
278 HRESULT hr = S_OK;
279
280 m_cReferences = 1;
281 ::InitializeCriticalSection(&m_cs);
282
283 m_hComplete = ::CreateEventW(NULL, TRUE, FALSE, NULL);
284 ExitOnNullWithLastError(m_hComplete, hr, "Failed to create BITS job complete event.");
285
286 m_contextError = BG_ERROR_CONTEXT_NONE;
287 m_hrError = S_OK;
288
289 m_pCallback = pCallback;
290
291 LExit:
292 *pHR = hr;
293 }
294
295 ~CBurnBitsCallback()
296 {
297 m_pCallback = NULL;
298 ReleaseHandle(m_hComplete);
299 ::DeleteCriticalSection(&m_cs);
300 }
301
302private:
303 long m_cReferences;
304 CRITICAL_SECTION m_cs;
305 BG_ERROR_CONTEXT m_contextError;
306 HRESULT m_hrError;
307
308 HANDLE m_hComplete;
309 DOWNLOAD_CACHE_CALLBACK* m_pCallback;
310};
311
312
313extern "C" HRESULT BitsDownloadUrl(
314 __in DOWNLOAD_CACHE_CALLBACK* pCallback,
315 __in DOWNLOAD_SOURCE* pDownloadSource,
316 __in_z LPCWSTR wzDestinationPath
317 )
318{
319 HRESULT hr = S_OK;
320 LPWSTR sczDownloadUrl = NULL;
321 CBurnBitsCallback* pBitsCallback = NULL;
322 IBackgroundCopyJob* pJob = NULL;
323 BOOL fRetry = FALSE;
324 BG_ERROR_CONTEXT contextError = BG_ERROR_CONTEXT_NONE;
325
326 // If the URL isn't at least 8 characters long (e.g.: "bits://X") then it
327 // isn't going to do us any good.
328 if (8 > lstrlenW(pDownloadSource->sczUrl))
329 {
330 hr = E_INVALIDARG;
331 ExitOnRootFailure(hr, "Invalid BITS engine URL: %ls", pDownloadSource->sczUrl);
332 }
333
334 // Fix the URL to be "http" instead of "bits".
335 hr = StrAllocString(&sczDownloadUrl, pDownloadSource->sczUrl, 0);
336 ExitOnFailure(hr, "Failed to copy download URL.");
337
338 sczDownloadUrl[0] = L'h';
339 sczDownloadUrl[1] = L't';
340 sczDownloadUrl[2] = L't';
341 sczDownloadUrl[3] = L'p';
342
343 // Create and configure the BITS job.
344 hr = CreateJob(&pJob);
345 ExitOnFailure(hr, "Failed to create BITS job.");
346
347 hr = SetCredentials(pJob, pDownloadSource->sczUser, pDownloadSource->sczPassword);
348 ExitOnFailure(hr, "Failed to set credentials for BITS job.");
349
350 hr = pJob->AddFile(sczDownloadUrl, wzDestinationPath);
351 ExitOnFailure(hr, "Failed to add file to BITS job.");
352
353 // Set the callback into the BITs job.
354 pBitsCallback = new CBurnBitsCallback(pCallback, &hr);
355 ExitOnNull(pBitsCallback, hr, E_OUTOFMEMORY, "Failed to create BITS job callback.");
356 ExitOnFailure(hr, "Failed to initialize BITS job callback.");
357
358 hr = pJob->SetNotifyInterface(pBitsCallback);
359 ExitOnFailure(hr, "Failed to set callback interface for BITS job.");
360
361 // Go into our retry download loop.
362 do
363 {
364 fRetry = FALSE;
365
366 pBitsCallback->Reset(); // ensure we are ready for the download to start (again?).
367
368 hr = pJob->Resume();
369 ExitOnFailure(hr, "Falied to start BITS job.");
370
371 hr = pBitsCallback->WaitForCompletion(pJob);
372 ExitOnFailure(hr, "Failed while waiting for BITS download.");
373
374 // See if there are any errors.
375 pBitsCallback->GetError(&hr, &contextError);
376 if (HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hr)
377 {
378 ExitFunction();
379 }
380 else if (FAILED(hr))
381 {
382 SendError(pCallback, pJob, hr, contextError, &fRetry);
383 }
384 } while (fRetry);
385 ExitOnFailure(hr, "Failed to download BITS job.");
386
387 // After all that, we should have the file downloaded so complete the job to get
388 // the file copied to the destination.
389 hr = pJob->Complete();
390 ExitOnFailure(hr, "Failed to complete BITS job.");
391
392LExit:
393 if (pJob)
394 {
395 pJob->SetNotifyInterface(NULL);
396
397 // If we failed, kill the job.
398 if (FAILED(hr))
399 {
400 pJob->Cancel(); // TODO: should we cancel if we're going to retry the package? Probably the right thing to do.
401 }
402 }
403
404 ReleaseObject(pBitsCallback);
405 ReleaseObject(pJob);
406 ReleaseStr(sczDownloadUrl);
407
408 return hr;
409}
410
411static HRESULT CreateJob(
412 __out IBackgroundCopyJob** ppJob
413 )
414{
415 HRESULT hr = S_OK;
416 IBackgroundCopyManager* pBitsManager = NULL;
417 IBackgroundCopyJob* pJob = NULL;
418 GUID guidJob = { };
419
420 hr = ::CoCreateInstance(__uuidof(BackgroundCopyManager), NULL, CLSCTX_ALL, __uuidof(IBackgroundCopyManager), reinterpret_cast<LPVOID*>(&pBitsManager));
421 ExitOnFailure(hr, "Failed to create IBackgroundCopyManager.");
422
423 hr = pBitsManager->CreateJob(L"WixBurn", BG_JOB_TYPE_DOWNLOAD, &guidJob, &pJob);
424 ExitOnFailure(hr, "Failed to create BITS job.");
425
426 hr = pJob->SetNotifyFlags(BG_NOTIFY_JOB_TRANSFERRED | BG_NOTIFY_JOB_ERROR | BG_NOTIFY_JOB_MODIFICATION);
427 ExitOnFailure(hr, "Failed to set notification flags for BITS job.");
428
429 hr = pJob->SetNoProgressTimeout(BITSENGINE_NO_PROGRESS_TIMEOUT); // use 2 minutes since default is 14 days.
430 ExitOnFailure(hr, "Failed to set progress timeout.");
431
432 hr = pJob->SetPriority(BG_JOB_PRIORITY_FOREGROUND);
433 ExitOnFailure(hr, "Failed to set BITS job to foreground.");
434
435 *ppJob = pJob;
436 pJob = NULL;
437
438LExit:
439 ReleaseObject(pJob);
440 ReleaseObject(pBitsManager);
441
442 return hr;
443}
444
445static HRESULT SetCredentials(
446 __in IBackgroundCopyJob* pJob,
447 __in_z_opt LPCWSTR wzUser,
448 __in_z_opt LPCWSTR wzPassword
449 )
450{
451 HRESULT hr = S_OK;
452 IBackgroundCopyJob2* pJob2 = NULL;
453 BG_AUTH_CREDENTIALS ac = { };
454
455 // If IBackgroundCopyJob2::SetCredentials() is supported, set the username/password.
456 hr = pJob->QueryInterface(IID_PPV_ARGS(&pJob2));
457 if (SUCCEEDED(hr))
458 {
459 ac.Target = BG_AUTH_TARGET_PROXY;
460 ac.Credentials.Basic.UserName = const_cast<LPWSTR>(wzUser);
461 ac.Credentials.Basic.Password = const_cast<LPWSTR>(wzPassword);
462
463 ac.Scheme = BG_AUTH_SCHEME_NTLM;
464 hr = pJob2->SetCredentials(&ac);
465 ExitOnFailure(hr, "Failed to set background copy NTLM credentials");
466
467 ac.Scheme = BG_AUTH_SCHEME_NEGOTIATE;
468 hr = pJob2->SetCredentials(&ac);
469 ExitOnFailure(hr, "Failed to set background copy negotiate credentials");
470 }
471
472 hr = S_OK;
473
474LExit:
475 ReleaseObject(pJob2);
476
477 return hr;
478}
479
480static void SendError(
481 __in DOWNLOAD_CACHE_CALLBACK* pCacheCallback,
482 __in IBackgroundCopyJob* pJob,
483 __in HRESULT hrError,
484 __in BG_ERROR_CONTEXT /*context*/,
485 __out_opt BOOL* pfRetry
486 )
487{
488 HRESULT hr = S_OK;
489 IBackgroundCopyError* pError = NULL;
490 LPWSTR pszErrorDescription = NULL;
491
492 hr = pJob->GetError(&pError);
493 if (SUCCEEDED(hr))
494 {
495 pError->GetErrorDescription(LANGIDFROMLCID(::GetThreadLocale()), &pszErrorDescription);
496 }
497
498 CacheSendErrorCallback(pCacheCallback, hrError, pszErrorDescription, pfRetry);
499
500 if (pszErrorDescription)
501 {
502 ::CoTaskMemFree(pszErrorDescription);
503 }
504 ReleaseObject(pError);
505}
diff --git a/src/engine/bitsengine.h b/src/engine/bitsengine.h
new file mode 100644
index 00000000..b1c1d805
--- /dev/null
+++ b/src/engine/bitsengine.h
@@ -0,0 +1,23 @@
1#pragma once
2// 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.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9// structs
10
11
12// functions
13
14HRESULT BitsDownloadUrl(
15 __in DOWNLOAD_CACHE_CALLBACK* pCallback,
16 __in DOWNLOAD_SOURCE* pDownloadSource,
17 __in LPCWSTR wzDestinationPath
18 );
19
20
21#ifdef __cplusplus
22}
23#endif
diff --git a/src/engine/cabextract.cpp b/src/engine/cabextract.cpp
new file mode 100644
index 00000000..04c2c6ec
--- /dev/null
+++ b/src/engine/cabextract.cpp
@@ -0,0 +1,974 @@
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#include "precomp.h"
4
5#include <fdi.h>
6
7#define ARRAY_GROWTH_SIZE 2
8
9const LPSTR INVALID_CAB_NAME = "<the>.cab";
10
11// structs
12
13typedef struct _BURN_CAB_CONTEXT
14{
15 HANDLE hFile;
16 DWORD64 qwOffset;
17 DWORD64 qwSize;
18
19 HANDLE hThread;
20 HANDLE hBeginOperationEvent;
21 HANDLE hOperationCompleteEvent;
22
23 BURN_CAB_OPERATION operation;
24 HRESULT hrError;
25
26 LPWSTR* psczStreamName;
27 LPCWSTR wzTargetFile;
28 HANDLE hTargetFile;
29 BYTE* pbTargetBuffer;
30 DWORD cbTargetBuffer;
31 DWORD iTargetBuffer;
32} BURN_CAB_CONTEXT;
33
34
35// internal function declarations
36
37static HRESULT BeginAndWaitForOperation(
38 __in BURN_CONTAINER_CONTEXT* pContext
39 );
40static HRESULT WaitForOperation(
41 __in BURN_CONTAINER_CONTEXT* pContext
42 );
43static DWORD WINAPI ExtractThreadProc(
44 __in LPVOID lpThreadParameter
45 );
46static INT_PTR DIAMONDAPI CabNotifyCallback(
47 __in FDINOTIFICATIONTYPE iNotification,
48 __inout FDINOTIFICATION *pFDINotify
49 );
50static INT_PTR CopyFileCallback(
51 __in BURN_CONTAINER_CONTEXT* pContext,
52 __inout FDINOTIFICATION *pFDINotify
53 );
54static INT_PTR CloseFileInfoCallback(
55 __in BURN_CONTAINER_CONTEXT* pContext,
56 __inout FDINOTIFICATION *pFDINotify
57 );
58static LPVOID DIAMONDAPI CabAlloc(
59 __in DWORD dwSize
60 );
61static void DIAMONDAPI CabFree(
62 __in LPVOID pvData
63 );
64static INT_PTR FAR DIAMONDAPI CabOpen(
65 __in char FAR *pszFile,
66 __in int /* oflag */,
67 __in int /* pmode */
68 );
69static UINT FAR DIAMONDAPI CabRead(
70 __in INT_PTR hf,
71 __out void FAR *pv,
72 __in UINT cb
73 );
74static UINT FAR DIAMONDAPI CabWrite(
75 __in INT_PTR hf,
76 __in void FAR *pv,
77 __in UINT cb
78 );
79static long FAR DIAMONDAPI CabSeek(
80 __in INT_PTR hf,
81 __in long dist,
82 __in int seektype
83 );
84static int FAR DIAMONDAPI CabClose(
85 __in INT_PTR hf
86 );
87static HRESULT AddVirtualFilePointer(
88 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
89 __in HANDLE hFile,
90 __in LONGLONG llInitialFilePointer
91 );
92static HRESULT ReadIfVirtualFilePointer(
93 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
94 __in HANDLE hFile,
95 __in DWORD cbRead
96 );
97static BOOL SetIfVirtualFilePointer(
98 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
99 __in HANDLE hFile,
100 __in LONGLONG llDistance,
101 __out LONGLONG* pllNewPostion,
102 __in DWORD dwSeekType
103 );
104static HRESULT CloseIfVirturalFilePointer(
105 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
106 __in HANDLE hFile
107 );
108static BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* GetVirtualFilePointer(
109 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
110 __in HANDLE hFile
111 );
112
113
114// internal variables
115
116__declspec(thread) static BURN_CONTAINER_CONTEXT* vpContext;
117
118
119// function definitions
120
121extern "C" void CabExtractInitialize()
122{
123}
124
125extern "C" HRESULT CabExtractOpen(
126 __in BURN_CONTAINER_CONTEXT* pContext,
127 __in LPCWSTR wzFilePath
128 )
129{
130 HRESULT hr = S_OK;
131
132 // initialize context
133 pContext->Cabinet.hTargetFile = INVALID_HANDLE_VALUE;
134
135 hr = StrAllocString(&pContext->Cabinet.sczFile, wzFilePath, 0);
136 ExitOnFailure(hr, "Failed to copy file name.");
137
138 // create events
139 pContext->Cabinet.hBeginOperationEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL);
140 ExitOnNullWithLastError(pContext->Cabinet.hBeginOperationEvent, hr, "Failed to create begin operation event.");
141
142 pContext->Cabinet.hOperationCompleteEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL);
143 ExitOnNullWithLastError(pContext->Cabinet.hOperationCompleteEvent, hr, "Failed to create operation complete event.");
144
145 // create extraction thread
146 pContext->Cabinet.hThread = ::CreateThread(NULL, 0, ExtractThreadProc, pContext, 0, NULL);
147 ExitOnNullWithLastError(pContext->Cabinet.hThread, hr, "Failed to create extraction thread.");
148
149 // wait for operation to complete
150 hr = WaitForOperation(pContext);
151 ExitOnFailure(hr, "Failed to wait for operation complete.");
152
153LExit:
154 return hr;
155}
156
157extern "C" HRESULT CabExtractNextStream(
158 __in BURN_CONTAINER_CONTEXT* pContext,
159 __inout_z LPWSTR* psczStreamName
160 )
161{
162 HRESULT hr = S_OK;
163
164 // set operation to move to next stream
165 pContext->Cabinet.operation = BURN_CAB_OPERATION_NEXT_STREAM;
166 pContext->Cabinet.psczStreamName = psczStreamName;
167
168 // begin operation and wait
169 hr = BeginAndWaitForOperation(pContext);
170 if (E_ABORT != hr && E_NOMOREITEMS != hr)
171 {
172 ExitOnFailure(hr, "Failed to begin and wait for operation.");
173 }
174
175LExit:
176 return hr;
177}
178
179extern "C" HRESULT CabExtractStreamToFile(
180 __in BURN_CONTAINER_CONTEXT* pContext,
181 __in_z LPCWSTR wzFileName
182 )
183{
184 HRESULT hr = S_OK;
185
186 // set operation to move to next stream
187 pContext->Cabinet.operation = BURN_CAB_OPERATION_STREAM_TO_FILE;
188 pContext->Cabinet.wzTargetFile = wzFileName;
189
190 // begin operation and wait
191 hr = BeginAndWaitForOperation(pContext);
192 ExitOnFailure(hr, "Failed to begin and wait for operation.");
193
194 // clear file name
195 pContext->Cabinet.wzTargetFile = NULL;
196
197LExit:
198 return hr;
199}
200
201extern "C" HRESULT CabExtractStreamToBuffer(
202 __in BURN_CONTAINER_CONTEXT* pContext,
203 __out BYTE** ppbBuffer,
204 __out SIZE_T* pcbBuffer
205 )
206{
207 HRESULT hr = S_OK;
208
209 // set operation to move to next stream
210 pContext->Cabinet.operation = BURN_CAB_OPERATION_STREAM_TO_BUFFER;
211
212 // begin operation and wait
213 hr = BeginAndWaitForOperation(pContext);
214 ExitOnFailure(hr, "Failed to begin and wait for operation.");
215
216 // return values
217 *ppbBuffer = pContext->Cabinet.pbTargetBuffer;
218 *pcbBuffer = pContext->Cabinet.cbTargetBuffer;
219
220 // clear buffer variables
221 pContext->Cabinet.pbTargetBuffer = NULL;
222 pContext->Cabinet.cbTargetBuffer = 0;
223 pContext->Cabinet.iTargetBuffer = 0;
224
225LExit:
226 return hr;
227}
228
229extern "C" HRESULT CabExtractSkipStream(
230 __in BURN_CONTAINER_CONTEXT* pContext
231 )
232{
233 HRESULT hr = S_OK;
234
235 // set operation to move to next stream
236 pContext->Cabinet.operation = BURN_CAB_OPERATION_SKIP_STREAM;
237
238 // begin operation and wait
239 hr = BeginAndWaitForOperation(pContext);
240 ExitOnFailure(hr, "Failed to begin and wait for operation.");
241
242LExit:
243 return hr;
244}
245
246extern "C" HRESULT CabExtractClose(
247 __in BURN_CONTAINER_CONTEXT* pContext
248 )
249{
250 HRESULT hr = S_OK;
251
252 // terminate worker thread
253 if (pContext->Cabinet.hThread)
254 {
255 // set operation to move to close
256 pContext->Cabinet.operation = BURN_CAB_OPERATION_CLOSE;
257
258 // set begin operation event
259 if (!::SetEvent(pContext->Cabinet.hBeginOperationEvent))
260 {
261 ExitWithLastError(hr, "Failed to set begin operation event.");
262 }
263
264 // wait for thread to terminate
265 if (WAIT_OBJECT_0 != ::WaitForSingleObject(pContext->Cabinet.hThread, INFINITE))
266 {
267 ExitWithLastError(hr, "Failed to wait for thread to terminate.");
268 }
269 }
270
271LExit:
272 ReleaseHandle(pContext->Cabinet.hThread);
273 ReleaseHandle(pContext->Cabinet.hBeginOperationEvent);
274 ReleaseHandle(pContext->Cabinet.hOperationCompleteEvent);
275 ReleaseMem(pContext->Cabinet.rgVirtualFilePointers);
276 ReleaseStr(pContext->Cabinet.sczFile);
277
278 return hr;
279}
280
281
282// internal helper functions
283
284static HRESULT BeginAndWaitForOperation(
285 __in BURN_CONTAINER_CONTEXT* pContext
286 )
287{
288 HRESULT hr = S_OK;
289
290 // set begin operation event
291 if (!::SetEvent(pContext->Cabinet.hBeginOperationEvent))
292 {
293 ExitWithLastError(hr, "Failed to set begin operation event.");
294 }
295
296 // wait for operation to complete
297 hr = WaitForOperation(pContext);
298
299LExit:
300 return hr;
301}
302
303static HRESULT WaitForOperation(
304 __in BURN_CONTAINER_CONTEXT* pContext
305 )
306{
307 HRESULT hr = S_OK;
308 HANDLE rghWait[2] = { };
309
310 // wait for operation complete event
311 rghWait[0] = pContext->Cabinet.hOperationCompleteEvent;
312 rghWait[1] = pContext->Cabinet.hThread;
313 switch (::WaitForMultipleObjects(countof(rghWait), rghWait, FALSE, INFINITE))
314 {
315 case WAIT_OBJECT_0:
316 if (!::ResetEvent(pContext->Cabinet.hOperationCompleteEvent))
317 {
318 ExitWithLastError(hr, "Failed to reset operation complete event.");
319 }
320 break;
321
322 case WAIT_OBJECT_0 + 1:
323 if (!::GetExitCodeThread(pContext->Cabinet.hThread, (DWORD*)&hr))
324 {
325 ExitWithLastError(hr, "Failed to get extraction thread exit code.");
326 }
327 ExitFunction();
328
329 case WAIT_FAILED: __fallthrough;
330 default:
331 ExitWithLastError(hr, "Failed to wait for operation complete event.");
332 }
333
334 // clear operation
335 pContext->Cabinet.operation = BURN_CAB_OPERATION_NONE;
336
337LExit:
338 return hr;
339}
340
341static DWORD WINAPI ExtractThreadProc(
342 __in LPVOID lpThreadParameter
343 )
344{
345 HRESULT hr = S_OK;
346 BURN_CONTAINER_CONTEXT* pContext = (BURN_CONTAINER_CONTEXT*)lpThreadParameter;
347 BOOL fComInitialized = FALSE;
348 HFDI hfdi = NULL;
349 ERF erf = { };
350
351 // initialize COM
352 hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
353 ExitOnFailure(hr, "Failed to initialize COM.");
354 fComInitialized = TRUE;
355
356 // save context in TLS storage
357 vpContext = pContext;
358
359 // create FDI context
360 hfdi = ::FDICreate(CabAlloc, CabFree, CabOpen, CabRead, CabWrite, CabClose, CabSeek, cpuUNKNOWN, &erf);
361 ExitOnNull(hfdi, hr, E_FAIL, "Failed to initialize cabinet.dll.");
362
363 // begin CAB extraction
364 if (!::FDICopy(hfdi, INVALID_CAB_NAME, "", 0, CabNotifyCallback, NULL, NULL))
365 {
366 hr = pContext->Cabinet.hrError;
367 if (E_ABORT == hr || E_NOMOREITEMS == hr)
368 {
369 ExitFunction();
370 }
371 else if (SUCCEEDED(hr))
372 {
373 if (ERROR_SUCCESS != erf.erfType)
374 {
375 hr = HRESULT_FROM_WIN32(erf.erfType);
376 }
377 else
378 {
379 switch (erf.erfOper)
380 {
381 case FDIERROR_NONE:
382 hr = E_UNEXPECTED;
383 break;
384 case FDIERROR_CABINET_NOT_FOUND:
385 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
386 break;
387 case FDIERROR_NOT_A_CABINET:
388 hr = HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
389 break;
390 case FDIERROR_UNKNOWN_CABINET_VERSION:
391 hr = HRESULT_FROM_WIN32(ERROR_VERSION_PARSE_ERROR);
392 break;
393 case FDIERROR_CORRUPT_CABINET:
394 hr = HRESULT_FROM_WIN32(ERROR_FILE_CORRUPT);
395 break;
396 case FDIERROR_ALLOC_FAIL:
397 hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
398 break;
399 case FDIERROR_BAD_COMPR_TYPE:
400 hr = HRESULT_FROM_WIN32(ERROR_UNSUPPORTED_COMPRESSION);
401 break;
402 case FDIERROR_MDI_FAIL:
403 hr = HRESULT_FROM_WIN32(ERROR_BAD_COMPRESSION_BUFFER);
404 break;
405 case FDIERROR_TARGET_FILE:
406 hr = HRESULT_FROM_WIN32(ERROR_WRITE_FAULT);
407 break;
408 case FDIERROR_RESERVE_MISMATCH:
409 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
410 break;
411 case FDIERROR_WRONG_CABINET:
412 hr = HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH);
413 break;
414 case FDIERROR_USER_ABORT:
415 hr = E_ABORT;
416 break;
417 default:
418 hr = E_FAIL;
419 break;
420 }
421 }
422 }
423 ExitOnFailure(hr, "Failed to extract all files from container, erf: %d:%X:%d", erf.fError, erf.erfOper, erf.erfType);
424 }
425
426 // set operation complete event
427 if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent))
428 {
429 ExitWithLastError(hr, "Failed to set operation complete event.");
430 }
431
432 // wait for begin operation event
433 if (WAIT_FAILED == ::WaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE))
434 {
435 ExitWithLastError(hr, "Failed to wait for begin operation event.");
436 }
437
438 if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent))
439 {
440 ExitWithLastError(hr, "Failed to reset begin operation event.");
441 }
442
443 // read operation
444 switch (pContext->Cabinet.operation)
445 {
446 case BURN_CAB_OPERATION_NEXT_STREAM:
447 ExitFunction1(hr = E_NOMOREITEMS);
448 break;
449
450 case BURN_CAB_OPERATION_CLOSE:
451 ExitFunction1(hr = S_OK);
452
453 default:
454 hr = E_INVALIDSTATE;
455 ExitOnRootFailure(hr, "Invalid operation for this state.");
456 }
457
458LExit:
459 if (hfdi)
460 {
461 ::FDIDestroy(hfdi);
462 }
463 if (fComInitialized)
464 {
465 ::CoUninitialize();
466 }
467
468 return (DWORD)hr;
469}
470
471static INT_PTR DIAMONDAPI CabNotifyCallback(
472 __in FDINOTIFICATIONTYPE iNotification,
473 __inout FDINOTIFICATION *pFDINotify
474 )
475{
476 BURN_CONTAINER_CONTEXT* pContext = vpContext;
477 INT_PTR ipResult = 0; // result to return on success
478
479 switch (iNotification)
480 {
481 case fdintCOPY_FILE:
482 ipResult = CopyFileCallback(pContext, pFDINotify);
483 break;
484
485 case fdintCLOSE_FILE_INFO: // resource extraction complete
486 ipResult = CloseFileInfoCallback(pContext, pFDINotify);
487 break;
488
489 case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages
490 case fdintNEXT_CABINET: __fallthrough;
491 case fdintENUMERATE: __fallthrough;
492 case fdintCABINET_INFO:
493 break;
494
495 default:
496 AssertSz(FALSE, "CabExtractCallback() - unknown FDI notification command");
497 };
498
499//LExit:
500 return ipResult;
501}
502
503static INT_PTR CopyFileCallback(
504 __in BURN_CONTAINER_CONTEXT* pContext,
505 __inout FDINOTIFICATION* pFDINotify
506 )
507{
508 HRESULT hr = S_OK;
509 INT_PTR ipResult = 1; // result to return on success
510 LPWSTR pwzPath = NULL;
511 LARGE_INTEGER li = { };
512
513 // set operation complete event
514 if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent))
515 {
516 ExitWithLastError(hr, "Failed to set operation complete event.");
517 }
518
519 // wait for begin operation event
520 if (WAIT_FAILED == ::WaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE))
521 {
522 ExitWithLastError(hr, "Failed to wait for begin operation event.");
523 }
524
525 if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent))
526 {
527 ExitWithLastError(hr, "Failed to reset begin operation event.");
528 }
529
530 // read operation
531 switch (pContext->Cabinet.operation)
532 {
533 case BURN_CAB_OPERATION_NEXT_STREAM:
534 break;
535
536 case BURN_CAB_OPERATION_CLOSE:
537 ExitFunction1(hr = E_ABORT);
538
539 default:
540 hr = E_INVALIDSTATE;
541 ExitOnRootFailure(hr, "Invalid operation for this state.");
542 }
543
544 // copy stream name
545 hr = StrAllocStringAnsi(pContext->Cabinet.psczStreamName, pFDINotify->psz1, 0, CP_UTF8);
546 ExitOnFailure(hr, "Failed to copy stream name: %ls", pFDINotify->psz1);
547
548 // set operation complete event
549 if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent))
550 {
551 ExitWithLastError(hr, "Failed to set operation complete event.");
552 }
553
554 // wait for begin operation event
555 if (WAIT_FAILED == ::WaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE))
556 {
557 ExitWithLastError(hr, "Failed to wait for begin operation event.");
558 }
559
560 if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent))
561 {
562 ExitWithLastError(hr, "Failed to reset begin operation event.");
563 }
564
565 // read operation
566 switch (pContext->Cabinet.operation)
567 {
568 case BURN_CAB_OPERATION_STREAM_TO_FILE:
569 // create file
570 pContext->Cabinet.hTargetFile = ::CreateFileW(pContext->Cabinet.wzTargetFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
571 if (INVALID_HANDLE_VALUE == pContext->Cabinet.hTargetFile)
572 {
573 ExitWithLastError(hr, "Failed to create file: %ls", pContext->Cabinet.wzTargetFile);
574 }
575
576 // set file size
577 li.QuadPart = pFDINotify->cb;
578 if (!::SetFilePointerEx(pContext->Cabinet.hTargetFile, li, NULL, FILE_BEGIN))
579 {
580 ExitWithLastError(hr, "Failed to set file pointer to end of file.");
581 }
582
583 if (!::SetEndOfFile(pContext->Cabinet.hTargetFile))
584 {
585 ExitWithLastError(hr, "Failed to set end of file.");
586 }
587
588 li.QuadPart = 0;
589 if (!::SetFilePointerEx(pContext->Cabinet.hTargetFile, li, NULL, FILE_BEGIN))
590 {
591 ExitWithLastError(hr, "Failed to set file pointer to beginning of file.");
592 }
593
594 break;
595
596 case BURN_CAB_OPERATION_STREAM_TO_BUFFER:
597 // allocate buffer for stream
598 pContext->Cabinet.pbTargetBuffer = (BYTE*)MemAlloc(pFDINotify->cb, TRUE);
599 ExitOnNull(pContext->Cabinet.pbTargetBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for stream.");
600
601 // set buffer size and write position
602 pContext->Cabinet.cbTargetBuffer = pFDINotify->cb;
603 pContext->Cabinet.iTargetBuffer = 0;
604
605 break;
606
607 case BURN_CAB_OPERATION_SKIP_STREAM:
608 ipResult = 0;
609 break;
610
611 case BURN_CAB_OPERATION_CLOSE:
612 ExitFunction1(hr = E_ABORT);
613
614 default:
615 hr = E_INVALIDSTATE;
616 ExitOnRootFailure(hr, "Invalid operation for this state.");
617 }
618
619LExit:
620 ReleaseStr(pwzPath);
621
622 pContext->Cabinet.hrError = hr;
623 return SUCCEEDED(hr) ? ipResult : -1;
624}
625
626static INT_PTR CloseFileInfoCallback(
627 __in BURN_CONTAINER_CONTEXT* pContext,
628 __inout FDINOTIFICATION *pFDINotify
629 )
630{
631 HRESULT hr = S_OK;
632 INT_PTR ipResult = 1; // result to return on success
633 FILETIME ftLocal = { };
634 FILETIME ft = { };
635
636 // read operation
637 switch (pContext->Cabinet.operation)
638 {
639 case BURN_CAB_OPERATION_STREAM_TO_FILE:
640 // Make a best effort to set the time on the new file before
641 // we close it.
642 if (::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ftLocal))
643 {
644 if (::LocalFileTimeToFileTime(&ftLocal, &ft))
645 {
646 ::SetFileTime(pContext->Cabinet.hTargetFile, &ft, &ft, &ft);
647 }
648 }
649
650 // close file
651 ReleaseFile(pContext->Cabinet.hTargetFile);
652 break;
653
654 case BURN_CAB_OPERATION_STREAM_TO_BUFFER:
655 break;
656
657 case BURN_CAB_OPERATION_CLOSE:
658 ExitFunction1(hr = E_ABORT);
659
660 default:
661 hr = E_INVALIDSTATE;
662 ExitOnRootFailure(hr, "Invalid operation for this state.");
663 }
664
665 //if (pContext->pfnProgress)
666 //{
667 // hr = StrAllocFormatted(&pwzPath, L"%s%ls", pContext->wzRootPath, pFDINotify->psz1);
668 // ExitOnFailure(hr, "Failed to calculate file path from: %ls and %s", pContext->wzRootPath, pFDINotify->psz1);
669 // if (SUCCEEDED(hr))
670 // {
671 // hr = pContext->pfnProgress(BOX_PROGRESS_DECOMPRESSION_END, pwzPath, 0, pContext->pvContext);
672 // if (S_OK != hr)
673 // {
674 // pContext->hrError = hr;
675 // ExitFunction();
676 // }
677 // }
678 //}
679
680LExit:
681 pContext->Cabinet.hrError = hr;
682 return SUCCEEDED(hr) ? ipResult : -1;
683}
684
685static LPVOID DIAMONDAPI CabAlloc(
686 __in DWORD dwSize
687 )
688{
689 return MemAlloc(dwSize, FALSE);
690}
691
692static void DIAMONDAPI CabFree(
693 __in LPVOID pvData
694 )
695{
696 MemFree(pvData);
697}
698
699static INT_PTR FAR DIAMONDAPI CabOpen(
700 __in char FAR * pszFile,
701 __in int /* oflag */,
702 __in int /* pmode */
703 )
704{
705 HRESULT hr = S_OK;
706 BURN_CONTAINER_CONTEXT* pContext = vpContext;
707 HANDLE hFile = INVALID_HANDLE_VALUE;
708
709 // If this is the invalid cab name, use our file handle.
710 if (CSTR_EQUAL == ::CompareStringA(LOCALE_NEUTRAL, 0, INVALID_CAB_NAME, -1, pszFile, -1))
711 {
712 if (!::DuplicateHandle(::GetCurrentProcess(), pContext->hFile, ::GetCurrentProcess(), &hFile, 0, FALSE, DUPLICATE_SAME_ACCESS))
713 {
714 ExitWithLastError(hr, "Failed to duplicate handle to cab container.");
715 }
716
717 // Use a virtual file pointer since duplicated file handles share their file pointer. Seek to container offset
718 // to start.
719 hr = AddVirtualFilePointer(&pContext->Cabinet, hFile, pContext->qwOffset);
720 ExitOnFailure(hr, "Failed to add virtual file pointer for cab container.");
721 }
722 else // open file requested. This is used in the rare cases where the CAB API wants to create a temp file.
723 {
724 hFile = ::CreateFileA(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
725 ExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open cabinet file: %hs", pszFile);
726 }
727
728LExit:
729 pContext->Cabinet.hrError = hr;
730 return FAILED(hr) ? -1 : (INT_PTR)hFile;
731}
732
733static UINT FAR DIAMONDAPI CabRead(
734 __in INT_PTR hf,
735 __out void FAR *pv,
736 __in UINT cb
737 )
738{
739 HRESULT hr = S_OK;
740 BURN_CONTAINER_CONTEXT* pContext = vpContext;
741 HANDLE hFile = (HANDLE)hf;
742 DWORD cbRead = 0;
743
744 ReadIfVirtualFilePointer(&pContext->Cabinet, hFile, cb);
745
746 if (!::ReadFile(hFile, pv, cb, &cbRead, NULL))
747 {
748 ExitWithLastError(hr, "Failed to read during cabinet extraction.");
749 }
750
751LExit:
752 pContext->Cabinet.hrError = hr;
753 return FAILED(hr) ? -1 : cbRead;
754}
755
756static UINT FAR DIAMONDAPI CabWrite(
757 __in INT_PTR /* hf */,
758 __in void FAR *pv,
759 __in UINT cb
760 )
761{
762 HRESULT hr = S_OK;
763 BURN_CONTAINER_CONTEXT* pContext = vpContext;
764 DWORD cbWrite = 0;
765
766 switch (pContext->Cabinet.operation)
767 {
768 case BURN_CAB_OPERATION_STREAM_TO_FILE:
769 // write file
770 if (!::WriteFile(pContext->Cabinet.hTargetFile, pv, cb, &cbWrite, NULL))
771 {
772 ExitWithLastError(hr, "Failed to write during cabinet extraction.");
773 }
774 break;
775
776 case BURN_CAB_OPERATION_STREAM_TO_BUFFER:
777 // copy to target buffer
778 memcpy_s(pContext->Cabinet.pbTargetBuffer + pContext->Cabinet.iTargetBuffer, pContext->Cabinet.cbTargetBuffer - pContext->Cabinet.iTargetBuffer, pv, cb);
779 pContext->Cabinet.iTargetBuffer += cb;
780
781 cbWrite = cb;
782 break;
783
784 default:
785 hr = E_INVALIDSTATE;
786 ExitOnFailure(hr, "Unexpected call to CabWrite().");
787 }
788
789LExit:
790 pContext->Cabinet.hrError = hr;
791 return FAILED(hr) ? -1 : cbWrite;
792}
793
794static long FAR DIAMONDAPI CabSeek(
795 __in INT_PTR hf,
796 __in long dist,
797 __in int seektype
798 )
799{
800 HRESULT hr = S_OK;
801 BURN_CONTAINER_CONTEXT* pContext = vpContext;
802 HANDLE hFile = (HANDLE)hf;
803 LARGE_INTEGER liDistance = { };
804 LARGE_INTEGER liNewPointer = { };
805 DWORD dwSeekType = 0;
806
807 // We assume that CabSeek() will only be called to seek the
808 // cabinet itself so we have to offset the seek operations to
809 // where the internal cabinet starts.
810 switch (seektype)
811 {
812 case FILE_BEGIN:
813 liDistance.QuadPart = pContext->qwOffset + dist;
814 dwSeekType = FILE_BEGIN;
815 break;
816
817 case FILE_CURRENT:
818 liDistance.QuadPart = dist;
819 dwSeekType = FILE_CURRENT;
820 break;
821
822 case FILE_END:
823 liDistance.QuadPart = pContext->qwOffset + pContext->qwSize + dist;
824 dwSeekType = FILE_BEGIN;
825 break;
826
827 default:
828 hr = E_INVALIDARG;
829 ExitOnFailure(hr, "Invalid seek type.");;
830 }
831
832 if (SetIfVirtualFilePointer(&pContext->Cabinet, hFile, liDistance.QuadPart, &liNewPointer.QuadPart, seektype))
833 {
834 // set file pointer
835 if (!::SetFilePointerEx(hFile, liDistance, &liNewPointer, seektype))
836 {
837 ExitWithLastError(hr, "Failed to move file pointer 0x%x bytes.", dist);
838 }
839 }
840
841 liNewPointer.QuadPart -= pContext->qwOffset;
842
843LExit:
844 pContext->Cabinet.hrError = hr;
845 return FAILED(hr) ? -1 : liNewPointer.LowPart;
846}
847
848static int FAR DIAMONDAPI CabClose(
849 __in INT_PTR hf
850 )
851{
852 BURN_CONTAINER_CONTEXT* pContext = vpContext;
853 HANDLE hFile = (HANDLE)hf;
854
855 CloseIfVirturalFilePointer(&pContext->Cabinet, hFile);
856 ReleaseFileHandle(hFile);
857
858 return 0;
859}
860
861static HRESULT AddVirtualFilePointer(
862 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
863 __in HANDLE hFile,
864 __in LONGLONG llInitialFilePointer
865 )
866{
867 HRESULT hr = S_OK;
868
869 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pCabinetContext->rgVirtualFilePointers), pCabinetContext->cVirtualFilePointers, sizeof(BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER), ARRAY_GROWTH_SIZE);
870 ExitOnFailure(hr, "Failed to allocate memory for the virtual file pointer array.");
871
872 pCabinetContext->rgVirtualFilePointers[pCabinetContext->cVirtualFilePointers].hFile = hFile;
873 pCabinetContext->rgVirtualFilePointers[pCabinetContext->cVirtualFilePointers].liPosition.QuadPart = llInitialFilePointer;
874 ++pCabinetContext->cVirtualFilePointers;
875
876LExit:
877 return hr;
878}
879
880static HRESULT ReadIfVirtualFilePointer(
881 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
882 __in HANDLE hFile,
883 __in DWORD cbRead
884 )
885{
886 HRESULT hr = E_NOTFOUND;
887
888 BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile);
889 if (pVfp)
890 {
891 // Set the file handle to the virtual file pointer.
892 if (!::SetFilePointerEx(hFile, pVfp->liPosition, NULL, FILE_BEGIN))
893 {
894 ExitWithLastError(hr, "Failed to move to virtual file pointer.");
895 }
896
897 pVfp->liPosition.QuadPart += cbRead; // add the amount that will be read to advance the pointer.
898 hr = S_OK;
899 }
900
901LExit:
902 return hr;
903}
904
905static BOOL SetIfVirtualFilePointer(
906 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
907 __in HANDLE hFile,
908 __in LONGLONG llDistance,
909 __out LONGLONG* pllNewPostion,
910 __in DWORD dwSeekType
911 )
912{
913 BOOL fFound = FALSE;
914
915 BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile);
916 if (pVfp)
917 {
918 switch (dwSeekType)
919 {
920 case FILE_BEGIN:
921 pVfp->liPosition.QuadPart = llDistance;
922 break;
923
924 case FILE_CURRENT:
925 pVfp->liPosition.QuadPart += llDistance;
926 break;
927
928 case FILE_END: __fallthrough;
929 default:
930 AssertSz(FALSE, "Unsupported seek type.");
931 break;
932 }
933
934 *pllNewPostion = pVfp->liPosition.QuadPart;
935 fFound = TRUE;
936 }
937
938 return fFound;
939}
940
941static HRESULT CloseIfVirturalFilePointer(
942 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
943 __in HANDLE hFile
944 )
945{
946 HRESULT hr = E_NOTFOUND;
947
948 BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile);
949 if (pVfp)
950 {
951 pVfp->hFile = INVALID_HANDLE_VALUE;
952 pVfp->liPosition.QuadPart = 0;
953 hr = S_OK;
954 }
955
956 return hr;
957}
958
959static BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* GetVirtualFilePointer(
960 __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext,
961 __in HANDLE hFile
962 )
963{
964 for (DWORD i = 0; i < pCabinetContext->cVirtualFilePointers; ++i)
965 {
966 BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = pCabinetContext->rgVirtualFilePointers + i;
967 if (pVfp->hFile == hFile)
968 {
969 return pVfp;
970 }
971 }
972
973 return NULL;
974}
diff --git a/src/engine/cabextract.h b/src/engine/cabextract.h
new file mode 100644
index 00000000..31667f2b
--- /dev/null
+++ b/src/engine/cabextract.h
@@ -0,0 +1,40 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// function declarations
11
12void CabExtractInitialize();
13HRESULT CabExtractOpen(
14 __in BURN_CONTAINER_CONTEXT* pContext,
15 __in LPCWSTR wzFilePath
16 );
17HRESULT CabExtractNextStream(
18 __in BURN_CONTAINER_CONTEXT* pContext,
19 __inout_z LPWSTR* psczStreamName
20 );
21HRESULT CabExtractStreamToFile(
22 __in BURN_CONTAINER_CONTEXT* pContext,
23 __in_z LPCWSTR wzFileName
24 );
25HRESULT CabExtractStreamToBuffer(
26 __in BURN_CONTAINER_CONTEXT* pContext,
27 __out BYTE** ppbBuffer,
28 __out SIZE_T* pcbBuffer
29 );
30HRESULT CabExtractSkipStream(
31 __in BURN_CONTAINER_CONTEXT* pContext
32 );
33HRESULT CabExtractClose(
34 __in BURN_CONTAINER_CONTEXT* pContext
35 );
36
37
38#if defined(__cplusplus)
39}
40#endif
diff --git a/src/engine/cache.cpp b/src/engine/cache.cpp
new file mode 100644
index 00000000..9338426d
--- /dev/null
+++ b/src/engine/cache.cpp
@@ -0,0 +1,2026 @@
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#include "precomp.h"
4
5static const LPCWSTR BUNDLE_CLEAN_ROOM_WORKING_FOLDER_NAME = L".cr";
6static const LPCWSTR BUNDLE_WORKING_FOLDER_NAME = L".be";
7static const LPCWSTR UNVERIFIED_CACHE_FOLDER_NAME = L".unverified";
8static const LPCWSTR PACKAGE_CACHE_FOLDER_NAME = L"Package Cache";
9static const DWORD FILE_OPERATION_RETRY_COUNT = 3;
10static const DWORD FILE_OPERATION_RETRY_WAIT = 2000;
11
12static BOOL vfInitializedCache = FALSE;
13static BOOL vfRunningFromCache = FALSE;
14static LPWSTR vsczSourceProcessPath = NULL;
15static LPWSTR vsczWorkingFolder = NULL;
16static LPWSTR vsczDefaultUserPackageCache = NULL;
17static LPWSTR vsczDefaultMachinePackageCache = NULL;
18static LPWSTR vsczCurrentMachinePackageCache = NULL;
19
20static HRESULT CalculateWorkingFolder(
21 __in_z LPCWSTR wzBundleId,
22 __deref_out_z LPWSTR* psczWorkingFolder
23 );
24static HRESULT GetLastUsedSourceFolder(
25 __in BURN_VARIABLES* pVariables,
26 __out_z LPWSTR* psczLastSource
27 );
28static HRESULT CreateCompletedPath(
29 __in BOOL fPerMachine,
30 __in LPCWSTR wzCacheId,
31 __out LPWSTR* psczCacheDirectory
32 );
33static HRESULT CreateUnverifiedPath(
34 __in BOOL fPerMachine,
35 __in_z LPCWSTR wzPayloadId,
36 __out_z LPWSTR* psczUnverifiedPayloadPath
37 );
38static HRESULT GetRootPath(
39 __in BOOL fPerMachine,
40 __in BOOL fAllowRedirect,
41 __deref_out_z LPWSTR* psczRootPath
42 );
43static HRESULT VerifyThenTransferContainer(
44 __in BURN_CONTAINER* pContainer,
45 __in_z LPCWSTR wzCachedPath,
46 __in_z LPCWSTR wzUnverifiedContainerPath,
47 __in BOOL fMove
48 );
49static HRESULT VerifyThenTransferPayload(
50 __in BURN_PAYLOAD* pPayload,
51 __in_z LPCWSTR wzCachedPath,
52 __in_z LPCWSTR wzUnverifiedPayloadPath,
53 __in BOOL fMove
54 );
55static HRESULT TransferWorkingPathToUnverifiedPath(
56 __in_z LPCWSTR wzWorkingPath,
57 __in_z LPCWSTR wzUnverifiedPayloadPath,
58 __in BOOL fMove
59 );
60static HRESULT VerifyFileAgainstPayload(
61 __in BURN_PAYLOAD* pPayload,
62 __in_z LPCWSTR wzVerifyPath
63 );
64static HRESULT ResetPathPermissions(
65 __in BOOL fPerMachine,
66 __in_z LPCWSTR wzPath
67 );
68static HRESULT SecurePath(
69 __in LPCWSTR wzPath
70 );
71static HRESULT CopyEngineToWorkingFolder(
72 __in_z LPCWSTR wzSourcePath,
73 __in_z LPCWSTR wzWorkingFolderName,
74 __in_z LPCWSTR wzExecutableName,
75 __in BURN_PAYLOADS* pUxPayloads,
76 __in BURN_SECTION* pSection,
77 __deref_out_z_opt LPWSTR* psczEngineWorkingPath
78 );
79static HRESULT CopyEngineWithSignatureFixup(
80 __in HANDLE hEngineFile,
81 __in_z LPCWSTR wzEnginePath,
82 __in_z LPCWSTR wzTargetPath,
83 __in BURN_SECTION* pSection
84 );
85static HRESULT RemoveBundleOrPackage(
86 __in BOOL fBundle,
87 __in BOOL fPerMachine,
88 __in_z LPCWSTR wzBundleOrPackageId,
89 __in_z LPCWSTR wzCacheId
90 );
91static HRESULT VerifyHash(
92 __in BYTE* pbHash,
93 __in DWORD cbHash,
94 __in_z LPCWSTR wzUnverifiedPayloadPath,
95 __in HANDLE hFile
96 );
97static HRESULT VerifyPayloadWithCatalog(
98 __in BURN_PAYLOAD* pPayload,
99 __in_z LPCWSTR wzUnverifiedPayloadPath,
100 __in HANDLE hFile
101 );
102static HRESULT VerifyPayloadAgainstChain(
103 __in BURN_PAYLOAD* pPayload,
104 __in PCCERT_CHAIN_CONTEXT pChainContext
105 );
106
107
108extern "C" HRESULT CacheInitialize(
109 __in BURN_REGISTRATION* pRegistration,
110 __in BURN_VARIABLES* pVariables,
111 __in_z_opt LPCWSTR wzSourceProcessPath
112 )
113{
114 HRESULT hr = S_OK;
115 LPWSTR sczCurrentPath = NULL;
116 LPWSTR sczCompletedFolder = NULL;
117 LPWSTR sczCompletedPath = NULL;
118 LPWSTR sczOriginalSource = NULL;
119 LPWSTR sczOriginalSourceFolder = NULL;
120 int nCompare = 0;
121
122 if (!vfInitializedCache)
123 {
124 hr = PathForCurrentProcess(&sczCurrentPath, NULL);
125 ExitOnFailure(hr, "Failed to get current process path.");
126
127 // Determine if we are running from the package cache or not.
128 hr = CacheGetCompletedPath(pRegistration->fPerMachine, pRegistration->sczId, &sczCompletedFolder);
129 ExitOnFailure(hr, "Failed to get completed path for bundle.");
130
131 hr = PathConcat(sczCompletedFolder, pRegistration->sczExecutableName, &sczCompletedPath);
132 ExitOnFailure(hr, "Failed to combine working path with engine file name.");
133
134 hr = PathCompare(sczCurrentPath, sczCompletedPath, &nCompare);
135 ExitOnFailure(hr, "Failed to compare current path for bundle: %ls", sczCurrentPath);
136
137 vfRunningFromCache = (CSTR_EQUAL == nCompare);
138
139 // If a source process path was not provided (e.g. we are not being
140 // run in a clean room) then use the current process path as the
141 // source process path.
142 if (!wzSourceProcessPath)
143 {
144 wzSourceProcessPath = sczCurrentPath;
145 }
146
147 hr = StrAllocString(&vsczSourceProcessPath, wzSourceProcessPath, 0);
148 ExitOnFailure(hr, "Failed to initialize cache source path.");
149
150 // If we're not running from the cache, ensure the original source is set.
151 if (!vfRunningFromCache)
152 {
153 // If the original source has not been set already then set it where the bundle is
154 // running from right now. This value will be persisted and we'll use it when launched
155 // from the clean room or package cache since none of our packages will be relative to
156 // those locations.
157 hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, &sczOriginalSource);
158 if (E_NOTFOUND == hr)
159 {
160 hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, wzSourceProcessPath, FALSE);
161 ExitOnFailure(hr, "Failed to set original source variable.");
162
163 hr = StrAllocString(&sczOriginalSource, wzSourceProcessPath, 0);
164 ExitOnFailure(hr, "Failed to copy current path to original source.");
165 }
166
167 hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, &sczOriginalSourceFolder);
168 if (E_NOTFOUND == hr)
169 {
170 hr = PathGetDirectory(sczOriginalSource, &sczOriginalSourceFolder);
171 ExitOnFailure(hr, "Failed to get directory from original source path.");
172
173 hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, sczOriginalSourceFolder, FALSE);
174 ExitOnFailure(hr, "Failed to set original source directory variable.");
175 }
176 }
177
178 vfInitializedCache = TRUE;
179 }
180
181LExit:
182 ReleaseStr(sczCurrentPath);
183 ReleaseStr(sczCompletedFolder);
184 ReleaseStr(sczCompletedPath);
185 ReleaseStr(sczOriginalSource);
186 ReleaseStr(sczOriginalSourceFolder);
187
188 return hr;
189}
190
191extern "C" HRESULT CacheEnsureWorkingFolder(
192 __in_z LPCWSTR wzBundleId,
193 __deref_out_z_opt LPWSTR* psczWorkingFolder
194 )
195{
196 HRESULT hr = S_OK;
197 LPWSTR sczWorkingFolder = NULL;
198
199 hr = CalculateWorkingFolder(wzBundleId, &sczWorkingFolder);
200 ExitOnFailure(hr, "Failed to calculate working folder to ensure it exists.");
201
202 hr = DirEnsureExists(sczWorkingFolder, NULL);
203 ExitOnFailure(hr, "Failed create working folder.");
204
205 // Best effort to ensure our working folder is not encrypted.
206 ::DecryptFileW(sczWorkingFolder, 0);
207
208 if (psczWorkingFolder)
209 {
210 hr = StrAllocString(psczWorkingFolder, sczWorkingFolder, 0);
211 ExitOnFailure(hr, "Failed to copy working folder.");
212 }
213
214LExit:
215 ReleaseStr(sczWorkingFolder);
216
217 return hr;
218}
219
220extern "C" HRESULT CacheCalculateBundleWorkingPath(
221 __in_z LPCWSTR wzBundleId,
222 __in LPCWSTR wzExecutableName,
223 __deref_out_z LPWSTR* psczWorkingPath
224 )
225{
226 Assert(vfInitializedCache);
227
228 HRESULT hr = S_OK;
229 LPWSTR sczWorkingFolder = NULL;
230
231 // If the bundle is running out of the package cache then we use that as the
232 // working folder since we feel safe in the package cache.
233 if (vfRunningFromCache)
234 {
235 hr = PathForCurrentProcess(psczWorkingPath, NULL);
236 ExitOnFailure(hr, "Failed to get current process path.");
237 }
238 else // Otherwise, use the real working folder.
239 {
240 hr = CalculateWorkingFolder(wzBundleId, &sczWorkingFolder);
241 ExitOnFailure(hr, "Failed to get working folder for bundle.");
242
243 hr = StrAllocFormatted(psczWorkingPath, L"%ls%ls\\%ls", sczWorkingFolder, BUNDLE_WORKING_FOLDER_NAME, wzExecutableName);
244 ExitOnFailure(hr, "Failed to calculate the bundle working path.");
245 }
246
247LExit:
248 ReleaseStr(sczWorkingFolder);
249
250 return hr;
251}
252
253extern "C" HRESULT CacheCalculateBundleLayoutWorkingPath(
254 __in_z LPCWSTR wzBundleId,
255 __deref_out_z LPWSTR* psczWorkingPath
256 )
257{
258 HRESULT hr = S_OK;
259 LPWSTR sczWorkingFolder = NULL;
260
261 hr = CalculateWorkingFolder(wzBundleId, psczWorkingPath);
262 ExitOnFailure(hr, "Failed to get working folder for bundle layout.");
263
264 hr = StrAllocConcat(psczWorkingPath, wzBundleId, 0);
265 ExitOnFailure(hr, "Failed to append bundle id for bundle layout working path.");
266
267LExit:
268 ReleaseStr(sczWorkingFolder);
269
270 return hr;
271}
272
273extern "C" HRESULT CacheCalculatePayloadWorkingPath(
274 __in_z LPCWSTR wzBundleId,
275 __in BURN_PAYLOAD* pPayload,
276 __deref_out_z LPWSTR* psczWorkingPath
277 )
278{
279 HRESULT hr = S_OK;
280
281 hr = CalculateWorkingFolder(wzBundleId, psczWorkingPath);
282 ExitOnFailure(hr, "Failed to get working folder for payload.");
283
284 hr = StrAllocConcat(psczWorkingPath, pPayload->sczKey, 0);
285 ExitOnFailure(hr, "Failed to append SHA1 hash as payload unverified path.");
286
287LExit:
288 return hr;
289}
290
291extern "C" HRESULT CacheCalculateContainerWorkingPath(
292 __in_z LPCWSTR wzBundleId,
293 __in BURN_CONTAINER* pContainer,
294 __deref_out_z LPWSTR* psczWorkingPath
295 )
296{
297 HRESULT hr = S_OK;
298
299 hr = CalculateWorkingFolder(wzBundleId, psczWorkingPath);
300 ExitOnFailure(hr, "Failed to get working folder for container.");
301
302 hr = StrAllocConcat(psczWorkingPath, pContainer->sczHash, 0);
303 ExitOnFailure(hr, "Failed to append SHA1 hash as container unverified path.");
304
305LExit:
306 return hr;
307}
308
309extern "C" HRESULT CacheGetRootCompletedPath(
310 __in BOOL fPerMachine,
311 __in BOOL fForceInitialize,
312 __deref_out_z LPWSTR* psczRootCompletedPath
313 )
314{
315 HRESULT hr = S_OK;
316
317 if (fForceInitialize)
318 {
319 hr = CreateCompletedPath(fPerMachine, L"", psczRootCompletedPath);
320 }
321 else
322 {
323 hr = GetRootPath(fPerMachine, TRUE, psczRootCompletedPath);
324 }
325
326 return hr;
327}
328
329extern "C" HRESULT CacheGetCompletedPath(
330 __in BOOL fPerMachine,
331 __in_z LPCWSTR wzCacheId,
332 __deref_out_z LPWSTR* psczCompletedPath
333 )
334{
335 HRESULT hr = S_OK;
336 BOOL fRedirected = FALSE;
337 LPWSTR sczRootPath = NULL;
338 LPWSTR sczCurrentCompletedPath = NULL;
339 LPWSTR sczDefaultCompletedPath = NULL;
340
341 hr = GetRootPath(fPerMachine, TRUE, &sczRootPath);
342 ExitOnFailure(hr, "Failed to get %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user");
343
344 // GetRootPath returns S_FALSE if the package cache is redirected elsewhere.
345 fRedirected = S_FALSE == hr;
346
347 hr = PathConcat(sczRootPath, wzCacheId, &sczCurrentCompletedPath);
348 ExitOnFailure(hr, "Failed to construct cache path.");
349
350 hr = PathBackslashTerminate(&sczCurrentCompletedPath);
351 ExitOnFailure(hr, "Failed to ensure cache path was backslash terminated.");
352
353 // Return the old package cache directory if the new directory does not exist but the old directory does.
354 // If neither package cache directory exists return the (possibly) redirected package cache directory.
355 if (fRedirected && !DirExists(sczCurrentCompletedPath, NULL))
356 {
357 hr = GetRootPath(fPerMachine, FALSE, &sczRootPath);
358 ExitOnFailure(hr, "Failed to get old %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user");
359
360 hr = PathConcat(sczRootPath, wzCacheId, &sczDefaultCompletedPath);
361 ExitOnFailure(hr, "Failed to construct cache path.");
362
363 hr = PathBackslashTerminate(&sczDefaultCompletedPath);
364 ExitOnFailure(hr, "Failed to ensure cache path was backslash terminated.");
365
366 if (DirExists(sczDefaultCompletedPath, NULL))
367 {
368 *psczCompletedPath = sczDefaultCompletedPath;
369 sczDefaultCompletedPath = NULL;
370
371 ExitFunction();
372 }
373 }
374
375 *psczCompletedPath = sczCurrentCompletedPath;
376 sczCurrentCompletedPath = NULL;
377
378LExit:
379 ReleaseNullStr(sczDefaultCompletedPath);
380 ReleaseNullStr(sczCurrentCompletedPath);
381 ReleaseNullStr(sczRootPath);
382
383 return hr;
384}
385
386extern "C" HRESULT CacheGetResumePath(
387 __in_z LPCWSTR wzPayloadWorkingPath,
388 __deref_out_z LPWSTR* psczResumePath
389 )
390{
391 HRESULT hr = S_OK;
392
393 hr = StrAllocFormatted(psczResumePath, L"%ls.R", wzPayloadWorkingPath);
394 ExitOnFailure(hr, "Failed to create resume path.");
395
396LExit:
397 return hr;
398}
399
400extern "C" HRESULT CacheFindLocalSource(
401 __in_z LPCWSTR wzSourcePath,
402 __in BURN_VARIABLES* pVariables,
403 __out BOOL* pfFound,
404 __out_z LPWSTR* psczSourceFullPath
405 )
406{
407 HRESULT hr = S_OK;
408 LPWSTR sczSourceProcessFolder = NULL;
409 LPWSTR sczCurrentPath = NULL;
410 LPWSTR sczLastSourcePath = NULL;
411 LPWSTR sczLastSourceFolder = NULL;
412 LPWSTR sczLayoutPath = NULL;
413 LPWSTR sczLayoutFolder = NULL;
414 LPCWSTR rgwzSearchPaths[3] = { };
415 DWORD cSearchPaths = 0;
416
417 // If the source path provided is a full path, obviously that is where we should be looking.
418 if (PathIsAbsolute(wzSourcePath))
419 {
420 rgwzSearchPaths[0] = wzSourcePath;
421 cSearchPaths = 1;
422 }
423 else
424 {
425 // If we're not running from cache or we couldn't get the last source, use
426 // the source path location first. In the case where we are in the bundle's
427 // package cache and couldn't find a last used source we unfortunately will
428 // be picking the package cache path which isn't likely to have what we are
429 // looking for.
430 hr = GetLastUsedSourceFolder(pVariables, &sczLastSourceFolder);
431 if (!vfRunningFromCache || FAILED(hr))
432 {
433 hr = PathGetDirectory(vsczSourceProcessPath, &sczSourceProcessFolder);
434 ExitOnFailure(hr, "Failed to get current process directory.");
435
436 hr = PathConcat(sczSourceProcessFolder, wzSourcePath, &sczCurrentPath);
437 ExitOnFailure(hr, "Failed to combine last source with source.");
438
439 rgwzSearchPaths[0] = sczCurrentPath;
440 cSearchPaths = 1;
441 }
442
443 // If we have a last used source and it does not duplicate the existing search path,
444 // add the last used source to the search path second.
445 if (sczLastSourceFolder && *sczLastSourceFolder)
446 {
447 hr = PathConcat(sczLastSourceFolder, wzSourcePath, &sczLastSourcePath);
448 ExitOnFailure(hr, "Failed to combine last source with source.");
449
450 if (0 == cSearchPaths || CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, rgwzSearchPaths[0], -1, sczLastSourcePath, -1))
451 {
452 rgwzSearchPaths[cSearchPaths] = sczLastSourcePath;
453 ++cSearchPaths;
454 }
455 }
456
457 // Also consider the layout directory if set on the command line or by the BA.
458 hr = VariableGetString(pVariables, BURN_BUNDLE_LAYOUT_DIRECTORY, &sczLayoutFolder);
459 if (E_NOTFOUND != hr)
460 {
461 ExitOnFailure(hr, "Failed to get bundle layout directory property.");
462
463 hr = PathConcat(sczLayoutFolder, wzSourcePath, &sczLayoutPath);
464 ExitOnFailure(hr, "Failed to combine layout source with source.");
465
466 rgwzSearchPaths[cSearchPaths] = sczLayoutPath;
467 ++cSearchPaths;
468 }
469 }
470
471 *pfFound = FALSE; // assume we won't find the file locally.
472
473 for (DWORD i = 0; i < cSearchPaths; ++i)
474 {
475 // If the file exists locally, copy its path.
476 if (FileExistsEx(rgwzSearchPaths[i], NULL))
477 {
478 hr = StrAllocString(psczSourceFullPath, rgwzSearchPaths[i], 0);
479 ExitOnFailure(hr, "Failed to copy source path.");
480
481 *pfFound = TRUE;
482 break;
483 }
484 }
485
486 // If nothing was found, return the first thing in our search path as the
487 // best path where we thought we should have found the file.
488 if (!*pfFound)
489 {
490 hr = StrAllocString(psczSourceFullPath, rgwzSearchPaths[0], 0);
491 ExitOnFailure(hr, "Failed to copy source path.");
492 }
493
494LExit:
495 ReleaseStr(sczCurrentPath);
496 ReleaseStr(sczSourceProcessFolder);
497 ReleaseStr(sczLastSourceFolder);
498 ReleaseStr(sczLastSourcePath);
499 ReleaseStr(sczLayoutFolder);
500 ReleaseStr(sczLayoutPath);
501
502 return hr;
503}
504
505extern "C" HRESULT CacheSetLastUsedSource(
506 __in BURN_VARIABLES* pVariables,
507 __in_z LPCWSTR wzSourcePath,
508 __in_z LPCWSTR wzRelativePath
509 )
510{
511 HRESULT hr = S_OK;
512 size_t cchSourcePath = 0;
513 size_t cchRelativePath = 0;
514 size_t iSourceRelativePath = 0;
515 LPWSTR sczSourceFolder = NULL;
516 LPWSTR sczLastSourceFolder = NULL;
517 int nCompare = 0;
518
519 hr = ::StringCchLengthW(wzSourcePath, STRSAFE_MAX_CCH, &cchSourcePath);
520 ExitOnFailure(hr, "Failed to determine length of source path.");
521
522 hr = ::StringCchLengthW(wzRelativePath, STRSAFE_MAX_CCH, &cchRelativePath);
523 ExitOnFailure(hr, "Failed to determine length of relative path.");
524
525 // If the source path is smaller than the relative path (plus space for "X:\") then we know they
526 // are not relative to each other.
527 if (cchSourcePath < cchRelativePath + 3)
528 {
529 ExitFunction();
530 }
531
532 // If the source path ends with the relative path then this source could be a new path.
533 iSourceRelativePath = cchSourcePath - cchRelativePath;
534 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzSourcePath + iSourceRelativePath, -1, wzRelativePath, -1))
535 {
536 hr = StrAllocString(&sczSourceFolder, wzSourcePath, iSourceRelativePath);
537 ExitOnFailure(hr, "Failed to trim source folder.");
538
539 hr = VariableGetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, &sczLastSourceFolder);
540 if (SUCCEEDED(hr))
541 {
542 nCompare = ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczSourceFolder, -1, sczLastSourceFolder, -1);
543 }
544 else if (E_NOTFOUND == hr)
545 {
546 nCompare = CSTR_GREATER_THAN;
547 hr = S_OK;
548 }
549
550 if (CSTR_EQUAL != nCompare)
551 {
552 hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, sczSourceFolder, FALSE);
553 ExitOnFailure(hr, "Failed to set last source.");
554 }
555 }
556
557LExit:
558 ReleaseStr(sczLastSourceFolder);
559 ReleaseStr(sczSourceFolder);
560
561 return hr;
562}
563
564extern "C" HRESULT CacheSendProgressCallback(
565 __in DOWNLOAD_CACHE_CALLBACK* pCallback,
566 __in DWORD64 dw64Progress,
567 __in DWORD64 dw64Total,
568 __in HANDLE hDestinationFile
569 )
570{
571 static LARGE_INTEGER LARGE_INTEGER_ZERO = { };
572
573 HRESULT hr = S_OK;
574 DWORD dwResult = PROGRESS_CONTINUE;
575 LARGE_INTEGER liTotalSize = { };
576 LARGE_INTEGER liTotalTransferred = { };
577
578 if (pCallback->pfnProgress)
579 {
580 liTotalSize.QuadPart = dw64Total;
581 liTotalTransferred.QuadPart = dw64Progress;
582
583 dwResult = (*pCallback->pfnProgress)(liTotalSize, liTotalTransferred, LARGE_INTEGER_ZERO, LARGE_INTEGER_ZERO, 1, CALLBACK_CHUNK_FINISHED, INVALID_HANDLE_VALUE, hDestinationFile, pCallback->pv);
584 switch (dwResult)
585 {
586 case PROGRESS_CONTINUE:
587 hr = S_OK;
588 break;
589
590 case PROGRESS_CANCEL: __fallthrough; // TODO: should cancel and stop be treated differently?
591 case PROGRESS_STOP:
592 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
593 ExitOnRootFailure(hr, "UX aborted on download progress.");
594
595 case PROGRESS_QUIET: // Not actually an error, just an indication to the caller to stop requesting progress.
596 pCallback->pfnProgress = NULL;
597 hr = S_OK;
598 break;
599
600 default:
601 hr = E_UNEXPECTED;
602 ExitOnRootFailure(hr, "Invalid return code from progress routine.");
603 }
604 }
605
606LExit:
607 return hr;
608}
609
610extern "C" void CacheSendErrorCallback(
611 __in DOWNLOAD_CACHE_CALLBACK* pCallback,
612 __in HRESULT hrError,
613 __in_z_opt LPCWSTR wzError,
614 __out_opt BOOL* pfRetry
615 )
616{
617 if (pfRetry)
618 {
619 *pfRetry = FALSE;
620 }
621
622 if (pCallback->pfnCancel)
623 {
624 int nResult = (*pCallback->pfnCancel)(hrError, wzError, pfRetry != NULL, pCallback->pv);
625 if (pfRetry && IDRETRY == nResult)
626 {
627 *pfRetry = TRUE;
628 }
629 }
630}
631
632extern "C" BOOL CacheBundleRunningFromCache()
633{
634 return vfRunningFromCache;
635}
636
637extern "C" HRESULT CacheBundleToCleanRoom(
638 __in BURN_PAYLOADS* pUxPayloads,
639 __in BURN_SECTION* pSection,
640 __deref_out_z_opt LPWSTR* psczCleanRoomBundlePath
641 )
642{
643 HRESULT hr = S_OK;
644 LPWSTR sczSourcePath = NULL;
645 LPWSTR wzExecutableName = NULL;
646
647 hr = PathForCurrentProcess(&sczSourcePath, NULL);
648 ExitOnFailure(hr, "Failed to get current path for process to cache to clean room.");
649
650 wzExecutableName = PathFile(sczSourcePath);
651
652 hr = CopyEngineToWorkingFolder(sczSourcePath, BUNDLE_CLEAN_ROOM_WORKING_FOLDER_NAME, wzExecutableName, pUxPayloads, pSection, psczCleanRoomBundlePath);
653 ExitOnFailure(hr, "Failed to cache bundle to clean room.");
654
655LExit:
656 ReleaseStr(sczSourcePath);
657
658 return hr;
659}
660
661extern "C" HRESULT CacheBundleToWorkingDirectory(
662 __in_z LPCWSTR /*wzBundleId*/,
663 __in_z LPCWSTR wzExecutableName,
664 __in BURN_PAYLOADS* pUxPayloads,
665 __in BURN_SECTION* pSection,
666 __deref_out_z_opt LPWSTR* psczEngineWorkingPath
667 )
668{
669 Assert(vfInitializedCache);
670
671 HRESULT hr = S_OK;
672 LPWSTR sczSourcePath = NULL;
673
674 // Initialize the source.
675 hr = PathForCurrentProcess(&sczSourcePath, NULL);
676 ExitOnFailure(hr, "Failed to get current process path.");
677
678 // If the bundle is running out of the package cache then we don't need to copy it to
679 // the working folder since we feel safe in the package cache and will run from there.
680 if (vfRunningFromCache)
681 {
682 hr = StrAllocString(psczEngineWorkingPath, sczSourcePath, 0);
683 ExitOnFailure(hr, "Failed to use current process path as target path.");
684 }
685 else // otherwise, carry on putting the bundle in the working folder.
686 {
687 hr = CopyEngineToWorkingFolder(sczSourcePath, BUNDLE_WORKING_FOLDER_NAME, wzExecutableName, pUxPayloads, pSection, psczEngineWorkingPath);
688 ExitOnFailure(hr, "Failed to copy engine to working folder.");
689 }
690
691LExit:
692 ReleaseStr(sczSourcePath);
693
694 return hr;
695}
696
697extern "C" HRESULT CacheLayoutBundle(
698 __in_z LPCWSTR wzExecutableName,
699 __in_z LPCWSTR wzLayoutDirectory,
700 __in_z LPCWSTR wzSourceBundlePath
701 )
702{
703 HRESULT hr = S_OK;
704 LPWSTR sczTargetPath = NULL;
705
706 hr = PathConcat(wzLayoutDirectory, wzExecutableName, &sczTargetPath);
707 ExitOnFailure(hr, "Failed to combine completed path with engine file name for layout.");
708
709 LogStringLine(REPORT_STANDARD, "Layout bundle from: '%ls' to: '%ls'", wzSourceBundlePath, sczTargetPath);
710
711 hr = FileEnsureMoveWithRetry(wzSourceBundlePath, sczTargetPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
712 ExitOnFailure(hr, "Failed to layout bundle from: '%ls' to '%ls'", wzSourceBundlePath, sczTargetPath);
713
714LExit:
715 ReleaseStr(sczTargetPath);
716
717 return hr;
718}
719
720extern "C" HRESULT CacheCompleteBundle(
721 __in BOOL fPerMachine,
722 __in_z LPCWSTR wzExecutableName,
723 __in_z LPCWSTR wzBundleId,
724 __in BURN_PAYLOADS* pUxPayloads,
725 __in_z LPCWSTR wzSourceBundlePath
726#ifdef DEBUG
727 , __in_z LPCWSTR wzExecutablePath
728#endif
729 )
730{
731 HRESULT hr = S_OK;
732 int nCompare = 0;
733 LPWSTR sczTargetDirectory = NULL;
734 LPWSTR sczTargetPath = NULL;
735 LPWSTR sczSourceDirectory = NULL;
736 LPWSTR sczPayloadSourcePath = NULL;
737
738 hr = CreateCompletedPath(fPerMachine, wzBundleId, &sczTargetDirectory);
739 ExitOnFailure(hr, "Failed to create completed cache path for bundle.");
740
741 hr = PathConcat(sczTargetDirectory, wzExecutableName, &sczTargetPath);
742 ExitOnFailure(hr, "Failed to combine completed path with engine file name.");
743
744 Assert(CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzExecutablePath, -1, sczTargetPath, -1));
745
746 // If the bundle is running out of the package cache then we don't need to copy it there
747 // (and don't want to since it'll be in use) so bail.
748 hr = PathCompare(wzSourceBundlePath, sczTargetPath, &nCompare);
749 ExitOnFailure(hr, "Failed to compare completed cache path for bundle: %ls", wzSourceBundlePath);
750
751 if (CSTR_EQUAL == nCompare)
752 {
753 ExitFunction();
754 }
755
756 // Otherwise, carry on putting the bundle in the cache.
757 LogStringLine(REPORT_STANDARD, "Caching bundle from: '%ls' to: '%ls'", wzSourceBundlePath, sczTargetPath);
758
759 FileRemoveFromPendingRename(sczTargetPath); // best effort to ensure bundle is not deleted from cache post restart.
760
761 hr = FileEnsureCopyWithRetry(wzSourceBundlePath, sczTargetPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
762 ExitOnFailure(hr, "Failed to cache bundle from: '%ls' to '%ls'", wzSourceBundlePath, sczTargetPath);
763
764 // Reset the path permissions in the cache.
765 hr = ResetPathPermissions(fPerMachine, sczTargetPath);
766 ExitOnFailure(hr, "Failed to reset permissions on cached bundle: '%ls'", sczTargetPath);
767
768 hr = PathGetDirectory(wzSourceBundlePath, &sczSourceDirectory);
769 ExitOnFailure(hr, "Failed to get directory from engine working path: %ls", wzSourceBundlePath);
770
771 // Cache external UX payloads to completed path.
772 for (DWORD i = 0; i < pUxPayloads->cPayloads; ++i)
773 {
774 BURN_PAYLOAD* pPayload = &pUxPayloads->rgPayloads[i];
775
776 if (BURN_PAYLOAD_PACKAGING_EXTERNAL == pPayload->packaging)
777 {
778 hr = PathConcat(sczSourceDirectory, pPayload->sczSourcePath, &sczPayloadSourcePath);
779 ExitOnFailure(hr, "Failed to build payload source path.");
780
781 hr = CacheCompletePayload(fPerMachine, pPayload, wzBundleId, sczPayloadSourcePath, FALSE);
782 ExitOnFailure(hr, "Failed to complete the cache of payload: %ls", pPayload->sczKey);
783 }
784 }
785
786LExit:
787 ReleaseStr(sczPayloadSourcePath);
788 ReleaseStr(sczSourceDirectory);
789 ReleaseStr(sczTargetPath);
790 ReleaseStr(sczTargetDirectory);
791
792 return hr;
793}
794
795extern "C" HRESULT CacheLayoutContainer(
796 __in BURN_CONTAINER* pContainer,
797 __in_z_opt LPCWSTR wzLayoutDirectory,
798 __in_z LPCWSTR wzUnverifiedContainerPath,
799 __in BOOL fMove
800 )
801{
802 HRESULT hr = S_OK;
803 LPWSTR sczCachedPath = NULL;
804
805 hr = PathConcat(wzLayoutDirectory, pContainer->sczFilePath, &sczCachedPath);
806 ExitOnFailure(hr, "Failed to concat complete cached path.");
807
808 hr = VerifyThenTransferContainer(pContainer, sczCachedPath, wzUnverifiedContainerPath, fMove);
809 ExitOnFailure(hr, "Failed to layout container from cached path: %ls", sczCachedPath);
810
811LExit:
812 ReleaseStr(sczCachedPath);
813
814 return hr;
815}
816
817extern "C" HRESULT CacheLayoutPayload(
818 __in BURN_PAYLOAD* pPayload,
819 __in_z_opt LPCWSTR wzLayoutDirectory,
820 __in_z LPCWSTR wzUnverifiedPayloadPath,
821 __in BOOL fMove
822 )
823{
824 HRESULT hr = S_OK;
825 LPWSTR sczCachedPath = NULL;
826
827 hr = PathConcat(wzLayoutDirectory, pPayload->sczFilePath, &sczCachedPath);
828 ExitOnFailure(hr, "Failed to concat complete cached path.");
829
830 hr = VerifyThenTransferPayload(pPayload, sczCachedPath, wzUnverifiedPayloadPath, fMove);
831 ExitOnFailure(hr, "Failed to layout payload from cached payload: %ls", sczCachedPath);
832
833LExit:
834 ReleaseStr(sczCachedPath);
835
836 return hr;
837}
838
839extern "C" HRESULT CacheCompletePayload(
840 __in BOOL fPerMachine,
841 __in BURN_PAYLOAD* pPayload,
842 __in_z_opt LPCWSTR wzCacheId,
843 __in_z LPCWSTR wzWorkingPayloadPath,
844 __in BOOL fMove
845 )
846{
847 HRESULT hr = S_OK;
848 LPWSTR sczCachedDirectory = NULL;
849 LPWSTR sczCachedPath = NULL;
850 LPWSTR sczUnverifiedPayloadPath = NULL;
851
852 hr = CreateCompletedPath(fPerMachine, wzCacheId, &sczCachedDirectory);
853 ExitOnFailure(hr, "Failed to get cached path for package with cache id: %ls", wzCacheId);
854
855 hr = PathConcat(sczCachedDirectory, pPayload->sczFilePath, &sczCachedPath);
856 ExitOnFailure(hr, "Failed to concat complete cached path.");
857
858 // If the cached file matches what we expected, we're good.
859 hr = VerifyFileAgainstPayload(pPayload, sczCachedPath);
860 if (SUCCEEDED(hr))
861 {
862 ::DecryptFileW(sczCachedPath, 0); // Let's try to make sure it's not encrypted.
863 LogId(REPORT_STANDARD, MSG_VERIFIED_EXISTING_PAYLOAD, pPayload->sczKey, sczCachedPath);
864 ExitFunction();
865 }
866 else if (E_PATHNOTFOUND != hr && E_FILENOTFOUND != hr)
867 {
868 LogErrorId(hr, MSG_FAILED_VERIFY_PAYLOAD, pPayload->sczKey, sczCachedPath, NULL);
869
870 FileEnsureDelete(sczCachedPath); // if the file existed but did not verify correctly, make it go away.
871 }
872
873 hr = CreateUnverifiedPath(fPerMachine, pPayload->sczKey, &sczUnverifiedPayloadPath);
874 ExitOnFailure(hr, "Failed to create unverified path.");
875
876 // If the working path exists, let's get it into the unverified path so we can reset the ACLs and verify the file.
877 if (FileExistsEx(wzWorkingPayloadPath, NULL))
878 {
879 hr = TransferWorkingPathToUnverifiedPath(wzWorkingPayloadPath, sczUnverifiedPayloadPath, fMove);
880 ExitOnFailure(hr, "Failed to transfer working path to unverified path for payload: %ls.", pPayload->sczKey);
881 }
882 else if (!FileExistsEx(sczUnverifiedPayloadPath, NULL)) // if the working path and unverified path do not exist, nothing we can do.
883 {
884 hr = E_FILENOTFOUND;
885 ExitOnFailure(hr, "Failed to find payload: %ls in working path: %ls and unverified path: %ls", pPayload->sczKey, wzWorkingPayloadPath, sczUnverifiedPayloadPath);
886 }
887
888 hr = ResetPathPermissions(fPerMachine, sczUnverifiedPayloadPath);
889 ExitOnFailure(hr, "Failed to reset permissions on unverified cached payload: %ls", pPayload->sczKey);
890
891 hr = VerifyFileAgainstPayload(pPayload, sczUnverifiedPayloadPath);
892 if (FAILED(hr))
893 {
894 LogErrorId(hr, MSG_FAILED_VERIFY_PAYLOAD, pPayload->sczKey, sczUnverifiedPayloadPath, NULL);
895
896 FileEnsureDelete(sczUnverifiedPayloadPath); // if the file did not verify correctly, make it go away.
897 ExitFunction();
898 }
899
900 LogId(REPORT_STANDARD, MSG_VERIFIED_ACQUIRED_PAYLOAD, pPayload->sczKey, sczUnverifiedPayloadPath, fMove ? "moving" : "copying", sczCachedPath);
901
902 hr = FileEnsureMoveWithRetry(sczUnverifiedPayloadPath, sczCachedPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
903 ExitOnFailure(hr, "Failed to move verified file to complete payload path: %ls", sczCachedPath);
904
905 ::DecryptFileW(sczCachedPath, 0); // Let's try to make sure it's not encrypted.
906
907LExit:
908 ReleaseStr(sczUnverifiedPayloadPath);
909 ReleaseStr(sczCachedPath);
910 ReleaseStr(sczCachedDirectory);
911
912 return hr;
913}
914
915extern "C" HRESULT CacheRemoveWorkingFolder(
916 __in_z_opt LPCWSTR wzBundleId
917 )
918{
919 HRESULT hr = S_OK;
920 LPWSTR sczWorkingFolder = NULL;
921
922 if (vfInitializedCache)
923 {
924 hr = CalculateWorkingFolder(wzBundleId, &sczWorkingFolder);
925 ExitOnFailure(hr, "Failed to calculate the working folder to remove it.");
926
927 // Try to clean out everything in the working folder.
928 hr = DirEnsureDeleteEx(sczWorkingFolder, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE);
929 TraceError(hr, "Could not delete bundle engine working folder.");
930 }
931
932LExit:
933 ReleaseStr(sczWorkingFolder);
934
935 return hr;
936}
937
938extern "C" HRESULT CacheRemoveBundle(
939 __in BOOL fPerMachine,
940 __in_z LPCWSTR wzBundleId
941 )
942{
943 HRESULT hr = S_OK;
944
945 hr = RemoveBundleOrPackage(TRUE, fPerMachine, wzBundleId, wzBundleId);
946 ExitOnFailure(hr, "Failed to remove bundle id: %ls.", wzBundleId);
947
948LExit:
949 return hr;
950}
951
952extern "C" HRESULT CacheRemovePackage(
953 __in BOOL fPerMachine,
954 __in_z LPCWSTR wzPackageId,
955 __in_z LPCWSTR wzCacheId
956 )
957{
958 HRESULT hr = S_OK;
959
960 hr = RemoveBundleOrPackage(FALSE, fPerMachine, wzPackageId, wzCacheId);
961 ExitOnFailure(hr, "Failed to remove package id: %ls.", wzPackageId);
962
963LExit:
964 return hr;
965}
966
967extern "C" HRESULT CacheVerifyPayloadSignature(
968 __in BURN_PAYLOAD* pPayload,
969 __in_z LPCWSTR wzUnverifiedPayloadPath,
970 __in HANDLE hFile
971 )
972{
973 HRESULT hr = S_OK;
974 LONG er = ERROR_SUCCESS;
975
976 GUID guidAuthenticode = WINTRUST_ACTION_GENERIC_VERIFY_V2;
977 WINTRUST_FILE_INFO wfi = { };
978 WINTRUST_DATA wtd = { };
979 CRYPT_PROVIDER_DATA* pProviderData = NULL;
980 CRYPT_PROVIDER_SGNR* pSigner = NULL;
981
982 // Verify the payload assuming online.
983 wfi.cbStruct = sizeof(wfi);
984 wfi.pcwszFilePath = wzUnverifiedPayloadPath;
985 wfi.hFile = hFile;
986
987 wtd.cbStruct = sizeof(wtd);
988 wtd.dwUnionChoice = WTD_CHOICE_FILE;
989 wtd.pFile = &wfi;
990 wtd.dwStateAction = WTD_STATEACTION_VERIFY;
991 wtd.dwProvFlags = WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
992 wtd.dwUIChoice = WTD_UI_NONE;
993
994 er = ::WinVerifyTrust(static_cast<HWND>(INVALID_HANDLE_VALUE), &guidAuthenticode, &wtd);
995 if (er)
996 {
997 // Verify the payload assuming offline.
998 wtd.dwProvFlags |= WTD_CACHE_ONLY_URL_RETRIEVAL;
999
1000 er = ::WinVerifyTrust(static_cast<HWND>(INVALID_HANDLE_VALUE), &guidAuthenticode, &wtd);
1001 ExitOnWin32Error(er, hr, "Failed authenticode verification of payload: %ls", wzUnverifiedPayloadPath);
1002 }
1003
1004 pProviderData = WTHelperProvDataFromStateData(wtd.hWVTStateData);
1005 ExitOnNullWithLastError(pProviderData, hr, "Failed to get provider state from authenticode certificate.");
1006
1007 pSigner = WTHelperGetProvSignerFromChain(pProviderData, 0, FALSE, 0);
1008 ExitOnNullWithLastError(pSigner, hr, "Failed to get signer chain from authenticode certificate.");
1009
1010 hr = VerifyPayloadAgainstChain(pPayload, pSigner->pChainContext);
1011 ExitOnFailure(hr, "Failed to verify expected payload against actual certificate chain.");
1012
1013LExit:
1014 return hr;
1015}
1016
1017extern "C" void CacheCleanup(
1018 __in BOOL fPerMachine,
1019 __in_z LPCWSTR wzBundleId
1020 )
1021{
1022 HRESULT hr = S_OK;
1023 LPWSTR sczFolder = NULL;
1024 LPWSTR sczFiles = NULL;
1025 LPWSTR sczDelete = NULL;
1026 HANDLE hFind = INVALID_HANDLE_VALUE;
1027 WIN32_FIND_DATAW wfd = { };
1028 DWORD cFileName = 0;
1029
1030 hr = CacheGetCompletedPath(fPerMachine, UNVERIFIED_CACHE_FOLDER_NAME, &sczFolder);
1031 if (SUCCEEDED(hr))
1032 {
1033 hr = DirEnsureDeleteEx(sczFolder, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE);
1034 }
1035
1036 if (!fPerMachine)
1037 {
1038 hr = CalculateWorkingFolder(wzBundleId, &sczFolder);
1039 if (SUCCEEDED(hr))
1040 {
1041 hr = PathConcat(sczFolder, L"*.*", &sczFiles);
1042 if (SUCCEEDED(hr))
1043 {
1044 hFind = ::FindFirstFileW(sczFiles, &wfd);
1045 if (INVALID_HANDLE_VALUE != hFind)
1046 {
1047 do
1048 {
1049 // Skip directories.
1050 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1051 {
1052 continue;
1053 }
1054
1055 // For extra safety and to silence OACR.
1056 wfd.cFileName[MAX_PATH - 1] = L'\0';
1057
1058 // Skip resume files (they end with ".R").
1059 cFileName = lstrlenW(wfd.cFileName);
1060 if (2 < cFileName && L'.' == wfd.cFileName[cFileName - 2] && (L'R' == wfd.cFileName[cFileName - 1] || L'r' == wfd.cFileName[cFileName - 1]))
1061 {
1062 continue;
1063 }
1064
1065 hr = PathConcat(sczFolder, wfd.cFileName, &sczDelete);
1066 if (SUCCEEDED(hr))
1067 {
1068 hr = FileEnsureDelete(sczDelete);
1069 }
1070 } while (::FindNextFileW(hFind, &wfd));
1071 }
1072 }
1073 }
1074 }
1075
1076 if (INVALID_HANDLE_VALUE != hFind)
1077 {
1078 ::FindClose(hFind);
1079 }
1080
1081 ReleaseStr(sczDelete);
1082 ReleaseStr(sczFiles);
1083 ReleaseStr(sczFolder);
1084}
1085
1086extern "C" void CacheUninitialize()
1087{
1088 ReleaseNullStr(vsczCurrentMachinePackageCache);
1089 ReleaseNullStr(vsczDefaultMachinePackageCache);
1090 ReleaseNullStr(vsczDefaultUserPackageCache);
1091 ReleaseNullStr(vsczWorkingFolder);
1092 ReleaseNullStr(vsczSourceProcessPath);
1093
1094 vfRunningFromCache = FALSE;
1095 vfInitializedCache = FALSE;
1096}
1097
1098// Internal functions.
1099
1100static HRESULT CalculateWorkingFolder(
1101 __in_z LPCWSTR /*wzBundleId*/,
1102 __deref_out_z LPWSTR* psczWorkingFolder
1103 )
1104{
1105 HRESULT hr = S_OK;
1106 RPC_STATUS rs = RPC_S_OK;
1107 BOOL fElevated = FALSE;
1108 WCHAR wzTempPath[MAX_PATH] = { };
1109 UUID guid = {};
1110 WCHAR wzGuid[39];
1111
1112 if (!vsczWorkingFolder)
1113 {
1114 ProcElevated(::GetCurrentProcess(), &fElevated);
1115
1116 if (fElevated)
1117 {
1118 if (!::GetWindowsDirectoryW(wzTempPath, countof(wzTempPath)))
1119 {
1120 ExitWithLastError(hr, "Failed to get windows path for working folder.");
1121 }
1122
1123 hr = PathFixedBackslashTerminate(wzTempPath, countof(wzTempPath));
1124 ExitOnFailure(hr, "Failed to ensure windows path for working folder ended in backslash.");
1125
1126 hr = ::StringCchCatW(wzTempPath, countof(wzTempPath), L"Temp\\");
1127 ExitOnFailure(hr, "Failed to concat Temp directory on windows path for working folder.");
1128 }
1129 else if (0 == ::GetTempPathW(countof(wzTempPath), wzTempPath))
1130 {
1131 ExitWithLastError(hr, "Failed to get temp path for working folder.");
1132 }
1133
1134 rs = ::UuidCreate(&guid);
1135 hr = HRESULT_FROM_RPC(rs);
1136 ExitOnFailure(hr, "Failed to create working folder guid.");
1137
1138 if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid)))
1139 {
1140 hr = E_OUTOFMEMORY;
1141 ExitOnRootFailure(hr, "Failed to convert working folder guid into string.");
1142 }
1143
1144 hr = StrAllocFormatted(&vsczWorkingFolder, L"%ls%ls\\", wzTempPath, wzGuid);
1145 ExitOnFailure(hr, "Failed to append bundle id on to temp path for working folder.");
1146 }
1147
1148 hr = StrAllocString(psczWorkingFolder, vsczWorkingFolder, 0);
1149 ExitOnFailure(hr, "Failed to copy working folder path.");
1150
1151LExit:
1152 return hr;
1153}
1154
1155static HRESULT GetRootPath(
1156 __in BOOL fPerMachine,
1157 __in BOOL fAllowRedirect,
1158 __deref_out_z LPWSTR* psczRootPath
1159 )
1160{
1161 HRESULT hr = S_OK;
1162 LPWSTR sczAppData = NULL;
1163 int nCompare = 0;
1164
1165 // Cache paths are initialized once so they cannot be changed while the engine is caching payloads.
1166 if (fPerMachine)
1167 {
1168 // Always construct the default machine package cache path so we can determine if we're redirected.
1169 if (!vsczDefaultMachinePackageCache)
1170 {
1171 hr = PathGetKnownFolder(CSIDL_COMMON_APPDATA, &sczAppData);
1172 ExitOnFailure(hr, "Failed to find local %hs appdata directory.", "per-machine");
1173
1174 hr = PathConcat(sczAppData, PACKAGE_CACHE_FOLDER_NAME, &vsczDefaultMachinePackageCache);
1175 ExitOnFailure(hr, "Failed to construct %hs package cache directory name.", "per-machine");
1176
1177 hr = PathBackslashTerminate(&vsczDefaultMachinePackageCache);
1178 ExitOnFailure(hr, "Failed to backslash terminate default %hs package cache directory name.", "per-machine");
1179 }
1180
1181 if (!vsczCurrentMachinePackageCache)
1182 {
1183 hr = PolcReadString(POLICY_BURN_REGISTRY_PATH, L"PackageCache", NULL, &vsczCurrentMachinePackageCache);
1184 ExitOnFailure(hr, "Failed to read PackageCache policy directory.");
1185
1186 if (vsczCurrentMachinePackageCache)
1187 {
1188 hr = PathBackslashTerminate(&vsczCurrentMachinePackageCache);
1189 ExitOnFailure(hr, "Failed to backslash terminate redirected per-machine package cache directory name.");
1190 }
1191 else
1192 {
1193 hr = StrAllocString(&vsczCurrentMachinePackageCache, vsczDefaultMachinePackageCache, 0);
1194 ExitOnFailure(hr, "Failed to copy default package cache directory to current package cache directory.");
1195 }
1196 }
1197
1198 hr = StrAllocString(psczRootPath, fAllowRedirect ? vsczCurrentMachinePackageCache : vsczDefaultMachinePackageCache, 0);
1199 ExitOnFailure(hr, "Failed to copy %hs package cache root directory.", "per-machine");
1200
1201 hr = PathCompare(vsczDefaultMachinePackageCache, *psczRootPath, &nCompare);
1202 ExitOnFailure(hr, "Failed to compare default and current package cache directories.");
1203
1204 // Return S_FALSE if the current location is not the default location (redirected).
1205 hr = CSTR_EQUAL == nCompare ? S_OK : S_FALSE;
1206 }
1207 else
1208 {
1209 if (!vsczDefaultUserPackageCache)
1210 {
1211 hr = PathGetKnownFolder(CSIDL_LOCAL_APPDATA, &sczAppData);
1212 ExitOnFailure(hr, "Failed to find local %hs appdata directory.", "per-user");
1213
1214 hr = PathConcat(sczAppData, PACKAGE_CACHE_FOLDER_NAME, &vsczDefaultUserPackageCache);
1215 ExitOnFailure(hr, "Failed to construct %hs package cache directory name.", "per-user");
1216
1217 hr = PathBackslashTerminate(&vsczDefaultUserPackageCache);
1218 ExitOnFailure(hr, "Failed to backslash terminate default %hs package cache directory name.", "per-user");
1219 }
1220
1221 hr = StrAllocString(psczRootPath, vsczDefaultUserPackageCache, 0);
1222 ExitOnFailure(hr, "Failed to copy %hs package cache root directory.", "per-user");
1223 }
1224
1225LExit:
1226 ReleaseStr(sczAppData);
1227
1228 return hr;
1229}
1230
1231static HRESULT GetLastUsedSourceFolder(
1232 __in BURN_VARIABLES* pVariables,
1233 __out_z LPWSTR* psczLastSource
1234 )
1235{
1236 HRESULT hr = S_OK;
1237 LPWSTR sczOriginalSource = NULL;
1238
1239 hr = VariableGetString(pVariables, BURN_BUNDLE_LAST_USED_SOURCE, psczLastSource);
1240 if (E_NOTFOUND == hr)
1241 {
1242 // Try the original source folder.
1243 hr = VariableGetString(pVariables, BURN_BUNDLE_ORIGINAL_SOURCE, &sczOriginalSource);
1244 if (SUCCEEDED(hr))
1245 {
1246 hr = PathGetDirectory(sczOriginalSource, psczLastSource);
1247 }
1248 }
1249
1250 return hr;
1251}
1252
1253static HRESULT CreateCompletedPath(
1254 __in BOOL fPerMachine,
1255 __in LPCWSTR wzId,
1256 __out LPWSTR* psczCacheDirectory
1257 )
1258{
1259 static BOOL fPerMachineCacheRootVerified = FALSE;
1260
1261 HRESULT hr = S_OK;
1262 LPWSTR sczCacheDirectory = NULL;
1263
1264 // If we are doing a permachine install but have not yet verified that the root cache folder
1265 // was created with the correct ACLs yet, do that now.
1266 if (fPerMachine && !fPerMachineCacheRootVerified)
1267 {
1268 hr = GetRootPath(fPerMachine, TRUE, &sczCacheDirectory);
1269 ExitOnFailure(hr, "Failed to get cache directory.");
1270
1271 hr = DirEnsureExists(sczCacheDirectory, NULL);
1272 ExitOnFailure(hr, "Failed to create cache directory: %ls", sczCacheDirectory);
1273
1274 hr = SecurePath(sczCacheDirectory);
1275 ExitOnFailure(hr, "Failed to secure cache directory: %ls", sczCacheDirectory);
1276
1277 fPerMachineCacheRootVerified = TRUE;
1278 }
1279
1280 // Get the cache completed path, ensure it exists, and reset any permissions people
1281 // might have tried to set on the directory so we inherit the (correct!) security
1282 // permissions from the parent directory.
1283 hr = CacheGetCompletedPath(fPerMachine, wzId, &sczCacheDirectory);
1284 ExitOnFailure(hr, "Failed to get cache directory.");
1285
1286 hr = DirEnsureExists(sczCacheDirectory, NULL);
1287 ExitOnFailure(hr, "Failed to create cache directory: %ls", sczCacheDirectory);
1288
1289 ResetPathPermissions(fPerMachine, sczCacheDirectory);
1290
1291 *psczCacheDirectory = sczCacheDirectory;
1292 sczCacheDirectory = NULL;
1293
1294LExit:
1295 ReleaseStr(sczCacheDirectory);
1296 return hr;
1297}
1298
1299static HRESULT CreateUnverifiedPath(
1300 __in BOOL fPerMachine,
1301 __in_z LPCWSTR wzPayloadId,
1302 __out_z LPWSTR* psczUnverifiedPayloadPath
1303 )
1304{
1305 static BOOL fUnverifiedCacheFolderCreated = FALSE;
1306
1307 HRESULT hr = S_OK;
1308 LPWSTR sczUnverifiedCacheFolder = NULL;
1309
1310 hr = CacheGetCompletedPath(fPerMachine, UNVERIFIED_CACHE_FOLDER_NAME, &sczUnverifiedCacheFolder);
1311 ExitOnFailure(hr, "Failed to get cache directory.");
1312
1313 if (!fUnverifiedCacheFolderCreated)
1314 {
1315 hr = DirEnsureExists(sczUnverifiedCacheFolder, NULL);
1316 ExitOnFailure(hr, "Failed to create unverified cache directory: %ls", sczUnverifiedCacheFolder);
1317
1318 ResetPathPermissions(fPerMachine, sczUnverifiedCacheFolder);
1319 }
1320
1321 hr = PathConcat(sczUnverifiedCacheFolder, wzPayloadId, psczUnverifiedPayloadPath);
1322 ExitOnFailure(hr, "Failed to concat payload id to unverified folder path.");
1323
1324LExit:
1325 ReleaseStr(sczUnverifiedCacheFolder);
1326
1327 return hr;
1328}
1329
1330static HRESULT VerifyThenTransferContainer(
1331 __in BURN_CONTAINER* pContainer,
1332 __in_z LPCWSTR wzCachedPath,
1333 __in_z LPCWSTR wzUnverifiedContainerPath,
1334 __in BOOL fMove
1335 )
1336{
1337 HRESULT hr = S_OK;
1338 HANDLE hFile = INVALID_HANDLE_VALUE;
1339
1340 // Get the container on disk actual hash.
1341 hFile = ::CreateFileW(wzUnverifiedContainerPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
1342 if (INVALID_HANDLE_VALUE == hFile)
1343 {
1344 ExitWithLastError(hr, "Failed to open container in working path: %ls", wzUnverifiedContainerPath);
1345 }
1346
1347 // Container should have a hash we can use to verify with.
1348 if (pContainer->pbHash)
1349 {
1350 hr = VerifyHash(pContainer->pbHash, pContainer->cbHash, wzUnverifiedContainerPath, hFile);
1351 ExitOnFailure(hr, "Failed to verify container hash: %ls", wzCachedPath);
1352 }
1353
1354 LogStringLine(REPORT_STANDARD, "%ls container from working path '%ls' to path '%ls'", fMove ? L"Moving" : L"Copying", wzUnverifiedContainerPath, wzCachedPath);
1355
1356 if (fMove)
1357 {
1358 hr = FileEnsureMoveWithRetry(wzUnverifiedContainerPath, wzCachedPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1359 ExitOnFailure(hr, "Failed to move %ls to %ls", wzUnverifiedContainerPath, wzCachedPath);
1360 }
1361 else
1362 {
1363 hr = FileEnsureCopyWithRetry(wzUnverifiedContainerPath, wzCachedPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1364 ExitOnFailure(hr, "Failed to copy %ls to %ls", wzUnverifiedContainerPath, wzCachedPath);
1365 }
1366
1367LExit:
1368 ReleaseFileHandle(hFile);
1369
1370 return hr;
1371}
1372
1373static HRESULT VerifyThenTransferPayload(
1374 __in BURN_PAYLOAD* pPayload,
1375 __in_z LPCWSTR wzCachedPath,
1376 __in_z LPCWSTR wzUnverifiedPayloadPath,
1377 __in BOOL fMove
1378 )
1379{
1380 HRESULT hr = S_OK;
1381 HANDLE hFile = INVALID_HANDLE_VALUE;
1382
1383 // Get the payload on disk actual hash.
1384 hFile = ::CreateFileW(wzUnverifiedPayloadPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
1385 if (INVALID_HANDLE_VALUE == hFile)
1386 {
1387 ExitWithLastError(hr, "Failed to open payload in working path: %ls", wzUnverifiedPayloadPath);
1388 }
1389
1390 // If the payload has a certificate root public key identifier provided, verify the certificate.
1391 if (pPayload->pbCertificateRootPublicKeyIdentifier)
1392 {
1393 hr = CacheVerifyPayloadSignature(pPayload, wzUnverifiedPayloadPath, hFile);
1394 ExitOnFailure(hr, "Failed to verify payload signature: %ls", wzCachedPath);
1395 }
1396 else if (pPayload->pCatalog) // If catalog files are specified, attempt to verify the file with a catalog file
1397 {
1398 hr = VerifyPayloadWithCatalog(pPayload, wzUnverifiedPayloadPath, hFile);
1399 ExitOnFailure(hr, "Failed to verify payload signature: %ls", wzCachedPath);
1400 }
1401 else if (pPayload->pbHash) // the payload should have a hash we can use to verify it.
1402 {
1403 hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, wzUnverifiedPayloadPath, hFile);
1404 ExitOnFailure(hr, "Failed to verify payload hash: %ls", wzCachedPath);
1405 }
1406
1407 LogStringLine(REPORT_STANDARD, "%ls payload from working path '%ls' to path '%ls'", fMove ? L"Moving" : L"Copying", wzUnverifiedPayloadPath, wzCachedPath);
1408
1409 if (fMove)
1410 {
1411 hr = FileEnsureMoveWithRetry(wzUnverifiedPayloadPath, wzCachedPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1412 ExitOnFailure(hr, "Failed to move %ls to %ls", wzUnverifiedPayloadPath, wzCachedPath);
1413 }
1414 else
1415 {
1416 hr = FileEnsureCopyWithRetry(wzUnverifiedPayloadPath, wzCachedPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1417 ExitOnFailure(hr, "Failed to copy %ls to %ls", wzUnverifiedPayloadPath, wzCachedPath);
1418 }
1419
1420LExit:
1421 ReleaseFileHandle(hFile);
1422
1423 return hr;
1424}
1425
1426static HRESULT TransferWorkingPathToUnverifiedPath(
1427 __in_z LPCWSTR wzWorkingPath,
1428 __in_z LPCWSTR wzUnverifiedPayloadPath,
1429 __in BOOL fMove
1430 )
1431{
1432 HRESULT hr = S_OK;
1433
1434 if (fMove)
1435 {
1436 hr = FileEnsureMoveWithRetry(wzWorkingPath, wzUnverifiedPayloadPath, TRUE, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1437 ExitOnFailure(hr, "Failed to move %ls to %ls", wzWorkingPath, wzUnverifiedPayloadPath);
1438 }
1439 else
1440 {
1441 hr = FileEnsureCopyWithRetry(wzWorkingPath, wzUnverifiedPayloadPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1442 ExitOnFailure(hr, "Failed to copy %ls to %ls", wzWorkingPath, wzUnverifiedPayloadPath);
1443 }
1444
1445LExit:
1446 return hr;
1447}
1448
1449static HRESULT VerifyFileAgainstPayload(
1450 __in BURN_PAYLOAD* pPayload,
1451 __in_z LPCWSTR wzVerifyPath
1452 )
1453{
1454 HRESULT hr = S_OK;
1455 HANDLE hFile = INVALID_HANDLE_VALUE;
1456
1457 // Get the payload on disk actual hash.
1458 hFile = ::CreateFileW(wzVerifyPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
1459 if (INVALID_HANDLE_VALUE == hFile)
1460 {
1461 hr = HRESULT_FROM_WIN32(::GetLastError());
1462 if (E_PATHNOTFOUND == hr || E_FILENOTFOUND == hr)
1463 {
1464 ExitFunction(); // do not log error when the file was not found.
1465 }
1466 ExitOnRootFailure(hr, "Failed to open payload at path: %ls", wzVerifyPath);
1467 }
1468
1469 // If the payload has a certificate root public key identifier provided, verify the certificate.
1470 if (pPayload->pbCertificateRootPublicKeyIdentifier)
1471 {
1472 hr = CacheVerifyPayloadSignature(pPayload, wzVerifyPath, hFile);
1473 ExitOnFailure(hr, "Failed to verify signature of payload: %ls", pPayload->sczKey);
1474 }
1475 else if (pPayload->pCatalog) // If catalog files are specified, attempt to verify the file with a catalog file
1476 {
1477 hr = VerifyPayloadWithCatalog(pPayload, wzVerifyPath, hFile);
1478 ExitOnFailure(hr, "Failed to verify catalog signature of payload: %ls", pPayload->sczKey);
1479 }
1480 else if (pPayload->pbHash) // the payload should have a hash we can use to verify it.
1481 {
1482 hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, wzVerifyPath, hFile);
1483 ExitOnFailure(hr, "Failed to verify hash of payload: %ls", pPayload->sczKey);
1484 }
1485
1486LExit:
1487 ReleaseFileHandle(hFile);
1488
1489 return hr;
1490}
1491
1492static HRESULT AllocateSid(
1493 __in WELL_KNOWN_SID_TYPE type,
1494 __out PSID* ppSid
1495 )
1496{
1497 HRESULT hr = S_OK;
1498 PSID pAllocSid = NULL;
1499 DWORD cbSid = SECURITY_MAX_SID_SIZE;
1500
1501 pAllocSid = static_cast<PSID>(MemAlloc(cbSid, TRUE));
1502 ExitOnNull(pAllocSid, hr, E_OUTOFMEMORY, "Failed to allocate memory for well known SID.");
1503
1504 if (!::CreateWellKnownSid(type, NULL, pAllocSid, &cbSid))
1505 {
1506 ExitWithLastError(hr, "Failed to create well known SID.");
1507 }
1508
1509 *ppSid = pAllocSid;
1510 pAllocSid = NULL;
1511
1512LExit:
1513 ReleaseMem(pAllocSid);
1514 return hr;
1515}
1516
1517
1518static HRESULT ResetPathPermissions(
1519 __in BOOL fPerMachine,
1520 __in LPCWSTR wzPath
1521 )
1522{
1523 HRESULT hr = S_OK;
1524 DWORD er = ERROR_SUCCESS;
1525 DWORD dwSetSecurity = DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION;
1526 ACL acl = { };
1527 PSID pSid = NULL;
1528
1529 if (fPerMachine)
1530 {
1531 hr = AllocateSid(WinBuiltinAdministratorsSid, &pSid);
1532 ExitOnFailure(hr, "Failed to allocate administrator SID.");
1533
1534 // Create an empty (not NULL!) ACL to reset the permissions on the file to purely inherit from parent.
1535 if (!::InitializeAcl(&acl, sizeof(acl), ACL_REVISION))
1536 {
1537 ExitWithLastError(hr, "Failed to initialize ACL.");
1538 }
1539
1540 dwSetSecurity |= OWNER_SECURITY_INFORMATION;
1541 }
1542
1543 hr = AclSetSecurityWithRetry(wzPath, SE_FILE_OBJECT, dwSetSecurity, pSid, NULL, &acl, NULL, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1544 ExitOnWin32Error(er, hr, "Failed to reset the ACL on cached file: %ls", wzPath);
1545
1546 ::SetFileAttributesW(wzPath, FILE_ATTRIBUTE_NORMAL); // Let's try to reset any possible read-only/system bits.
1547
1548LExit:
1549 ReleaseMem(pSid);
1550 return hr;
1551}
1552
1553
1554static HRESULT GrantAccessAndAllocateSid(
1555 __in WELL_KNOWN_SID_TYPE type,
1556 __in DWORD dwGrantAccess,
1557 __in EXPLICIT_ACCESS* pAccess
1558 )
1559{
1560 HRESULT hr = S_OK;
1561
1562 hr = AllocateSid(type, reinterpret_cast<PSID*>(&pAccess->Trustee.ptstrName));
1563 ExitOnFailure(hr, "Failed to allocate SID to grate access.");
1564
1565 pAccess->grfAccessMode = GRANT_ACCESS;
1566 pAccess->grfAccessPermissions = dwGrantAccess;
1567 pAccess->grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
1568 pAccess->Trustee.TrusteeForm = TRUSTEE_IS_SID;
1569 pAccess->Trustee.TrusteeType = TRUSTEE_IS_GROUP;
1570
1571LExit:
1572 return hr;
1573}
1574
1575
1576static HRESULT SecurePath(
1577 __in LPCWSTR wzPath
1578 )
1579{
1580 HRESULT hr = S_OK;
1581 DWORD er = ERROR_SUCCESS;
1582 EXPLICIT_ACCESSW access[4] = { };
1583 PACL pAcl = NULL;
1584
1585 // Administrators must be the first one in the array so we can reuse the allocated SID below.
1586 hr = GrantAccessAndAllocateSid(WinBuiltinAdministratorsSid, FILE_ALL_ACCESS, &access[0]);
1587 ExitOnFailure(hr, "Failed to allocate access for Administrators group to path: %ls", wzPath);
1588
1589 hr = GrantAccessAndAllocateSid(WinLocalSystemSid, FILE_ALL_ACCESS, &access[1]);
1590 ExitOnFailure(hr, "Failed to allocate access for SYSTEM group to path: %ls", wzPath);
1591
1592 hr = GrantAccessAndAllocateSid(WinWorldSid, GENERIC_READ | GENERIC_EXECUTE, &access[2]);
1593 ExitOnFailure(hr, "Failed to allocate access for Everyone group to path: %ls", wzPath);
1594
1595 hr = GrantAccessAndAllocateSid(WinBuiltinUsersSid, GENERIC_READ | GENERIC_EXECUTE, &access[3]);
1596 ExitOnFailure(hr, "Failed to allocate access for Users group to path: %ls", wzPath);
1597
1598 er = ::SetEntriesInAclW(countof(access), access, NULL, &pAcl);
1599 ExitOnWin32Error(er, hr, "Failed to create ACL to secure cache path: %ls", wzPath);
1600
1601 // Set the ACL and ensure the Administrators group ends up the owner
1602 hr = AclSetSecurityWithRetry(wzPath, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
1603 reinterpret_cast<PSID>(access[0].Trustee.ptstrName), NULL, pAcl, NULL, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1604 ExitOnFailure(hr, "Failed to secure cache path: %ls", wzPath);
1605
1606LExit:
1607 if (pAcl)
1608 {
1609 ::LocalFree(pAcl);
1610 }
1611
1612 for (DWORD i = 0; i < countof(access); ++i)
1613 {
1614 ReleaseMem(access[i].Trustee.ptstrName);
1615 }
1616
1617 return hr;
1618}
1619
1620
1621static HRESULT CopyEngineToWorkingFolder(
1622 __in_z LPCWSTR wzSourcePath,
1623 __in_z LPCWSTR wzWorkingFolderName,
1624 __in_z LPCWSTR wzExecutableName,
1625 __in BURN_PAYLOADS* pUxPayloads,
1626 __in BURN_SECTION* pSection,
1627 __deref_out_z_opt LPWSTR* psczEngineWorkingPath
1628 )
1629{
1630 HRESULT hr = S_OK;
1631 LPWSTR sczWorkingFolder = NULL;
1632 LPWSTR sczTargetDirectory = NULL;
1633 LPWSTR sczTargetPath = NULL;
1634 LPWSTR sczSourceDirectory = NULL;
1635 LPWSTR sczPayloadSourcePath = NULL;
1636 LPWSTR sczPayloadTargetPath = NULL;
1637
1638 hr = CacheEnsureWorkingFolder(NULL, &sczWorkingFolder);
1639 ExitOnFailure(hr, "Failed to create working path to copy engine.");
1640
1641 hr = PathConcat(sczWorkingFolder, wzWorkingFolderName, &sczTargetDirectory);
1642 ExitOnFailure(hr, "Failed to calculate the bundle working folder target name.");
1643
1644 hr = DirEnsureExists(sczTargetDirectory, NULL);
1645 ExitOnFailure(hr, "Failed create bundle working folder.");
1646
1647 hr = PathConcat(sczTargetDirectory, wzExecutableName, &sczTargetPath);
1648 ExitOnFailure(hr, "Failed to combine working path with engine file name.");
1649
1650 // Copy the engine without any attached containers to the working path.
1651 hr = CopyEngineWithSignatureFixup(pSection->hEngineFile, wzSourcePath, sczTargetPath, pSection);
1652 ExitOnFailure(hr, "Failed to copy engine: '%ls' to working path: %ls", wzSourcePath, sczTargetPath);
1653
1654 // Copy external UX payloads to working path.
1655 for (DWORD i = 0; i < pUxPayloads->cPayloads; ++i)
1656 {
1657 BURN_PAYLOAD* pPayload = &pUxPayloads->rgPayloads[i];
1658
1659 if (BURN_PAYLOAD_PACKAGING_EXTERNAL == pPayload->packaging)
1660 {
1661 if (!sczSourceDirectory)
1662 {
1663 hr = PathGetDirectory(wzSourcePath, &sczSourceDirectory);
1664 ExitOnFailure(hr, "Failed to get directory from engine path: %ls", wzSourcePath);
1665 }
1666
1667 hr = PathConcat(sczSourceDirectory, pPayload->sczSourcePath, &sczPayloadSourcePath);
1668 ExitOnFailure(hr, "Failed to build payload source path for working copy.");
1669
1670 hr = PathConcat(sczTargetDirectory, pPayload->sczFilePath, &sczPayloadTargetPath);
1671 ExitOnFailure(hr, "Failed to build payload target path for working copy.");
1672
1673 hr = FileEnsureCopyWithRetry(sczPayloadSourcePath, sczPayloadTargetPath, TRUE, FILE_OPERATION_RETRY_COUNT, FILE_OPERATION_RETRY_WAIT);
1674 ExitOnFailure(hr, "Failed to copy UX payload from: '%ls' to: '%ls'", sczPayloadSourcePath, sczPayloadTargetPath);
1675 }
1676 }
1677
1678 if (psczEngineWorkingPath)
1679 {
1680 hr = StrAllocString(psczEngineWorkingPath, sczTargetPath, 0);
1681 ExitOnFailure(hr, "Failed to copy target path for engine working path.");
1682 }
1683
1684LExit:
1685 ReleaseStr(sczPayloadTargetPath);
1686 ReleaseStr(sczPayloadSourcePath);
1687 ReleaseStr(sczSourceDirectory);
1688 ReleaseStr(sczTargetPath);
1689 ReleaseStr(sczTargetDirectory);
1690 ReleaseStr(sczWorkingFolder);
1691
1692 return hr;
1693}
1694
1695
1696static HRESULT CopyEngineWithSignatureFixup(
1697 __in HANDLE hEngineFile,
1698 __in_z LPCWSTR wzEnginePath,
1699 __in_z LPCWSTR wzTargetPath,
1700 __in BURN_SECTION* pSection
1701 )
1702{
1703 HRESULT hr = S_OK;
1704 HANDLE hTarget = INVALID_HANDLE_VALUE;
1705 LARGE_INTEGER li = { };
1706 DWORD dwZeroOriginals[3] = { };
1707
1708 hTarget = ::CreateFileW(wzTargetPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
1709 if (INVALID_HANDLE_VALUE == hTarget)
1710 {
1711 ExitWithLastError(hr, "Failed to create engine file at path: %ls", wzTargetPath);
1712 }
1713
1714 hr = FileSetPointer(hEngineFile, 0, NULL, FILE_BEGIN);
1715 ExitOnFailure(hr, "Failed to seek to beginning of engine file: %ls", wzEnginePath);
1716
1717 hr = FileCopyUsingHandles(hEngineFile, hTarget, pSection->cbEngineSize, NULL);
1718 ExitOnFailure(hr, "Failed to copy engine from: %ls to: %ls", wzEnginePath, wzTargetPath);
1719
1720 // If the original executable was signed, let's put back the checksum and signature.
1721 if (pSection->dwOriginalSignatureOffset)
1722 {
1723 // Fix up the checksum.
1724 li.QuadPart = pSection->dwChecksumOffset;
1725 if (!::SetFilePointerEx(hTarget, li, NULL, FILE_BEGIN))
1726 {
1727 ExitWithLastError(hr, "Failed to seek to checksum in exe header.");
1728 }
1729
1730 hr = FileWriteHandle(hTarget, reinterpret_cast<LPBYTE>(&pSection->dwOriginalChecksum), sizeof(pSection->dwOriginalChecksum));
1731 ExitOnFailure(hr, "Failed to update signature offset.");
1732
1733 // Fix up the signature information.
1734 li.QuadPart = pSection->dwCertificateTableOffset;
1735 if (!::SetFilePointerEx(hTarget, li, NULL, FILE_BEGIN))
1736 {
1737 ExitWithLastError(hr, "Failed to seek to signature table in exe header.");
1738 }
1739
1740 hr = FileWriteHandle(hTarget, reinterpret_cast<LPBYTE>(&pSection->dwOriginalSignatureOffset), sizeof(pSection->dwOriginalSignatureOffset));
1741 ExitOnFailure(hr, "Failed to update signature offset.");
1742
1743 hr = FileWriteHandle(hTarget, reinterpret_cast<LPBYTE>(&pSection->dwOriginalSignatureSize), sizeof(pSection->dwOriginalSignatureSize));
1744 ExitOnFailure(hr, "Failed to update signature offset.");
1745
1746 // Zero out the original information since that is how it was when the file was originally signed.
1747 li.QuadPart = pSection->dwOriginalChecksumAndSignatureOffset;
1748 if (!::SetFilePointerEx(hTarget, li, NULL, FILE_BEGIN))
1749 {
1750 ExitWithLastError(hr, "Failed to seek to original data in exe burn section header.");
1751 }
1752
1753 hr = FileWriteHandle(hTarget, reinterpret_cast<LPBYTE>(&dwZeroOriginals), sizeof(dwZeroOriginals));
1754 ExitOnFailure(hr, "Failed to zero out original data offset.");
1755 }
1756
1757LExit:
1758 ReleaseFileHandle(hTarget);
1759
1760 return hr;
1761}
1762
1763
1764static HRESULT RemoveBundleOrPackage(
1765 __in BOOL fBundle,
1766 __in BOOL fPerMachine,
1767 __in_z LPCWSTR wzBundleOrPackageId,
1768 __in_z LPCWSTR wzCacheId
1769 )
1770{
1771 HRESULT hr = S_OK;
1772 LPWSTR sczRootCacheDirectory = NULL;
1773 LPWSTR sczDirectory = NULL;
1774
1775 hr = CacheGetCompletedPath(fPerMachine, wzCacheId, &sczDirectory);
1776 ExitOnFailure(hr, "Failed to calculate cache path.");
1777
1778 LogId(REPORT_STANDARD, fBundle ? MSG_UNCACHE_BUNDLE : MSG_UNCACHE_PACKAGE, wzBundleOrPackageId, sczDirectory);
1779
1780 // Try really hard to remove the cache directory.
1781 hr = E_FAIL;
1782 for (DWORD iRetry = 0; FAILED(hr) && iRetry < FILE_OPERATION_RETRY_COUNT; ++iRetry)
1783 {
1784 if (0 < iRetry)
1785 {
1786 ::Sleep(FILE_OPERATION_RETRY_WAIT);
1787 }
1788
1789 hr = DirEnsureDeleteEx(sczDirectory, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE);
1790 if (E_PATHNOTFOUND == hr)
1791 {
1792 break;
1793 }
1794 }
1795
1796 if (FAILED(hr))
1797 {
1798 LogId(REPORT_STANDARD, fBundle ? MSG_UNABLE_UNCACHE_BUNDLE : MSG_UNABLE_UNCACHE_PACKAGE, wzBundleOrPackageId, sczDirectory, hr);
1799 hr = S_OK;
1800 }
1801 else
1802 {
1803 // Try to remove root package cache in the off chance it is now empty.
1804 hr = GetRootPath(fPerMachine, TRUE, &sczRootCacheDirectory);
1805 ExitOnFailure(hr, "Failed to get %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user");
1806 DirEnsureDeleteEx(sczRootCacheDirectory, DIR_DELETE_SCHEDULE);
1807
1808 // GetRootPath returns S_FALSE if the package cache is redirected elsewhere.
1809 if (S_FALSE == hr)
1810 {
1811 hr = GetRootPath(fPerMachine, FALSE, &sczRootCacheDirectory);
1812 ExitOnFailure(hr, "Failed to get old %hs package cache root directory.", fPerMachine ? "per-machine" : "per-user");
1813 DirEnsureDeleteEx(sczRootCacheDirectory, DIR_DELETE_SCHEDULE);
1814 }
1815 }
1816
1817LExit:
1818 ReleaseStr(sczDirectory);
1819 ReleaseStr(sczRootCacheDirectory);
1820
1821 return hr;
1822}
1823
1824static HRESULT VerifyHash(
1825 __in BYTE* pbHash,
1826 __in DWORD cbHash,
1827 __in_z LPCWSTR wzUnverifiedPayloadPath,
1828 __in HANDLE hFile
1829 )
1830{
1831 UNREFERENCED_PARAMETER(wzUnverifiedPayloadPath);
1832
1833 HRESULT hr = S_OK;
1834 BYTE rgbActualHash[SHA1_HASH_LEN] = { };
1835 DWORD64 qwHashedBytes;
1836 LPWSTR pszExpected = NULL;
1837 LPWSTR pszActual = NULL;
1838
1839 // TODO: create a cryp hash file that sends progress.
1840 hr = CrypHashFileHandle(hFile, PROV_RSA_FULL, CALG_SHA1, rgbActualHash, sizeof(rgbActualHash), &qwHashedBytes);
1841 ExitOnFailure(hr, "Failed to calculate hash for path: %ls", wzUnverifiedPayloadPath);
1842
1843 // Compare hashes.
1844 if (cbHash != sizeof(rgbActualHash) || 0 != memcmp(pbHash, rgbActualHash, SHA1_HASH_LEN))
1845 {
1846 hr = CRYPT_E_HASH_VALUE;
1847
1848 // Best effort to log the expected and actual hash value strings.
1849 if (SUCCEEDED(StrAllocHexEncode(pbHash, cbHash, &pszExpected)) &&
1850 SUCCEEDED(StrAllocHexEncode(rgbActualHash, SHA1_HASH_LEN, &pszActual)))
1851 {
1852 ExitOnFailure(hr, "Hash mismatch for path: %ls, expected: %ls, actual: %ls", wzUnverifiedPayloadPath, pszExpected, pszActual);
1853 }
1854 else
1855 {
1856 ExitOnFailure(hr, "Hash mismatch for path: %ls", wzUnverifiedPayloadPath);
1857 }
1858 }
1859
1860LExit:
1861 ReleaseStr(pszActual);
1862 ReleaseStr(pszExpected);
1863
1864 return hr;
1865}
1866
1867static HRESULT VerifyPayloadWithCatalog(
1868 __in BURN_PAYLOAD* pPayload,
1869 __in_z LPCWSTR wzUnverifiedPayloadPath,
1870 __in HANDLE hFile
1871 )
1872{
1873 HRESULT hr = S_FALSE;
1874 DWORD er = ERROR_SUCCESS;
1875 WINTRUST_DATA WinTrustData = { };
1876 WINTRUST_CATALOG_INFO WinTrustCatalogInfo = { };
1877 GUID gSubSystemDriver = WINTRUST_ACTION_GENERIC_VERIFY_V2;
1878 LPWSTR sczLowerCaseFile = NULL;
1879 LPWSTR pCurrent = NULL;
1880 LPWSTR sczName = NULL;
1881 DWORD dwHashSize = 0;
1882 DWORD dwTagSize;
1883 LPBYTE pbHash = NULL;
1884
1885 // Get lower case file name. Older operating systems need a lower case file
1886 // to match in the catalog
1887 hr = StrAllocString(&sczLowerCaseFile, wzUnverifiedPayloadPath, 0);
1888 ExitOnFailure(hr, "Failed to allocate memory");
1889
1890 // Go through each character doing the lower case of each letter
1891 pCurrent = sczLowerCaseFile;
1892 while ('\0' != *pCurrent)
1893 {
1894 *pCurrent = (WCHAR)_tolower(*pCurrent);
1895 pCurrent++;
1896 }
1897
1898 // Get file hash
1899 CryptCATAdminCalcHashFromFileHandle(hFile, &dwHashSize, pbHash, 0);
1900 er = ::GetLastError();
1901 if (ERROR_INSUFFICIENT_BUFFER == er)
1902 {
1903 pbHash = (LPBYTE)MemAlloc(dwHashSize, TRUE);
1904 if (!CryptCATAdminCalcHashFromFileHandle(hFile, &dwHashSize, pbHash, 0))
1905 {
1906 ExitWithLastError(hr, "Failed to get file hash.");
1907 }
1908 }
1909 else
1910 {
1911 ExitOnWin32Error(er, hr, "Failed to get file hash.");
1912 }
1913
1914 // Make the hash into a string. This is the member tag for the catalog
1915 dwTagSize = (dwHashSize * 2) + 1;
1916 hr = StrAlloc(&sczName, dwTagSize);
1917 ExitOnFailure(hr, "Failed to allocate string.");
1918 hr = StrHexEncode(pbHash, dwHashSize, sczName, dwTagSize);
1919 ExitOnFailure(hr, "Failed to encode file hash.");
1920
1921 // Set up the WinVerifyTrust structures assuming online.
1922 WinTrustData.cbStruct = sizeof(WINTRUST_DATA);
1923 WinTrustData.dwUIChoice = WTD_UI_NONE;
1924 WinTrustData.dwUnionChoice = WTD_CHOICE_CATALOG;
1925 WinTrustData.dwStateAction = WTD_STATEACTION_VERIFY;
1926 WinTrustData.dwProvFlags = WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
1927 WinTrustData.pCatalog = &WinTrustCatalogInfo;
1928
1929 WinTrustCatalogInfo.cbStruct = sizeof(WINTRUST_CATALOG_INFO);
1930 WinTrustCatalogInfo.pbCalculatedFileHash = pbHash;
1931 WinTrustCatalogInfo.cbCalculatedFileHash = dwHashSize;
1932 WinTrustCatalogInfo.hMemberFile = hFile;
1933 WinTrustCatalogInfo.pcwszMemberTag = sczName;
1934 WinTrustCatalogInfo.pcwszMemberFilePath = sczLowerCaseFile;
1935 WinTrustCatalogInfo.pcwszCatalogFilePath = pPayload->pCatalog->sczLocalFilePath;
1936
1937 hr = ::WinVerifyTrust(static_cast<HWND>(INVALID_HANDLE_VALUE), &gSubSystemDriver, &WinTrustData);
1938 if (hr)
1939 {
1940 // Set up the WinVerifyTrust structures assuming online.
1941 WinTrustData.dwProvFlags |= WTD_CACHE_ONLY_URL_RETRIEVAL;
1942
1943 er = ::WinVerifyTrust(static_cast<HWND>(INVALID_HANDLE_VALUE), &gSubSystemDriver, &WinTrustData);
1944
1945 // WinVerifyTrust returns 0 for success, a few different Win32 error codes if it can't
1946 // find the provider, and any other error code is provider specific, so may not
1947 // be an actual Win32 error code
1948 ExitOnWin32Error(er, hr, "Could not verify file %ls.", wzUnverifiedPayloadPath);
1949 }
1950
1951 // Need to close the WinVerifyTrust action
1952 WinTrustData.dwStateAction = WTD_STATEACTION_CLOSE;
1953 er = ::WinVerifyTrust(static_cast<HWND>(INVALID_HANDLE_VALUE), &gSubSystemDriver, &WinTrustData);
1954 ExitOnWin32Error(er, hr, "Could not close verify handle.");
1955
1956LExit:
1957 ReleaseStr(sczLowerCaseFile);
1958 ReleaseStr(sczName);
1959 ReleaseMem(pbHash);
1960
1961 return hr;
1962}
1963
1964static HRESULT VerifyPayloadAgainstChain(
1965 __in BURN_PAYLOAD* pPayload,
1966 __in PCCERT_CHAIN_CONTEXT pChainContext
1967 )
1968{
1969 HRESULT hr = S_OK;
1970 PCCERT_CONTEXT pChainElementCertContext = NULL;
1971
1972 BYTE rgbPublicKeyIdentifier[SHA1_HASH_LEN] = { };
1973 DWORD cbPublicKeyIdentifier = sizeof(rgbPublicKeyIdentifier);
1974 BYTE* pbThumbprint = NULL;
1975 DWORD cbThumbprint = 0;
1976
1977 // Walk up the chain looking for a certificate in the chain that matches our expected public key identifier
1978 // and thumbprint (if a thumbprint was provided).
1979 HRESULT hrChainVerification = E_NOTFOUND; // assume we won't find a match.
1980 for (DWORD i = 0; i < pChainContext->rgpChain[0]->cElement; ++i)
1981 {
1982 pChainElementCertContext = pChainContext->rgpChain[0]->rgpElement[i]->pCertContext;
1983
1984 // Get the certificate's public key identifier.
1985 if (!::CryptHashPublicKeyInfo(NULL, CALG_SHA1, 0, X509_ASN_ENCODING, &pChainElementCertContext->pCertInfo->SubjectPublicKeyInfo, rgbPublicKeyIdentifier, &cbPublicKeyIdentifier))
1986 {
1987 ExitWithLastError(hr, "Failed to get certificate public key identifier.");
1988 }
1989
1990 // Compare the certificate's public key identifier with the payload's public key identifier. If they
1991 // match, we're one step closer to the a positive result.
1992 if (pPayload->cbCertificateRootPublicKeyIdentifier == cbPublicKeyIdentifier &&
1993 0 == memcmp(pPayload->pbCertificateRootPublicKeyIdentifier, rgbPublicKeyIdentifier, cbPublicKeyIdentifier))
1994 {
1995 // If the payload specified a thumbprint for the certificate, verify it.
1996 if (pPayload->pbCertificateRootThumbprint)
1997 {
1998 hr = CertReadProperty(pChainElementCertContext, CERT_SHA1_HASH_PROP_ID, &pbThumbprint, &cbThumbprint);
1999 ExitOnFailure(hr, "Failed to read certificate thumbprint.");
2000
2001 if (pPayload->cbCertificateRootThumbprint == cbThumbprint &&
2002 0 == memcmp(pPayload->pbCertificateRootThumbprint, pbThumbprint, cbThumbprint))
2003 {
2004 // If we got here, we found that our payload public key identifier and thumbprint
2005 // matched an element in the certficate chain.
2006 hrChainVerification = S_OK;
2007 break;
2008 }
2009
2010 ReleaseNullMem(pbThumbprint);
2011 }
2012 else // no thumbprint match necessary so we're good to go.
2013 {
2014 hrChainVerification = S_OK;
2015 break;
2016 }
2017 }
2018 }
2019 hr = hrChainVerification;
2020 ExitOnFailure(hr, "Failed to find expected public key in certificate chain.");
2021
2022LExit:
2023 ReleaseMem(pbThumbprint);
2024
2025 return hr;
2026}
diff --git a/src/engine/cache.h b/src/engine/cache.h
new file mode 100644
index 00000000..95e6cb90
--- /dev/null
+++ b/src/engine/cache.h
@@ -0,0 +1,150 @@
1#pragma once
2// 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.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9// structs
10
11// functions
12
13HRESULT CacheInitialize(
14 __in BURN_REGISTRATION* pRegistration,
15 __in BURN_VARIABLES* pVariables,
16 __in_z_opt LPCWSTR wzSourceProcessPath
17 );
18HRESULT CacheEnsureWorkingFolder(
19 __in LPCWSTR wzBundleId,
20 __deref_out_z_opt LPWSTR* psczWorkingFolder
21 );
22HRESULT CacheCalculateBundleWorkingPath(
23 __in_z LPCWSTR wzBundleId,
24 __in LPCWSTR wzExecutableName,
25 __deref_out_z LPWSTR* psczWorkingPath
26 );
27HRESULT CacheCalculateBundleLayoutWorkingPath(
28 __in_z LPCWSTR wzBundleId,
29 __deref_out_z LPWSTR* psczWorkingPath
30 );
31HRESULT CacheCalculatePayloadWorkingPath(
32 __in_z LPCWSTR wzBundleId,
33 __in BURN_PAYLOAD* pPayload,
34 __deref_out_z LPWSTR* psczWorkingPath
35 );
36HRESULT CacheCalculateContainerWorkingPath(
37 __in_z LPCWSTR wzBundleId,
38 __in BURN_CONTAINER* pContainer,
39 __deref_out_z LPWSTR* psczWorkingPath
40 );
41HRESULT CacheGetRootCompletedPath(
42 __in BOOL fPerMachine,
43 __in BOOL fForceInitialize,
44 __deref_out_z LPWSTR* psczRootCompletedPath
45 );
46HRESULT CacheGetCompletedPath(
47 __in BOOL fPerMachine,
48 __in_z LPCWSTR wzCacheId,
49 __deref_out_z LPWSTR* psczCompletedPath
50 );
51HRESULT CacheGetResumePath(
52 __in_z LPCWSTR wzPayloadWorkingPath,
53 __deref_out_z LPWSTR* psczResumePath
54 );
55HRESULT CacheFindLocalSource(
56 __in_z LPCWSTR wzSourcePath,
57 __in BURN_VARIABLES* pVariables,
58 __out BOOL* pfFound,
59 __out_z LPWSTR* psczSourceFullPath
60 );
61HRESULT CacheSetLastUsedSource(
62 __in BURN_VARIABLES* pVariables,
63 __in_z LPCWSTR wzSourcePath,
64 __in_z LPCWSTR wzRelativePath
65 );
66HRESULT CacheSendProgressCallback(
67 __in DOWNLOAD_CACHE_CALLBACK* pCallback,
68 __in DWORD64 dw64Progress,
69 __in DWORD64 dw64Total,
70 __in HANDLE hDestinationFile
71 );
72void CacheSendErrorCallback(
73 __in DOWNLOAD_CACHE_CALLBACK* pCallback,
74 __in HRESULT hrError,
75 __in_z_opt LPCWSTR wzError,
76 __out_opt BOOL* pfRetry
77 );
78BOOL CacheBundleRunningFromCache();
79HRESULT CacheBundleToCleanRoom(
80 __in BURN_PAYLOADS* pUxPayloads,
81 __in BURN_SECTION* pSection,
82 __deref_out_z_opt LPWSTR* psczCleanRoomBundlePath
83 );
84HRESULT CacheBundleToWorkingDirectory(
85 __in_z LPCWSTR wzBundleId,
86 __in_z LPCWSTR wzExecutableName,
87 __in BURN_PAYLOADS* pUxPayloads,
88 __in BURN_SECTION* pSection,
89 __deref_out_z_opt LPWSTR* psczEngineWorkingPath
90 );
91HRESULT CacheLayoutBundle(
92 __in_z LPCWSTR wzExecutableName,
93 __in_z LPCWSTR wzLayoutDirectory,
94 __in_z LPCWSTR wzSourceBundlePath
95 );
96HRESULT CacheCompleteBundle(
97 __in BOOL fPerMachine,
98 __in_z LPCWSTR wzExecutableName,
99 __in_z LPCWSTR wzBundleId,
100 __in BURN_PAYLOADS* pUxPayloads,
101 __in_z LPCWSTR wzSourceBundlePath
102#ifdef DEBUG
103 , __in_z LPCWSTR wzExecutablePath
104#endif
105 );
106HRESULT CacheLayoutContainer(
107 __in BURN_CONTAINER* pContainer,
108 __in_z_opt LPCWSTR wzLayoutDirectory,
109 __in_z LPCWSTR wzUnverifiedContainerPath,
110 __in BOOL fMove
111 );
112HRESULT CacheLayoutPayload(
113 __in BURN_PAYLOAD* pPayload,
114 __in_z_opt LPCWSTR wzLayoutDirectory,
115 __in_z LPCWSTR wzUnverifiedPayloadPath,
116 __in BOOL fMove
117 );
118HRESULT CacheCompletePayload(
119 __in BOOL fPerMachine,
120 __in BURN_PAYLOAD* pPayload,
121 __in_z_opt LPCWSTR wzCacheId,
122 __in_z LPCWSTR wzUnverifiedPayloadPath,
123 __in BOOL fMove
124 );
125HRESULT CacheRemoveWorkingFolder(
126 __in_z_opt LPCWSTR wzBundleId
127 );
128HRESULT CacheRemoveBundle(
129 __in BOOL fPerMachine,
130 __in_z LPCWSTR wzPackageId
131 );
132HRESULT CacheRemovePackage(
133 __in BOOL fPerMachine,
134 __in_z LPCWSTR wzPackageId,
135 __in_z LPCWSTR wzCacheId
136 );
137HRESULT CacheVerifyPayloadSignature(
138 __in BURN_PAYLOAD* pPayload,
139 __in_z LPCWSTR wzUnverifiedPayloadPath,
140 __in HANDLE hFile
141 );
142void CacheCleanup(
143 __in BOOL fPerMachine,
144 __in_z LPCWSTR wzBundleId
145 );
146void CacheUninitialize();
147
148#ifdef __cplusplus
149}
150#endif
diff --git a/src/engine/catalog.cpp b/src/engine/catalog.cpp
new file mode 100644
index 00000000..da086545
--- /dev/null
+++ b/src/engine/catalog.cpp
@@ -0,0 +1,180 @@
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#include "precomp.h"
4
5
6// function definitions
7
8extern "C" HRESULT CatalogsParseFromXml(
9 __in BURN_CATALOGS* pCatalogs,
10 __in IXMLDOMNode* pixnBundle
11 )
12{
13 HRESULT hr = S_OK;
14 IXMLDOMNodeList* pixnNodes = NULL;
15 IXMLDOMNode* pixnNode = NULL;
16 DWORD cNodes = 0;
17 LPWSTR scz = NULL;
18
19 // select catalog nodes
20 hr = XmlSelectNodes(pixnBundle, L"Catalog", &pixnNodes);
21 ExitOnFailure(hr, "Failed to select catalog nodes.");
22
23 // get catalog node count
24 hr = pixnNodes->get_length((long*)&cNodes);
25 ExitOnFailure(hr, "Failed to get payload node count.");
26 if (!cNodes)
27 {
28 ExitFunction();
29 }
30
31 // allocate memory for catalogs
32 pCatalogs->rgCatalogs = (BURN_CATALOG*)MemAlloc(sizeof(BURN_CATALOG) * cNodes, TRUE);
33 ExitOnNull(pCatalogs->rgCatalogs, hr, E_OUTOFMEMORY, "Failed to allocate memory for payload structs.");
34
35 pCatalogs->cCatalogs = cNodes;
36
37 // parse catalog elements
38 for (DWORD i = 0; i < cNodes; ++i)
39 {
40 BURN_CATALOG* pCatalog = &pCatalogs->rgCatalogs[i];
41 pCatalog->hFile = INVALID_HANDLE_VALUE;
42
43 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
44 ExitOnFailure(hr, "Failed to get next node.");
45
46 // @Id
47 hr = XmlGetAttributeEx(pixnNode, L"Id", &pCatalog->sczKey);
48 ExitOnFailure(hr, "Failed to get @Id.");
49
50 // @Payload
51 hr = XmlGetAttributeEx(pixnNode, L"Payload", &pCatalog->sczPayload);
52 ExitOnFailure(hr, "Failed to get @Payload.");
53
54 // prepare next iteration
55 ReleaseNullObject(pixnNode);
56 }
57
58LExit:
59 ReleaseObject(pixnNodes);
60 ReleaseObject(pixnNode);
61 ReleaseStr(scz);
62
63 return hr;
64}
65
66extern "C" HRESULT CatalogFindById(
67 __in BURN_CATALOGS* pCatalogs,
68 __in_z LPCWSTR wzId,
69 __out BURN_CATALOG** ppCatalog
70 )
71{
72 HRESULT hr = S_OK;
73 BURN_CATALOG* pCatalog = NULL;
74
75 for (DWORD i = 0; i < pCatalogs->cCatalogs; ++i)
76 {
77 pCatalog = &pCatalogs->rgCatalogs[i];
78
79 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pCatalog->sczKey, -1, wzId, -1))
80 {
81 *ppCatalog = pCatalog;
82 ExitFunction1(hr = S_OK);
83 }
84 }
85
86 hr = E_NOTFOUND;
87
88LExit:
89 return hr;
90}
91
92extern "C" HRESULT CatalogLoadFromPayload(
93 __in BURN_CATALOGS* pCatalogs,
94 __in BURN_PAYLOADS* pPayloads
95 )
96{
97 HRESULT hr = S_OK;
98 BURN_CATALOG* pCatalog = NULL;
99 BURN_PAYLOAD* pPayload = NULL;
100
101 // go through each catalog file
102 for (DWORD i = 0; i < pCatalogs->cCatalogs; i++)
103 {
104 pCatalog = &pCatalogs->rgCatalogs[i];
105
106 // get the payload for this catalog file
107 hr = PayloadFindById(pPayloads, pCatalog->sczPayload, &pPayload);
108 ExitOnFailure(hr, "Failed to find payload for catalog file.");
109
110 // Get the local file name
111 hr = StrAllocString(&pCatalog->sczLocalFilePath, pPayload->sczLocalFilePath, 0);
112 ExitOnFailure(hr, "Failed to get catalog local file path");
113
114 // Get a handle to the file
115 pCatalog->hFile = ::CreateFileW(pCatalog->sczLocalFilePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
116 if (INVALID_HANDLE_VALUE == pCatalog->hFile)
117 {
118 ExitWithLastError(hr, "Failed to open catalog in working path: %ls", pCatalog->sczLocalFilePath);
119 }
120
121 // Verify the catalog file
122 hr = CacheVerifyPayloadSignature(pPayload, pCatalog->sczLocalFilePath, pCatalog->hFile);
123 ExitOnFailure(hr, "Failed to verify catalog signature: %ls", pCatalog->sczLocalFilePath);
124 }
125
126LExit:
127 return hr;
128}
129
130extern "C" HRESULT CatalogElevatedUpdateCatalogFile(
131 __in BURN_CATALOGS* pCatalogs,
132 __in_z LPCWSTR wzId,
133 __in_z LPCWSTR wzPath
134 )
135{
136 HRESULT hr = S_OK;
137 BURN_CATALOG* pCatalog = NULL;
138
139 // Find the catalog
140 hr = CatalogFindById(pCatalogs, wzId, &pCatalog);
141 ExitOnFailure(hr, "Failed to locate catalog information.");
142
143 if (NULL == pCatalog->sczLocalFilePath)
144 {
145 hr = StrAllocString(&pCatalog->sczLocalFilePath, wzPath, 0);
146 ExitOnFailure(hr, "Failed to allocated catalog path.");
147
148 // Get a handle to the file
149 pCatalog->hFile = ::CreateFileW(pCatalog->sczLocalFilePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
150 if (INVALID_HANDLE_VALUE == pCatalog->hFile)
151 {
152 ExitWithLastError(hr, "Failed to open catalog in working path: %ls", pCatalog->sczLocalFilePath);
153 }
154 }
155
156LExit:
157 return hr;
158}
159
160extern "C" void CatalogUninitialize(
161 __in BURN_CATALOGS* pCatalogs
162 )
163{
164 if (pCatalogs->rgCatalogs)
165 {
166 for (DWORD i = 0; i < pCatalogs->cCatalogs; ++i)
167 {
168 BURN_CATALOG* pCatalog = &pCatalogs->rgCatalogs[i];
169
170 ReleaseHandle(pCatalog->hFile);
171 ReleaseStr(pCatalog->sczKey);
172 ReleaseStr(pCatalog->sczLocalFilePath);
173 ReleaseStr(pCatalog->sczPayload);
174 }
175 MemFree(pCatalogs->rgCatalogs);
176 }
177
178 // clear struct
179 memset(pCatalogs, 0, sizeof(BURN_CATALOGS));
180}
diff --git a/src/engine/catalog.h b/src/engine/catalog.h
new file mode 100644
index 00000000..3a87d0d2
--- /dev/null
+++ b/src/engine/catalog.h
@@ -0,0 +1,56 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9// structs
10
11typedef struct _BURN_CATALOG
12{
13 LPWSTR sczKey;
14 LPWSTR sczPayload;
15
16 // mutable members
17 LPWSTR sczLocalFilePath; // location of extracted or downloaded copy
18 HANDLE hFile;
19} BURN_CATALOG;
20
21typedef struct _BURN_CATALOGS
22{
23 BURN_CATALOG* rgCatalogs;
24 DWORD cCatalogs;
25} BURN_CATALOGS;
26
27typedef struct _BURN_PAYLOADS BURN_PAYLOADS;
28
29
30// functions
31
32HRESULT CatalogsParseFromXml(
33 __in BURN_CATALOGS* pCatalogs,
34 __in IXMLDOMNode* pixnBundle
35 );
36HRESULT CatalogFindById(
37 __in BURN_CATALOGS* pCatalogs,
38 __in_z LPCWSTR wzId,
39 __out BURN_CATALOG** ppCatalog
40 );
41HRESULT CatalogLoadFromPayload(
42 __in BURN_CATALOGS* pCatalogs,
43 __in BURN_PAYLOADS* pPayloads
44 );
45HRESULT CatalogElevatedUpdateCatalogFile(
46 __in BURN_CATALOGS* pCatalogs,
47 __in_z LPCWSTR wzId,
48 __in_z LPCWSTR wzPath
49 );
50void CatalogUninitialize(
51 __in BURN_CATALOGS* pCatalogs
52 );
53
54#if defined(__cplusplus)
55}
56#endif
diff --git a/src/engine/condition.cpp b/src/engine/condition.cpp
new file mode 100644
index 00000000..28391d2d
--- /dev/null
+++ b/src/engine/condition.cpp
@@ -0,0 +1,1030 @@
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#include "precomp.h"
4
5
6//
7// parse rules
8//
9// value variable | literal | integer | version
10// comparison-operator < | > | <= | >= | = | <> | >< | << | >>
11// term value | value comparison-operator value | ( expression )
12// boolean-factor term | NOT term
13// boolean-term boolean-factor | boolean-factor AND boolean-term
14// expression boolean-term | boolean-term OR expression
15//
16
17
18// constants
19
20#define COMPARISON 0x00010000
21#define INSENSITIVE 0x00020000
22
23enum BURN_SYMBOL_TYPE
24{
25 // terminals
26 BURN_SYMBOL_TYPE_NONE = 0,
27 BURN_SYMBOL_TYPE_END = 1,
28 BURN_SYMBOL_TYPE_OR = 2, // OR
29 BURN_SYMBOL_TYPE_AND = 3, // AND
30 BURN_SYMBOL_TYPE_NOT = 4, // NOT
31 BURN_SYMBOL_TYPE_LT = 5 | COMPARISON, // <
32 BURN_SYMBOL_TYPE_GT = 6 | COMPARISON, // >
33 BURN_SYMBOL_TYPE_LE = 7 | COMPARISON, // <=
34 BURN_SYMBOL_TYPE_GE = 8 | COMPARISON, // >=
35 BURN_SYMBOL_TYPE_EQ = 9 | COMPARISON, // =
36 BURN_SYMBOL_TYPE_NE = 10 | COMPARISON, // <>
37 BURN_SYMBOL_TYPE_BAND = 11 | COMPARISON, // ><
38 BURN_SYMBOL_TYPE_HIEQ = 12 | COMPARISON, // <<
39 BURN_SYMBOL_TYPE_LOEQ = 13 | COMPARISON, // >>
40 BURN_SYMBOL_TYPE_LT_I = 5 | COMPARISON | INSENSITIVE, // ~<
41 BURN_SYMBOL_TYPE_GT_I = 6 | COMPARISON | INSENSITIVE, // ~>
42 BURN_SYMBOL_TYPE_LE_I = 7 | COMPARISON | INSENSITIVE, // ~<=
43 BURN_SYMBOL_TYPE_GE_I = 8 | COMPARISON | INSENSITIVE, // ~>=
44 BURN_SYMBOL_TYPE_EQ_I = 9 | COMPARISON | INSENSITIVE, // ~=
45 BURN_SYMBOL_TYPE_NE_I = 10 | COMPARISON | INSENSITIVE, // ~<>
46 BURN_SYMBOL_TYPE_BAND_I = 11 | COMPARISON | INSENSITIVE, // ~><
47 BURN_SYMBOL_TYPE_HIEQ_I = 12 | COMPARISON | INSENSITIVE, // ~<<
48 BURN_SYMBOL_TYPE_LOEQ_I = 13 | COMPARISON | INSENSITIVE, // ~>>
49 BURN_SYMBOL_TYPE_LPAREN = 14, // (
50 BURN_SYMBOL_TYPE_RPAREN = 15, // )
51 BURN_SYMBOL_TYPE_NUMBER = 16,
52 BURN_SYMBOL_TYPE_IDENTIFIER = 17,
53 BURN_SYMBOL_TYPE_LITERAL = 18,
54 BURN_SYMBOL_TYPE_VERSION = 19,
55};
56
57
58// structs
59
60struct BURN_SYMBOL
61{
62 BURN_SYMBOL_TYPE Type;
63 DWORD iPosition;
64 BURN_VARIANT Value;
65};
66
67struct BURN_CONDITION_PARSE_CONTEXT
68{
69 BURN_VARIABLES* pVariables;
70 LPCWSTR wzCondition;
71 LPCWSTR wzRead;
72 BURN_SYMBOL NextSymbol;
73 BOOL fError;
74};
75
76
77// internal function declarations
78
79static HRESULT ParseExpression(
80 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
81 __out BOOL* pf
82 );
83static HRESULT ParseBooleanTerm(
84 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
85 __out BOOL* pf
86 );
87static HRESULT ParseBooleanFactor(
88 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
89 __out BOOL* pf
90 );
91static HRESULT ParseTerm(
92 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
93 __out BOOL* pf
94 );
95static HRESULT ParseValue(
96 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
97 __out BURN_VARIANT* pValue
98 );
99static HRESULT Expect(
100 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
101 __in BURN_SYMBOL_TYPE symbolType
102 );
103static HRESULT NextSymbol(
104 __in BURN_CONDITION_PARSE_CONTEXT* pContext
105 );
106static HRESULT CompareValues(
107 __in BURN_SYMBOL_TYPE comparison,
108 __in BURN_VARIANT leftOperand,
109 __in BURN_VARIANT rightOperand,
110 __out BOOL* pfResult
111 );
112static HRESULT CompareStringValues(
113 __in BURN_SYMBOL_TYPE comparison,
114 __in_z LPCWSTR wzLeftOperand,
115 __in_z LPCWSTR wzRightOperand,
116 __out BOOL* pfResult
117 );
118static HRESULT CompareIntegerValues(
119 __in BURN_SYMBOL_TYPE comparison,
120 __in LONGLONG llLeftOperand,
121 __in LONGLONG llRightOperand,
122 __out BOOL* pfResult
123 );
124static HRESULT CompareVersionValues(
125 __in BURN_SYMBOL_TYPE comparison,
126 __in DWORD64 qwLeftOperand,
127 __in DWORD64 qwRightOperand,
128 __out BOOL* pfResult
129 );
130
131
132// function definitions
133
134extern "C" HRESULT ConditionEvaluate(
135 __in BURN_VARIABLES* pVariables,
136 __in_z LPCWSTR wzCondition,
137 __out BOOL* pf
138 )
139{
140 HRESULT hr = S_OK;
141 BURN_CONDITION_PARSE_CONTEXT context = { };
142 BOOL f = FALSE;
143
144 context.pVariables = pVariables;
145 context.wzCondition = wzCondition;
146 context.wzRead = wzCondition;
147
148 hr = NextSymbol(&context);
149 ExitOnFailure(hr, "Failed to read next symbol.");
150
151 hr = ParseExpression(&context, &f);
152 ExitOnFailure(hr, "Failed to parse expression.");
153
154 hr = Expect(&context, BURN_SYMBOL_TYPE_END);
155 ExitOnFailure(hr, "Failed to expect end symbol.");
156
157 LogId(REPORT_VERBOSE, MSG_CONDITION_RESULT, wzCondition, LoggingTrueFalseToString(f));
158
159 *pf = f;
160 hr = S_OK;
161
162LExit:
163 if (context.fError)
164 {
165 Assert(FAILED(hr));
166 LogErrorId(hr, MSG_FAILED_PARSE_CONDITION, wzCondition, NULL, NULL);
167 }
168
169 return hr;
170}
171
172extern "C" HRESULT ConditionGlobalCheck(
173 __in BURN_VARIABLES* pVariables,
174 __in BURN_CONDITION* pCondition,
175 __in BOOTSTRAPPER_DISPLAY display,
176 __in_z LPCWSTR wzBundleName,
177 __out DWORD *pdwExitCode,
178 __out BOOL *pfContinueExecution
179 )
180{
181 HRESULT hr = S_OK;
182 BOOL fSuccess = TRUE;
183 HRESULT hrError = HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION);
184 OS_VERSION osv = OS_VERSION_UNKNOWN;
185 DWORD dwServicePack = 0;
186
187 OsGetVersion(&osv, &dwServicePack);
188
189 // Always error on Windows 2000 or lower
190 if (OS_VERSION_WIN2000 >= osv)
191 {
192 fSuccess = FALSE;
193 }
194 else
195 {
196 if (NULL != pCondition->sczConditionString)
197 {
198 hr = ConditionEvaluate(pVariables, pCondition->sczConditionString, &fSuccess);
199 ExitOnFailure(hr, "Failed to evaluate condition: %ls", pCondition->sczConditionString);
200 }
201 }
202
203 if (!fSuccess)
204 {
205 // Display the error messagebox, as long as we're in an appropriate display mode
206 hr = SplashScreenDisplayError(display, wzBundleName, hrError);
207 ExitOnFailure(hr, "Failed to display error dialog");
208
209 *pdwExitCode = static_cast<DWORD>(hrError);
210 *pfContinueExecution = FALSE;
211 }
212
213LExit:
214 return hr;
215}
216
217HRESULT ConditionGlobalParseFromXml(
218 __in BURN_CONDITION* pCondition,
219 __in IXMLDOMNode* pixnBundle
220 )
221{
222 HRESULT hr = S_OK;
223 IXMLDOMNode* pixnNode = NULL;
224 BSTR bstrExpression = NULL;
225
226 // select variable nodes
227 hr = XmlSelectSingleNode(pixnBundle, L"Condition", &pixnNode);
228 if (S_FALSE == hr)
229 {
230 ExitFunction1(hr = S_OK);
231 }
232 ExitOnFailure(hr, "Failed to select condition node.");
233
234 // @Condition
235 hr = XmlGetText(pixnNode, &bstrExpression);
236 ExitOnFailure(hr, "Failed to get Condition inner text.");
237
238 hr = StrAllocString(&pCondition->sczConditionString, bstrExpression, 0);
239 ExitOnFailure(hr, "Failed to copy condition string from BSTR");
240
241LExit:
242 ReleaseBSTR(bstrExpression);
243 ReleaseObject(pixnNode);
244
245 return hr;
246}
247
248
249// internal function definitions
250
251static HRESULT ParseExpression(
252 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
253 __out BOOL* pf
254 )
255{
256 HRESULT hr = S_OK;
257 BOOL fFirst = FALSE;
258 BOOL fSecond = FALSE;
259
260 hr = ParseBooleanTerm(pContext, &fFirst);
261 ExitOnFailure(hr, "Failed to parse boolean-term.");
262
263 if (BURN_SYMBOL_TYPE_OR == pContext->NextSymbol.Type)
264 {
265 hr = NextSymbol(pContext);
266 ExitOnFailure(hr, "Failed to read next symbol.");
267
268 hr = ParseExpression(pContext, &fSecond);
269 ExitOnFailure(hr, "Failed to parse expression.");
270
271 *pf = fFirst || fSecond;
272 }
273 else
274 {
275 *pf = fFirst;
276 }
277
278LExit:
279 return hr;
280}
281
282static HRESULT ParseBooleanTerm(
283 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
284 __out BOOL* pf
285 )
286{
287 HRESULT hr = S_OK;
288 BOOL fFirst = FALSE;
289 BOOL fSecond = FALSE;
290
291 hr = ParseBooleanFactor(pContext, &fFirst);
292 ExitOnFailure(hr, "Failed to parse boolean-factor.");
293
294 if (BURN_SYMBOL_TYPE_AND == pContext->NextSymbol.Type)
295 {
296 hr = NextSymbol(pContext);
297 ExitOnFailure(hr, "Failed to read next symbol.");
298
299 hr = ParseBooleanTerm(pContext, &fSecond);
300 ExitOnFailure(hr, "Failed to parse boolean-term.");
301
302 *pf = fFirst && fSecond;
303 }
304 else
305 {
306 *pf = fFirst;
307 }
308
309LExit:
310 return hr;
311}
312
313static HRESULT ParseBooleanFactor(
314 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
315 __out BOOL* pf
316 )
317{
318 HRESULT hr = S_OK;
319 BOOL fNot = FALSE;
320 BOOL f = FALSE;
321
322 if (BURN_SYMBOL_TYPE_NOT == pContext->NextSymbol.Type)
323 {
324 hr = NextSymbol(pContext);
325 ExitOnFailure(hr, "Failed to read next symbol.");
326
327 fNot = TRUE;
328 }
329
330 hr = ParseTerm(pContext, &f);
331 ExitOnFailure(hr, "Failed to parse term.");
332
333 *pf = fNot ? !f : f;
334
335LExit:
336 return hr;
337}
338
339static HRESULT ParseTerm(
340 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
341 __out BOOL* pf
342 )
343{
344 HRESULT hr = S_OK;
345 BURN_VARIANT firstValue = { };
346 BURN_VARIANT secondValue = { };
347
348 if (BURN_SYMBOL_TYPE_LPAREN == pContext->NextSymbol.Type)
349 {
350 hr = NextSymbol(pContext);
351 ExitOnFailure(hr, "Failed to read next symbol.");
352
353 hr = ParseExpression(pContext, pf);
354 ExitOnFailure(hr, "Failed to parse expression.");
355
356 hr = Expect(pContext, BURN_SYMBOL_TYPE_RPAREN);
357 ExitOnFailure(hr, "Failed to expect right parenthesis.");
358
359 ExitFunction1(hr = S_OK);
360 }
361
362 hr = ParseValue(pContext, &firstValue);
363 ExitOnFailure(hr, "Failed to parse value.");
364
365 if (COMPARISON & pContext->NextSymbol.Type)
366 {
367 BURN_SYMBOL_TYPE comparison = pContext->NextSymbol.Type;
368
369 hr = NextSymbol(pContext);
370 ExitOnFailure(hr, "Failed to read next symbol.");
371
372 hr = ParseValue(pContext, &secondValue);
373 ExitOnFailure(hr, "Failed to parse value.");
374
375 hr = CompareValues(comparison, firstValue, secondValue, pf);
376 ExitOnFailure(hr, "Failed to compare value.");
377 }
378 else
379 {
380 LONGLONG llValue = 0;
381 LPWSTR sczValue = NULL;
382 DWORD64 qwValue = 0;
383 switch (firstValue.Type)
384 {
385 case BURN_VARIANT_TYPE_NONE:
386 *pf = FALSE;
387 break;
388 case BURN_VARIANT_TYPE_STRING:
389 hr = BVariantGetString(&firstValue, &sczValue);
390 if (SUCCEEDED(hr))
391 {
392 *pf = sczValue && *sczValue;
393 }
394 StrSecureZeroFreeString(sczValue);
395 break;
396 case BURN_VARIANT_TYPE_NUMERIC:
397 hr = BVariantGetNumeric(&firstValue, &llValue);
398 if (SUCCEEDED(hr))
399 {
400 *pf = 0 != llValue;
401 }
402 SecureZeroMemory(&llValue, sizeof(llValue));
403 break;
404 case BURN_VARIANT_TYPE_VERSION:
405 hr = BVariantGetVersion(&firstValue, &qwValue);
406 if (SUCCEEDED(hr))
407 {
408 *pf = 0 != qwValue;
409 }
410 SecureZeroMemory(&llValue, sizeof(qwValue));
411 break;
412 default:
413 ExitFunction1(hr = E_UNEXPECTED);
414 }
415 }
416
417LExit:
418 BVariantUninitialize(&firstValue);
419 BVariantUninitialize(&secondValue);
420 return hr;
421}
422
423static HRESULT ParseValue(
424 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
425 __out BURN_VARIANT* pValue
426 )
427{
428 HRESULT hr = S_OK;
429
430 // Symbols don't encrypt their value, so can access the value directly.
431 switch (pContext->NextSymbol.Type)
432 {
433 case BURN_SYMBOL_TYPE_IDENTIFIER:
434 Assert(BURN_VARIANT_TYPE_STRING == pContext->NextSymbol.Value.Type);
435
436 // find variable
437 hr = VariableGetVariant(pContext->pVariables, pContext->NextSymbol.Value.sczValue, pValue);
438 if (E_NOTFOUND != hr)
439 {
440 ExitOnRootFailure(hr, "Failed to find variable.");
441 }
442 break;
443
444 case BURN_SYMBOL_TYPE_NUMBER: __fallthrough;
445 case BURN_SYMBOL_TYPE_LITERAL: __fallthrough;
446 case BURN_SYMBOL_TYPE_VERSION:
447 // steal value of symbol
448 memcpy_s(pValue, sizeof(BURN_VARIANT), &pContext->NextSymbol.Value, sizeof(BURN_VARIANT));
449 memset(&pContext->NextSymbol.Value, 0, sizeof(BURN_VARIANT));
450 break;
451
452 default:
453 pContext->fError = TRUE;
454 hr = E_INVALIDDATA;
455 ExitOnRootFailure(hr, "Failed to parse condition '%ls' at position: %u", pContext->wzCondition, pContext->NextSymbol.iPosition);
456 }
457
458 // get next symbol
459 hr = NextSymbol(pContext);
460 ExitOnFailure(hr, "Failed to read next symbol.");
461
462LExit:
463 return hr;
464}
465
466//
467// Expect - expects a symbol.
468//
469static HRESULT Expect(
470 __in BURN_CONDITION_PARSE_CONTEXT* pContext,
471 __in BURN_SYMBOL_TYPE symbolType
472 )
473{
474 HRESULT hr = S_OK;
475
476 if (pContext->NextSymbol.Type != symbolType)
477 {
478 pContext->fError = TRUE;
479 hr = E_INVALIDDATA;
480 ExitOnRootFailure(hr, "Failed to parse condition '%ls' at position: %u", pContext->wzCondition, pContext->NextSymbol.iPosition);
481 }
482
483 hr = NextSymbol(pContext);
484 ExitOnFailure(hr, "Failed to read next symbol.");
485
486LExit:
487 return hr;
488}
489
490//
491// NextSymbol - finds the next symbol in an expression string.
492//
493static HRESULT NextSymbol(
494 __in BURN_CONDITION_PARSE_CONTEXT* pContext
495 )
496{
497 HRESULT hr = S_OK;
498 WORD charType = 0;
499 DWORD iPosition = 0;
500 DWORD n = 0;
501
502 // free existing symbol
503 BVariantUninitialize(&pContext->NextSymbol.Value);
504 memset(&pContext->NextSymbol, 0, sizeof(BURN_SYMBOL));
505
506 // skip past blanks
507 while (L'\0' != pContext->wzRead[0])
508 {
509 ::GetStringTypeW(CT_CTYPE1, pContext->wzRead, 1, &charType);
510 if (0 == (C1_BLANK & charType))
511 {
512 break; // no blank, done
513 }
514 ++pContext->wzRead;
515 }
516 iPosition = (DWORD)(pContext->wzRead - pContext->wzCondition);
517
518 // read depending on first character type
519 switch (pContext->wzRead[0])
520 {
521 case L'\0':
522 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_END;
523 break;
524 case L'~':
525 switch (pContext->wzRead[1])
526 {
527 case L'=':
528 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_EQ_I;
529 n = 2;
530 break;
531 case L'>':
532 switch (pContext->wzRead[2])
533 {
534 case '=':
535 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GE_I;
536 n = 3;
537 break;
538 case L'>':
539 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LOEQ_I;
540 n = 3;
541 break;
542 case L'<':
543 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_BAND_I;
544 n = 3;
545 break;
546 default:
547 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GT_I;
548 n = 2;
549 }
550 break;
551 case L'<':
552 switch (pContext->wzRead[2])
553 {
554 case '=':
555 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LE_I;
556 n = 3;
557 break;
558 case L'<':
559 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_HIEQ_I;
560 n = 3;
561 break;
562 case '>':
563 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NE_I;
564 n = 3;
565 break;
566 default:
567 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LT_I;
568 n = 2;
569 }
570 break;
571 default:
572 // error
573 pContext->fError = TRUE;
574 hr = E_INVALIDDATA;
575 ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Unexpected '~' operator at position %d.", pContext->wzCondition, iPosition);
576 }
577 break;
578 case L'>':
579 switch (pContext->wzRead[1])
580 {
581 case L'=':
582 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GE;
583 n = 2;
584 break;
585 case L'>':
586 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LOEQ;
587 n = 2;
588 break;
589 case L'<':
590 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_BAND;
591 n = 2;
592 break;
593 default:
594 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_GT;
595 n = 1;
596 }
597 break;
598 case L'<':
599 switch (pContext->wzRead[1])
600 {
601 case L'=':
602 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LE;
603 n = 2;
604 break;
605 case L'<':
606 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_HIEQ;
607 n = 2;
608 break;
609 case L'>':
610 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NE;
611 n = 2;
612 break;
613 default:
614 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LT;
615 n = 1;
616 }
617 break;
618 case L'=':
619 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_EQ;
620 n = 1;
621 break;
622 case L'(':
623 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LPAREN;
624 n = 1;
625 break;
626 case L')':
627 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_RPAREN;
628 n = 1;
629 break;
630 case L'"': // literal
631 do
632 {
633 ++n;
634 if (L'\0' == pContext->wzRead[n])
635 {
636 // error
637 pContext->fError = TRUE;
638 hr = E_INVALIDDATA;
639 ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Unterminated literal at position %d.", pContext->wzCondition, iPosition);
640 }
641 } while (L'"' != pContext->wzRead[n]);
642 ++n; // terminating '"'
643
644 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_LITERAL;
645 hr = BVariantSetString(&pContext->NextSymbol.Value, &pContext->wzRead[1], n - 2);
646 ExitOnFailure(hr, "Failed to set symbol value.");
647 break;
648 default:
649 if (C1_DIGIT & charType || L'-' == pContext->wzRead[0])
650 {
651 do
652 {
653 ++n;
654 ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[n], 1, &charType);
655 if (C1_ALPHA & charType || L'_' == pContext->wzRead[n])
656 {
657 // error, identifier cannot start with a digit
658 pContext->fError = TRUE;
659 hr = E_INVALIDDATA;
660 ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Identifier cannot start at a digit, at position %d.", pContext->wzCondition, iPosition);
661 }
662 } while (C1_DIGIT & charType);
663
664 // number
665 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NUMBER;
666
667 LONGLONG ll = 0;
668 hr = StrStringToInt64(pContext->wzRead, n, &ll);
669 if (FAILED(hr))
670 {
671 pContext->fError = TRUE;
672 hr = E_INVALIDDATA;
673 ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Constant too big, at position %d.", pContext->wzCondition, iPosition);
674 }
675
676 hr = BVariantSetNumeric(&pContext->NextSymbol.Value, ll);
677 ExitOnFailure(hr, "Failed to set symbol value.");
678 }
679 else if (C1_ALPHA & charType || L'_' == pContext->wzRead[0])
680 {
681 ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[1], 1, &charType);
682 if (L'v' == pContext->wzRead[0] && C1_DIGIT & charType)
683 {
684 // version
685 DWORD cParts = 1;
686 for (;;)
687 {
688 ++n;
689 if (L'.' == pContext->wzRead[n])
690 {
691 ++cParts;
692 if (4 < cParts)
693 {
694 // error, too many parts in version
695 pContext->fError = TRUE;
696 hr = E_INVALIDDATA;
697 ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Version can have a maximum of 4 parts, at position %d.", pContext->wzCondition, iPosition);
698 }
699 }
700 else
701 {
702 ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[n], 1, &charType);
703 if (C1_DIGIT != (C1_DIGIT & charType))
704 {
705 break;
706 }
707 }
708 }
709
710 // Symbols don't encrypt their value, so can access the value directly.
711 hr = FileVersionFromStringEx(&pContext->wzRead[1], n - 1, &pContext->NextSymbol.Value.qwValue);
712 if (FAILED(hr))
713 {
714 pContext->fError = TRUE;
715 hr = E_INVALIDDATA;
716 ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Invalid version format, at position %d.", pContext->wzCondition, iPosition);
717 }
718
719 pContext->NextSymbol.Value.Type = BURN_VARIANT_TYPE_VERSION;
720 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_VERSION;
721 }
722 else
723 {
724 do
725 {
726 ++n;
727 ::GetStringTypeW(CT_CTYPE1, &pContext->wzRead[n], 1, &charType);
728 } while (C1_ALPHA & charType || C1_DIGIT & charType || L'_' == pContext->wzRead[n]);
729
730 if (2 == n && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pContext->wzRead, 2, L"OR", 2))
731 {
732 // OR
733 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_OR;
734 }
735 else if (3 == n && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pContext->wzRead, 3, L"AND", 3))
736 {
737 // AND
738 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_AND;
739 }
740 else if (3 == n && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pContext->wzRead, 3, L"NOT", 3))
741 {
742 // NOT
743 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_NOT;
744 }
745 else
746 {
747 // identifier
748 pContext->NextSymbol.Type = BURN_SYMBOL_TYPE_IDENTIFIER;
749 hr = BVariantSetString(&pContext->NextSymbol.Value, pContext->wzRead, n);
750 ExitOnFailure(hr, "Failed to set symbol value.");
751 }
752 }
753 }
754 else
755 {
756 // error, unexpected character
757 pContext->fError = TRUE;
758 hr = E_INVALIDDATA;
759 ExitOnRootFailure(hr, "Failed to parse condition \"%ls\". Unexpected character at position %d.", pContext->wzCondition, iPosition);
760 }
761 }
762 pContext->NextSymbol.iPosition = iPosition;
763 pContext->wzRead += n;
764
765LExit:
766 return hr;
767}
768
769//
770// CompareValues - compares two variant values using a given comparison.
771//
772static HRESULT CompareValues(
773 __in BURN_SYMBOL_TYPE comparison,
774 __in BURN_VARIANT leftOperand,
775 __in BURN_VARIANT rightOperand,
776 __out BOOL* pfResult
777 )
778{
779 HRESULT hr = S_OK;
780 LONGLONG llLeft = 0;
781 DWORD64 qwLeft = 0;
782 LPWSTR sczLeft = NULL;
783 LONGLONG llRight = 0;
784 DWORD64 qwRight = 0;
785 LPWSTR sczRight = NULL;
786
787 // get values to compare based on type
788 if (BURN_VARIANT_TYPE_STRING == leftOperand.Type && BURN_VARIANT_TYPE_STRING == rightOperand.Type)
789 {
790 hr = BVariantGetString(&leftOperand, &sczLeft);
791 ExitOnFailure(hr, "Failed to get the left string");
792 hr = BVariantGetString(&rightOperand, &sczRight);
793 ExitOnFailure(hr, "Failed to get the right string");
794 hr = CompareStringValues(comparison, sczLeft, sczRight, pfResult);
795 }
796 else if (BURN_VARIANT_TYPE_NUMERIC == leftOperand.Type && BURN_VARIANT_TYPE_NUMERIC == rightOperand.Type)
797 {
798 hr = BVariantGetNumeric(&leftOperand, &llLeft);
799 ExitOnFailure(hr, "Failed to get the left numeric");
800 hr = BVariantGetNumeric(&rightOperand, &llRight);
801 ExitOnFailure(hr, "Failed to get the right numeric");
802 hr = CompareIntegerValues(comparison, llLeft, llRight, pfResult);
803 }
804 else if (BURN_VARIANT_TYPE_VERSION == leftOperand.Type && BURN_VARIANT_TYPE_VERSION == rightOperand.Type)
805 {
806 hr = BVariantGetVersion(&leftOperand, &qwLeft);
807 ExitOnFailure(hr, "Failed to get the left version");
808 hr = BVariantGetVersion(&rightOperand, &qwRight);
809 ExitOnFailure(hr, "Failed to get the right version");
810 hr = CompareVersionValues(comparison, qwLeft, qwRight, pfResult);
811 }
812 else if (BURN_VARIANT_TYPE_VERSION == leftOperand.Type && BURN_VARIANT_TYPE_STRING == rightOperand.Type)
813 {
814 hr = BVariantGetVersion(&leftOperand, &qwLeft);
815 ExitOnFailure(hr, "Failed to get the left version");
816 hr = BVariantGetVersion(&rightOperand, &qwRight);
817 if (FAILED(hr))
818 {
819 if (DISP_E_TYPEMISMATCH != hr)
820 {
821 ExitOnFailure(hr, "Failed to get the right version");
822 }
823 *pfResult = (BURN_SYMBOL_TYPE_NE == comparison);
824 hr = S_OK;
825 }
826 else
827 {
828 hr = CompareVersionValues(comparison, qwLeft, qwRight, pfResult);
829 }
830 }
831 else if (BURN_VARIANT_TYPE_STRING == leftOperand.Type && BURN_VARIANT_TYPE_VERSION == rightOperand.Type)
832 {
833 hr = BVariantGetVersion(&rightOperand, &qwRight);
834 ExitOnFailure(hr, "Failed to get the right version");
835 hr = BVariantGetVersion(&leftOperand, &qwLeft);
836 if (FAILED(hr))
837 {
838 if (DISP_E_TYPEMISMATCH != hr)
839 {
840 ExitOnFailure(hr, "Failed to get the left version");
841 }
842 *pfResult = (BURN_SYMBOL_TYPE_NE == comparison);
843 hr = S_OK;
844 }
845 else
846 {
847 hr = CompareVersionValues(comparison, qwLeft, qwRight, pfResult);
848 }
849 }
850 else if (BURN_VARIANT_TYPE_NUMERIC == leftOperand.Type && BURN_VARIANT_TYPE_STRING == rightOperand.Type)
851 {
852 hr = BVariantGetNumeric(&leftOperand, &llLeft);
853 ExitOnFailure(hr, "Failed to get the left numeric");
854 hr = BVariantGetNumeric(&rightOperand, &llRight);
855 if (FAILED(hr))
856 {
857 if (DISP_E_TYPEMISMATCH != hr)
858 {
859 ExitOnFailure(hr, "Failed to get the right numeric");
860 }
861 *pfResult = (BURN_SYMBOL_TYPE_NE == comparison);
862 hr = S_OK;
863 }
864 else
865 {
866 hr = CompareIntegerValues(comparison, llLeft, llRight, pfResult);
867 }
868 }
869 else if (BURN_VARIANT_TYPE_STRING == leftOperand.Type && BURN_VARIANT_TYPE_NUMERIC == rightOperand.Type)
870 {
871 hr = BVariantGetNumeric(&rightOperand, &llRight);
872 ExitOnFailure(hr, "Failed to get the right numeric");
873 hr = BVariantGetNumeric(&leftOperand, &llLeft);
874 if (FAILED(hr))
875 {
876 if (DISP_E_TYPEMISMATCH != hr)
877 {
878 ExitOnFailure(hr, "Failed to get the left numeric");
879 }
880 *pfResult = (BURN_SYMBOL_TYPE_NE == comparison);
881 hr = S_OK;
882 }
883 else
884 {
885 hr = CompareIntegerValues(comparison, llLeft, llRight, pfResult);
886 }
887 }
888 else
889 {
890 // not a combination that can be compared
891 *pfResult = (BURN_SYMBOL_TYPE_NE == comparison);
892 }
893
894LExit:
895 SecureZeroMemory(&qwLeft, sizeof(DWORD64));
896 SecureZeroMemory(&llLeft, sizeof(LONGLONG));
897 StrSecureZeroFreeString(sczLeft);
898 SecureZeroMemory(&qwRight, sizeof(DWORD64));
899 SecureZeroMemory(&llRight, sizeof(LONGLONG));
900 StrSecureZeroFreeString(sczRight);
901
902 return hr;
903}
904
905//
906// CompareStringValues - compares two string values using a given comparison.
907//
908static HRESULT CompareStringValues(
909 __in BURN_SYMBOL_TYPE comparison,
910 __in_z LPCWSTR wzLeftOperand,
911 __in_z LPCWSTR wzRightOperand,
912 __out BOOL* pfResult
913 )
914{
915 HRESULT hr = S_OK;
916 DWORD dwCompareString = (comparison & INSENSITIVE) ? NORM_IGNORECASE : 0;
917 int cchLeft = lstrlenW(wzLeftOperand);
918 int cchRight = lstrlenW(wzRightOperand);
919
920 switch (comparison)
921 {
922 case BURN_SYMBOL_TYPE_LT:
923 case BURN_SYMBOL_TYPE_GT:
924 case BURN_SYMBOL_TYPE_LE:
925 case BURN_SYMBOL_TYPE_GE:
926 case BURN_SYMBOL_TYPE_EQ:
927 case BURN_SYMBOL_TYPE_NE:
928 case BURN_SYMBOL_TYPE_LT_I:
929 case BURN_SYMBOL_TYPE_GT_I:
930 case BURN_SYMBOL_TYPE_LE_I:
931 case BURN_SYMBOL_TYPE_GE_I:
932 case BURN_SYMBOL_TYPE_EQ_I:
933 case BURN_SYMBOL_TYPE_NE_I:
934 {
935 int i = ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand, cchLeft, wzRightOperand, cchRight);
936 hr = CompareIntegerValues(comparison, i, CSTR_EQUAL, pfResult);
937 }
938 break;
939 case BURN_SYMBOL_TYPE_BAND:
940 case BURN_SYMBOL_TYPE_BAND_I:
941 // test if left string contains right string
942 for (int i = 0; (i + cchRight) <= cchLeft; ++i)
943 {
944 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand + i, cchRight, wzRightOperand, cchRight))
945 {
946 *pfResult = TRUE;
947 ExitFunction();
948 }
949 }
950 *pfResult = FALSE;
951 break;
952 case BURN_SYMBOL_TYPE_HIEQ:
953 case BURN_SYMBOL_TYPE_HIEQ_I:
954 // test if left string starts with right string
955 *pfResult = cchLeft >= cchRight && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand, cchRight, wzRightOperand, cchRight);
956 break;
957 case BURN_SYMBOL_TYPE_LOEQ:
958 case BURN_SYMBOL_TYPE_LOEQ_I:
959 // test if left string ends with right string
960 *pfResult = cchLeft >= cchRight && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, dwCompareString, wzLeftOperand + (cchLeft - cchRight), cchRight, wzRightOperand, cchRight);
961 break;
962 default:
963 ExitFunction1(hr = E_INVALIDARG);
964 }
965
966LExit:
967 return hr;
968}
969
970//
971// CompareIntegerValues - compares two integer values using a given comparison.
972//
973static HRESULT CompareIntegerValues(
974 __in BURN_SYMBOL_TYPE comparison,
975 __in LONGLONG llLeftOperand,
976 __in LONGLONG llRightOperand,
977 __out BOOL* pfResult
978 )
979{
980 HRESULT hr = S_OK;
981
982 switch (comparison)
983 {
984 case BURN_SYMBOL_TYPE_LT: case BURN_SYMBOL_TYPE_LT_I: *pfResult = llLeftOperand < llRightOperand; break;
985 case BURN_SYMBOL_TYPE_GT: case BURN_SYMBOL_TYPE_GT_I: *pfResult = llLeftOperand > llRightOperand; break;
986 case BURN_SYMBOL_TYPE_LE: case BURN_SYMBOL_TYPE_LE_I: *pfResult = llLeftOperand <= llRightOperand; break;
987 case BURN_SYMBOL_TYPE_GE: case BURN_SYMBOL_TYPE_GE_I: *pfResult = llLeftOperand >= llRightOperand; break;
988 case BURN_SYMBOL_TYPE_EQ: case BURN_SYMBOL_TYPE_EQ_I: *pfResult = llLeftOperand == llRightOperand; break;
989 case BURN_SYMBOL_TYPE_NE: case BURN_SYMBOL_TYPE_NE_I: *pfResult = llLeftOperand != llRightOperand; break;
990 case BURN_SYMBOL_TYPE_BAND: case BURN_SYMBOL_TYPE_BAND_I: *pfResult = (llLeftOperand & llRightOperand) ? TRUE : FALSE; break;
991 case BURN_SYMBOL_TYPE_HIEQ: case BURN_SYMBOL_TYPE_HIEQ_I: *pfResult = ((llLeftOperand >> 16) & 0xFFFF) == llRightOperand; break;
992 case BURN_SYMBOL_TYPE_LOEQ: case BURN_SYMBOL_TYPE_LOEQ_I: *pfResult = (llLeftOperand & 0xFFFF) == llRightOperand; break;
993 default:
994 ExitFunction1(hr = E_INVALIDARG);
995 }
996
997LExit:
998 return hr;
999}
1000
1001//
1002// CompareVersionValues - compares two quad-word version values using a given comparison.
1003//
1004static HRESULT CompareVersionValues(
1005 __in BURN_SYMBOL_TYPE comparison,
1006 __in DWORD64 qwLeftOperand,
1007 __in DWORD64 qwRightOperand,
1008 __out BOOL* pfResult
1009 )
1010{
1011 HRESULT hr = S_OK;
1012
1013 switch (comparison)
1014 {
1015 case BURN_SYMBOL_TYPE_LT: *pfResult = qwLeftOperand < qwRightOperand; break;
1016 case BURN_SYMBOL_TYPE_GT: *pfResult = qwLeftOperand > qwRightOperand; break;
1017 case BURN_SYMBOL_TYPE_LE: *pfResult = qwLeftOperand <= qwRightOperand; break;
1018 case BURN_SYMBOL_TYPE_GE: *pfResult = qwLeftOperand >= qwRightOperand; break;
1019 case BURN_SYMBOL_TYPE_EQ: *pfResult = qwLeftOperand == qwRightOperand; break;
1020 case BURN_SYMBOL_TYPE_NE: *pfResult = qwLeftOperand != qwRightOperand; break;
1021 case BURN_SYMBOL_TYPE_BAND: *pfResult = (qwLeftOperand & qwRightOperand) ? TRUE : FALSE; break;
1022 case BURN_SYMBOL_TYPE_HIEQ: *pfResult = ((qwLeftOperand >> 16) & 0xFFFF) == qwRightOperand; break;
1023 case BURN_SYMBOL_TYPE_LOEQ: *pfResult = (qwLeftOperand & 0xFFFF) == qwRightOperand; break;
1024 default:
1025 ExitFunction1(hr = E_INVALIDARG);
1026 }
1027
1028LExit:
1029 return hr;
1030}
diff --git a/src/engine/condition.h b/src/engine/condition.h
new file mode 100644
index 00000000..91627f3c
--- /dev/null
+++ b/src/engine/condition.h
@@ -0,0 +1,39 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10typedef struct _BURN_CONDITION
11{
12 // The is an expression a condition string to fire the built-in "need newer OS" message
13 LPWSTR sczConditionString;
14} BURN_CONDITION;
15
16
17// function declarations
18
19HRESULT ConditionEvaluate(
20 __in BURN_VARIABLES* pVariables,
21 __in_z LPCWSTR wzCondition,
22 __out BOOL* pf
23 );
24HRESULT ConditionGlobalCheck(
25 __in BURN_VARIABLES* pVariables,
26 __in BURN_CONDITION* pBlock,
27 __in BOOTSTRAPPER_DISPLAY display,
28 __in_z LPCWSTR wzBundleName,
29 __out DWORD *pdwExitCode,
30 __out BOOL *pfContinueExecution
31 );
32HRESULT ConditionGlobalParseFromXml(
33 __in BURN_CONDITION* pBlock,
34 __in IXMLDOMNode* pixnBundle
35 );
36
37#if defined(__cplusplus)
38}
39#endif
diff --git a/src/engine/container.cpp b/src/engine/container.cpp
new file mode 100644
index 00000000..ada9025b
--- /dev/null
+++ b/src/engine/container.cpp
@@ -0,0 +1,386 @@
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#include "precomp.h"
4
5
6// internal function declarations
7
8static HRESULT GetAttachedContainerInfo(
9 __in HANDLE hFile,
10 __in DWORD iContainerIndex,
11 __out DWORD* pdwFormat,
12 __out DWORD64* pqwOffset,
13 __out DWORD64* pqwSize
14 );
15
16
17// function definitions
18
19extern "C" HRESULT ContainersParseFromXml(
20 __in BURN_SECTION* pSection,
21 __in BURN_CONTAINERS* pContainers,
22 __in IXMLDOMNode* pixnBundle
23 )
24{
25 HRESULT hr = S_OK;
26 IXMLDOMNodeList* pixnNodes = NULL;
27 IXMLDOMNode* pixnNode = NULL;
28 DWORD cNodes = 0;
29 LPWSTR scz = NULL;
30
31 // select container nodes
32 hr = XmlSelectNodes(pixnBundle, L"Container", &pixnNodes);
33 ExitOnFailure(hr, "Failed to select container nodes.");
34
35 // get container node count
36 hr = pixnNodes->get_length((long*)&cNodes);
37 ExitOnFailure(hr, "Failed to get container node count.");
38
39 if (!cNodes)
40 {
41 ExitFunction();
42 }
43
44 // allocate memory for searches
45 pContainers->rgContainers = (BURN_CONTAINER*)MemAlloc(sizeof(BURN_CONTAINER) * cNodes, TRUE);
46 ExitOnNull(pContainers->rgContainers, hr, E_OUTOFMEMORY, "Failed to allocate memory for container structs.");
47
48 pContainers->cContainers = cNodes;
49
50 // parse search elements
51 for (DWORD i = 0; i < cNodes; ++i)
52 {
53 BURN_CONTAINER* pContainer = &pContainers->rgContainers[i];
54
55 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
56 ExitOnFailure(hr, "Failed to get next node.");
57
58 // TODO: Read type from manifest. Today only CABINET is supported.
59 pContainer->type = BURN_CONTAINER_TYPE_CABINET;
60
61 // @Id
62 hr = XmlGetAttributeEx(pixnNode, L"Id", &pContainer->sczId);
63 ExitOnFailure(hr, "Failed to get @Id.");
64
65 // @Primary
66 hr = XmlGetYesNoAttribute(pixnNode, L"Primary", &pContainer->fPrimary);
67 if (E_NOTFOUND != hr)
68 {
69 ExitOnFailure(hr, "Failed to get @Primary.");
70 }
71
72 // @Attached
73 hr = XmlGetYesNoAttribute(pixnNode, L"Attached", &pContainer->fAttached);
74 if (E_NOTFOUND != hr || pContainer->fPrimary) // if it is a primary container, it has to be attached
75 {
76 ExitOnFailure(hr, "Failed to get @Attached.");
77 }
78
79 // @AttachedIndex
80 hr = XmlGetAttributeNumber(pixnNode, L"AttachedIndex", &pContainer->dwAttachedIndex);
81 if (E_NOTFOUND != hr || pContainer->fAttached) // if it is an attached container it must have an index
82 {
83 ExitOnFailure(hr, "Failed to get @AttachedIndex.");
84 }
85
86 // Attached containers are always found attached to the current process, so use the current proccess's
87 // name instead of what may be in the manifest.
88 if (pContainer->fAttached)
89 {
90 hr = PathForCurrentProcess(&scz, NULL);
91 ExitOnFailure(hr, "Failed to get path to current process for attached container.");
92
93 LPCWSTR wzFileName = PathFile(scz);
94
95 hr = StrAllocString(&pContainer->sczFilePath, wzFileName, 0);
96 ExitOnFailure(hr, "Failed to set attached container file path.");
97 }
98 else
99 {
100 // @FilePath
101 hr = XmlGetAttributeEx(pixnNode, L"FilePath", &pContainer->sczFilePath);
102 if (E_NOTFOUND != hr)
103 {
104 ExitOnFailure(hr, "Failed to get @FilePath.");
105 }
106 }
107
108 // The source path starts as the file path.
109 hr = StrAllocString(&pContainer->sczSourcePath, pContainer->sczFilePath, 0);
110 ExitOnFailure(hr, "Failed to copy @FilePath");
111
112 // @DownloadUrl
113 hr = XmlGetAttributeEx(pixnNode, L"DownloadUrl", &pContainer->downloadSource.sczUrl);
114 if (E_NOTFOUND != hr || (!pContainer->fPrimary && !pContainer->sczSourcePath)) // if the package is not a primary package, it must have a source path or a download url
115 {
116 ExitOnFailure(hr, "Failed to get @DownloadUrl. Either @SourcePath or @DownloadUrl needs to be provided.");
117 }
118
119 // @Hash
120 hr = XmlGetAttributeEx(pixnNode, L"Hash", &pContainer->sczHash);
121 if (SUCCEEDED(hr))
122 {
123 hr = StrAllocHexDecode(pContainer->sczHash, &pContainer->pbHash, &pContainer->cbHash);
124 ExitOnFailure(hr, "Failed to hex decode the Container/@Hash.");
125 }
126 else if (E_NOTFOUND != hr)
127 {
128 ExitOnFailure(hr, "Failed to get @Hash.");
129 }
130
131 // If the container is attached, make sure the information in the section matches what the
132 // manifest contained and get the offset to the container.
133 if (pContainer->fAttached)
134 {
135 hr = SectionGetAttachedContainerInfo(pSection, pContainer->dwAttachedIndex, pContainer->type, &pContainer->qwAttachedOffset, &pContainer->qwFileSize, &pContainer->fActuallyAttached);
136 ExitOnFailure(hr, "Failed to get attached container information.");
137 }
138
139 // prepare next iteration
140 ReleaseNullObject(pixnNode);
141 }
142
143 hr = S_OK;
144
145LExit:
146 ReleaseObject(pixnNodes);
147 ReleaseObject(pixnNode);
148 ReleaseStr(scz);
149
150 return hr;
151}
152
153extern "C" void ContainersUninitialize(
154 __in BURN_CONTAINERS* pContainers
155 )
156{
157 if (pContainers->rgContainers)
158 {
159 for (DWORD i = 0; i < pContainers->cContainers; ++i)
160 {
161 BURN_CONTAINER* pContainer = &pContainers->rgContainers[i];
162
163 ReleaseStr(pContainer->sczId);
164 ReleaseStr(pContainer->sczHash);
165 ReleaseStr(pContainer->sczSourcePath);
166 ReleaseStr(pContainer->sczFilePath);
167 ReleaseMem(pContainer->pbHash);
168 ReleaseStr(pContainer->downloadSource.sczUrl);
169 ReleaseStr(pContainer->downloadSource.sczUser);
170 ReleaseStr(pContainer->downloadSource.sczPassword);
171 }
172 MemFree(pContainers->rgContainers);
173 }
174
175 // clear struct
176 memset(pContainers, 0, sizeof(BURN_CONTAINERS));
177}
178
179extern "C" HRESULT ContainerOpenUX(
180 __in BURN_SECTION* pSection,
181 __in BURN_CONTAINER_CONTEXT* pContext
182 )
183{
184 HRESULT hr = S_OK;
185 BURN_CONTAINER container = { };
186 LPWSTR sczExecutablePath = NULL;
187
188 // open attached container
189 container.type = BURN_CONTAINER_TYPE_CABINET;
190 container.fPrimary = TRUE;
191 container.fAttached = TRUE;
192 container.dwAttachedIndex = 0;
193
194 hr = SectionGetAttachedContainerInfo(pSection, container.dwAttachedIndex, container.type, &container.qwAttachedOffset, &container.qwFileSize, &container.fActuallyAttached);
195 ExitOnFailure(hr, "Failed to get container information for UX container.");
196
197 AssertSz(container.fActuallyAttached, "The BA container must always be found attached.");
198
199 hr = PathForCurrentProcess(&sczExecutablePath, NULL);
200 ExitOnFailure(hr, "Failed to get path for executing module.");
201
202 hr = ContainerOpen(pContext, &container, pSection->hEngineFile, sczExecutablePath);
203 ExitOnFailure(hr, "Failed to open attached container.");
204
205LExit:
206 ReleaseStr(sczExecutablePath);
207
208 return hr;
209}
210
211extern "C" HRESULT ContainerOpen(
212 __in BURN_CONTAINER_CONTEXT* pContext,
213 __in BURN_CONTAINER* pContainer,
214 __in HANDLE hContainerFile,
215 __in_z LPCWSTR wzFilePath
216 )
217{
218 HRESULT hr = S_OK;
219 LARGE_INTEGER li = { };
220
221 // initialize context
222 pContext->type = pContainer->type;
223 pContext->qwSize = pContainer->qwFileSize;
224 pContext->qwOffset = pContainer->qwAttachedOffset;
225
226 // If the handle to the container is not open already, open container file
227 if (INVALID_HANDLE_VALUE == hContainerFile)
228 {
229 pContext->hFile = ::CreateFileW(wzFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
230 ExitOnInvalidHandleWithLastError(pContext->hFile, hr, "Failed to open file: %ls", wzFilePath);
231 }
232 else // use the container file handle.
233 {
234 if (!::DuplicateHandle(::GetCurrentProcess(), hContainerFile, ::GetCurrentProcess(), &pContext->hFile, 0, FALSE, DUPLICATE_SAME_ACCESS))
235 {
236 ExitWithLastError(hr, "Failed to duplicate handle to container: %ls", wzFilePath);
237 }
238 }
239
240 // If it is a container attached to an executable, seek to the container offset.
241 if (pContainer->fAttached)
242 {
243 li.QuadPart = (LONGLONG)pContext->qwOffset;
244 }
245
246 if (!::SetFilePointerEx(pContext->hFile, li, NULL, FILE_BEGIN))
247 {
248 ExitWithLastError(hr, "Failed to move file pointer to container offset.");
249 }
250
251 // open the archive
252 switch (pContext->type)
253 {
254 case BURN_CONTAINER_TYPE_CABINET:
255 hr = CabExtractOpen(pContext, wzFilePath);
256 break;
257 }
258 ExitOnFailure(hr, "Failed to open container.");
259
260LExit:
261 return hr;
262}
263
264extern "C" HRESULT ContainerNextStream(
265 __in BURN_CONTAINER_CONTEXT* pContext,
266 __inout_z LPWSTR* psczStreamName
267 )
268{
269 HRESULT hr = S_OK;
270
271 switch (pContext->type)
272 {
273 case BURN_CONTAINER_TYPE_CABINET:
274 hr = CabExtractNextStream(pContext, psczStreamName);
275 break;
276 }
277
278//LExit:
279 return hr;
280}
281
282extern "C" HRESULT ContainerStreamToFile(
283 __in BURN_CONTAINER_CONTEXT* pContext,
284 __in_z LPCWSTR wzFileName
285 )
286{
287 HRESULT hr = S_OK;
288
289 switch (pContext->type)
290 {
291 case BURN_CONTAINER_TYPE_CABINET:
292 hr = CabExtractStreamToFile(pContext, wzFileName);
293 break;
294 }
295
296//LExit:
297 return hr;
298}
299
300extern "C" HRESULT ContainerStreamToBuffer(
301 __in BURN_CONTAINER_CONTEXT* pContext,
302 __out BYTE** ppbBuffer,
303 __out SIZE_T* pcbBuffer
304 )
305{
306 HRESULT hr = S_OK;
307
308 switch (pContext->type)
309 {
310 case BURN_CONTAINER_TYPE_CABINET:
311 hr = CabExtractStreamToBuffer(pContext, ppbBuffer, pcbBuffer);
312 break;
313 }
314
315//LExit:
316 return hr;
317}
318
319extern "C" HRESULT ContainerSkipStream(
320 __in BURN_CONTAINER_CONTEXT* pContext
321 )
322{
323 HRESULT hr = S_OK;
324
325 switch (pContext->type)
326 {
327 case BURN_CONTAINER_TYPE_CABINET:
328 hr = CabExtractSkipStream(pContext);
329 break;
330 }
331
332//LExit:
333 return hr;
334}
335
336extern "C" HRESULT ContainerClose(
337 __in BURN_CONTAINER_CONTEXT* pContext
338 )
339{
340 HRESULT hr = S_OK;
341
342 // close container
343 switch (pContext->type)
344 {
345 case BURN_CONTAINER_TYPE_CABINET:
346 hr = CabExtractClose(pContext);
347 ExitOnFailure(hr, "Failed to close cabinet.");
348 break;
349 }
350
351LExit:
352 ReleaseFile(pContext->hFile);
353
354 if (SUCCEEDED(hr))
355 {
356 memset(pContext, 0, sizeof(BURN_CONTAINER_CONTEXT));
357 }
358
359 return hr;
360}
361
362extern "C" HRESULT ContainerFindById(
363 __in BURN_CONTAINERS* pContainers,
364 __in_z LPCWSTR wzId,
365 __out BURN_CONTAINER** ppContainer
366 )
367{
368 HRESULT hr = S_OK;
369 BURN_CONTAINER* pContainer = NULL;
370
371 for (DWORD i = 0; i < pContainers->cContainers; ++i)
372 {
373 pContainer = &pContainers->rgContainers[i];
374
375 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pContainer->sczId, -1, wzId, -1))
376 {
377 *ppContainer = pContainer;
378 ExitFunction1(hr = S_OK);
379 }
380 }
381
382 hr = E_NOTFOUND;
383
384LExit:
385 return hr;
386}
diff --git a/src/engine/container.h b/src/engine/container.h
new file mode 100644
index 00000000..2ca3d7ad
--- /dev/null
+++ b/src/engine/container.h
@@ -0,0 +1,183 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// typedefs
11
12//typedef HRESULT (*PFN_EXTRACTOPEN)(
13// __in HANDLE hFile,
14// __in DWORD64 qwOffset,
15// __in DWORD64 qwSize,
16// __out void** ppCookie
17// );
18//typedef HRESULT (*PFN_EXTRACTNEXTSTREAM)(
19// __in void* pCookie,
20// __inout_z LPWSTR* psczStreamName
21// );
22//typedef HRESULT (*PFN_EXTRACTSTREAMTOFILE)(
23// __in void* pCookie,
24// __in_z LPCWSTR wzFileName
25// );
26//typedef HRESULT (*PFN_EXTRACTSTREAMTOBUFFER)(
27// __in void* pCookie,
28// __out BYTE** ppbBuffer,
29// __out SIZE_T* pcbBuffer
30// );
31//typedef HRESULT (*PFN_EXTRACTCLOSE)(
32// __in void* pCookie
33// );
34
35
36// constants
37
38enum BURN_CONTAINER_TYPE
39{
40 BURN_CONTAINER_TYPE_NONE,
41 BURN_CONTAINER_TYPE_CABINET,
42 BURN_CONTAINER_TYPE_SEVENZIP,
43};
44
45enum BURN_CAB_OPERATION
46{
47 BURN_CAB_OPERATION_NONE,
48 BURN_CAB_OPERATION_NEXT_STREAM,
49 BURN_CAB_OPERATION_STREAM_TO_FILE,
50 BURN_CAB_OPERATION_STREAM_TO_BUFFER,
51 BURN_CAB_OPERATION_SKIP_STREAM,
52 BURN_CAB_OPERATION_CLOSE,
53};
54
55
56// structs
57
58typedef struct _BURN_CONTAINER
59{
60 LPWSTR sczId;
61 BURN_CONTAINER_TYPE type;
62 BOOL fPrimary;
63 BOOL fAttached;
64 DWORD dwAttachedIndex;
65 DWORD64 qwFileSize;
66 LPWSTR sczHash;
67 LPWSTR sczFilePath; // relative path to container.
68 LPWSTR sczSourcePath;
69 DOWNLOAD_SOURCE downloadSource;
70
71 BYTE* pbHash;
72 DWORD cbHash;
73 DWORD64 qwAttachedOffset;
74 BOOL fActuallyAttached; // indicates whether an attached container is attached or missing.
75
76 //LPWSTR* rgsczPayloads;
77 //DWORD cPayloads;
78} BURN_CONTAINER;
79
80typedef struct _BURN_CONTAINERS
81{
82 BURN_CONTAINER* rgContainers;
83 DWORD cContainers;
84} BURN_CONTAINERS;
85
86typedef struct _BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER
87{
88 HANDLE hFile;
89 LARGE_INTEGER liPosition;
90} BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER;
91
92typedef struct _BURN_CONTAINER_CONTEXT_CABINET
93{
94 LPWSTR sczFile;
95
96 HANDLE hThread;
97 HANDLE hBeginOperationEvent;
98 HANDLE hOperationCompleteEvent;
99
100 BURN_CAB_OPERATION operation;
101 HRESULT hrError;
102
103 LPWSTR* psczStreamName;
104 LPCWSTR wzTargetFile;
105 HANDLE hTargetFile;
106 BYTE* pbTargetBuffer;
107 DWORD cbTargetBuffer;
108 DWORD iTargetBuffer;
109
110 BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* rgVirtualFilePointers;
111 DWORD cVirtualFilePointers;
112} BURN_CONTAINER_CONTEXT_CABINET;
113
114typedef struct _BURN_CONTAINER_CONTEXT
115{
116 HANDLE hFile;
117 DWORD64 qwOffset;
118 DWORD64 qwSize;
119
120 //PFN_EXTRACTOPEN pfnExtractOpen;
121 //PFN_EXTRACTNEXTSTREAM pfnExtractNextStream;
122 //PFN_EXTRACTSTREAMTOFILE pfnExtractStreamToFile;
123 //PFN_EXTRACTSTREAMTOBUFFER pfnExtractStreamToBuffer;
124 //PFN_EXTRACTCLOSE pfnExtractClose;
125 //void* pCookie;
126 BURN_CONTAINER_TYPE type;
127 union
128 {
129 BURN_CONTAINER_CONTEXT_CABINET Cabinet;
130 };
131
132} BURN_CONTAINER_CONTEXT;
133
134
135// functions
136
137HRESULT ContainersParseFromXml(
138 __in BURN_SECTION* pSection,
139 __in BURN_CONTAINERS* pContainers,
140 __in IXMLDOMNode* pixnBundle
141 );
142void ContainersUninitialize(
143 __in BURN_CONTAINERS* pContainers
144 );
145HRESULT ContainerOpenUX(
146 __in BURN_SECTION* pSection,
147 __in BURN_CONTAINER_CONTEXT* pContext
148 );
149HRESULT ContainerOpen(
150 __in BURN_CONTAINER_CONTEXT* pContext,
151 __in BURN_CONTAINER* pContainer,
152 __in HANDLE hContainerFile,
153 __in_z LPCWSTR wzFilePath
154 );
155HRESULT ContainerNextStream(
156 __inout BURN_CONTAINER_CONTEXT* pContext,
157 __inout_z LPWSTR* psczStreamName
158 );
159HRESULT ContainerStreamToFile(
160 __in BURN_CONTAINER_CONTEXT* pContext,
161 __in_z LPCWSTR wzFileName
162 );
163HRESULT ContainerStreamToBuffer(
164 __in BURN_CONTAINER_CONTEXT* pContext,
165 __out BYTE** ppbBuffer,
166 __out SIZE_T* pcbBuffer
167 );
168HRESULT ContainerSkipStream(
169 __in BURN_CONTAINER_CONTEXT* pContext
170 );
171HRESULT ContainerClose(
172 __in BURN_CONTAINER_CONTEXT* pContext
173 );
174HRESULT ContainerFindById(
175 __in BURN_CONTAINERS* pContainers,
176 __in_z LPCWSTR wzId,
177 __out BURN_CONTAINER** ppContainer
178 );
179
180
181#if defined(__cplusplus)
182}
183#endif
diff --git a/src/engine/core.cpp b/src/engine/core.cpp
new file mode 100644
index 00000000..519012e9
--- /dev/null
+++ b/src/engine/core.cpp
@@ -0,0 +1,1705 @@
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#include "precomp.h"
4
5
6// structs
7
8struct BURN_CACHE_THREAD_CONTEXT
9{
10 BURN_ENGINE_STATE* pEngineState;
11 DWORD* pcOverallProgressTicks;
12 BOOL* pfRollback;
13};
14
15
16// internal function declarations
17
18static HRESULT ParseCommandLine(
19 __in int argc,
20 __in LPWSTR* argv,
21 __in BOOTSTRAPPER_COMMAND* pCommand,
22 __in BURN_PIPE_CONNECTION* pCompanionConnection,
23 __in BURN_PIPE_CONNECTION* pEmbeddedConnection,
24 __in BURN_VARIABLES* pVariables,
25 __out BURN_MODE* pMode,
26 __out BURN_AU_PAUSE_ACTION* pAutomaticUpdates,
27 __out BOOL* pfDisableSystemRestore,
28 __out_z LPWSTR* psczSourceProcessPath,
29 __out_z LPWSTR* psczOriginalSource,
30 __out BOOL* pfDisableUnelevate,
31 __out DWORD *pdwLoggingAttributes,
32 __out_z LPWSTR* psczLogFile,
33 __out_z LPWSTR* psczActiveParent,
34 __out_z LPWSTR* psczIgnoreDependencies,
35 __out_z LPWSTR* psczAncestors,
36 __out_z LPWSTR* psczSanitizedCommandLine
37 );
38static HRESULT ParsePipeConnection(
39 __in LPWSTR* rgArgs,
40 __in BURN_PIPE_CONNECTION* pConnection
41 );
42static HRESULT DetectPackage(
43 __in BURN_ENGINE_STATE* pEngineState,
44 __in BURN_PACKAGE* pPackage
45 );
46static HRESULT DetectPackagePayloadsCached(
47 __in BURN_PACKAGE* pPackage
48 );
49static DWORD WINAPI CacheThreadProc(
50 __in LPVOID lpThreadParameter
51 );
52static HRESULT WaitForCacheThread(
53 __in HANDLE hCacheThread
54 );
55static void LogPackages(
56 __in_opt const BURN_PACKAGE* pUpgradeBundlePackage,
57 __in_opt const BURN_PACKAGE* pForwardCompatibleBundlePackage,
58 __in const BURN_PACKAGES* pPackages,
59 __in const BURN_RELATED_BUNDLES* pRelatedBundles,
60 __in const BOOTSTRAPPER_ACTION action
61 );
62
63
64// function definitions
65
66extern "C" HRESULT CoreInitialize(
67 __in BURN_ENGINE_STATE* pEngineState
68 )
69{
70 HRESULT hr = S_OK;
71 LPWSTR sczSanitizedCommandLine = NULL;
72 LPWSTR sczStreamName = NULL;
73 BYTE* pbBuffer = NULL;
74 SIZE_T cbBuffer = 0;
75 BURN_CONTAINER_CONTEXT containerContext = { };
76 BOOL fElevated = FALSE;
77 LPWSTR sczSourceProcessPath = NULL;
78 LPWSTR sczSourceProcessFolder = NULL;
79 LPWSTR sczOriginalSource = NULL;
80
81 // Initialize variables.
82 hr = VariableInitialize(&pEngineState->variables);
83 ExitOnFailure(hr, "Failed to initialize variables.");
84
85 // Open attached UX container.
86 hr = ContainerOpenUX(&pEngineState->section, &containerContext);
87 ExitOnFailure(hr, "Failed to open attached UX container.");
88
89 // Load manifest.
90 hr = ContainerNextStream(&containerContext, &sczStreamName);
91 ExitOnFailure(hr, "Failed to open manifest stream.");
92
93 hr = ContainerStreamToBuffer(&containerContext, &pbBuffer, &cbBuffer);
94 ExitOnFailure(hr, "Failed to get manifest stream from container.");
95
96 hr = ManifestLoadXmlFromBuffer(pbBuffer, cbBuffer, pEngineState);
97 ExitOnFailure(hr, "Failed to load manifest.");
98
99 // Parse command line.
100 hr = ParseCommandLine(pEngineState->argc, pEngineState->argv, &pEngineState->command, &pEngineState->companionConnection, &pEngineState->embeddedConnection, &pEngineState->variables, &pEngineState->mode, &pEngineState->automaticUpdates, &pEngineState->fDisableSystemRestore, &sczSourceProcessPath, &sczOriginalSource, &pEngineState->fDisableUnelevate, &pEngineState->log.dwAttributes, &pEngineState->log.sczPath, &pEngineState->registration.sczActiveParent, &pEngineState->sczIgnoreDependencies, &pEngineState->registration.sczAncestors, &sczSanitizedCommandLine);
101 ExitOnFailure(hr, "Failed to parse command line.");
102
103 LogId(REPORT_STANDARD, MSG_BURN_COMMAND_LINE, sczSanitizedCommandLine ? sczSanitizedCommandLine : L"");
104
105 // Retain whether bundle was initially run elevated.
106 ProcElevated(::GetCurrentProcess(), &fElevated);
107
108 hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_ELEVATED, fElevated, TRUE);
109 ExitOnFailure(hr, "Failed to overwrite the %ls built-in variable.", BURN_BUNDLE_ELEVATED);
110
111 hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_UILEVEL, pEngineState->command.display, TRUE);
112 ExitOnFailure(hr, "Failed to overwrite the %ls built-in variable.", BURN_BUNDLE_UILEVEL);
113
114 if (sczSourceProcessPath)
115 {
116 hr = VariableSetLiteralString(&pEngineState->variables, BURN_BUNDLE_SOURCE_PROCESS_PATH, sczSourceProcessPath, TRUE);
117 ExitOnFailure(hr, "Failed to set source process path variable.");
118
119 hr = PathGetDirectory(sczSourceProcessPath, &sczSourceProcessFolder);
120 ExitOnFailure(hr, "Failed to get source process folder from path.");
121
122 hr = VariableSetLiteralString(&pEngineState->variables, BURN_BUNDLE_SOURCE_PROCESS_FOLDER, sczSourceProcessFolder, TRUE);
123 ExitOnFailure(hr, "Failed to set source process folder variable.");
124 }
125
126 // Set BURN_BUNDLE_ORIGINAL_SOURCE, if it was passed in on the command line.
127 // Needs to be done after ManifestLoadXmlFromBuffer.
128 if (sczOriginalSource)
129 {
130 hr = VariableSetLiteralString(&pEngineState->variables, BURN_BUNDLE_ORIGINAL_SOURCE, sczOriginalSource, FALSE);
131 ExitOnFailure(hr, "Failed to set original source variable.");
132 }
133
134 if (BURN_MODE_UNTRUSTED == pEngineState->mode || BURN_MODE_NORMAL == pEngineState->mode || BURN_MODE_EMBEDDED == pEngineState->mode)
135 {
136 hr = CacheInitialize(&pEngineState->registration, &pEngineState->variables, sczSourceProcessPath);
137 ExitOnFailure(hr, "Failed to initialize internal cache functionality.");
138 }
139
140 // If we're not elevated then we'll be loading the bootstrapper application, so extract
141 // the payloads from the BA container.
142 if (BURN_MODE_NORMAL == pEngineState->mode || BURN_MODE_EMBEDDED == pEngineState->mode)
143 {
144 // Extract all UX payloads to working folder.
145 hr = UserExperienceEnsureWorkingFolder(pEngineState->registration.sczId, &pEngineState->userExperience.sczTempDirectory);
146 ExitOnFailure(hr, "Failed to get unique temporary folder for bootstrapper application.");
147
148 hr = PayloadExtractFromContainer(&pEngineState->userExperience.payloads, NULL, &containerContext, pEngineState->userExperience.sczTempDirectory);
149 ExitOnFailure(hr, "Failed to extract bootstrapper application payloads.");
150
151 // Load the catalog files as soon as they are extracted.
152 hr = CatalogLoadFromPayload(&pEngineState->catalogs, &pEngineState->userExperience.payloads);
153 ExitOnFailure(hr, "Failed to load catalog files.");
154 }
155
156LExit:
157 ReleaseStr(sczOriginalSource);
158 ReleaseStr(sczSourceProcessFolder);
159 ReleaseStr(sczSourceProcessPath);
160 ContainerClose(&containerContext);
161 ReleaseStr(sczStreamName);
162 ReleaseStr(sczSanitizedCommandLine);
163 ReleaseMem(pbBuffer);
164
165 return hr;
166}
167
168extern "C" HRESULT CoreSerializeEngineState(
169 __in BURN_ENGINE_STATE* pEngineState,
170 __inout BYTE** ppbBuffer,
171 __inout SIZE_T* piBuffer
172 )
173{
174 HRESULT hr = S_OK;
175
176 hr = VariableSerialize(&pEngineState->variables, TRUE, ppbBuffer, piBuffer);
177 ExitOnFailure(hr, "Failed to serialize variables.");
178
179LExit:
180 return hr;
181}
182
183extern "C" HRESULT CoreQueryRegistration(
184 __in BURN_ENGINE_STATE* pEngineState
185 )
186{
187 HRESULT hr = S_OK;
188 BYTE* pbBuffer = NULL;
189 SIZE_T cbBuffer = 0;
190 SIZE_T iBuffer = 0;
191
192 // Detect if bundle is already installed.
193 hr = RegistrationDetectInstalled(&pEngineState->registration, &pEngineState->registration.fInstalled);
194 ExitOnFailure(hr, "Failed to detect bundle install state.");
195
196 // detect resume type
197 hr = RegistrationDetectResumeType(&pEngineState->registration, &pEngineState->command.resumeType);
198 ExitOnFailure(hr, "Failed to detect resume type.");
199
200 // If we have a resume mode that suggests the bundle might already be present, try to load any
201 // previously stored state.
202 if (BOOTSTRAPPER_RESUME_TYPE_INVALID < pEngineState->command.resumeType)
203 {
204 // load resume state
205 hr = RegistrationLoadState(&pEngineState->registration, &pbBuffer, &cbBuffer);
206 if (SUCCEEDED(hr))
207 {
208 hr = VariableDeserialize(&pEngineState->variables, TRUE, pbBuffer, cbBuffer, &iBuffer);
209 }
210
211 // Log any failures and continue.
212 if (FAILED(hr))
213 {
214 LogId(REPORT_STANDARD, MSG_CANNOT_LOAD_STATE_FILE, hr, pEngineState->registration.sczStateFile);
215 hr = S_OK;
216 }
217 }
218
219LExit:
220 ReleaseBuffer(pbBuffer);
221
222 return hr;
223}
224
225extern "C" HRESULT CoreDetect(
226 __in BURN_ENGINE_STATE* pEngineState,
227 __in_opt HWND hwndParent
228 )
229{
230 HRESULT hr = S_OK;
231 BOOL fActivated = FALSE;
232 BOOL fDetectBegan = FALSE;
233 BURN_PACKAGE* pPackage = NULL;
234 HRESULT hrFirstPackageFailure = S_OK;
235
236 LogId(REPORT_STANDARD, MSG_DETECT_BEGIN, pEngineState->packages.cPackages);
237
238 hr = UserExperienceActivateEngine(&pEngineState->userExperience, &fActivated);
239 ExitOnFailure(hr, "Engine cannot start detect because it is busy with another action.");
240
241 // Detect if bundle installed state has changed since start up. This
242 // only happens if Apply() changed the state of bundle (installed or
243 // uninstalled). In that case, Detect() can be used here to reset
244 // the installed state.
245 hr = RegistrationDetectInstalled(&pEngineState->registration, &pEngineState->registration.fInstalled);
246 ExitOnFailure(hr, "Failed to detect bundle install state.");
247
248 if (pEngineState->registration.fInstalled)
249 {
250 hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_INSTALLED, 1, TRUE);
251 ExitOnFailure(hr, "Failed to set the bundle installed built-in variable.");
252 }
253 else
254 {
255 hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_INSTALLED, NULL, TRUE);
256 ExitOnFailure(hr, "Failed to unset the bundle installed built-in variable.");
257 }
258
259 fDetectBegan = TRUE;
260 hr = UserExperienceOnDetectBegin(&pEngineState->userExperience, pEngineState->registration.fInstalled, pEngineState->packages.cPackages);
261 ExitOnRootFailure(hr, "UX aborted detect begin.");
262
263 pEngineState->userExperience.hwndDetect = hwndParent;
264
265 // Always reset the detect state which means the plan should be reset too.
266 DetectReset(&pEngineState->registration, &pEngineState->packages);
267 PlanReset(&pEngineState->plan, &pEngineState->packages);
268
269 hr = SearchesExecute(&pEngineState->searches, &pEngineState->variables);
270 ExitOnFailure(hr, "Failed to execute searches.");
271
272 // Load all of the related bundles.
273 hr = RegistrationDetectRelatedBundles(&pEngineState->registration);
274 ExitOnFailure(hr, "Failed to detect related bundles.");
275
276 hr = DependencyDetectProviderKeyBundleId(&pEngineState->registration);
277 if (SUCCEEDED(hr))
278 {
279 hr = DetectForwardCompatibleBundle(&pEngineState->userExperience, &pEngineState->command, &pEngineState->registration);
280 ExitOnFailure(hr, "Failed to detect forward compatible bundle.");
281
282 // If a forward compatible bundle was detected, skip rest of bundle detection
283 // since we will passthrough.
284 if (pEngineState->registration.fEnabledForwardCompatibleBundle)
285 {
286 ExitFunction();
287 }
288 }
289 else if (E_NOTFOUND == hr)
290 {
291 hr = S_OK;
292 }
293 ExitOnFailure(hr, "Failed to detect provider key bundle id.");
294
295 // Report the related bundles.
296 hr = DetectReportRelatedBundles(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, pEngineState->command.action);
297 ExitOnFailure(hr, "Failed to report detected related bundles.");
298
299 // Do update detection.
300 hr = DetectUpdate(pEngineState->registration.sczId, &pEngineState->userExperience, &pEngineState->update);
301 ExitOnFailure(hr, "Failed to detect update.");
302
303 // Detecting MSPs requires special initialization before processing each package but
304 // only do the detection if there are actually patch packages to detect because it
305 // can be expensive.
306 if (pEngineState->packages.cPatchInfo)
307 {
308 hr = MspEngineDetectInitialize(&pEngineState->packages);
309 ExitOnFailure(hr, "Failed to initialize MSP engine detection.");
310 }
311
312 for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i)
313 {
314 pPackage = pEngineState->packages.rgPackages + i;
315
316 hr = DetectPackage(pEngineState, pPackage);
317
318 // If the package detection failed, ensure the package state is set to unknown.
319 if (FAILED(hr))
320 {
321 if (SUCCEEDED(hrFirstPackageFailure))
322 {
323 hrFirstPackageFailure = hr;
324 }
325
326 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN;
327 }
328 }
329
330 // Log the detected states.
331 for (DWORD iPackage = 0; iPackage < pEngineState->packages.cPackages; ++iPackage)
332 {
333 pPackage = pEngineState->packages.rgPackages + iPackage;
334
335 LogId(REPORT_STANDARD, MSG_DETECTED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingCacheStateToString(pPackage->cache));
336
337 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
338 {
339 for (DWORD iFeature = 0; iFeature < pPackage->Msi.cFeatures; ++iFeature)
340 {
341 const BURN_MSIFEATURE* pFeature = pPackage->Msi.rgFeatures + iFeature;
342 LogId(REPORT_STANDARD, MSG_DETECTED_MSI_FEATURE, pPackage->sczId, pFeature->sczId, LoggingMsiFeatureStateToString(pFeature->currentState));
343 }
344 }
345 else if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
346 {
347 for (DWORD iTargetProduct = 0; iTargetProduct < pPackage->Msp.cTargetProductCodes; ++iTargetProduct)
348 {
349 const BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + iTargetProduct;
350 LogId(REPORT_STANDARD, MSG_DETECTED_MSP_TARGET, pPackage->sczId, pTargetProduct->wzTargetProductCode, LoggingPackageStateToString(pTargetProduct->patchPackageState));
351 }
352 }
353 }
354
355LExit:
356 if (SUCCEEDED(hr))
357 {
358 hr = hrFirstPackageFailure;
359 }
360
361 if (fActivated)
362 {
363 UserExperienceDeactivateEngine(&pEngineState->userExperience);
364 }
365
366 if (fDetectBegan)
367 {
368 UserExperienceOnDetectComplete(&pEngineState->userExperience, hr);
369 }
370
371 pEngineState->userExperience.hwndDetect = NULL;
372
373 LogId(REPORT_STANDARD, MSG_DETECT_COMPLETE, hr);
374
375 return hr;
376}
377
378extern "C" HRESULT CorePlan(
379 __in BURN_ENGINE_STATE* pEngineState,
380 __in BOOTSTRAPPER_ACTION action
381 )
382{
383 HRESULT hr = S_OK;
384 BOOL fActivated = FALSE;
385 BOOL fPlanBegan = FALSE;
386 LPWSTR sczLayoutDirectory = NULL;
387 HANDLE hSyncpointEvent = NULL;
388 BURN_PACKAGE* pUpgradeBundlePackage = NULL;
389 BURN_PACKAGE* pForwardCompatibleBundlePackage = NULL;
390
391 LogId(REPORT_STANDARD, MSG_PLAN_BEGIN, pEngineState->packages.cPackages, LoggingBurnActionToString(action));
392
393 hr = UserExperienceActivateEngine(&pEngineState->userExperience, &fActivated);
394 ExitOnFailure(hr, "Engine cannot start plan because it is busy with another action.");
395
396 fPlanBegan = TRUE;
397 hr = UserExperienceOnPlanBegin(&pEngineState->userExperience, pEngineState->packages.cPackages);
398 ExitOnRootFailure(hr, "BA aborted plan begin.");
399
400 // Always reset the plan.
401 PlanReset(&pEngineState->plan, &pEngineState->packages);
402
403 // Remember the overall action state in the plan since it shapes the changes
404 // we make everywhere.
405 pEngineState->plan.action = action;
406 pEngineState->plan.wzBundleId = pEngineState->registration.sczId;
407 pEngineState->plan.wzBundleProviderKey = pEngineState->registration.sczId;
408
409 hr = PlanSetVariables(action, &pEngineState->variables);
410 ExitOnFailure(hr, "Failed to update action.");
411
412 // Set resume commandline
413 hr = PlanSetResumeCommand(&pEngineState->registration, action, &pEngineState->command, &pEngineState->log);
414 ExitOnFailure(hr, "Failed to set resume command");
415
416 hr = DependencyPlanInitialize(pEngineState, &pEngineState->plan);
417 ExitOnFailure(hr, "Failed to initialize the dependencies for the plan.");
418
419 if (BOOTSTRAPPER_ACTION_LAYOUT == action)
420 {
421 Assert(!pEngineState->plan.fPerMachine);
422
423 // Plan the bundle's layout.
424 hr = PlanLayoutBundle(&pEngineState->plan, pEngineState->registration.sczExecutableName, pEngineState->section.qwBundleSize, &pEngineState->variables, &pEngineState->payloads, &sczLayoutDirectory);
425 ExitOnFailure(hr, "Failed to plan the layout of the bundle.");
426
427 // Plan the packages' layout.
428 hr = PlanPackages(&pEngineState->registration, &pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, FALSE, pEngineState->command.display, pEngineState->command.relationType, sczLayoutDirectory, &hSyncpointEvent);
429 ExitOnFailure(hr, "Failed to plan packages.");
430 }
431 else if (BOOTSTRAPPER_ACTION_UPDATE_REPLACE == action || BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED == action)
432 {
433 Assert(!pEngineState->plan.fPerMachine);
434
435 pUpgradeBundlePackage = &pEngineState->update.package;
436
437 hr = PlanUpdateBundle(&pEngineState->userExperience, pUpgradeBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType, &hSyncpointEvent);
438 ExitOnFailure(hr, "Failed to plan update.");
439 }
440 else if (pEngineState->registration.fEnabledForwardCompatibleBundle)
441 {
442 Assert(!pEngineState->plan.fPerMachine);
443
444 pForwardCompatibleBundlePackage = &pEngineState->registration.forwardCompatibleBundle;
445
446 hr = PlanPassThroughBundle(&pEngineState->userExperience, pForwardCompatibleBundlePackage, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->command.relationType, &hSyncpointEvent);
447 ExitOnFailure(hr, "Failed to plan passthrough.");
448 }
449 else // doing an action that modifies the machine state.
450 {
451 BOOL fContinuePlanning = TRUE; // assume we'll be able to keep planning after registration.
452 pEngineState->plan.fPerMachine = pEngineState->registration.fPerMachine; // default the scope of the plan to the per-machine state of the bundle.
453
454 hr = PlanRegistration(&pEngineState->plan, &pEngineState->registration, pEngineState->command.resumeType, pEngineState->command.relationType, pEngineState->sczIgnoreDependencies, &fContinuePlanning);
455 ExitOnFailure(hr, "Failed to plan registration.");
456
457 if (fContinuePlanning)
458 {
459 // Remember the early index, because we want to be able to insert some related bundles
460 // into the plan before other executed packages. This particularly occurs for uninstallation
461 // of addons and patches, which should be uninstalled before the main product.
462 DWORD dwExecuteActionEarlyIndex = pEngineState->plan.cExecuteActions;
463
464 // Plan the related bundles first to support downgrades with ref-counting.
465 hr = PlanRelatedBundlesBegin(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, &pEngineState->plan);
466 ExitOnFailure(hr, "Failed to plan related bundles.");
467
468 hr = PlanPackages(&pEngineState->registration, &pEngineState->userExperience, &pEngineState->packages, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, pEngineState->registration.fInstalled, pEngineState->command.display, pEngineState->command.relationType, NULL, &hSyncpointEvent);
469 ExitOnFailure(hr, "Failed to plan packages.");
470
471 // Schedule the update of related bundles last.
472 hr = PlanRelatedBundlesComplete(&pEngineState->registration, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, &hSyncpointEvent, dwExecuteActionEarlyIndex);
473 ExitOnFailure(hr, "Failed to schedule related bundles.");
474 }
475 }
476
477 // Remove unnecessary actions.
478 hr = PlanFinalizeActions(&pEngineState->plan);
479 ExitOnFailure(hr, "Failed to remove unnecessary actions from plan.");
480
481 // Finally, display all packages and related bundles in the log.
482 LogPackages(pUpgradeBundlePackage, pForwardCompatibleBundlePackage, &pEngineState->packages, &pEngineState->registration.relatedBundles, action);
483
484#ifdef DEBUG
485 PlanDump(&pEngineState->plan);
486#endif
487
488LExit:
489 if (fActivated)
490 {
491 UserExperienceDeactivateEngine(&pEngineState->userExperience);
492 }
493
494 if (fPlanBegan)
495 {
496 UserExperienceOnPlanComplete(&pEngineState->userExperience, hr);
497 }
498
499 LogId(REPORT_STANDARD, MSG_PLAN_COMPLETE, hr);
500 ReleaseStr(sczLayoutDirectory);
501
502 return hr;
503}
504
505extern "C" HRESULT CoreElevate(
506 __in BURN_ENGINE_STATE* pEngineState,
507 __in_opt HWND hwndParent
508 )
509{
510 HRESULT hr = S_OK;
511
512 // If the elevated companion pipe isn't created yet, let's make that happen.
513 if (INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe)
514 {
515 if (!pEngineState->sczBundleEngineWorkingPath)
516 {
517 hr = CacheBundleToWorkingDirectory(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &pEngineState->userExperience.payloads, &pEngineState->section, &pEngineState->sczBundleEngineWorkingPath);
518 ExitOnFailure(hr, "Failed to cache engine to working directory.");
519 }
520
521 hr = ElevationElevate(pEngineState, hwndParent);
522 ExitOnFailure(hr, "Failed to actually elevate.");
523
524 hr = VariableSetNumeric(&pEngineState->variables, BURN_BUNDLE_ELEVATED, TRUE, TRUE);
525 ExitOnFailure(hr, "Failed to overwrite the %ls built-in variable.", BURN_BUNDLE_ELEVATED);
526 }
527
528LExit:
529 return hr;
530}
531
532extern "C" HRESULT CoreApply(
533 __in BURN_ENGINE_STATE* pEngineState,
534 __in_opt HWND hwndParent
535 )
536{
537 HRESULT hr = S_OK;
538 BOOL fActivated = FALSE;
539 HANDLE hLock = NULL;
540 DWORD cOverallProgressTicks = 0;
541 HANDLE hCacheThread = NULL;
542 BOOL fElevated = FALSE;
543 BOOL fRegistered = FALSE;
544 BOOL fKeepRegistration = pEngineState->plan.fKeepRegistrationDefault;
545 BOOL fRollback = FALSE;
546 BOOL fSuspend = FALSE;
547 BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
548 BURN_CACHE_THREAD_CONTEXT cacheThreadContext = { };
549 DWORD dwPhaseCount = 0;
550 BOOTSTRAPPER_APPLYCOMPLETE_ACTION applyCompleteAction = BOOTSTRAPPER_APPLYCOMPLETE_ACTION_NONE;
551
552 LogId(REPORT_STANDARD, MSG_APPLY_BEGIN);
553
554 hr = UserExperienceActivateEngine(&pEngineState->userExperience, &fActivated);
555 ExitOnFailure(hr, "Engine cannot start apply because it is busy with another action.");
556
557 // Ensure any previous attempts to execute are reset.
558 ApplyReset(&pEngineState->userExperience, &pEngineState->packages);
559
560 if (pEngineState->plan.cCacheActions)
561 {
562 ++dwPhaseCount;
563 }
564 if (pEngineState->plan.cExecuteActions)
565 {
566 ++dwPhaseCount;
567 }
568
569 hr = UserExperienceOnApplyBegin(&pEngineState->userExperience, dwPhaseCount);
570 ExitOnRootFailure(hr, "BA aborted apply begin.");
571
572 // Abort if this bundle already requires a restart.
573 if (BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING == pEngineState->command.resumeType)
574 {
575 restart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
576
577 hr = HRESULT_FROM_WIN32(ERROR_FAIL_NOACTION_REBOOT);
578 UserExperienceSendError(&pEngineState->userExperience, BOOTSTRAPPER_ERROR_TYPE_APPLY, NULL, hr, NULL, MB_ICONERROR | MB_OK, IDNOACTION); // ignore return value.
579 ExitFunction();
580 }
581
582 hr = ApplyLock(FALSE, &hLock);
583 ExitOnFailure(hr, "Another per-user setup is already executing.");
584
585 // Initialize only after getting a lock.
586 ApplyInitialize();
587
588 pEngineState->userExperience.hwndApply = hwndParent;
589
590 hr = ApplySetVariables(&pEngineState->variables);
591 ExitOnFailure(hr, "Failed to set initial apply variables.");
592
593 // If the plan is empty of work to do, skip everything.
594 if (!(pEngineState->plan.cRegistrationActions || pEngineState->plan.cCacheActions || pEngineState->plan.cExecuteActions || pEngineState->plan.cCleanActions))
595 {
596 LogId(REPORT_STANDARD, MSG_APPLY_SKIPPED);
597 ExitFunction();
598 }
599
600 // Ensure the engine is cached to the working path.
601 if (!pEngineState->sczBundleEngineWorkingPath)
602 {
603 hr = CacheBundleToWorkingDirectory(pEngineState->registration.sczId, pEngineState->registration.sczExecutableName, &pEngineState->userExperience.payloads, &pEngineState->section, &pEngineState->sczBundleEngineWorkingPath);
604 ExitOnFailure(hr, "Failed to cache engine to working directory.");
605 }
606
607 // Elevate.
608 if (pEngineState->plan.fPerMachine)
609 {
610 hr = CoreElevate(pEngineState, pEngineState->userExperience.hwndApply);
611 ExitOnFailure(hr, "Failed to elevate.");
612
613 hr = ElevationApplyInitialize(pEngineState->companionConnection.hPipe, &pEngineState->variables, pEngineState->plan.action, pEngineState->automaticUpdates, !pEngineState->fDisableSystemRestore);
614 ExitOnFailure(hr, "Another per-machine setup is already executing.");
615
616 fElevated = TRUE;
617 }
618
619 // Register.
620 if (pEngineState->plan.fRegister)
621 {
622 hr = ApplyRegister(pEngineState);
623 ExitOnFailure(hr, "Failed to register bundle.");
624 fRegistered = TRUE;
625 }
626
627 // Cache.
628 if (pEngineState->plan.cCacheActions)
629 {
630 // Launch the cache thread.
631 cacheThreadContext.pEngineState = pEngineState;
632 cacheThreadContext.pcOverallProgressTicks = &cOverallProgressTicks;
633 cacheThreadContext.pfRollback = &fRollback;
634
635 hCacheThread = ::CreateThread(NULL, 0, CacheThreadProc, &cacheThreadContext, 0, NULL);
636 ExitOnNullWithLastError(hCacheThread, hr, "Failed to create cache thread.");
637
638 // If we're not caching in parallel, wait for the cache thread to terminate.
639 if (!pEngineState->fParallelCacheAndExecute)
640 {
641 hr = WaitForCacheThread(hCacheThread);
642 ExitOnFailure(hr, "Failed while caching, aborting execution.");
643
644 ReleaseHandle(hCacheThread);
645 }
646 }
647
648 // Execute.
649 if (pEngineState->plan.cExecuteActions)
650 {
651 hr = ApplyExecute(pEngineState, hCacheThread, &cOverallProgressTicks, &fKeepRegistration, &fRollback, &fSuspend, &restart);
652 UserExperienceExecutePhaseComplete(&pEngineState->userExperience, hr); // signal that execute completed.
653 }
654
655 // Wait for cache thread to terminate, this should return immediately unless we're waiting for layout to complete.
656 if (hCacheThread)
657 {
658 HRESULT hrCached = WaitForCacheThread(hCacheThread);
659 if (SUCCEEDED(hr))
660 {
661 hr = hrCached;
662 }
663 }
664
665 // If something went wrong or force restarted, skip cleaning.
666 if (FAILED(hr) || fRollback || fSuspend || BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart)
667 {
668 ExitFunction();
669 }
670
671 // Clean.
672 if (pEngineState->plan.cCleanActions)
673 {
674 ApplyClean(&pEngineState->userExperience, &pEngineState->plan, pEngineState->companionConnection.hPipe);
675 }
676
677LExit:
678 // Unregister.
679 if (fRegistered)
680 {
681 ApplyUnregister(pEngineState, FAILED(hr) || fRollback, fKeepRegistration || pEngineState->plan.fDisallowRemoval, fSuspend, restart);
682 }
683
684 if (fElevated)
685 {
686 ElevationApplyUninitialize(pEngineState->companionConnection.hPipe);
687 }
688
689 pEngineState->userExperience.hwndApply = NULL;
690
691 ApplyUninitialize();
692
693 if (hLock)
694 {
695 ::ReleaseMutex(hLock);
696 ::CloseHandle(hLock);
697 }
698
699 if (fActivated)
700 {
701 UserExperienceDeactivateEngine(&pEngineState->userExperience);
702 }
703
704 ReleaseHandle(hCacheThread);
705
706 UserExperienceOnApplyComplete(&pEngineState->userExperience, hr, restart, &applyCompleteAction);
707 if (BOOTSTRAPPER_APPLYCOMPLETE_ACTION_RESTART == applyCompleteAction)
708 {
709 pEngineState->fRestart = TRUE;
710 }
711
712 LogId(REPORT_STANDARD, MSG_APPLY_COMPLETE, hr, LoggingRestartToString(restart), LoggingBoolToString(pEngineState->fRestart));
713
714 return hr;
715}
716
717extern "C" HRESULT CoreLaunchApprovedExe(
718 __in BURN_ENGINE_STATE* pEngineState,
719 __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe
720 )
721{
722 HRESULT hr = S_OK;
723 BOOL fActivated = FALSE;
724 DWORD dwProcessId = 0;
725
726 LogId(REPORT_STANDARD, MSG_LAUNCH_APPROVED_EXE_BEGIN, pLaunchApprovedExe->sczId);
727
728 hr = UserExperienceActivateEngine(&pEngineState->userExperience, &fActivated);
729 ExitOnFailure(hr, "Engine cannot start LaunchApprovedExe because it is busy with another action.");
730
731 hr = UserExperienceOnLaunchApprovedExeBegin(&pEngineState->userExperience);
732 ExitOnRootFailure(hr, "BA aborted LaunchApprovedExe begin.");
733
734 // Elevate.
735 hr = CoreElevate(pEngineState, pLaunchApprovedExe->hwndParent);
736 ExitOnFailure(hr, "Failed to elevate.");
737
738 // Launch.
739 hr = ElevationLaunchApprovedExe(pEngineState->companionConnection.hPipe, pLaunchApprovedExe, &dwProcessId);
740
741LExit:
742 if (fActivated)
743 {
744 UserExperienceDeactivateEngine(&pEngineState->userExperience);
745 }
746
747 UserExperienceOnLaunchApprovedExeComplete(&pEngineState->userExperience, hr, dwProcessId);
748
749 LogId(REPORT_STANDARD, MSG_LAUNCH_APPROVED_EXE_COMPLETE, hr, dwProcessId);
750
751 ApprovedExesUninitializeLaunch(pLaunchApprovedExe);
752
753 return hr;
754}
755
756extern "C" HRESULT CoreQuit(
757 __in BURN_ENGINE_STATE* pEngineState,
758 __in int nExitCode
759 )
760{
761 HRESULT hr = S_OK;
762
763 // Save engine state if resume mode is unequal to "none".
764 if (BURN_RESUME_MODE_NONE != pEngineState->resumeMode)
765 {
766 hr = CoreSaveEngineState(pEngineState);
767 if (FAILED(hr))
768 {
769 LogErrorId(hr, MSG_STATE_NOT_SAVED, NULL, NULL, NULL);
770 hr = S_OK;
771 }
772 }
773
774 LogId(REPORT_STANDARD, MSG_QUIT, nExitCode);
775
776 ::PostQuitMessage(nExitCode); // go bye-bye.
777
778 return hr;
779}
780
781extern "C" HRESULT CoreSaveEngineState(
782 __in BURN_ENGINE_STATE* pEngineState
783 )
784{
785 HRESULT hr = S_OK;
786 BYTE* pbBuffer = NULL;
787 SIZE_T cbBuffer = 0;
788
789 // serialize engine state
790 hr = CoreSerializeEngineState(pEngineState, &pbBuffer, &cbBuffer);
791 ExitOnFailure(hr, "Failed to serialize engine state.");
792
793 // write to registration store
794 if (pEngineState->registration.fPerMachine)
795 {
796 hr = ElevationSaveState(pEngineState->companionConnection.hPipe, pbBuffer, cbBuffer);
797 ExitOnFailure(hr, "Failed to save engine state in per-machine process.");
798 }
799 else
800 {
801 hr = RegistrationSaveState(&pEngineState->registration, pbBuffer, cbBuffer);
802 ExitOnFailure(hr, "Failed to save engine state.");
803 }
804
805LExit:
806 ReleaseBuffer(pbBuffer);
807
808 return hr;
809}
810
811extern "C" LPCWSTR CoreRelationTypeToCommandLineString(
812 __in BOOTSTRAPPER_RELATION_TYPE relationType
813 )
814{
815 LPCWSTR wzRelationTypeCommandLine = NULL;
816 switch (relationType)
817 {
818 case BOOTSTRAPPER_RELATION_DETECT:
819 wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_DETECT;
820 break;
821 case BOOTSTRAPPER_RELATION_UPGRADE:
822 wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_UPGRADE;
823 break;
824 case BOOTSTRAPPER_RELATION_ADDON:
825 wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_ADDON;
826 break;
827 case BOOTSTRAPPER_RELATION_PATCH:
828 wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_PATCH;
829 break;
830 case BOOTSTRAPPER_RELATION_UPDATE:
831 wzRelationTypeCommandLine = BURN_COMMANDLINE_SWITCH_RELATED_UPDATE;
832 break;
833 case BOOTSTRAPPER_RELATION_DEPENDENT:
834 break;
835 case BOOTSTRAPPER_RELATION_NONE: __fallthrough;
836 default:
837 wzRelationTypeCommandLine = NULL;
838 break;
839 }
840
841 return wzRelationTypeCommandLine;
842}
843
844extern "C" HRESULT CoreRecreateCommandLine(
845 __deref_inout_z LPWSTR* psczCommandLine,
846 __in BOOTSTRAPPER_ACTION action,
847 __in BOOTSTRAPPER_DISPLAY display,
848 __in BOOTSTRAPPER_RESTART restart,
849 __in BOOTSTRAPPER_RELATION_TYPE relationType,
850 __in BOOL fPassthrough,
851 __in_z_opt LPCWSTR wzActiveParent,
852 __in_z_opt LPCWSTR wzAncestors,
853 __in_z_opt LPCWSTR wzAppendLogPath,
854 __in_z_opt LPCWSTR wzAdditionalCommandLineArguments
855 )
856{
857 HRESULT hr = S_OK;
858 LPWSTR scz = NULL;
859 LPCWSTR wzRelationTypeCommandLine = CoreRelationTypeToCommandLineString(relationType);
860
861 hr = StrAllocString(psczCommandLine, L"", 0);
862 ExitOnFailure(hr, "Failed to empty command line.");
863
864 switch (display)
865 {
866 case BOOTSTRAPPER_DISPLAY_NONE:
867 hr = StrAllocConcat(psczCommandLine, L" /quiet", 0);
868 break;
869 case BOOTSTRAPPER_DISPLAY_PASSIVE:
870 hr = StrAllocConcat(psczCommandLine, L" /passive", 0);
871 break;
872 }
873 ExitOnFailure(hr, "Failed to append display state to command-line");
874
875 switch (action)
876 {
877 case BOOTSTRAPPER_ACTION_MODIFY:
878 hr = StrAllocConcat(psczCommandLine, L" /modify", 0);
879 break;
880 case BOOTSTRAPPER_ACTION_REPAIR:
881 hr = StrAllocConcat(psczCommandLine, L" /repair", 0);
882 break;
883 case BOOTSTRAPPER_ACTION_UNINSTALL:
884 hr = StrAllocConcat(psczCommandLine, L" /uninstall", 0);
885 break;
886 }
887 ExitOnFailure(hr, "Failed to append action state to command-line");
888
889 switch (restart)
890 {
891 case BOOTSTRAPPER_RESTART_ALWAYS:
892 hr = StrAllocConcat(psczCommandLine, L" /forcerestart", 0);
893 break;
894 case BOOTSTRAPPER_RESTART_NEVER:
895 hr = StrAllocConcat(psczCommandLine, L" /norestart", 0);
896 break;
897 }
898 ExitOnFailure(hr, "Failed to append restart state to command-line");
899
900 if (wzActiveParent)
901 {
902 if (*wzActiveParent)
903 {
904 hr = StrAllocFormatted(&scz, L" /%ls \"%ls\"", BURN_COMMANDLINE_SWITCH_PARENT, wzActiveParent);
905 ExitOnFailure(hr, "Failed to format active parent command-line for command-line.");
906 }
907 else
908 {
909 hr = StrAllocFormatted(&scz, L" /%ls", BURN_COMMANDLINE_SWITCH_PARENT_NONE);
910 ExitOnFailure(hr, "Failed to format parent:none command-line for command-line.");
911 }
912
913 hr = StrAllocConcat(psczCommandLine, scz, 0);
914 ExitOnFailure(hr, "Failed to append active parent command-line to command-line.");
915 }
916
917 if (wzAncestors)
918 {
919 hr = StrAllocFormatted(&scz, L" /%ls=%ls", BURN_COMMANDLINE_SWITCH_ANCESTORS, wzAncestors);
920 ExitOnFailure(hr, "Failed to format ancestors for command-line.");
921
922 hr = StrAllocConcat(psczCommandLine, scz, 0);
923 ExitOnFailure(hr, "Failed to append ancestors to command-line.");
924 }
925
926 if (wzRelationTypeCommandLine)
927 {
928 hr = StrAllocFormatted(&scz, L" /%ls", wzRelationTypeCommandLine);
929 ExitOnFailure(hr, "Failed to format relation type for command-line.");
930
931 hr = StrAllocConcat(psczCommandLine, scz, 0);
932 ExitOnFailure(hr, "Failed to append relation type to command-line.");
933 }
934
935 if (fPassthrough)
936 {
937 hr = StrAllocFormatted(&scz, L" /%ls", BURN_COMMANDLINE_SWITCH_PASSTHROUGH);
938 ExitOnFailure(hr, "Failed to format passthrough for command-line.");
939
940 hr = StrAllocConcat(psczCommandLine, scz, 0);
941 ExitOnFailure(hr, "Failed to append passthrough to command-line.");
942 }
943
944 if (wzAppendLogPath && *wzAppendLogPath)
945 {
946 hr = StrAllocFormatted(&scz, L" /%ls \"%ls\"", BURN_COMMANDLINE_SWITCH_LOG_APPEND, wzAppendLogPath);
947 ExitOnFailure(hr, "Failed to format append log command-line for command-line.");
948
949 hr = StrAllocConcat(psczCommandLine, scz, 0);
950 ExitOnFailure(hr, "Failed to append log command-line to command-line");
951 }
952
953 if (wzAdditionalCommandLineArguments && *wzAdditionalCommandLineArguments)
954 {
955 hr = StrAllocConcat(psczCommandLine, L" ", 0);
956 ExitOnFailure(hr, "Failed to append space to command-line.");
957
958 hr = StrAllocConcat(psczCommandLine, wzAdditionalCommandLineArguments, 0);
959 ExitOnFailure(hr, "Failed to append command-line to command-line.");
960 }
961
962LExit:
963 ReleaseStr(scz);
964
965 return hr;
966}
967
968extern "C" HRESULT CoreAppendFileHandleAttachedToCommandLine(
969 __in HANDLE hFileWithAttachedContainer,
970 __out HANDLE* phExecutableFile,
971 __deref_inout_z LPWSTR* psczCommandLine
972 )
973{
974 HRESULT hr = S_OK;
975 HANDLE hExecutableFile = INVALID_HANDLE_VALUE;
976
977 *phExecutableFile = INVALID_HANDLE_VALUE;
978
979 if (!::DuplicateHandle(::GetCurrentProcess(), hFileWithAttachedContainer, ::GetCurrentProcess(), &hExecutableFile, 0, TRUE, DUPLICATE_SAME_ACCESS))
980 {
981 ExitWithLastError(hr, "Failed to duplicate file handle for attached container.");
982 }
983
984 hr = StrAllocFormattedSecure(psczCommandLine, L"%ls -%ls=%u", *psczCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, hExecutableFile);
985 ExitOnFailure(hr, "Failed to append the file handle to the command line.");
986
987 *phExecutableFile = hExecutableFile;
988 hExecutableFile = INVALID_HANDLE_VALUE;
989
990LExit:
991 ReleaseFileHandle(hExecutableFile);
992
993 return hr;
994}
995
996extern "C" HRESULT CoreAppendFileHandleSelfToCommandLine(
997 __in LPCWSTR wzExecutablePath,
998 __out HANDLE* phExecutableFile,
999 __deref_inout_z LPWSTR* psczCommandLine,
1000 __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine
1001 )
1002{
1003 HRESULT hr = S_OK;
1004 HANDLE hExecutableFile = INVALID_HANDLE_VALUE;
1005 SECURITY_ATTRIBUTES securityAttributes = { };
1006 securityAttributes.bInheritHandle = TRUE;
1007 *phExecutableFile = INVALID_HANDLE_VALUE;
1008
1009 hExecutableFile = ::CreateFileW(wzExecutablePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, &securityAttributes, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1010 if (INVALID_HANDLE_VALUE != hExecutableFile)
1011 {
1012 hr = StrAllocFormattedSecure(psczCommandLine, L"%ls -%ls=%u", *psczCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, hExecutableFile);
1013 ExitOnFailure(hr, "Failed to append the file handle to the command line.");
1014
1015 if (psczObfuscatedCommandLine)
1016 {
1017 hr = StrAllocFormatted(psczObfuscatedCommandLine, L"%ls -%ls=%u", *psczObfuscatedCommandLine, BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, hExecutableFile);
1018 ExitOnFailure(hr, "Failed to append the file handle to the obfuscated command line.");
1019 }
1020
1021 *phExecutableFile = hExecutableFile;
1022 hExecutableFile = INVALID_HANDLE_VALUE;
1023 }
1024
1025LExit:
1026 ReleaseFileHandle(hExecutableFile);
1027
1028 return hr;
1029}
1030
1031// internal helper functions
1032
1033static HRESULT ParseCommandLine(
1034 __in int argc,
1035 __in LPWSTR* argv,
1036 __in BOOTSTRAPPER_COMMAND* pCommand,
1037 __in BURN_PIPE_CONNECTION* pCompanionConnection,
1038 __in BURN_PIPE_CONNECTION* pEmbeddedConnection,
1039 __in BURN_VARIABLES* pVariables,
1040 __out BURN_MODE* pMode,
1041 __out BURN_AU_PAUSE_ACTION* pAutomaticUpdates,
1042 __out BOOL* pfDisableSystemRestore,
1043 __out_z LPWSTR* psczSourceProcessPath,
1044 __out_z LPWSTR* psczOriginalSource,
1045 __out BOOL* pfDisableUnelevate,
1046 __out DWORD *pdwLoggingAttributes,
1047 __out_z LPWSTR* psczLogFile,
1048 __out_z LPWSTR* psczActiveParent,
1049 __out_z LPWSTR* psczIgnoreDependencies,
1050 __out_z LPWSTR* psczAncestors,
1051 __out_z LPWSTR* psczSanitizedCommandLine
1052 )
1053{
1054 HRESULT hr = S_OK;
1055 BOOL fUnknownArg = FALSE;
1056 BOOL fHidden = FALSE;
1057 LPWSTR sczCommandLine = NULL;
1058 LPWSTR sczSanitizedArgument = NULL;
1059 LPWSTR sczVariableName = NULL;
1060
1061 for (int i = 0; i < argc; ++i)
1062 {
1063 fUnknownArg = FALSE;
1064 int originalIndex = i;
1065 ReleaseNullStr(sczSanitizedArgument);
1066
1067 if (argv[i][0] == L'-' || argv[i][0] == L'/')
1068 {
1069 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"l", -1) ||
1070 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"log", -1))
1071 {
1072 *pdwLoggingAttributes &= ~BURN_LOGGING_ATTRIBUTE_APPEND;
1073
1074 if (i + 1 >= argc)
1075 {
1076 ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for log.");
1077 }
1078
1079 ++i;
1080
1081 hr = StrAllocString(psczLogFile, argv[i], 0);
1082 ExitOnFailure(hr, "Failed to copy log file path.");
1083 }
1084 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"?", -1) ||
1085 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"h", -1) ||
1086 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"help", -1))
1087 {
1088 pCommand->action = BOOTSTRAPPER_ACTION_HELP;
1089 }
1090 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"q", -1) ||
1091 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"quiet", -1) ||
1092 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"s", -1) ||
1093 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"silent", -1))
1094 {
1095 pCommand->display = BOOTSTRAPPER_DISPLAY_NONE;
1096
1097 if (BOOTSTRAPPER_RESTART_UNKNOWN == pCommand->restart)
1098 {
1099 pCommand->restart = BOOTSTRAPPER_RESTART_AUTOMATIC;
1100 }
1101 }
1102 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"passive", -1))
1103 {
1104 pCommand->display = BOOTSTRAPPER_DISPLAY_PASSIVE;
1105
1106 if (BOOTSTRAPPER_RESTART_UNKNOWN == pCommand->restart)
1107 {
1108 pCommand->restart = BOOTSTRAPPER_RESTART_AUTOMATIC;
1109 }
1110 }
1111 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"norestart", -1))
1112 {
1113 pCommand->restart = BOOTSTRAPPER_RESTART_NEVER;
1114 }
1115 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"forcerestart", -1))
1116 {
1117 pCommand->restart = BOOTSTRAPPER_RESTART_ALWAYS;
1118 }
1119 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"promptrestart", -1))
1120 {
1121 pCommand->restart = BOOTSTRAPPER_RESTART_PROMPT;
1122 }
1123 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"layout", -1))
1124 {
1125 if (BOOTSTRAPPER_ACTION_HELP != pCommand->action)
1126 {
1127 pCommand->action = BOOTSTRAPPER_ACTION_LAYOUT;
1128 }
1129
1130 // If there is another command line argument and it is not a switch, use that as the layout directory.
1131 if (i + 1 < argc && argv[i + 1][0] != L'-' && argv[i + 1][0] != L'/')
1132 {
1133 ++i;
1134
1135 hr = PathExpand(&pCommand->wzLayoutDirectory, argv[i], PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH);
1136 ExitOnFailure(hr, "Failed to copy path for layout directory.");
1137 }
1138 }
1139 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"uninstall", -1))
1140 {
1141 if (BOOTSTRAPPER_ACTION_HELP != pCommand->action)
1142 {
1143 pCommand->action = BOOTSTRAPPER_ACTION_UNINSTALL;
1144 }
1145 }
1146 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"repair", -1))
1147 {
1148 if (BOOTSTRAPPER_ACTION_HELP != pCommand->action)
1149 {
1150 pCommand->action = BOOTSTRAPPER_ACTION_REPAIR;
1151 }
1152 }
1153 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"modify", -1))
1154 {
1155 if (BOOTSTRAPPER_ACTION_HELP != pCommand->action)
1156 {
1157 pCommand->action = BOOTSTRAPPER_ACTION_MODIFY;
1158 }
1159 }
1160 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"package", -1) ||
1161 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"update", -1))
1162 {
1163 if (BOOTSTRAPPER_ACTION_UNKNOWN == pCommand->action)
1164 {
1165 pCommand->action = BOOTSTRAPPER_ACTION_INSTALL;
1166 }
1167 }
1168 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"noaupause", -1))
1169 {
1170 *pAutomaticUpdates = BURN_AU_PAUSE_ACTION_NONE;
1171 }
1172 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"keepaupaused", -1))
1173 {
1174 // Switch /noaupause takes precedence.
1175 if (BURN_AU_PAUSE_ACTION_NONE != *pAutomaticUpdates)
1176 {
1177 *pAutomaticUpdates = BURN_AU_PAUSE_ACTION_IFELEVATED_NORESUME;
1178 }
1179 }
1180 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"disablesystemrestore", -1))
1181 {
1182 *pfDisableSystemRestore = TRUE;
1183 }
1184 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"originalsource", -1))
1185 {
1186 if (i + 1 >= argc)
1187 {
1188 ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for original source.");
1189 }
1190
1191 ++i;
1192 hr = StrAllocString(psczOriginalSource, argv[i], 0);
1193 ExitOnFailure(hr, "Failed to copy last used source.");
1194 }
1195 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PARENT, -1))
1196 {
1197 if (i + 1 >= argc)
1198 {
1199 ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a value for parent.");
1200 }
1201
1202 ++i;
1203
1204 hr = StrAllocString(psczActiveParent, argv[i], 0);
1205 ExitOnFailure(hr, "Failed to copy parent.");
1206 }
1207 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PARENT_NONE, -1))
1208 {
1209 hr = StrAllocString(psczActiveParent, L"", 0);
1210 ExitOnFailure(hr, "Failed to initialize parent to none.");
1211 }
1212 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_LOG_APPEND, -1))
1213 {
1214 if (i + 1 >= argc)
1215 {
1216 ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a path for append log.");
1217 }
1218
1219 ++i;
1220
1221 hr = StrAllocString(psczLogFile, argv[i], 0);
1222 ExitOnFailure(hr, "Failed to copy append log file path.");
1223
1224 *pdwLoggingAttributes |= BURN_LOGGING_ATTRIBUTE_APPEND;
1225 }
1226 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_ELEVATED, -1))
1227 {
1228 if (i + 3 >= argc)
1229 {
1230 ExitOnRootFailure(hr = E_INVALIDARG, "Must specify the elevated name, token and parent process id.");
1231 }
1232
1233 if (BURN_MODE_UNTRUSTED != *pMode)
1234 {
1235 ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided.");
1236 }
1237
1238 *pMode = BURN_MODE_ELEVATED;
1239
1240 ++i;
1241
1242 hr = ParsePipeConnection(argv + i, pCompanionConnection);
1243 ExitOnFailure(hr, "Failed to parse elevated connection.");
1244
1245 i += 2;
1246 }
1247 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM), BURN_COMMANDLINE_SWITCH_CLEAN_ROOM, lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM)))
1248 {
1249 // Get a pointer to the next character after the switch.
1250 LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM)];
1251 if (L'=' != wzParam[0] || L'\0' == wzParam[1])
1252 {
1253 ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_CLEAN_ROOM);
1254 }
1255
1256 if (BURN_MODE_UNTRUSTED != *pMode)
1257 {
1258 ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided.");
1259 }
1260
1261 *pMode = BURN_MODE_NORMAL;
1262
1263 hr = StrAllocString(psczSourceProcessPath, wzParam + 1, 0);
1264 ExitOnFailure(hr, "Failed to copy source process path.");
1265 }
1266 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_EMBEDDED, -1))
1267 {
1268 if (i + 3 >= argc)
1269 {
1270 ExitOnRootFailure(hr = E_INVALIDARG, "Must specify the embedded name, token and parent process id.");
1271 }
1272
1273 switch (*pMode)
1274 {
1275 case BURN_MODE_UNTRUSTED:
1276 // Leave mode as UNTRUSTED to launch the clean room process.
1277 break;
1278 case BURN_MODE_NORMAL:
1279 // The initialization code already assumes that the
1280 // clean room switch is at the beginning of the command line,
1281 // so it's safe to assume that the mode is NORMAL in the clean room.
1282 *pMode = BURN_MODE_EMBEDDED;
1283 break;
1284 default:
1285 ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided.");
1286 }
1287
1288 ++i;
1289
1290 hr = ParsePipeConnection(argv + i, pEmbeddedConnection);
1291 ExitOnFailure(hr, "Failed to parse embedded connection.");
1292
1293 i += 2;
1294 }
1295 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_DETECT, -1))
1296 {
1297 pCommand->relationType = BOOTSTRAPPER_RELATION_DETECT;
1298
1299 LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1300 }
1301 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_UPGRADE, -1))
1302 {
1303 pCommand->relationType = BOOTSTRAPPER_RELATION_UPGRADE;
1304
1305 LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1306 }
1307 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_ADDON, -1))
1308 {
1309 pCommand->relationType = BOOTSTRAPPER_RELATION_ADDON;
1310
1311 LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1312 }
1313 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_PATCH, -1))
1314 {
1315 pCommand->relationType = BOOTSTRAPPER_RELATION_PATCH;
1316
1317 LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1318 }
1319 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RELATED_UPDATE, -1))
1320 {
1321 pCommand->relationType = BOOTSTRAPPER_RELATION_UPDATE;
1322
1323 LogId(REPORT_STANDARD, MSG_BURN_RUN_BY_RELATED_BUNDLE, LoggingRelationTypeToString(pCommand->relationType));
1324 }
1325 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_PASSTHROUGH, -1))
1326 {
1327 pCommand->fPassthrough = TRUE;
1328 }
1329 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_DISABLE_UNELEVATE, -1))
1330 {
1331 *pfDisableUnelevate = TRUE;
1332 }
1333 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, BURN_COMMANDLINE_SWITCH_RUNONCE, -1))
1334 {
1335 if (BURN_MODE_UNTRUSTED != *pMode)
1336 {
1337 ExitOnRootFailure(hr = E_INVALIDARG, "Multiple mode command-line switches were provided.");
1338 }
1339
1340 *pMode = BURN_MODE_RUNONCE;
1341 }
1342 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES), BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, lstrlenW(BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES)))
1343 {
1344 // Get a pointer to the next character after the switch.
1345 LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES)];
1346 if (L'=' != wzParam[0] || L'\0' == wzParam[1])
1347 {
1348 ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES);
1349 }
1350
1351 hr = StrAllocString(psczIgnoreDependencies, &wzParam[1], 0);
1352 ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore.");
1353 }
1354 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_ANCESTORS), BURN_COMMANDLINE_SWITCH_ANCESTORS, lstrlenW(BURN_COMMANDLINE_SWITCH_ANCESTORS)))
1355 {
1356 // Get a pointer to the next character after the switch.
1357 LPCWSTR wzParam = &argv[i][1 + lstrlenW(BURN_COMMANDLINE_SWITCH_ANCESTORS)];
1358 if (L'=' != wzParam[0] || L'\0' == wzParam[1])
1359 {
1360 ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_ANCESTORS);
1361 }
1362
1363 hr = StrAllocString(psczAncestors, &wzParam[1], 0);
1364 ExitOnFailure(hr, "Failed to allocate the list of ancestors.");
1365 }
1366 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED), BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED)))
1367 {
1368 // Already processed in InitializeEngineState.
1369 }
1370 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF), BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF)))
1371 {
1372 // Already processed in InitializeEngineState.
1373 }
1374 else if (lstrlenW(&argv[i][1]) >= lstrlenW(BURN_COMMANDLINE_SWITCH_PREFIX) &&
1375 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_PREFIX), BURN_COMMANDLINE_SWITCH_PREFIX, lstrlenW(BURN_COMMANDLINE_SWITCH_PREFIX)))
1376 {
1377 // Skip (but log) any other private burn switches we don't recognize, so that
1378 // adding future private variables doesn't break old bundles
1379 LogId(REPORT_STANDARD, MSG_BURN_UNKNOWN_PRIVATE_SWITCH, &argv[i][1]);
1380 }
1381 else
1382 {
1383 fUnknownArg = TRUE;
1384 }
1385 }
1386 else
1387 {
1388 fUnknownArg = TRUE;
1389
1390 const wchar_t* pwc = wcschr(argv[i], L'=');
1391 if (pwc)
1392 {
1393 hr = StrAllocString(&sczVariableName, argv[i], pwc - argv[i]);
1394 ExitOnFailure(hr, "Failed to copy variable name.");
1395
1396 hr = VariableIsHidden(pVariables, sczVariableName, &fHidden);
1397 ExitOnFailure(hr, "Failed to determine whether variable is hidden.");
1398
1399 if (fHidden)
1400 {
1401 hr = StrAllocFormatted(&sczSanitizedArgument, L"%ls=*****", sczVariableName);
1402 ExitOnFailure(hr, "Failed to copy sanitized argument.");
1403 }
1404 }
1405 }
1406
1407 // Remember command-line switch to pass off to UX.
1408 if (fUnknownArg)
1409 {
1410 PathCommandLineAppend(&pCommand->wzCommandLine, argv[i]);
1411 }
1412
1413 if (sczSanitizedArgument)
1414 {
1415 PathCommandLineAppend(psczSanitizedCommandLine, sczSanitizedArgument);
1416 }
1417 else
1418 {
1419 for (; originalIndex <= i; ++originalIndex)
1420 {
1421 PathCommandLineAppend(psczSanitizedCommandLine, argv[originalIndex]);
1422 }
1423 }
1424 }
1425
1426 // If embedded, ensure the display goes embedded as well.
1427 if (BURN_MODE_EMBEDDED == *pMode)
1428 {
1429 pCommand->display = BOOTSTRAPPER_DISPLAY_EMBEDDED;
1430 }
1431
1432 // Set the defaults if nothing was set above.
1433 if (BOOTSTRAPPER_ACTION_UNKNOWN == pCommand->action)
1434 {
1435 pCommand->action = BOOTSTRAPPER_ACTION_INSTALL;
1436 }
1437
1438 if (BOOTSTRAPPER_DISPLAY_UNKNOWN == pCommand->display)
1439 {
1440 pCommand->display = BOOTSTRAPPER_DISPLAY_FULL;
1441 }
1442
1443 if (BOOTSTRAPPER_RESTART_UNKNOWN == pCommand->restart)
1444 {
1445 pCommand->restart = BOOTSTRAPPER_RESTART_PROMPT;
1446 }
1447
1448LExit:
1449 ReleaseStr(sczVariableName);
1450 ReleaseStr(sczSanitizedArgument);
1451 ReleaseStr(sczCommandLine);
1452
1453 return hr;
1454}
1455
1456static HRESULT ParsePipeConnection(
1457 __in_ecount(3) LPWSTR* rgArgs,
1458 __in BURN_PIPE_CONNECTION* pConnection
1459 )
1460{
1461 HRESULT hr = S_OK;
1462
1463 hr = StrAllocString(&pConnection->sczName, rgArgs[0], 0);
1464 ExitOnFailure(hr, "Failed to copy connection name from command line.");
1465
1466 hr = StrAllocString(&pConnection->sczSecret, rgArgs[1], 0);
1467 ExitOnFailure(hr, "Failed to copy connection secret from command line.");
1468
1469 hr = StrStringToUInt32(rgArgs[2], 0, reinterpret_cast<UINT*>(&pConnection->dwProcessId));
1470 ExitOnFailure(hr, "Failed to copy parent process id from command line.");
1471
1472LExit:
1473 return hr;
1474}
1475
1476static HRESULT DetectPackage(
1477 __in BURN_ENGINE_STATE* pEngineState,
1478 __in BURN_PACKAGE* pPackage
1479 )
1480{
1481 HRESULT hr = S_OK;
1482 BOOL fBegan = FALSE;
1483
1484 fBegan = TRUE;
1485 hr = UserExperienceOnDetectPackageBegin(&pEngineState->userExperience, pPackage->sczId);
1486 ExitOnRootFailure(hr, "BA aborted detect package begin.");
1487
1488 // Detect the cache state of the package.
1489 hr = DetectPackagePayloadsCached(pPackage);
1490 ExitOnFailure(hr, "Failed to detect if payloads are all cached for package: %ls", pPackage->sczId);
1491
1492 // Use the correct engine to detect the package.
1493 switch (pPackage->type)
1494 {
1495 case BURN_PACKAGE_TYPE_EXE:
1496 hr = ExeEngineDetectPackage(pPackage, &pEngineState->variables);
1497 break;
1498
1499 case BURN_PACKAGE_TYPE_MSI:
1500 hr = MsiEngineDetectPackage(pPackage, &pEngineState->userExperience);
1501 break;
1502
1503 case BURN_PACKAGE_TYPE_MSP:
1504 hr = MspEngineDetectPackage(pPackage, &pEngineState->userExperience);
1505 break;
1506
1507 case BURN_PACKAGE_TYPE_MSU:
1508 hr = MsuEngineDetectPackage(pPackage, &pEngineState->variables);
1509 break;
1510
1511 default:
1512 hr = E_NOTIMPL;
1513 ExitOnRootFailure(hr, "Package type not supported by detect yet.");
1514 }
1515
1516 // TODO: consider how to notify the UX that a package is cached.
1517 //else if (BOOTSTRAPPER_PACKAGE_STATE_CACHED > pPackage->currentState && pPackage->fCached)
1518 //{
1519 // pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_CACHED;
1520 //}
1521
1522LExit:
1523 if (FAILED(hr))
1524 {
1525 LogErrorId(hr, MSG_FAILED_DETECT_PACKAGE, pPackage->sczId, NULL, NULL);
1526 }
1527
1528 if (fBegan)
1529 {
1530 UserExperienceOnDetectPackageComplete(&pEngineState->userExperience, pPackage->sczId, hr, pPackage->currentState);
1531 }
1532
1533 return hr;
1534}
1535
1536static HRESULT DetectPackagePayloadsCached(
1537 __in BURN_PACKAGE* pPackage
1538 )
1539{
1540 HRESULT hr = S_OK;
1541 LPWSTR sczCachePath = NULL;
1542 BURN_CACHE_STATE cache = BURN_CACHE_STATE_NONE; // assume the package will not be cached.
1543 LPWSTR sczPayloadCachePath = NULL;
1544 LONGLONG llSize = 0;
1545
1546 if (pPackage->sczCacheId && *pPackage->sczCacheId)
1547 {
1548 hr = CacheGetCompletedPath(pPackage->fPerMachine, pPackage->sczCacheId, &sczCachePath);
1549 ExitOnFailure(hr, "Failed to get completed cache path.");
1550
1551 // If the cached directory exists, we have something.
1552 if (DirExists(sczCachePath, NULL))
1553 {
1554 cache = BURN_CACHE_STATE_COMPLETE; // assume all payloads are cached.
1555
1556 // Check all payloads to see if any are missing or not the right size.
1557 for (DWORD i = 0; i < pPackage->cPayloads; ++i)
1558 {
1559 BURN_PACKAGE_PAYLOAD* pPackagePayload = pPackage->rgPayloads + i;
1560
1561 hr = PathConcat(sczCachePath, pPackagePayload->pPayload->sczFilePath, &sczPayloadCachePath);
1562 ExitOnFailure(hr, "Failed to concat payload cache path.");
1563
1564 hr = FileSize(sczPayloadCachePath, &llSize);
1565 if (SUCCEEDED(hr) && static_cast<DWORD64>(llSize) != pPackagePayload->pPayload->qwFileSize)
1566 {
1567 hr = HRESULT_FROM_WIN32(ERROR_FILE_CORRUPT); // size did not match expectations, so cache must have the wrong file.
1568 }
1569
1570 if (SUCCEEDED(hr))
1571 {
1572 // TODO: should we do a full on hash verification on the file to ensure
1573 // the exact right file is cached?
1574
1575 pPackagePayload->fCached = TRUE;
1576 }
1577 else
1578 {
1579 LogId(REPORT_STANDARD, MSG_DETECT_PACKAGE_NOT_FULLY_CACHED, pPackage->sczId, pPackagePayload->pPayload->sczKey, hr);
1580
1581 cache = BURN_CACHE_STATE_PARTIAL; // found a payload that was not cached so we are partial.
1582 hr = S_OK;
1583 }
1584 }
1585 }
1586 }
1587
1588 pPackage->cache = cache;
1589
1590LExit:
1591 ReleaseStr(sczPayloadCachePath);
1592 ReleaseStr(sczCachePath);
1593 return hr;
1594}
1595
1596static DWORD WINAPI CacheThreadProc(
1597 __in LPVOID lpThreadParameter
1598 )
1599{
1600 HRESULT hr = S_OK;
1601 BURN_CACHE_THREAD_CONTEXT* pContext = reinterpret_cast<BURN_CACHE_THREAD_CONTEXT*>(lpThreadParameter);
1602 BURN_ENGINE_STATE* pEngineState = pContext->pEngineState;
1603 DWORD* pcOverallProgressTicks = pContext->pcOverallProgressTicks;
1604 BOOL* pfRollback = pContext->pfRollback;
1605 BOOL fComInitialized = FALSE;
1606
1607 // initialize COM
1608 hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
1609 ExitOnFailure(hr, "Failed to initialize COM on cache thread.");
1610 fComInitialized = TRUE;
1611
1612 // cache packages
1613 hr = ApplyCache(pEngineState->section.hSourceEngineFile, &pEngineState->userExperience, &pEngineState->variables, &pEngineState->plan, pEngineState->companionConnection.hCachePipe, pcOverallProgressTicks, pfRollback);
1614
1615LExit:
1616 UserExperienceExecutePhaseComplete(&pEngineState->userExperience, hr); // signal that cache completed.
1617
1618 if (fComInitialized)
1619 {
1620 ::CoUninitialize();
1621 }
1622
1623 return (DWORD)hr;
1624}
1625
1626static HRESULT WaitForCacheThread(
1627 __in HANDLE hCacheThread
1628 )
1629{
1630 HRESULT hr = S_OK;
1631
1632 if (WAIT_OBJECT_0 != ::WaitForSingleObject(hCacheThread, INFINITE))
1633 {
1634 ExitWithLastError(hr, "Failed to wait for cache thread to terminate.");
1635 }
1636
1637 if (!::GetExitCodeThread(hCacheThread, (DWORD*)&hr))
1638 {
1639 ExitWithLastError(hr, "Failed to get cache thread exit code.");
1640 }
1641
1642LExit:
1643 return hr;
1644}
1645
1646static void LogPackages(
1647 __in_opt const BURN_PACKAGE* pUpgradeBundlePackage,
1648 __in_opt const BURN_PACKAGE* pForwardCompatibleBundlePackage,
1649 __in const BURN_PACKAGES* pPackages,
1650 __in const BURN_RELATED_BUNDLES* pRelatedBundles,
1651 __in const BOOTSTRAPPER_ACTION action
1652 )
1653{
1654 if (pUpgradeBundlePackage)
1655 {
1656 LogId(REPORT_STANDARD, MSG_PLANNED_UPGRADE_BUNDLE, pUpgradeBundlePackage->sczId, LoggingRequestStateToString(pUpgradeBundlePackage->defaultRequested), LoggingRequestStateToString(pUpgradeBundlePackage->requested), LoggingActionStateToString(pUpgradeBundlePackage->execute), LoggingActionStateToString(pUpgradeBundlePackage->rollback), LoggingDependencyActionToString(pUpgradeBundlePackage->dependencyExecute));
1657 }
1658 else if (pForwardCompatibleBundlePackage)
1659 {
1660 LogId(REPORT_STANDARD, MSG_PLANNED_FORWARD_COMPATIBLE_BUNDLE, pForwardCompatibleBundlePackage->sczId, LoggingRequestStateToString(pForwardCompatibleBundlePackage->defaultRequested), LoggingRequestStateToString(pForwardCompatibleBundlePackage->requested), LoggingActionStateToString(pForwardCompatibleBundlePackage->execute), LoggingActionStateToString(pForwardCompatibleBundlePackage->rollback), LoggingDependencyActionToString(pForwardCompatibleBundlePackage->dependencyExecute));
1661 }
1662 else
1663 {
1664 // Display related bundles first if uninstalling.
1665 if (BOOTSTRAPPER_ACTION_UNINSTALL == action && 0 < pRelatedBundles->cRelatedBundles)
1666 {
1667 for (int i = pRelatedBundles->cRelatedBundles - 1; 0 <= i; --i)
1668 {
1669 const BURN_RELATED_BUNDLE* pRelatedBundle = &pRelatedBundles->rgRelatedBundles[i];
1670 const BURN_PACKAGE* pPackage = &pRelatedBundle->package;
1671
1672 LogId(REPORT_STANDARD, MSG_PLANNED_RELATED_BUNDLE, pPackage->sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingDependencyActionToString(pPackage->dependencyExecute));
1673 }
1674 }
1675
1676 // Display all the packages in the log.
1677 for (DWORD i = 0; i < pPackages->cPackages; ++i)
1678 {
1679 const DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == action) ? pPackages->cPackages - 1 - i : i;
1680 const BURN_PACKAGE* pPackage = &pPackages->rgPackages[iPackage];
1681
1682 LogId(REPORT_STANDARD, MSG_PLANNED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingBoolToString(pPackage->fAcquire), LoggingBoolToString(pPackage->fUncache), LoggingDependencyActionToString(pPackage->dependencyExecute));
1683 }
1684
1685 for (DWORD i = 0; i < pPackages->cCompatiblePackages; ++i)
1686 {
1687 const DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == action) ? pPackages->cCompatiblePackages - 1 - i : i;
1688 const BURN_PACKAGE* pPackage = &pPackages->rgCompatiblePackages[iPackage];
1689
1690 LogId(REPORT_STANDARD, MSG_PLANNED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingBoolToString(pPackage->fAcquire), LoggingBoolToString(pPackage->fUncache), LoggingDependencyActionToString(pPackage->dependencyExecute));
1691 }
1692
1693 // Display related bundles last if caching, installing, modifying, or repairing.
1694 if (BOOTSTRAPPER_ACTION_UNINSTALL < action && 0 < pRelatedBundles->cRelatedBundles)
1695 {
1696 for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i)
1697 {
1698 const BURN_RELATED_BUNDLE* pRelatedBundle = &pRelatedBundles->rgRelatedBundles[i];
1699 const BURN_PACKAGE* pPackage = &pRelatedBundle->package;
1700
1701 LogId(REPORT_STANDARD, MSG_PLANNED_RELATED_BUNDLE, pPackage->sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingDependencyActionToString(pPackage->dependencyExecute));
1702 }
1703 }
1704 }
1705}
diff --git a/src/engine/core.h b/src/engine/core.h
new file mode 100644
index 00000000..6a6da2b1
--- /dev/null
+++ b/src/engine/core.h
@@ -0,0 +1,211 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// constants
11
12const LPCWSTR BURN_POLICY_REGISTRY_PATH = L"WiX\\Burn";
13
14const LPCWSTR BURN_COMMANDLINE_SWITCH_PARENT = L"parent";
15const LPCWSTR BURN_COMMANDLINE_SWITCH_PARENT_NONE = L"parent:none";
16const LPCWSTR BURN_COMMANDLINE_SWITCH_CLEAN_ROOM = L"burn.clean.room";
17const LPCWSTR BURN_COMMANDLINE_SWITCH_ELEVATED = L"burn.elevated";
18const LPCWSTR BURN_COMMANDLINE_SWITCH_EMBEDDED = L"burn.embedded";
19const LPCWSTR BURN_COMMANDLINE_SWITCH_RUNONCE = L"burn.runonce";
20const LPCWSTR BURN_COMMANDLINE_SWITCH_LOG_APPEND = L"burn.log.append";
21const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_DETECT = L"burn.related.detect";
22const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_UPGRADE = L"burn.related.upgrade";
23const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_ADDON = L"burn.related.addon";
24const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_PATCH = L"burn.related.patch";
25const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_UPDATE = L"burn.related.update";
26const LPCWSTR BURN_COMMANDLINE_SWITCH_PASSTHROUGH = L"burn.passthrough";
27const LPCWSTR BURN_COMMANDLINE_SWITCH_DISABLE_UNELEVATE = L"burn.disable.unelevate";
28const LPCWSTR BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES = L"burn.ignoredependencies";
29const LPCWSTR BURN_COMMANDLINE_SWITCH_ANCESTORS = L"burn.ancestors";
30const LPCWSTR BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED = L"burn.filehandle.attached";
31const LPCWSTR BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF = L"burn.filehandle.self";
32const LPCWSTR BURN_COMMANDLINE_SWITCH_PREFIX = L"burn.";
33
34const LPCWSTR BURN_BUNDLE_LAYOUT_DIRECTORY = L"WixBundleLayoutDirectory";
35const LPCWSTR BURN_BUNDLE_ACTION = L"WixBundleAction";
36const LPCWSTR BURN_BUNDLE_ACTIVE_PARENT = L"WixBundleActiveParent";
37const LPCWSTR BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER = L"WixBundleExecutePackageCacheFolder";
38const LPCWSTR BURN_BUNDLE_EXECUTE_PACKAGE_ACTION = L"WixBundleExecutePackageAction";
39const LPCWSTR BURN_BUNDLE_FORCED_RESTART_PACKAGE = L"WixBundleForcedRestartPackage";
40const LPCWSTR BURN_BUNDLE_INSTALLED = L"WixBundleInstalled";
41const LPCWSTR BURN_BUNDLE_ELEVATED = L"WixBundleElevated";
42const LPCWSTR BURN_BUNDLE_PROVIDER_KEY = L"WixBundleProviderKey";
43const LPCWSTR BURN_BUNDLE_MANUFACTURER = L"WixBundleManufacturer";
44const LPCWSTR BURN_BUNDLE_SOURCE_PROCESS_PATH = L"WixBundleSourceProcessPath";
45const LPCWSTR BURN_BUNDLE_SOURCE_PROCESS_FOLDER = L"WixBundleSourceProcessFolder";
46const LPCWSTR BURN_BUNDLE_TAG = L"WixBundleTag";
47const LPCWSTR BURN_BUNDLE_UILEVEL = L"WixBundleUILevel";
48const LPCWSTR BURN_BUNDLE_VERSION = L"WixBundleVersion";
49
50// The following constants must stay in sync with src\wix\Binder.cs
51const LPCWSTR BURN_BUNDLE_NAME = L"WixBundleName";
52const LPCWSTR BURN_BUNDLE_ORIGINAL_SOURCE = L"WixBundleOriginalSource";
53const LPCWSTR BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER = L"WixBundleOriginalSourceFolder";
54const LPCWSTR BURN_BUNDLE_LAST_USED_SOURCE = L"WixBundleLastUsedSource";
55
56
57// enums
58
59enum BURN_MODE
60{
61 BURN_MODE_UNTRUSTED,
62 BURN_MODE_NORMAL,
63 BURN_MODE_ELEVATED,
64 BURN_MODE_EMBEDDED,
65 BURN_MODE_RUNONCE,
66};
67
68enum BURN_AU_PAUSE_ACTION
69{
70 BURN_AU_PAUSE_ACTION_NONE,
71 BURN_AU_PAUSE_ACTION_IFELEVATED,
72 BURN_AU_PAUSE_ACTION_IFELEVATED_NORESUME,
73};
74
75
76// structs
77
78typedef struct _BURN_ENGINE_STATE
79{
80 // synchronization
81 CRITICAL_SECTION csActive; // Any call from the UX that reads or alters the engine state
82 // needs to be syncronized through this critical section.
83 // Note: The engine must never do a UX callback while in this critical section.
84
85 // UX flow control
86 //BOOL fSuspend; // Is TRUE when UX made Suspend() call on core.
87 //BOOL fForcedReboot; // Is TRUE when UX made Reboot() call on core.
88 //BOOL fCancelled; // Is TRUE when UX return cancel on UX OnXXX() methods.
89 //BOOL fReboot; // Is TRUE when UX confirms OnRestartRequried().
90 BOOL fRestart; // Set TRUE when UX returns IDRESTART during Apply().
91
92 // engine data
93 BOOTSTRAPPER_COMMAND command;
94 BURN_SECTION section;
95 BURN_VARIABLES variables;
96 BURN_CONDITION condition;
97 BURN_SEARCHES searches;
98 BURN_USER_EXPERIENCE userExperience;
99 BURN_REGISTRATION registration;
100 BURN_CONTAINERS containers;
101 BURN_CATALOGS catalogs;
102 BURN_PAYLOADS payloads;
103 BURN_PACKAGES packages;
104 BURN_UPDATE update;
105 BURN_APPROVED_EXES approvedExes;
106
107 HWND hMessageWindow;
108 HANDLE hMessageWindowThread;
109
110 BOOL fDisableRollback;
111 BOOL fDisableSystemRestore;
112 BOOL fParallelCacheAndExecute;
113
114 BURN_LOGGING log;
115
116 BURN_PLAN plan;
117
118 BURN_MODE mode;
119 BURN_AU_PAUSE_ACTION automaticUpdates;
120
121 DWORD dwElevatedLoggingTlsId;
122
123 LPWSTR sczBundleEngineWorkingPath;
124 BURN_PIPE_CONNECTION companionConnection;
125 BURN_PIPE_CONNECTION embeddedConnection;
126
127 BURN_RESUME_MODE resumeMode;
128 BOOL fDisableUnelevate;
129
130 LPWSTR sczIgnoreDependencies;
131
132 int argc;
133 LPWSTR* argv;
134} BURN_ENGINE_STATE;
135
136
137// function declarations
138
139HRESULT CoreInitialize(
140 __in BURN_ENGINE_STATE* pEngineState
141 );
142HRESULT CoreSerializeEngineState(
143 __in BURN_ENGINE_STATE* pEngineState,
144 __inout BYTE** ppbBuffer,
145 __inout SIZE_T* piBuffer
146 );
147HRESULT CoreQueryRegistration(
148 __in BURN_ENGINE_STATE* pEngineState
149 );
150//HRESULT CoreDeserializeEngineState(
151// __in BURN_ENGINE_STATE* pEngineState,
152// __in_bcount(cbBuffer) BYTE* pbBuffer,
153// __in SIZE_T cbBuffer
154// );
155HRESULT CoreDetect(
156 __in BURN_ENGINE_STATE* pEngineState,
157 __in_opt HWND hwndParent
158 );
159HRESULT CorePlan(
160 __in BURN_ENGINE_STATE* pEngineState,
161 __in BOOTSTRAPPER_ACTION action
162 );
163HRESULT CoreElevate(
164 __in BURN_ENGINE_STATE* pEngineState,
165 __in_opt HWND hwndParent
166 );
167HRESULT CoreApply(
168 __in BURN_ENGINE_STATE* pEngineState,
169 __in_opt HWND hwndParent
170 );
171HRESULT CoreLaunchApprovedExe(
172 __in BURN_ENGINE_STATE* pEngineState,
173 __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe
174 );
175HRESULT CoreQuit(
176 __in BURN_ENGINE_STATE* pEngineState,
177 __in int nExitCode
178 );
179HRESULT CoreSaveEngineState(
180 __in BURN_ENGINE_STATE* pEngineState
181 );
182LPCWSTR CoreRelationTypeToCommandLineString(
183 __in BOOTSTRAPPER_RELATION_TYPE relationType
184 );
185HRESULT CoreRecreateCommandLine(
186 __deref_inout_z LPWSTR* psczCommandLine,
187 __in BOOTSTRAPPER_ACTION action,
188 __in BOOTSTRAPPER_DISPLAY display,
189 __in BOOTSTRAPPER_RESTART restart,
190 __in BOOTSTRAPPER_RELATION_TYPE relationType,
191 __in BOOL fPassthrough,
192 __in_z_opt LPCWSTR wzActiveParent,
193 __in_z_opt LPCWSTR wzAncestors,
194 __in_z_opt LPCWSTR wzAppendLogPath,
195 __in_z_opt LPCWSTR wzAdditionalCommandLineArguments
196 );
197HRESULT CoreAppendFileHandleAttachedToCommandLine(
198 __in HANDLE hFileWithAttachedContainer,
199 __out HANDLE* phExecutableFile,
200 __deref_inout_z LPWSTR* psczCommandLine
201 );
202HRESULT CoreAppendFileHandleSelfToCommandLine(
203 __in LPCWSTR wzExecutablePath,
204 __out HANDLE* phExecutableFile,
205 __deref_inout_z LPWSTR* psczCommandLine,
206 __deref_inout_z_opt LPWSTR* psczObfuscatedCommandLine
207 );
208
209#if defined(__cplusplus)
210}
211#endif
diff --git a/src/engine/dependency.cpp b/src/engine/dependency.cpp
new file mode 100644
index 00000000..c7c6e024
--- /dev/null
+++ b/src/engine/dependency.cpp
@@ -0,0 +1,1203 @@
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#include "precomp.h"
4
5// constants
6
7#define INITIAL_STRINGDICT_SIZE 48
8const LPCWSTR vcszIgnoreDependenciesDelim = L";";
9
10
11// internal function declarations
12
13static HRESULT SplitIgnoreDependencies(
14 __in_z LPCWSTR wzIgnoreDependencies,
15 __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies,
16 __inout LPUINT pcDependencies
17 );
18
19static HRESULT JoinIgnoreDependencies(
20 __out_z LPWSTR* psczIgnoreDependencies,
21 __in_ecount(cDependencies) const DEPENDENCY* rgDependencies,
22 __in UINT cDependencies
23 );
24
25static HRESULT GetIgnoredDependents(
26 __in const BURN_PACKAGE* pPackage,
27 __in const BURN_PLAN* pPlan,
28 __deref_inout STRINGDICT_HANDLE* psdIgnoredDependents
29 );
30
31static HRESULT GetProviderInformation(
32 __in HKEY hkRoot,
33 __in_z LPCWSTR wzProviderKey,
34 __deref_opt_out_z_opt LPWSTR* psczProviderKey,
35 __deref_opt_out_z_opt LPWSTR* psczId
36 );
37
38static void CalculateDependencyActionStates(
39 __in const BURN_PACKAGE* pPackage,
40 __in const BOOTSTRAPPER_ACTION action,
41 __out BURN_DEPENDENCY_ACTION* pDependencyExecuteAction,
42 __out BURN_DEPENDENCY_ACTION* pDependencyRollbackAction
43 );
44
45static HRESULT AddPackageDependencyActions(
46 __in_opt DWORD *pdwInsertSequence,
47 __in const BURN_PACKAGE* pPackage,
48 __in BURN_PLAN* pPlan,
49 __in const BURN_DEPENDENCY_ACTION dependencyExecuteAction,
50 __in const BURN_DEPENDENCY_ACTION dependencyRollbackAction
51 );
52
53static HRESULT RegisterPackageProvider(
54 __in const BURN_PACKAGE* pPackage
55 );
56
57static void UnregisterPackageProvider(
58 __in const BURN_PACKAGE* pPackage
59 );
60
61static HRESULT RegisterPackageDependency(
62 __in BOOL fPerMachine,
63 __in const BURN_PACKAGE* pPackage,
64 __in_z LPCWSTR wzDependentProviderKey
65 );
66
67static void UnregisterPackageDependency(
68 __in BOOL fPerMachine,
69 __in const BURN_PACKAGE* pPackage,
70 __in_z LPCWSTR wzDependentProviderKey
71 );
72
73static BOOL PackageProviderExists(
74 __in const BURN_PACKAGE* pPackage
75 );
76
77
78// functions
79
80extern "C" void DependencyUninitialize(
81 __in BURN_DEPENDENCY_PROVIDER* pProvider
82 )
83{
84 ReleaseStr(pProvider->sczKey);
85 ReleaseStr(pProvider->sczVersion);
86 ReleaseStr(pProvider->sczDisplayName);
87 memset(pProvider, 0, sizeof(BURN_DEPENDENCY_PROVIDER));
88}
89
90extern "C" HRESULT DependencyParseProvidersFromXml(
91 __in BURN_PACKAGE* pPackage,
92 __in IXMLDOMNode* pixnPackage
93 )
94{
95 HRESULT hr = S_OK;
96 IXMLDOMNodeList* pixnNodes = NULL;
97 DWORD cNodes = 0;
98 IXMLDOMNode* pixnNode = NULL;
99
100 // Select dependency provider nodes.
101 hr = XmlSelectNodes(pixnPackage, L"Provides", &pixnNodes);
102 ExitOnFailure(hr, "Failed to select dependency provider nodes.");
103
104 // Get dependency provider node count.
105 hr = pixnNodes->get_length((long*)&cNodes);
106 ExitOnFailure(hr, "Failed to get the dependency provider node count.");
107
108 if (!cNodes)
109 {
110 ExitFunction1(hr = S_OK);
111 }
112
113 // Allocate memory for dependency provider pointers.
114 pPackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER) * cNodes, TRUE);
115 ExitOnNull(pPackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate memory for dependency providers.");
116
117 pPackage->cDependencyProviders = cNodes;
118
119 // Parse dependency provider elements.
120 for (DWORD i = 0; i < cNodes; i++)
121 {
122 BURN_DEPENDENCY_PROVIDER* pDependencyProvider = &pPackage->rgDependencyProviders[i];
123
124 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
125 ExitOnFailure(hr, "Failed to get the next dependency provider node.");
126
127 // @Key
128 hr = XmlGetAttributeEx(pixnNode, L"Key", &pDependencyProvider->sczKey);
129 ExitOnFailure(hr, "Failed to get the Key attribute.");
130
131 // @Version
132 hr = XmlGetAttributeEx(pixnNode, L"Version", &pDependencyProvider->sczVersion);
133 if (E_NOTFOUND != hr)
134 {
135 ExitOnFailure(hr, "Failed to get the Version attribute.");
136 }
137
138 // @DisplayName
139 hr = XmlGetAttributeEx(pixnNode, L"DisplayName", &pDependencyProvider->sczDisplayName);
140 if (E_NOTFOUND != hr)
141 {
142 ExitOnFailure(hr, "Failed to get the DisplayName attribute.");
143 }
144
145 // @Imported
146 hr = XmlGetYesNoAttribute(pixnNode, L"Imported", &pDependencyProvider->fImported);
147 if (E_NOTFOUND != hr)
148 {
149 ExitOnFailure(hr, "Failed to get the Imported attribute.");
150 }
151 else
152 {
153 pDependencyProvider->fImported = FALSE;
154 hr = S_OK;
155 }
156
157 // Prepare next iteration.
158 ReleaseNullObject(pixnNode);
159 }
160
161 hr = S_OK;
162
163LExit:
164 ReleaseObject(pixnNode);
165 ReleaseObject(pixnNodes);
166
167 return hr;
168}
169
170extern "C" HRESULT DependencyDetectProviderKeyPackageId(
171 __in const BURN_PACKAGE* pPackage,
172 __deref_opt_out_z_opt LPWSTR* psczProviderKey,
173 __deref_opt_out_z_opt LPWSTR* psczId
174 )
175{
176 HRESULT hr = E_NOTFOUND;
177 LPWSTR wzProviderKey = NULL;
178 HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
179
180 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
181 {
182 const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
183
184 // Find the first package id registered for the provider key.
185 hr = GetProviderInformation(hkRoot, pProvider->sczKey, psczProviderKey, psczId);
186 if (E_NOTFOUND == hr)
187 {
188 continue;
189 }
190 ExitOnFailure(hr, "Failed to get the package provider information.");
191
192 ExitFunction();
193 }
194
195 // Older bundles may not have written the id so try the default.
196 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
197 {
198 wzProviderKey = pPackage->Msi.sczProductCode;
199 }
200
201 if (wzProviderKey)
202 {
203 hr = GetProviderInformation(hkRoot, wzProviderKey, psczProviderKey, psczId);
204 if (E_NOTFOUND == hr)
205 {
206 ExitFunction();
207 }
208 ExitOnFailure(hr, "Failed to get the package default provider information.");
209 }
210
211LExit:
212 return hr;
213}
214
215extern "C" HRESULT DependencyDetectProviderKeyBundleId(
216 __in BURN_REGISTRATION* pRegistration
217 )
218{
219 HRESULT hr = S_OK;
220
221 hr = DepGetProviderInformation(pRegistration->hkRoot, pRegistration->sczProviderKey, &pRegistration->sczDetectedProviderKeyBundleId, NULL, NULL);
222 if (E_NOTFOUND == hr)
223 {
224 ExitFunction();
225 }
226 ExitOnFailure(hr, "Failed to get provider key bundle id.");
227
228 // If a bundle id was not explicitly set, default the provider key bundle id to this bundle's provider key.
229 if (!pRegistration->sczDetectedProviderKeyBundleId || !*pRegistration->sczDetectedProviderKeyBundleId)
230 {
231 hr = StrAllocString(&pRegistration->sczDetectedProviderKeyBundleId, pRegistration->sczProviderKey, 0);
232 ExitOnFailure(hr, "Failed to initialize provider key bundle id.");
233 }
234
235LExit:
236 return hr;
237}
238
239extern "C" HRESULT DependencyPlanInitialize(
240 __in const BURN_ENGINE_STATE* pEngineState,
241 __in BURN_PLAN* pPlan
242 )
243{
244 HRESULT hr = S_OK;
245
246 // The current bundle provider key should always be ignored for dependency checks.
247 hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pEngineState->registration.sczProviderKey, NULL);
248 ExitOnFailure(hr, "Failed to add the bundle provider key to the list of dependencies to ignore.");
249
250 // Add the list of dependencies to ignore to the plan.
251 if (pEngineState->sczIgnoreDependencies)
252 {
253 // TODO: After adding enumeration to STRINGDICT, a single STRINGDICT_HANDLE can be used everywhere.
254 hr = SplitIgnoreDependencies(pEngineState->sczIgnoreDependencies, &pPlan->rgPlannedProviders, &pPlan->cPlannedProviders);
255 ExitOnFailure(hr, "Failed to split the list of dependencies to ignore.");
256 }
257
258LExit:
259 return hr;
260}
261
262extern "C" HRESULT DependencyAllocIgnoreDependencies(
263 __in const BURN_PLAN *pPlan,
264 __out_z LPWSTR* psczIgnoreDependencies
265 )
266{
267 HRESULT hr = S_OK;
268
269 // Join the list of dependencies to ignore for each related bundle.
270 if (0 < pPlan->cPlannedProviders)
271 {
272 hr = JoinIgnoreDependencies(psczIgnoreDependencies, pPlan->rgPlannedProviders, pPlan->cPlannedProviders);
273 ExitOnFailure(hr, "Failed to join the list of dependencies to ignore.");
274 }
275
276LExit:
277 return hr;
278}
279
280extern "C" HRESULT DependencyAddIgnoreDependencies(
281 __in STRINGDICT_HANDLE sdIgnoreDependencies,
282 __in_z LPCWSTR wzAddIgnoreDependencies
283 )
284{
285 HRESULT hr = S_OK;
286 LPWSTR wzContext = NULL;
287
288 // Parse through the semicolon-delimited tokens and add to the array.
289 for (LPCWSTR wzToken = ::wcstok_s(const_cast<LPWSTR>(wzAddIgnoreDependencies), vcszIgnoreDependenciesDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, vcszIgnoreDependenciesDelim, &wzContext))
290 {
291 hr = DictKeyExists(sdIgnoreDependencies, wzToken);
292 if (E_NOTFOUND != hr)
293 {
294 ExitOnFailure(hr, "Failed to check the dictionary of unique dependencies.");
295 }
296 else
297 {
298 hr = DictAddKey(sdIgnoreDependencies, wzToken);
299 ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzToken);
300 }
301 }
302
303LExit:
304 return hr;
305}
306
307extern "C" BOOL DependencyDependentExists(
308 __in const BURN_REGISTRATION* pRegistration,
309 __in_z LPCWSTR wzDependentProviderKey
310 )
311{
312 HRESULT hr = S_OK;
313
314 hr = DepDependentExists(pRegistration->hkRoot, pRegistration->sczProviderKey, wzDependentProviderKey);
315 return SUCCEEDED(hr);
316}
317
318extern "C" HRESULT DependencyPlanPackageBegin(
319 __in BOOL fPerMachine,
320 __in BURN_PACKAGE* pPackage,
321 __in BURN_PLAN* pPlan
322 )
323{
324 HRESULT hr = S_OK;
325 STRINGDICT_HANDLE sdIgnoredDependents = NULL;
326 DEPENDENCY* rgDependents = NULL;
327 UINT cDependents = 0;
328 HKEY hkHive = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
329 BURN_DEPENDENCY_ACTION dependencyExecuteAction = BURN_DEPENDENCY_ACTION_NONE;
330 BURN_DEPENDENCY_ACTION dependencyRollbackAction = BURN_DEPENDENCY_ACTION_NONE;
331
332 pPackage->dependencyExecute = BURN_DEPENDENCY_ACTION_NONE;
333 pPackage->dependencyRollback = BURN_DEPENDENCY_ACTION_NONE;
334
335 // Make sure the package defines at least one provider.
336 if (0 == pPackage->cDependencyProviders)
337 {
338 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_SKIP_NOPROVIDERS, pPackage->sczId);
339 ExitFunction1(hr = S_OK);
340 }
341
342 // Make sure the package is in the same scope as the bundle.
343 if (fPerMachine != pPackage->fPerMachine)
344 {
345 LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE, pPackage->sczId, LoggingPerMachineToString(fPerMachine), LoggingPerMachineToString(pPackage->fPerMachine));
346 ExitFunction1(hr = S_OK);
347 }
348
349 // If we're uninstalling the package, check if any dependents are registered.
350 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute)
351 {
352 // Build up a list of dependents to ignore, including the current bundle.
353 hr = GetIgnoredDependents(pPackage, pPlan, &sdIgnoredDependents);
354 ExitOnFailure(hr, "Failed to build the list of ignored dependents.");
355
356 // Skip the dependency check if "ALL" was authored for IGNOREDEPENDENCIES.
357 hr = DictKeyExists(sdIgnoredDependents, L"ALL");
358 if (E_NOTFOUND != hr)
359 {
360 ExitOnFailure(hr, "Failed to check if \"ALL\" was set in IGNOREDEPENDENCIES.");
361 }
362 else
363 {
364 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
365 {
366 const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
367
368 hr = DepCheckDependents(hkHive, pProvider->sczKey, 0, sdIgnoredDependents, &rgDependents, &cDependents);
369 if (E_FILENOTFOUND != hr)
370 {
371 ExitOnFailure(hr, "Failed dependents check on package provider: %ls", pProvider->sczKey);
372 }
373 else
374 {
375 hr = S_OK;
376 }
377 }
378 }
379 }
380
381 // Calculate the dependency actions before the package itself is planned.
382 CalculateDependencyActionStates(pPackage, pPlan->action, &dependencyExecuteAction, &dependencyRollbackAction);
383
384 // If dependents were found, change the action to not uninstall the package.
385 if (0 < cDependents)
386 {
387 LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS, pPackage->sczId, cDependents);
388
389 for (DWORD i = 0; i < cDependents; ++i)
390 {
391 const DEPENDENCY* pDependency = &rgDependents[i];
392 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_DEPENDENT, pDependency->sczKey, LoggingStringOrUnknownIfNull(pDependency->sczName));
393 }
394
395 pPackage->fDependencyManagerWasHere = TRUE;
396 pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE;
397 pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
398 }
399 // Use the calculated dependency actions as the provider actions if there
400 // are any non-imported providers that need to be registered and the package
401 // is current (not obsolete).
402 else if (BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE != pPackage->currentState)
403 {
404 BOOL fAllImportedProviders = TRUE; // assume all providers were imported.
405 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
406 {
407 const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
408 if (!pProvider->fImported)
409 {
410 fAllImportedProviders = FALSE;
411 break;
412 }
413 }
414
415 if (!fAllImportedProviders)
416 {
417 pPackage->providerExecute = dependencyExecuteAction;
418 pPackage->providerRollback = dependencyRollbackAction;
419 }
420 }
421
422 // If the package will be removed, add its providers to the growing list in the plan.
423 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute)
424 {
425 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
426 {
427 const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
428
429 hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pProvider->sczKey, NULL);
430 ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the planned list.", pProvider->sczKey);
431 }
432 }
433
434 pPackage->dependencyExecute = dependencyExecuteAction;
435 pPackage->dependencyRollback = dependencyRollbackAction;
436
437LExit:
438 ReleaseDependencyArray(rgDependents, cDependents);
439 ReleaseDict(sdIgnoredDependents);
440
441 return hr;
442}
443
444extern "C" HRESULT DependencyPlanPackage(
445 __in_opt DWORD *pdwInsertSequence,
446 __in const BURN_PACKAGE* pPackage,
447 __in BURN_PLAN* pPlan
448 )
449{
450 HRESULT hr = S_OK;
451 BURN_EXECUTE_ACTION* pAction = NULL;
452
453 // If the dependency execution action is to unregister, add the dependency actions to the plan
454 // *before* the provider key is potentially removed.
455 if (BURN_DEPENDENCY_ACTION_UNREGISTER == pPackage->dependencyExecute)
456 {
457 hr = AddPackageDependencyActions(pdwInsertSequence, pPackage, pPlan, pPackage->dependencyExecute, pPackage->dependencyRollback);
458 ExitOnFailure(hr, "Failed to plan the dependency actions for package: %ls", pPackage->sczId);
459 }
460
461 // Add the provider rollback plan.
462 if (BURN_DEPENDENCY_ACTION_NONE != pPackage->providerRollback)
463 {
464 hr = PlanAppendRollbackAction(pPlan, &pAction);
465 ExitOnFailure(hr, "Failed to append provider rollback action.");
466
467 pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER;
468 pAction->packageProvider.pPackage = const_cast<BURN_PACKAGE*>(pPackage);
469 pAction->packageProvider.action = pPackage->providerRollback;
470
471 // Put a checkpoint before the execute action so that rollback happens
472 // if execute fails.
473 hr = PlanExecuteCheckpoint(pPlan);
474 ExitOnFailure(hr, "Failed to plan provider checkpoint action.");
475 }
476
477 // Add the provider execute plan. This comes after rollback so if something goes wrong
478 // rollback will try to clean up after us.
479 if (BURN_DEPENDENCY_ACTION_NONE != pPackage->providerExecute)
480 {
481 if (NULL != pdwInsertSequence)
482 {
483 hr = PlanInsertExecuteAction(*pdwInsertSequence, pPlan, &pAction);
484 ExitOnFailure(hr, "Failed to insert provider execute action.");
485
486 // Always move the sequence after this dependency action so the provider registration
487 // stays in front of the inserted actions.
488 ++(*pdwInsertSequence);
489 }
490 else
491 {
492 hr = PlanAppendExecuteAction(pPlan, &pAction);
493 ExitOnFailure(hr, "Failed to append provider execute action.");
494 }
495
496 pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER;
497 pAction->packageProvider.pPackage = const_cast<BURN_PACKAGE*>(pPackage);
498 pAction->packageProvider.action = pPackage->providerExecute;
499 }
500
501LExit:
502 return hr;
503}
504
505extern "C" HRESULT DependencyPlanPackageComplete(
506 __in BURN_PACKAGE* pPackage,
507 __in BURN_PLAN* pPlan
508 )
509{
510 HRESULT hr = S_OK;
511
512 // Registration of dependencies happens here, after the package is planned to be
513 // installed and all that good stuff.
514 if (BURN_DEPENDENCY_ACTION_REGISTER == pPackage->dependencyExecute)
515 {
516 // Recalculate the dependency actions in case other operations may have changed
517 // the package execution state.
518 CalculateDependencyActionStates(pPackage, pPlan->action, &pPackage->dependencyExecute, &pPackage->dependencyRollback);
519
520 // If the dependency execution action is *still* to register, add the dependency actions to the plan.
521 if (BURN_DEPENDENCY_ACTION_REGISTER == pPackage->dependencyExecute)
522 {
523 hr = AddPackageDependencyActions(NULL, pPackage, pPlan, pPackage->dependencyExecute, pPackage->dependencyRollback);
524 ExitOnFailure(hr, "Failed to plan the dependency actions for package: %ls", pPackage->sczId);
525 }
526 }
527
528LExit:
529 return hr;
530}
531
532extern "C" HRESULT DependencyExecutePackageProviderAction(
533 __in const BURN_EXECUTE_ACTION* pAction
534 )
535{
536 AssertSz(BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER == pAction->type, "Execute action type not supported by this function.");
537
538 HRESULT hr = S_OK;
539 const BURN_PACKAGE* pPackage = pAction->packageProvider.pPackage;
540
541 // Register or unregister the package provider(s).
542 if (BURN_DEPENDENCY_ACTION_REGISTER == pAction->packageProvider.action)
543 {
544 hr = RegisterPackageProvider(pPackage);
545 ExitOnFailure(hr, "Failed to register the package providers.");
546 }
547 else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pAction->packageProvider.action)
548 {
549 UnregisterPackageProvider(pPackage);
550 }
551
552LExit:
553 if (!pPackage->fVital)
554 {
555 hr = S_OK;
556 }
557
558 return hr;
559}
560
561extern "C" HRESULT DependencyExecutePackageDependencyAction(
562 __in BOOL fPerMachine,
563 __in const BURN_EXECUTE_ACTION* pAction
564 )
565{
566 AssertSz(BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY == pAction->type, "Execute action type not supported by this function.");
567
568 HRESULT hr = S_OK;
569 const BURN_PACKAGE* pPackage = pAction->packageDependency.pPackage;
570
571 // Register or unregister the bundle as a dependent of each package dependency provider.
572 if (BURN_DEPENDENCY_ACTION_REGISTER == pAction->packageDependency.action)
573 {
574 hr = RegisterPackageDependency(fPerMachine, pPackage, pAction->packageDependency.sczBundleProviderKey);
575 ExitOnFailure(hr, "Failed to register the dependency on the package provider.");
576 }
577 else if (BURN_DEPENDENCY_ACTION_UNREGISTER == pAction->packageDependency.action)
578 {
579 UnregisterPackageDependency(fPerMachine, pPackage, pAction->packageDependency.sczBundleProviderKey);
580 }
581
582LExit:
583 if (!pPackage->fVital)
584 {
585 hr = S_OK;
586 }
587
588 return hr;
589}
590
591extern "C" HRESULT DependencyRegisterBundle(
592 __in const BURN_REGISTRATION* pRegistration
593 )
594{
595 HRESULT hr = S_OK;
596 LPWSTR sczVersion = NULL;
597
598 hr = FileVersionToStringEx(pRegistration->qwVersion, &sczVersion);
599 ExitOnFailure(hr, "Failed to format the registration version string.");
600
601 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_REGISTER, pRegistration->sczProviderKey, sczVersion);
602
603 // Register the bundle provider key.
604 hr = DepRegisterDependency(pRegistration->hkRoot, pRegistration->sczProviderKey, sczVersion, pRegistration->sczDisplayName, pRegistration->sczId, 0);
605 ExitOnFailure(hr, "Failed to register the bundle dependency provider.");
606
607LExit:
608 ReleaseStr(sczVersion);
609
610 return hr;
611}
612
613extern "C" HRESULT DependencyProcessDependentRegistration(
614 __in const BURN_REGISTRATION* pRegistration,
615 __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction
616 )
617{
618 HRESULT hr = S_OK;
619
620 switch (pAction->type)
621 {
622 case BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER:
623 hr = DepRegisterDependent(pRegistration->hkRoot, pRegistration->sczProviderKey, pAction->sczDependentProviderKey, NULL, NULL, 0);
624 ExitOnFailure(hr, "Failed to register dependent: %ls", pAction->sczDependentProviderKey);
625 break;
626
627 case BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER:
628 hr = DepUnregisterDependent(pRegistration->hkRoot, pRegistration->sczProviderKey, pAction->sczDependentProviderKey);
629 ExitOnFailure(hr, "Failed to unregister dependent: %ls", pAction->sczDependentProviderKey);
630 break;
631
632 default:
633 hr = E_INVALIDARG;
634 ExitOnRootFailure(hr, "Unrecognized registration action type: %d", pAction->type);
635 }
636
637LExit:
638 return hr;
639}
640
641extern "C" void DependencyUnregisterBundle(
642 __in const BURN_REGISTRATION* pRegistration
643 )
644{
645 HRESULT hr = S_OK;
646
647 // Remove the bundle provider key.
648 hr = DepUnregisterDependency(pRegistration->hkRoot, pRegistration->sczProviderKey);
649 if (SUCCEEDED(hr))
650 {
651 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_UNREGISTERED, pRegistration->sczProviderKey);
652 }
653 else if (FAILED(hr) && E_FILENOTFOUND != hr)
654 {
655 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_UNREGISTERED_FAILED, pRegistration->sczProviderKey, hr);
656 }
657}
658
659// internal functions
660
661/********************************************************************
662 SplitIgnoreDependencies - Splits a semicolon-delimited
663 string into a list of unique dependencies to ignore.
664
665*********************************************************************/
666static HRESULT SplitIgnoreDependencies(
667 __in_z LPCWSTR wzIgnoreDependencies,
668 __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies,
669 __inout LPUINT pcDependencies
670 )
671{
672 HRESULT hr = S_OK;
673 LPWSTR wzContext = NULL;
674 STRINGDICT_HANDLE sdIgnoreDependencies = NULL;
675
676 // Create a dictionary to hold unique dependencies.
677 hr = DictCreateStringList(&sdIgnoreDependencies, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE);
678 ExitOnFailure(hr, "Failed to create the string dictionary.");
679
680 // Parse through the semicolon-delimited tokens and add to the array.
681 for (LPCWSTR wzToken = ::wcstok_s(const_cast<LPWSTR>(wzIgnoreDependencies), vcszIgnoreDependenciesDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, vcszIgnoreDependenciesDelim, &wzContext))
682 {
683 hr = DictKeyExists(sdIgnoreDependencies, wzToken);
684 if (E_NOTFOUND != hr)
685 {
686 ExitOnFailure(hr, "Failed to check the dictionary of unique dependencies.");
687 }
688 else
689 {
690 hr = DepDependencyArrayAlloc(prgDependencies, pcDependencies, wzToken, NULL);
691 ExitOnFailure(hr, "Failed to add \"%ls\" to the list of dependencies to ignore.", wzToken);
692
693 hr = DictAddKey(sdIgnoreDependencies, wzToken);
694 ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", wzToken);
695 }
696 }
697
698LExit:
699 ReleaseDict(sdIgnoreDependencies);
700
701 return hr;
702}
703
704/********************************************************************
705 JoinIgnoreDependencies - Joins a list of dependencies
706 to ignore into a semicolon-delimited string of unique values.
707
708*********************************************************************/
709static HRESULT JoinIgnoreDependencies(
710 __out_z LPWSTR* psczIgnoreDependencies,
711 __in_ecount(cDependencies) const DEPENDENCY* rgDependencies,
712 __in UINT cDependencies
713 )
714{
715 HRESULT hr = S_OK;
716 STRINGDICT_HANDLE sdIgnoreDependencies = NULL;
717
718 // Make sure we pass back an empty string if there are no dependencies.
719 if (0 == cDependencies)
720 {
721 ExitFunction1(hr = S_OK);
722 }
723
724 // Create a dictionary to hold unique dependencies.
725 hr = DictCreateStringList(&sdIgnoreDependencies, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE);
726 ExitOnFailure(hr, "Failed to create the string dictionary.");
727
728 for (UINT i = 0; i < cDependencies; ++i)
729 {
730 const DEPENDENCY* pDependency = &rgDependencies[i];
731
732 hr = DictKeyExists(sdIgnoreDependencies, pDependency->sczKey);
733 if (E_NOTFOUND != hr)
734 {
735 ExitOnFailure(hr, "Failed to check the dictionary of unique dependencies.");
736 }
737 else
738 {
739 if (0 < i)
740 {
741 hr = StrAllocConcat(psczIgnoreDependencies, vcszIgnoreDependenciesDelim, 1);
742 ExitOnFailure(hr, "Failed to append the string delimiter.");
743 }
744
745 hr = StrAllocConcat(psczIgnoreDependencies, pDependency->sczKey, 0);
746 ExitOnFailure(hr, "Failed to append the key \"%ls\".", pDependency->sczKey);
747
748 hr = DictAddKey(sdIgnoreDependencies, pDependency->sczKey);
749 ExitOnFailure(hr, "Failed to add \"%ls\" to the string dictionary.", pDependency->sczKey);
750 }
751 }
752
753LExit:
754 ReleaseDict(sdIgnoreDependencies);
755
756 return hr;
757}
758
759/********************************************************************
760 GetIgnoredDependents - Combines the current bundle's
761 provider key, packages' provider keys that are being uninstalled,
762 and any ignored dependencies authored for packages into a string
763 list to pass to deputil.
764
765*********************************************************************/
766static HRESULT GetIgnoredDependents(
767 __in const BURN_PACKAGE* pPackage,
768 __in const BURN_PLAN* pPlan,
769 __deref_inout STRINGDICT_HANDLE* psdIgnoredDependents
770 )
771{
772 HRESULT hr = S_OK;
773 LPWSTR sczIgnoreDependencies = NULL;
774
775 // Create the dictionary and add the bundle provider key initially.
776 hr = DictCreateStringList(psdIgnoredDependents, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE);
777 ExitOnFailure(hr, "Failed to create the string dictionary.");
778
779 hr = DictAddKey(*psdIgnoredDependents, pPlan->wzBundleProviderKey);
780 ExitOnFailure(hr, "Failed to add the bundle provider key \"%ls\" to the list of ignored dependencies.", pPlan->wzBundleProviderKey);
781
782 // Add previously planned package providers to the dictionary.
783 for (DWORD i = 0; i < pPlan->cPlannedProviders; ++i)
784 {
785 const DEPENDENCY* pDependency = &pPlan->rgPlannedProviders[i];
786
787 hr = DictAddKey(*psdIgnoredDependents, pDependency->sczKey);
788 ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the list of ignored dependencies.", pDependency->sczKey);
789 }
790
791 // Get the IGNOREDEPENDENCIES property if defined.
792 hr = PackageGetProperty(pPackage, DEPENDENCY_IGNOREDEPENDENCIES, &sczIgnoreDependencies);
793 if (E_NOTFOUND != hr)
794 {
795 ExitOnFailure(hr, "Failed to get the package property: %ls", DEPENDENCY_IGNOREDEPENDENCIES);
796
797 hr = DependencyAddIgnoreDependencies(*psdIgnoredDependents, sczIgnoreDependencies);
798 ExitOnFailure(hr, "Failed to add the authored ignored dependencies to the cumulative list of ignored dependencies.");
799 }
800 else
801 {
802 hr = S_OK;
803 }
804
805LExit:
806 ReleaseStr(sczIgnoreDependencies);
807
808 return hr;
809}
810
811/********************************************************************
812 GetProviderId - Gets the ID of the package given the provider key.
813
814*********************************************************************/
815static HRESULT GetProviderInformation(
816 __in HKEY hkRoot,
817 __in_z LPCWSTR wzProviderKey,
818 __deref_opt_out_z_opt LPWSTR* psczProviderKey,
819 __deref_opt_out_z_opt LPWSTR* psczId
820 )
821{
822 HRESULT hr = S_OK;
823 LPWSTR sczId = NULL;
824
825 hr = DepGetProviderInformation(hkRoot, wzProviderKey, &sczId, NULL, NULL);
826 if (E_NOTFOUND == hr)
827 {
828 ExitFunction();
829 }
830 ExitOnFailure(hr, "Failed to get the provider key package id.");
831
832 // If the id was registered return it and exit.
833 if (sczId && *sczId)
834 {
835 if (psczProviderKey)
836 {
837 hr = StrAllocString(psczProviderKey, wzProviderKey, 0);
838 ExitOnFailure(hr, "Failed to copy the provider key.");
839 }
840
841 if (psczId)
842 {
843 *psczId = sczId;
844 sczId = NULL;
845 }
846
847 ExitFunction();
848 }
849 else
850 {
851 hr = E_NOTFOUND;
852 }
853
854LExit:
855 ReleaseStr(sczId);
856
857 return hr;
858}
859
860/********************************************************************
861 CalculateDependencyActionStates - Calculates the dependency execute and
862 rollback actions for a package.
863
864*********************************************************************/
865static void CalculateDependencyActionStates(
866 __in const BURN_PACKAGE* pPackage,
867 __in const BOOTSTRAPPER_ACTION action,
868 __out BURN_DEPENDENCY_ACTION* pDependencyExecuteAction,
869 __out BURN_DEPENDENCY_ACTION* pDependencyRollbackAction
870 )
871{
872 switch (action)
873 {
874 case BOOTSTRAPPER_ACTION_UNINSTALL:
875 // Always remove the dependency when uninstalling a bundle even if the package is absent.
876 *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_UNREGISTER;
877 break;
878 case BOOTSTRAPPER_ACTION_INSTALL: __fallthrough;
879 case BOOTSTRAPPER_ACTION_CACHE:
880 // Always remove the dependency during rollback when installing a bundle.
881 *pDependencyRollbackAction = BURN_DEPENDENCY_ACTION_UNREGISTER;
882 __fallthrough;
883 case BOOTSTRAPPER_ACTION_MODIFY: __fallthrough;
884 case BOOTSTRAPPER_ACTION_REPAIR:
885 switch (pPackage->execute)
886 {
887 case BOOTSTRAPPER_ACTION_STATE_NONE:
888 switch (pPackage->requested)
889 {
890 case BOOTSTRAPPER_REQUEST_STATE_NONE:
891 // Register if a newer, compatible package is already installed.
892 switch (pPackage->currentState)
893 {
894 case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE:
895 if (!PackageProviderExists(pPackage))
896 {
897 break;
898 }
899 __fallthrough;
900 case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
901 *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_REGISTER;
902 break;
903 }
904 break;
905 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
906 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
907 // Register if the package is requested but already installed.
908 switch (pPackage->currentState)
909 {
910 case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE:
911 if (!PackageProviderExists(pPackage))
912 {
913 break;
914 }
915 __fallthrough;
916 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough;
917 case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
918 *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_REGISTER;
919 break;
920 }
921 break;
922 }
923 break;
924 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
925 *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_UNREGISTER;
926 break;
927 case BOOTSTRAPPER_ACTION_STATE_INSTALL: __fallthrough;
928 case BOOTSTRAPPER_ACTION_STATE_MODIFY: __fallthrough;
929 case BOOTSTRAPPER_ACTION_STATE_REPAIR: __fallthrough;
930 case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE: __fallthrough;
931 case BOOTSTRAPPER_ACTION_STATE_MAJOR_UPGRADE: __fallthrough;
932 case BOOTSTRAPPER_ACTION_STATE_PATCH:
933 *pDependencyExecuteAction = BURN_DEPENDENCY_ACTION_REGISTER;
934 break;
935 }
936 break;
937 }
938
939 switch (*pDependencyExecuteAction)
940 {
941 case BURN_DEPENDENCY_ACTION_REGISTER:
942 switch (pPackage->currentState)
943 {
944 case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough;
945 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough;
946 case BOOTSTRAPPER_PACKAGE_STATE_CACHED:
947 *pDependencyRollbackAction = BURN_DEPENDENCY_ACTION_UNREGISTER;
948 break;
949 }
950 break;
951 case BURN_DEPENDENCY_ACTION_UNREGISTER:
952 switch (pPackage->currentState)
953 {
954 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough;
955 case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
956 *pDependencyRollbackAction = BURN_DEPENDENCY_ACTION_REGISTER;
957 break;
958 }
959 break;
960 }
961}
962
963/********************************************************************
964 AddPackageDependencyActions - Adds the dependency execute and rollback
965 actions to the plan.
966
967*********************************************************************/
968static HRESULT AddPackageDependencyActions(
969 __in_opt DWORD *pdwInsertSequence,
970 __in const BURN_PACKAGE* pPackage,
971 __in BURN_PLAN* pPlan,
972 __in const BURN_DEPENDENCY_ACTION dependencyExecuteAction,
973 __in const BURN_DEPENDENCY_ACTION dependencyRollbackAction
974 )
975{
976 HRESULT hr = S_OK;
977 BURN_EXECUTE_ACTION* pAction = NULL;
978
979 // Add the rollback plan.
980 if (BURN_DEPENDENCY_ACTION_NONE != dependencyRollbackAction)
981 {
982 hr = PlanAppendRollbackAction(pPlan, &pAction);
983 ExitOnFailure(hr, "Failed to append rollback action.");
984
985 pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY;
986 pAction->packageDependency.pPackage = const_cast<BURN_PACKAGE*>(pPackage);
987 pAction->packageDependency.action = dependencyRollbackAction;
988
989 hr = StrAllocString(&pAction->packageDependency.sczBundleProviderKey, pPlan->wzBundleProviderKey, 0);
990 ExitOnFailure(hr, "Failed to copy the bundle dependency provider.");
991
992 // Put a checkpoint before the execute action so that rollback happens
993 // if execute fails.
994 hr = PlanExecuteCheckpoint(pPlan);
995 ExitOnFailure(hr, "Failed to plan dependency checkpoint action.");
996 }
997
998 // Add the execute plan. This comes after rollback so if something goes wrong
999 // rollback will try to clean up after us correctly.
1000 if (BURN_DEPENDENCY_ACTION_NONE != dependencyExecuteAction)
1001 {
1002 if (NULL != pdwInsertSequence)
1003 {
1004 hr = PlanInsertExecuteAction(*pdwInsertSequence, pPlan, &pAction);
1005 ExitOnFailure(hr, "Failed to insert execute action.");
1006
1007 // Always move the sequence after this dependency action so the dependency registration
1008 // stays in front of the inserted actions.
1009 ++(*pdwInsertSequence);
1010 }
1011 else
1012 {
1013 hr = PlanAppendExecuteAction(pPlan, &pAction);
1014 ExitOnFailure(hr, "Failed to append execute action.");
1015 }
1016
1017 pAction->type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY;
1018 pAction->packageDependency.pPackage = const_cast<BURN_PACKAGE*>(pPackage);
1019 pAction->packageDependency.action = dependencyExecuteAction;
1020
1021 hr = StrAllocString(&pAction->packageDependency.sczBundleProviderKey, pPlan->wzBundleProviderKey, 0);
1022 ExitOnFailure(hr, "Failed to copy the bundle dependency provider.");
1023 }
1024
1025LExit:
1026 return hr;
1027}
1028
1029static HRESULT RegisterPackageProvider(
1030 __in const BURN_PACKAGE* pPackage
1031 )
1032{
1033 HRESULT hr = S_OK;
1034 LPWSTR wzId = NULL;
1035 HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1036
1037 if (pPackage->rgDependencyProviders)
1038 {
1039 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
1040 {
1041 wzId = pPackage->Msi.sczProductCode;
1042 }
1043 else if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
1044 {
1045 wzId = pPackage->Msp.sczPatchCode;
1046 }
1047
1048 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
1049 {
1050 const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
1051
1052 if (!pProvider->fImported)
1053 {
1054 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_REGISTER, pProvider->sczKey, pProvider->sczVersion, pPackage->sczId);
1055
1056 hr = DepRegisterDependency(hkRoot, pProvider->sczKey, pProvider->sczVersion, pProvider->sczDisplayName, wzId, 0);
1057 ExitOnFailure(hr, "Failed to register the package dependency provider: %ls", pProvider->sczKey);
1058 }
1059 }
1060 }
1061
1062LExit:
1063 if (!pPackage->fVital)
1064 {
1065 hr = S_OK;
1066 }
1067
1068 return hr;
1069}
1070
1071/********************************************************************
1072 UnregisterPackageProvider - Removes each dependency provider
1073 for the package (if not imported from the package itself).
1074
1075 Note: Does not check for existing dependents before removing the key.
1076*********************************************************************/
1077static void UnregisterPackageProvider(
1078 __in const BURN_PACKAGE* pPackage
1079 )
1080{
1081 HRESULT hr = S_OK;
1082 HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1083
1084 if (pPackage->rgDependencyProviders)
1085 {
1086 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
1087 {
1088 const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
1089
1090 if (!pProvider->fImported)
1091 {
1092 hr = DepUnregisterDependency(hkRoot, pProvider->sczKey);
1093 if (SUCCEEDED(hr))
1094 {
1095 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED, pProvider->sczKey, pPackage->sczId);
1096 }
1097 else if (FAILED(hr) && E_FILENOTFOUND != hr)
1098 {
1099 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED_FAILED, pProvider->sczKey, pPackage->sczId, hr);
1100 }
1101 }
1102 }
1103 }
1104}
1105
1106/********************************************************************
1107 RegisterPackageDependency - Registers the provider key
1108 as a dependent of a package.
1109
1110*********************************************************************/
1111static HRESULT RegisterPackageDependency(
1112 __in BOOL fPerMachine,
1113 __in const BURN_PACKAGE* pPackage,
1114 __in_z LPCWSTR wzDependentProviderKey
1115 )
1116{
1117 HRESULT hr = S_OK;
1118 HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1119
1120 // Do not register a dependency on a package in a different install context.
1121 if (fPerMachine != pPackage->fPerMachine)
1122 {
1123 LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE, pPackage->sczId, LoggingPerMachineToString(fPerMachine), LoggingPerMachineToString(pPackage->fPerMachine));
1124 ExitFunction1(hr = S_OK);
1125 }
1126
1127 if (pPackage->rgDependencyProviders)
1128 {
1129 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
1130 {
1131 const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
1132
1133 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_REGISTER_DEPENDENCY, wzDependentProviderKey, pProvider->sczKey, pPackage->sczId);
1134
1135 hr = DepRegisterDependent(hkRoot, pProvider->sczKey, wzDependentProviderKey, NULL, NULL, 0);
1136 if (E_FILENOTFOUND != hr || pPackage->fVital)
1137 {
1138 ExitOnFailure(hr, "Failed to register the dependency on package dependency provider: %ls", pProvider->sczKey);
1139 }
1140 else
1141 {
1142 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_SKIP_MISSING, pProvider->sczKey, pPackage->sczId);
1143 hr = S_OK;
1144 }
1145 }
1146 }
1147
1148LExit:
1149 return hr;
1150}
1151
1152/********************************************************************
1153 UnregisterPackageDependency - Unregisters the provider key
1154 as a dependent of a package.
1155
1156*********************************************************************/
1157static void UnregisterPackageDependency(
1158 __in BOOL fPerMachine,
1159 __in const BURN_PACKAGE* pPackage,
1160 __in_z LPCWSTR wzDependentProviderKey
1161 )
1162{
1163 HRESULT hr = S_OK;
1164 HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1165
1166 // Should be no registration to remove since we don't write keys across contexts.
1167 if (fPerMachine != pPackage->fPerMachine)
1168 {
1169 LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE, pPackage->sczId, LoggingPerMachineToString(fPerMachine), LoggingPerMachineToString(pPackage->fPerMachine));
1170 return;
1171 }
1172
1173 // Loop through each package provider and remove the bundle dependency key.
1174 if (pPackage->rgDependencyProviders)
1175 {
1176 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
1177 {
1178 const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
1179
1180 hr = DepUnregisterDependent(hkRoot, pProvider->sczKey, wzDependentProviderKey);
1181 if (SUCCEEDED(hr))
1182 {
1183 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY, wzDependentProviderKey, pProvider->sczKey, pPackage->sczId);
1184 }
1185 else if (FAILED(hr) && E_FILENOTFOUND != hr)
1186 {
1187 LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY_FAILED, wzDependentProviderKey, pProvider->sczKey, pPackage->sczId, hr);
1188 }
1189 }
1190 }
1191}
1192
1193/********************************************************************
1194 PackageProviderExists - Checks if a package provider is registered.
1195
1196*********************************************************************/
1197static BOOL PackageProviderExists(
1198 __in const BURN_PACKAGE* pPackage
1199 )
1200{
1201 HRESULT hr = DependencyDetectProviderKeyPackageId(pPackage, NULL, NULL);
1202 return SUCCEEDED(hr);
1203}
diff --git a/src/engine/dependency.h b/src/engine/dependency.h
new file mode 100644
index 00000000..905857e0
--- /dev/null
+++ b/src/engine/dependency.h
@@ -0,0 +1,176 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9// constants
10
11const LPCWSTR DEPENDENCY_IGNOREDEPENDENCIES = L"IGNOREDEPENDENCIES";
12
13
14// function declarations
15
16/********************************************************************
17 DependencyUninitialize - Frees and zeros memory allocated in the
18 dependency.
19
20*********************************************************************/
21void DependencyUninitialize(
22 __in BURN_DEPENDENCY_PROVIDER* pProvider
23 );
24
25/********************************************************************
26 DependencyParseProvidersFromXml - Parses dependency information
27 from the manifest for the specified package.
28
29*********************************************************************/
30HRESULT DependencyParseProvidersFromXml(
31 __in BURN_PACKAGE* pPackage,
32 __in IXMLDOMNode* pixnPackage
33 );
34
35/********************************************************************
36 DependencyDetectProviderKeyPackageId - Detect if the provider key is
37 registered and if so what package code is registered.
38
39 Note: Returns E_NOTFOUND if the provider key is not registered.
40*********************************************************************/
41HRESULT DependencyDetectProviderKeyPackageId(
42 __in const BURN_PACKAGE* pPackage,
43 __deref_opt_out_z_opt LPWSTR* psczProviderKey,
44 __deref_opt_out_z_opt LPWSTR* psczId
45 );
46
47/********************************************************************
48 DependencyDetectProviderKeyBundleId - Detect if the provider key is
49 registered and if so what bundle is registered.
50
51 Note: Returns E_NOTFOUND if the provider key is not registered.
52*********************************************************************/
53HRESULT DependencyDetectProviderKeyBundleId(
54 __in BURN_REGISTRATION* pRegistration
55 );
56
57/********************************************************************
58 DependencyPlanInitialize - Initializes the plan.
59
60*********************************************************************/
61HRESULT DependencyPlanInitialize(
62 __in const BURN_ENGINE_STATE* pEngineState,
63 __in BURN_PLAN* pPlan
64 );
65
66/********************************************************************
67 DependencyAllocIgnoreDependencies - Allocates the dependencies to
68 ignore as a semicolon-delimited string.
69
70*********************************************************************/
71HRESULT DependencyAllocIgnoreDependencies(
72 __in const BURN_PLAN *pPlan,
73 __out_z LPWSTR* psczIgnoreDependencies
74 );
75
76/********************************************************************
77 DependencyAddIgnoreDependencies - Populates the ignore dependency
78 names.
79
80*********************************************************************/
81HRESULT DependencyAddIgnoreDependencies(
82 __in STRINGDICT_HANDLE sdIgnoreDependencies,
83 __in_z LPCWSTR wzAddIgnoreDependencies
84 );
85
86/********************************************************************
87 DependencyDependentExists - Checks to see if the provider key is
88 already dependent on this bundle.
89
90*********************************************************************/
91BOOL DependencyDependentExists(
92 __in const BURN_REGISTRATION* pRegistration,
93 __in_z LPCWSTR wzDependentProviderKey
94 );
95
96/********************************************************************
97 DependencyPlanPackageBegin - Updates the dependency registration
98 action depending on the calculated state for the package.
99
100*********************************************************************/
101HRESULT DependencyPlanPackageBegin(
102 __in BOOL fPerMachine,
103 __in BURN_PACKAGE* pPackage,
104 __in BURN_PLAN* pPlan
105 );
106
107/********************************************************************
108 DependencyPlanPackage - adds dependency related actions to the plan
109 for this package.
110
111*********************************************************************/
112HRESULT DependencyPlanPackage(
113 __in_opt DWORD *pdwInsertSequence,
114 __in const BURN_PACKAGE* pPackage,
115 __in BURN_PLAN* pPlan
116 );
117
118/********************************************************************
119 DependencyPlanPackageComplete - Updates the dependency registration
120 action depending on the planned action for the package.
121
122*********************************************************************/
123HRESULT DependencyPlanPackageComplete(
124 __in BURN_PACKAGE* pPackage,
125 __in BURN_PLAN* pPlan
126 );
127
128/********************************************************************
129 DependencyExecutePackageProviderAction - Registers or unregisters
130 provider information for the package contained within the action.
131
132*********************************************************************/
133HRESULT DependencyExecutePackageProviderAction(
134 __in const BURN_EXECUTE_ACTION* pAction
135 );
136
137/********************************************************************
138 DependencyExecutePackageDependencyAction - Registers or unregisters
139 dependency information for the package contained within the action.
140
141*********************************************************************/
142HRESULT DependencyExecutePackageDependencyAction(
143 __in BOOL fPerMachine,
144 __in const BURN_EXECUTE_ACTION* pAction
145 );
146
147/********************************************************************
148 DependencyRegisterBundle - Registers the bundle dependency provider.
149
150*********************************************************************/
151HRESULT DependencyRegisterBundle(
152 __in const BURN_REGISTRATION* pRegistration
153 );
154
155/********************************************************************
156 DependencyProcessDependentRegistration - Registers or unregisters dependents
157 on the bundle based on the action.
158
159*********************************************************************/
160HRESULT DependencyProcessDependentRegistration(
161 __in const BURN_REGISTRATION* pRegistration,
162 __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction
163 );
164
165/********************************************************************
166 DependencyUnregisterBundle - Removes the bundle dependency provider.
167
168 Note: Does not check for existing dependents before removing the key.
169*********************************************************************/
170void DependencyUnregisterBundle(
171 __in const BURN_REGISTRATION* pRegistration
172 );
173
174#if defined(__cplusplus)
175}
176#endif
diff --git a/src/engine/detect.cpp b/src/engine/detect.cpp
new file mode 100644
index 00000000..7953daf5
--- /dev/null
+++ b/src/engine/detect.cpp
@@ -0,0 +1,431 @@
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#include "precomp.h"
4
5typedef struct _DETECT_AUTHENTICATION_REQUIRED_DATA
6{
7 BURN_USER_EXPERIENCE* pUX;
8 LPCWSTR wzPackageOrContainerId;
9} DETECT_AUTHENTICATION_REQUIRED_DATA;
10
11// internal function definitions
12static HRESULT AuthenticationRequired(
13 __in LPVOID pData,
14 __in HINTERNET hUrl,
15 __in long lHttpCode,
16 __out BOOL* pfRetrySend,
17 __out BOOL* pfRetry
18 );
19
20static HRESULT DetectAtomFeedUpdate(
21 __in_z LPCWSTR wzBundleId,
22 __in BURN_USER_EXPERIENCE* pUX,
23 __in BURN_UPDATE* pUpdate
24 );
25
26static HRESULT DownloadUpdateFeed(
27 __in_z LPCWSTR wzBundleId,
28 __in BURN_USER_EXPERIENCE* pUX,
29 __in BURN_UPDATE* pUpdate,
30 __deref_inout_z LPWSTR* psczTempFile
31 );
32
33// function definitions
34
35extern "C" void DetectReset(
36 __in BURN_REGISTRATION* pRegistration,
37 __in BURN_PACKAGES* pPackages
38 )
39{
40 RelatedBundlesUninitialize(&pRegistration->relatedBundles);
41 ReleaseNullStr(pRegistration->sczDetectedProviderKeyBundleId);
42 pRegistration->fEnabledForwardCompatibleBundle = FALSE;
43 PackageUninitialize(&pRegistration->forwardCompatibleBundle);
44
45 for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage)
46 {
47 BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage;
48
49 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN;
50
51 pPackage->cache = BURN_CACHE_STATE_NONE;
52 for (DWORD iPayload = 0; iPayload < pPackage->cPayloads; ++iPayload)
53 {
54 BURN_PACKAGE_PAYLOAD* pPayload = pPackage->rgPayloads + iPayload;
55 pPayload->fCached = FALSE;
56 }
57
58 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
59 {
60 for (DWORD iFeature = 0; iFeature < pPackage->Msi.cFeatures; ++iFeature)
61 {
62 BURN_MSIFEATURE* pFeature = pPackage->Msi.rgFeatures + iFeature;
63
64 pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN;
65 }
66
67 pPackage->Msi.fCompatibleInstalled = FALSE;
68 }
69 else if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
70 {
71 ReleaseNullMem(pPackage->Msp.rgTargetProducts);
72 pPackage->Msp.cTargetProductCodes = 0;
73 }
74 }
75
76 for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo)
77 {
78 MSIPATCHSEQUENCEINFOW* pPatchInfo = pPackages->rgPatchInfo + iPatchInfo;
79 pPatchInfo->dwOrder = 0;
80 pPatchInfo->uStatus = 0;
81 }
82}
83
84extern "C" HRESULT DetectForwardCompatibleBundle(
85 __in BURN_USER_EXPERIENCE* pUX,
86 __in BOOTSTRAPPER_COMMAND* pCommand,
87 __in BURN_REGISTRATION* pRegistration
88 )
89{
90 HRESULT hr = S_OK;
91 BOOL fRecommendIgnore = TRUE;
92 BOOL fIgnoreBundle = FALSE;
93
94 if (pRegistration->sczDetectedProviderKeyBundleId &&
95 CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczDetectedProviderKeyBundleId, -1, pRegistration->sczId, -1))
96 {
97 // Only change the recommendation if an active parent was provided.
98 if (pRegistration->sczActiveParent && *pRegistration->sczActiveParent)
99 {
100 // On install, recommend running the forward compatible bundle because there is an active parent. This
101 // will essentially register the parent with the forward compatible bundle.
102 if (BOOTSTRAPPER_ACTION_INSTALL == pCommand->action)
103 {
104 fRecommendIgnore = FALSE;
105 }
106 else if (BOOTSTRAPPER_ACTION_UNINSTALL == pCommand->action ||
107 BOOTSTRAPPER_ACTION_MODIFY == pCommand->action ||
108 BOOTSTRAPPER_ACTION_REPAIR == pCommand->action)
109 {
110 // When modifying the bundle, only recommend running the forward compatible bundle if the parent
111 // is already registered as a dependent of the provider key.
112 if (DependencyDependentExists(pRegistration, pRegistration->sczActiveParent))
113 {
114 fRecommendIgnore = FALSE;
115 }
116 }
117 }
118
119 for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle)
120 {
121 BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle;
122 fIgnoreBundle = fRecommendIgnore;
123
124 if (BOOTSTRAPPER_RELATION_UPGRADE == pRelatedBundle->relationType &&
125 pRegistration->qwVersion <= pRelatedBundle->qwVersion &&
126 CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczDetectedProviderKeyBundleId, -1, pRelatedBundle->package.sczId, -1))
127 {
128 hr = UserExperienceOnDetectForwardCompatibleBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->qwVersion, &fIgnoreBundle);
129 ExitOnRootFailure(hr, "BA aborted detect forward compatible bundle.");
130
131 if (!fIgnoreBundle)
132 {
133 hr = PseudoBundleInitializePassthrough(&pRegistration->forwardCompatibleBundle, pCommand, NULL, pRegistration->sczActiveParent, pRegistration->sczAncestors, &pRelatedBundle->package);
134 ExitOnFailure(hr, "Failed to initialize update bundle.");
135
136 pRegistration->fEnabledForwardCompatibleBundle = TRUE;
137 }
138
139 LogId(REPORT_STANDARD, MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), LoggingVersionToString(pRelatedBundle->qwVersion), LoggingBoolToString(pRegistration->fEnabledForwardCompatibleBundle));
140 break;
141 }
142 }
143 }
144
145LExit:
146 return hr;
147}
148
149extern "C" HRESULT DetectReportRelatedBundles(
150 __in BURN_USER_EXPERIENCE* pUX,
151 __in BURN_REGISTRATION* pRegistration,
152 __in BOOTSTRAPPER_RELATION_TYPE relationType,
153 __in BOOTSTRAPPER_ACTION action
154 )
155{
156 HRESULT hr = S_OK;
157
158 for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle)
159 {
160 const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle;
161 BOOTSTRAPPER_RELATED_OPERATION operation = BOOTSTRAPPER_RELATED_OPERATION_NONE;
162
163 switch (pRelatedBundle->relationType)
164 {
165 case BOOTSTRAPPER_RELATION_UPGRADE:
166 if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL < action)
167 {
168 if (pRegistration->qwVersion > pRelatedBundle->qwVersion)
169 {
170 operation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE;
171 }
172 else if (pRegistration->qwVersion < pRelatedBundle->qwVersion)
173 {
174 operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE;
175 }
176 }
177 break;
178
179 case BOOTSTRAPPER_RELATION_PATCH: __fallthrough;
180 case BOOTSTRAPPER_RELATION_ADDON:
181 if (BOOTSTRAPPER_ACTION_UNINSTALL == action)
182 {
183 operation = BOOTSTRAPPER_RELATED_OPERATION_REMOVE;
184 }
185 else if (BOOTSTRAPPER_ACTION_INSTALL == action || BOOTSTRAPPER_ACTION_MODIFY == action)
186 {
187 operation = BOOTSTRAPPER_RELATED_OPERATION_INSTALL;
188 }
189 else if (BOOTSTRAPPER_ACTION_REPAIR == action)
190 {
191 operation = BOOTSTRAPPER_RELATED_OPERATION_REPAIR;
192 }
193 break;
194
195 case BOOTSTRAPPER_RELATION_DETECT: __fallthrough;
196 case BOOTSTRAPPER_RELATION_DEPENDENT:
197 break;
198
199 default:
200 hr = E_FAIL;
201 ExitOnRootFailure(hr, "Unexpected relation type encountered: %d", pRelatedBundle->relationType);
202 break;
203 }
204
205 LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), LoggingVersionToString(pRelatedBundle->qwVersion), LoggingRelatedOperationToString(operation));
206
207 hr = UserExperienceOnDetectRelatedBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->qwVersion, operation);
208 ExitOnRootFailure(hr, "BA aborted detect related bundle.");
209 }
210
211LExit:
212 return hr;
213}
214
215extern "C" HRESULT DetectUpdate(
216 __in_z LPCWSTR wzBundleId,
217 __in BURN_USER_EXPERIENCE* pUX,
218 __in BURN_UPDATE* pUpdate
219 )
220{
221 HRESULT hr = S_OK;
222 BOOL fBeginCalled = FALSE;
223 BOOL fSkip = TRUE;
224 BOOL fIgnoreError = FALSE;
225
226 // If no update source was specified, skip update detection.
227 if (!pUpdate->sczUpdateSource || !*pUpdate->sczUpdateSource)
228 {
229 ExitFunction();
230 }
231
232 fBeginCalled = TRUE;
233 hr = UserExperienceOnDetectUpdateBegin(pUX, pUpdate->sczUpdateSource, &fSkip);
234 ExitOnRootFailure(hr, "BA aborted detect update begin.");
235
236 if (!fSkip)
237 {
238 hr = DetectAtomFeedUpdate(wzBundleId, pUX, pUpdate);
239 ExitOnFailure(hr, "Failed to detect atom feed update.");
240 }
241
242LExit:
243 if (fBeginCalled)
244 {
245 UserExperienceOnDetectUpdateComplete(pUX, hr, &fIgnoreError);
246 if (fIgnoreError)
247 {
248 hr = S_OK;
249 }
250 }
251
252 return hr;
253}
254
255static HRESULT AuthenticationRequired(
256 __in LPVOID pData,
257 __in HINTERNET hUrl,
258 __in long lHttpCode,
259 __out BOOL* pfRetrySend,
260 __out BOOL* pfRetry
261 )
262{
263 Assert(401 == lHttpCode || 407 == lHttpCode);
264
265 HRESULT hr = S_OK;
266 DWORD er = ERROR_SUCCESS;
267 BOOTSTRAPPER_ERROR_TYPE errorType = (401 == lHttpCode) ? BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER : BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY;
268 LPWSTR sczError = NULL;
269 DETECT_AUTHENTICATION_REQUIRED_DATA* pAuthenticationData = reinterpret_cast<DETECT_AUTHENTICATION_REQUIRED_DATA*>(pData);
270 int nResult = IDNOACTION;
271
272 *pfRetrySend = FALSE;
273 *pfRetry = FALSE;
274
275 hr = StrAllocFromError(&sczError, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), NULL);
276 ExitOnFailure(hr, "Failed to allocation error string.");
277
278 UserExperienceOnError(pAuthenticationData->pUX, errorType, pAuthenticationData->wzPackageOrContainerId, ERROR_ACCESS_DENIED, sczError, MB_RETRYTRYAGAIN, 0, NULL, &nResult); // ignore return value.
279 nResult = UserExperienceCheckExecuteResult(pAuthenticationData->pUX, FALSE, MB_RETRYTRYAGAIN, nResult);
280 if (IDTRYAGAIN == nResult && pAuthenticationData->pUX->hwndDetect)
281 {
282 er = ::InternetErrorDlg(pAuthenticationData->pUX->hwndDetect, hUrl, ERROR_INTERNET_INCORRECT_PASSWORD, FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, NULL);
283 if (ERROR_SUCCESS == er || ERROR_CANCELLED == er)
284 {
285 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
286 }
287 else if (ERROR_INTERNET_FORCE_RETRY == er)
288 {
289 *pfRetrySend = TRUE;
290 hr = S_OK;
291 }
292 else
293 {
294 hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
295 }
296 }
297 else if (IDRETRY == nResult)
298 {
299 *pfRetry = TRUE;
300 hr = S_OK;
301 }
302 else
303 {
304 hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
305 }
306
307LExit:
308 ReleaseStr(sczError);
309
310 return hr;
311}
312
313static HRESULT DownloadUpdateFeed(
314 __in_z LPCWSTR wzBundleId,
315 __in BURN_USER_EXPERIENCE* pUX,
316 __in BURN_UPDATE* pUpdate,
317 __deref_inout_z LPWSTR* psczTempFile
318 )
319{
320 HRESULT hr = S_OK;
321 DOWNLOAD_SOURCE downloadSource = { };
322 DOWNLOAD_CACHE_CALLBACK cacheCallback = { };
323 DOWNLOAD_AUTHENTICATION_CALLBACK authenticationCallback = { };
324 DETECT_AUTHENTICATION_REQUIRED_DATA authenticationData = { };
325 LPWSTR sczUpdateId = NULL;
326 LPWSTR sczError = NULL;
327 DWORD64 qwDownloadSize = 0;
328
329 // Always do our work in the working folder, even if cached.
330 hr = PathCreateTimeBasedTempFile(NULL, L"UpdateFeed", NULL, L"xml", psczTempFile, NULL);
331 ExitOnFailure(hr, "Failed to create UpdateFeed based on current system time.");
332
333 // Do we need a means of the BA to pass in a user name and password? If so, we should copy it to downloadSource here
334 hr = StrAllocString(&downloadSource.sczUrl, pUpdate->sczUpdateSource, 0);
335 ExitOnFailure(hr, "Failed to copy update url.");
336
337 cacheCallback.pfnProgress = NULL; //UpdateProgressRoutine;
338 cacheCallback.pfnCancel = NULL; // TODO: set this
339 cacheCallback.pv = NULL; //pProgress;
340
341 authenticationData.pUX = pUX;
342 authenticationData.wzPackageOrContainerId = wzBundleId;
343
344 authenticationCallback.pv = static_cast<LPVOID>(&authenticationData);
345 authenticationCallback.pfnAuthenticate = &AuthenticationRequired;
346
347 hr = DownloadUrl(&downloadSource, qwDownloadSize, *psczTempFile, &cacheCallback, &authenticationCallback);
348 ExitOnFailure(hr, "Failed attempt to download update feed from URL: '%ls' to: '%ls'", downloadSource.sczUrl, *psczTempFile);
349
350LExit:
351 if (FAILED(hr))
352 {
353 if (*psczTempFile)
354 {
355 FileEnsureDelete(*psczTempFile);
356 }
357
358 ReleaseNullStr(*psczTempFile);
359 }
360
361 ReleaseStr(downloadSource.sczUrl);
362 ReleaseStr(downloadSource.sczUser);
363 ReleaseStr(downloadSource.sczPassword);
364 ReleaseStr(sczUpdateId);
365 ReleaseStr(sczError);
366 return hr;
367}
368
369
370static HRESULT DetectAtomFeedUpdate(
371 __in_z LPCWSTR wzBundleId,
372 __in BURN_USER_EXPERIENCE* pUX,
373 __in BURN_UPDATE* pUpdate
374 )
375{
376 Assert(pUpdate && pUpdate->sczUpdateSource && *pUpdate->sczUpdateSource);
377#ifdef DEBUG
378 LogStringLine(REPORT_STANDARD, "DetectAtomFeedUpdate() - update location: %ls", pUpdate->sczUpdateSource);
379#endif
380
381
382 HRESULT hr = S_OK;
383 LPWSTR sczUpdateFeedTempFile = NULL;
384 ATOM_FEED* pAtomFeed = NULL;
385 APPLICATION_UPDATE_CHAIN* pApupChain = NULL;
386 BOOL fStopProcessingUpdates = FALSE;
387
388 hr = AtomInitialize();
389 ExitOnFailure(hr, "Failed to initialize Atom.");
390
391 hr = DownloadUpdateFeed(wzBundleId, pUX, pUpdate, &sczUpdateFeedTempFile);
392 ExitOnFailure(hr, "Failed to download update feed.");
393
394 hr = AtomParseFromFile(sczUpdateFeedTempFile, &pAtomFeed);
395 ExitOnFailure(hr, "Failed to parse update atom feed: %ls.", sczUpdateFeedTempFile);
396
397 hr = ApupAllocChainFromAtom(pAtomFeed, &pApupChain);
398 ExitOnFailure(hr, "Failed to allocate update chain from atom feed.");
399
400 if (0 < pApupChain->cEntries)
401 {
402 for (DWORD i = 0; i < pApupChain->cEntries; ++i)
403 {
404 APPLICATION_UPDATE_ENTRY* pAppUpdateEntry = &pApupChain->rgEntries[i];
405
406 hr = UserExperienceOnDetectUpdate(pUX, pAppUpdateEntry->rgEnclosures ? pAppUpdateEntry->rgEnclosures->wzUrl : NULL,
407 pAppUpdateEntry->rgEnclosures ? pAppUpdateEntry->rgEnclosures->dw64Size : 0,
408 pAppUpdateEntry->dw64Version, pAppUpdateEntry->wzTitle,
409 pAppUpdateEntry->wzSummary, pAppUpdateEntry->wzContentType, pAppUpdateEntry->wzContent, &fStopProcessingUpdates);
410 ExitOnRootFailure(hr, "BA aborted detect update.");
411
412 if (fStopProcessingUpdates)
413 {
414 break;
415 }
416 }
417 }
418
419LExit:
420 if (sczUpdateFeedTempFile && *sczUpdateFeedTempFile)
421 {
422 FileEnsureDelete(sczUpdateFeedTempFile);
423 }
424
425 ApupFreeChain(pApupChain);
426 AtomFreeFeed(pAtomFeed);
427 ReleaseStr(sczUpdateFeedTempFile);
428 AtomUninitialize();
429
430 return hr;
431}
diff --git a/src/engine/detect.h b/src/engine/detect.h
new file mode 100644
index 00000000..01488f1a
--- /dev/null
+++ b/src/engine/detect.h
@@ -0,0 +1,44 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// constants
11
12
13// structs
14
15
16// functions
17
18void DetectReset(
19 __in BURN_REGISTRATION* pRegistration,
20 __in BURN_PACKAGES* pPackages
21 );
22
23HRESULT DetectForwardCompatibleBundle(
24 __in BURN_USER_EXPERIENCE* pUX,
25 __in BOOTSTRAPPER_COMMAND* pCommand,
26 __in BURN_REGISTRATION* pRegistration
27 );
28
29HRESULT DetectReportRelatedBundles(
30 __in BURN_USER_EXPERIENCE* pUX,
31 __in BURN_REGISTRATION* pRegistration,
32 __in BOOTSTRAPPER_RELATION_TYPE relationType,
33 __in BOOTSTRAPPER_ACTION action
34 );
35
36HRESULT DetectUpdate(
37 __in_z LPCWSTR wzBundleId,
38 __in BURN_USER_EXPERIENCE* pUX,
39 __in BURN_UPDATE* pUpdate
40 );
41
42#if defined(__cplusplus)
43}
44#endif
diff --git a/src/engine/elevation.cpp b/src/engine/elevation.cpp
new file mode 100644
index 00000000..1b9336e0
--- /dev/null
+++ b/src/engine/elevation.cpp
@@ -0,0 +1,2814 @@
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#include "precomp.h"
4
5
6const DWORD BURN_TIMEOUT = 5 * 60 * 1000; // TODO: is 5 minutes good?
7
8typedef enum _BURN_ELEVATION_MESSAGE_TYPE
9{
10 BURN_ELEVATION_MESSAGE_TYPE_UNKNOWN,
11 BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE,
12 BURN_ELEVATION_MESSAGE_TYPE_APPLY_UNINITIALIZE,
13 BURN_ELEVATION_MESSAGE_TYPE_SESSION_BEGIN,
14 BURN_ELEVATION_MESSAGE_TYPE_SESSION_RESUME,
15 BURN_ELEVATION_MESSAGE_TYPE_SESSION_END,
16 BURN_ELEVATION_MESSAGE_TYPE_SAVE_STATE,
17 BURN_ELEVATION_MESSAGE_TYPE_LAYOUT_BUNDLE,
18 BURN_ELEVATION_MESSAGE_TYPE_CACHE_OR_LAYOUT_CONTAINER_OR_PAYLOAD,
19 BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP,
20 BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION,
21 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE,
22 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE,
23 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE,
24 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE,
25 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER,
26 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY,
27 BURN_ELEVATION_MESSAGE_TYPE_LOAD_COMPATIBLE_PACKAGE,
28 BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_EMBEDDED_CHILD,
29 BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE,
30 BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE,
31
32 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS,
33 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR,
34 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE,
35 BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE,
36 BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID,
37
38 BURN_ELEVATION_TRANSACTION_BEGIN,
39 BURN_ELEVATION_TRANSACTION_COMMIT,
40 BURN_ELEVATION_TRANSACTION_ROLLBACK
41
42} BURN_ELEVATION_MESSAGE_TYPE;
43
44
45// struct
46
47typedef struct _BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT
48{
49 PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler;
50 LPVOID pvContext;
51} BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT;
52
53typedef struct _BURN_ELEVATION_MSI_MESSAGE_CONTEXT
54{
55 PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler;
56 LPVOID pvContext;
57} BURN_ELEVATION_MSI_MESSAGE_CONTEXT;
58
59typedef struct _BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT
60{
61 DWORD dwProcessId;
62} BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT;
63
64typedef struct _BURN_ELEVATION_CHILD_MESSAGE_CONTEXT
65{
66 DWORD dwLoggingTlsId;
67 HANDLE hPipe;
68 HANDLE* phLock;
69 BOOL* pfDisabledAutomaticUpdates;
70 BURN_APPROVED_EXES* pApprovedExes;
71 BURN_CONTAINERS* pContainers;
72 BURN_PACKAGES* pPackages;
73 BURN_PAYLOADS* pPayloads;
74 BURN_VARIABLES* pVariables;
75 BURN_REGISTRATION* pRegistration;
76 BURN_USER_EXPERIENCE* pUserExperience;
77
78 MSIHANDLE hMsiTrns;
79 HANDLE hMsiTrnsEvent;
80} BURN_ELEVATION_CHILD_MESSAGE_CONTEXT;
81
82
83// internal function declarations
84
85static HRESULT OnMsiBeginTransaction(
86 __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext
87);
88static HRESULT OnMsiCommitTransaction(
89 __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext
90);
91static HRESULT OnMsiRollbackTransaction(
92 __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext
93);
94
95static DWORD WINAPI ElevatedChildCacheThreadProc(
96 __in LPVOID lpThreadParameter
97 );
98static HRESULT WaitForElevatedChildCacheThread(
99 __in HANDLE hCacheThread,
100 __in DWORD dwExpectedExitCode
101 );
102static HRESULT OnLoadCompatiblePackage(
103 __in BURN_PACKAGES* pPackages,
104 __in BYTE* pbData,
105 __in DWORD cbData
106 );
107static HRESULT ProcessGenericExecuteMessages(
108 __in BURN_PIPE_MESSAGE* pMsg,
109 __in_opt LPVOID pvContext,
110 __out DWORD* pdwResult
111 );
112static HRESULT ProcessMsiPackageMessages(
113 __in BURN_PIPE_MESSAGE* pMsg,
114 __in_opt LPVOID pvContext,
115 __out DWORD* pdwResult
116 );
117static HRESULT ProcessLaunchApprovedExeMessages(
118 __in BURN_PIPE_MESSAGE* pMsg,
119 __in_opt LPVOID pvContext,
120 __out DWORD* pdwResult
121 );
122static HRESULT ProcessElevatedChildMessage(
123 __in BURN_PIPE_MESSAGE* pMsg,
124 __in_opt LPVOID pvContext,
125 __out DWORD* pdwResult
126 );
127static HRESULT ProcessElevatedChildCacheMessage(
128 __in BURN_PIPE_MESSAGE* pMsg,
129 __in_opt LPVOID pvContext,
130 __out DWORD* pdwResult
131 );
132static HRESULT ProcessResult(
133 __in DWORD dwResult,
134 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
135 );
136static HRESULT OnApplyInitialize(
137 __in BURN_VARIABLES* pVariables,
138 __in BURN_REGISTRATION* pRegistration,
139 __in HANDLE* phLock,
140 __in BOOL* pfDisabledWindowsUpdate,
141 __in BYTE* pbData,
142 __in DWORD cbData
143 );
144static HRESULT OnApplyUninitialize(
145 __in HANDLE* phLock
146 );
147static HRESULT OnSessionBegin(
148 __in BURN_REGISTRATION* pRegistration,
149 __in BURN_VARIABLES* pVariables,
150 __in BURN_USER_EXPERIENCE* pUserExperience,
151 __in BYTE* pbData,
152 __in DWORD cbData
153 );
154static HRESULT OnSessionResume(
155 __in BURN_REGISTRATION* pRegistration,
156 __in BURN_VARIABLES* pVariables,
157 __in BYTE* pbData,
158 __in DWORD cbData
159 );
160static HRESULT OnSessionEnd(
161 __in BURN_REGISTRATION* pRegistration,
162 __in BYTE* pbData,
163 __in DWORD cbData
164 );
165static HRESULT OnSaveState(
166 __in BURN_REGISTRATION* pRegistration,
167 __in BYTE* pbData,
168 __in DWORD cbData
169 );
170static HRESULT OnLayoutBundle(
171 __in_z LPCWSTR wzExecutableName,
172 __in BYTE* pbData,
173 __in DWORD cbData
174 );
175static HRESULT OnCacheOrLayoutContainerOrPayload(
176 __in BURN_CONTAINERS* pContainers,
177 __in BURN_PACKAGES* pPackages,
178 __in BURN_PAYLOADS* pPayloads,
179 __in BYTE* pbData,
180 __in DWORD cbData
181 );
182static void OnCacheCleanup(
183 __in_z LPCWSTR wzBundleId
184 );
185static HRESULT OnProcessDependentRegistration(
186 __in const BURN_REGISTRATION* pRegistration,
187 __in BYTE* pbData,
188 __in DWORD cbData
189 );
190static HRESULT OnExecuteExePackage(
191 __in HANDLE hPipe,
192 __in BURN_PACKAGES* pPackages,
193 __in BURN_RELATED_BUNDLES* pRelatedBundles,
194 __in BURN_VARIABLES* pVariables,
195 __in BYTE* pbData,
196 __in DWORD cbData
197 );
198static HRESULT OnExecuteMsiPackage(
199 __in HANDLE hPipe,
200 __in BURN_PACKAGES* pPackages,
201 __in BURN_VARIABLES* pVariables,
202 __in BYTE* pbData,
203 __in DWORD cbData
204 );
205static HRESULT OnExecuteMspPackage(
206 __in HANDLE hPipe,
207 __in BURN_PACKAGES* pPackages,
208 __in BURN_VARIABLES* pVariables,
209 __in BYTE* pbData,
210 __in DWORD cbData
211 );
212static HRESULT OnExecuteMsuPackage(
213 __in HANDLE hPipe,
214 __in BURN_PACKAGES* pPackages,
215 __in BURN_VARIABLES* pVariables,
216 __in BYTE* pbData,
217 __in DWORD cbData
218 );
219static HRESULT OnExecutePackageProviderAction(
220 __in BURN_PACKAGES* pPackages,
221 __in BURN_RELATED_BUNDLES* pRelatedBundles,
222 __in BYTE* pbData,
223 __in DWORD cbData
224 );
225static HRESULT OnExecutePackageDependencyAction(
226 __in BURN_PACKAGES* pPackages,
227 __in BURN_RELATED_BUNDLES* pRelatedBundles,
228 __in BYTE* pbData,
229 __in DWORD cbData
230 );
231static int GenericExecuteMessageHandler(
232 __in GENERIC_EXECUTE_MESSAGE* pMessage,
233 __in LPVOID pvContext
234 );
235static int MsiExecuteMessageHandler(
236 __in WIU_MSI_EXECUTE_MESSAGE* pMessage,
237 __in_opt LPVOID pvContext
238 );
239static HRESULT OnCleanPackage(
240 __in BURN_PACKAGES* pPackages,
241 __in BYTE* pbData,
242 __in DWORD cbData
243 );
244static HRESULT OnLaunchApprovedExe(
245 __in HANDLE hPipe,
246 __in BURN_APPROVED_EXES* pApprovedExes,
247 __in BURN_VARIABLES* pVariables,
248 __in BYTE* pbData,
249 __in DWORD cbData
250 );
251
252
253// function definitions
254
255extern "C" HRESULT ElevationElevate(
256 __in BURN_ENGINE_STATE* pEngineState,
257 __in_opt HWND hwndParent
258 )
259{
260 Assert(BURN_MODE_ELEVATED != pEngineState->mode);
261 Assert(!pEngineState->companionConnection.sczName);
262 Assert(!pEngineState->companionConnection.sczSecret);
263 Assert(!pEngineState->companionConnection.hProcess);
264 Assert(!pEngineState->companionConnection.dwProcessId);
265 Assert(INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe);
266 Assert(INVALID_HANDLE_VALUE == pEngineState->companionConnection.hCachePipe);
267
268 HRESULT hr = S_OK;
269 int nResult = IDOK;
270 HANDLE hPipesCreatedEvent = INVALID_HANDLE_VALUE;
271
272 hr = UserExperienceOnElevateBegin(&pEngineState->userExperience);
273 ExitOnRootFailure(hr, "BA aborted elevation requirement.");
274
275 hr = PipeCreateNameAndSecret(&pEngineState->companionConnection.sczName, &pEngineState->companionConnection.sczSecret);
276 ExitOnFailure(hr, "Failed to create pipe name and client token.");
277
278 hr = PipeCreatePipes(&pEngineState->companionConnection, TRUE, &hPipesCreatedEvent);
279 ExitOnFailure(hr, "Failed to create pipe and cache pipe.");
280
281 LogId(REPORT_STANDARD, MSG_LAUNCH_ELEVATED_ENGINE_STARTING);
282
283 do
284 {
285 nResult = IDOK;
286
287 // Create the elevated process and if successful, wait for it to connect.
288 hr = PipeLaunchChildProcess(pEngineState->sczBundleEngineWorkingPath, &pEngineState->companionConnection, TRUE, hwndParent);
289 if (SUCCEEDED(hr))
290 {
291 LogId(REPORT_STANDARD, MSG_LAUNCH_ELEVATED_ENGINE_SUCCESS);
292
293 hr = PipeWaitForChildConnect(&pEngineState->companionConnection);
294 ExitOnFailure(hr, "Failed to connect to elevated child process.");
295
296 LogId(REPORT_STANDARD, MSG_CONNECT_TO_ELEVATED_ENGINE_SUCCESS);
297 }
298 else if (HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr)
299 {
300 // The user clicked "Cancel" on the elevation prompt or the elevation prompt timed out, provide the notification with the option to retry.
301 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
302 nResult = UserExperienceSendError(&pEngineState->userExperience, BOOTSTRAPPER_ERROR_TYPE_ELEVATE, NULL, hr, NULL, MB_ICONERROR | MB_RETRYCANCEL, IDNOACTION);
303 }
304 } while (IDRETRY == nResult);
305 ExitOnFailure(hr, "Failed to elevate.");
306
307LExit:
308 ReleaseHandle(hPipesCreatedEvent);
309
310 if (FAILED(hr))
311 {
312 PipeConnectionUninitialize(&pEngineState->companionConnection);
313 }
314
315 UserExperienceOnElevateComplete(&pEngineState->userExperience, hr);
316
317 return hr;
318}
319
320extern "C" HRESULT ElevationApplyInitialize(
321 __in HANDLE hPipe,
322 __in BURN_VARIABLES* pVariables,
323 __in BOOTSTRAPPER_ACTION action,
324 __in BURN_AU_PAUSE_ACTION auAction,
325 __in BOOL fTakeSystemRestorePoint
326 )
327{
328 HRESULT hr = S_OK;
329 BYTE* pbData = NULL;
330 SIZE_T cbData = 0;
331 DWORD dwResult = 0;
332
333 // serialize message data
334 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)action);
335 ExitOnFailure(hr, "Failed to write action to message buffer.");
336
337 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)auAction);
338 ExitOnFailure(hr, "Failed to write update action to message buffer.");
339
340 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fTakeSystemRestorePoint);
341 ExitOnFailure(hr, "Failed to write system restore point action to message buffer.");
342
343 hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData);
344 ExitOnFailure(hr, "Failed to write variables.");
345
346 // send message
347 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE, pbData, cbData, NULL, NULL, &dwResult);
348 ExitOnFailure(hr, "Failed to send message to per-machine process.");
349
350 hr = (HRESULT)dwResult;
351
352LExit:
353 ReleaseBuffer(pbData);
354
355 return hr;
356}
357
358extern "C" HRESULT ElevationApplyUninitialize(
359 __in HANDLE hPipe
360 )
361{
362 HRESULT hr = S_OK;
363 BYTE* pbData = NULL;
364 SIZE_T cbData = 0;
365 DWORD dwResult = 0;
366
367 // send message
368 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_APPLY_UNINITIALIZE, pbData, cbData, NULL, NULL, &dwResult);
369 ExitOnFailure(hr, "Failed to send message to per-machine process.");
370
371 hr = (HRESULT)dwResult;
372
373LExit:
374 ReleaseBuffer(pbData);
375
376 return hr;
377}
378
379/*******************************************************************
380 ElevationSessionBegin -
381
382*******************************************************************/
383extern "C" HRESULT ElevationSessionBegin(
384 __in HANDLE hPipe,
385 __in_z LPCWSTR wzEngineWorkingPath,
386 __in_z LPCWSTR wzResumeCommandLine,
387 __in BOOL fDisableResume,
388 __in BURN_VARIABLES* pVariables,
389 __in DWORD dwRegistrationOperations,
390 __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction,
391 __in DWORD64 qwEstimatedSize
392 )
393{
394 HRESULT hr = S_OK;
395 BYTE* pbData = NULL;
396 SIZE_T cbData = 0;
397 DWORD dwResult = 0;
398
399 // serialize message data
400 hr = BuffWriteString(&pbData, &cbData, wzEngineWorkingPath);
401 ExitOnFailure(hr, "Failed to write engine working path to message buffer.");
402
403 hr = BuffWriteString(&pbData, &cbData, wzResumeCommandLine);
404 ExitOnFailure(hr, "Failed to write resume command line to message buffer.");
405
406 hr = BuffWriteNumber(&pbData, &cbData, fDisableResume);
407 ExitOnFailure(hr, "Failed to write resume flag.");
408
409 hr = BuffWriteNumber(&pbData, &cbData, dwRegistrationOperations);
410 ExitOnFailure(hr, "Failed to write registration operations to message buffer.");
411
412 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)dependencyRegistrationAction);
413 ExitOnFailure(hr, "Failed to write dependency registration action to message buffer.");
414
415 hr = BuffWriteNumber64(&pbData, &cbData, qwEstimatedSize);
416 ExitOnFailure(hr, "Failed to write estimated size to message buffer.");
417
418 hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData);
419 ExitOnFailure(hr, "Failed to write variables.");
420
421 // send message
422 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SESSION_BEGIN, pbData, cbData, NULL, NULL, &dwResult);
423 ExitOnFailure(hr, "Failed to send message to per-machine process.");
424
425 hr = (HRESULT)dwResult;
426
427LExit:
428 ReleaseBuffer(pbData);
429
430 return hr;
431}
432
433/*******************************************************************
434 ElevationSessionResume -
435
436*******************************************************************/
437extern "C" HRESULT ElevationSessionResume(
438 __in HANDLE hPipe,
439 __in_z LPCWSTR wzResumeCommandLine,
440 __in BOOL fDisableResume,
441 __in BURN_VARIABLES* pVariables
442 )
443{
444 HRESULT hr = S_OK;
445 BYTE* pbData = NULL;
446 SIZE_T cbData = 0;
447 DWORD dwResult = 0;
448
449 // serialize message data
450 hr = BuffWriteString(&pbData, &cbData, wzResumeCommandLine);
451 ExitOnFailure(hr, "Failed to write resume command line to message buffer.");
452
453 hr = BuffWriteNumber(&pbData, &cbData, fDisableResume);
454 ExitOnFailure(hr, "Failed to write resume flag.");
455
456 hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData);
457 ExitOnFailure(hr, "Failed to write variables.");
458
459 // send message
460 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SESSION_RESUME, pbData, cbData, NULL, NULL, &dwResult);
461 ExitOnFailure(hr, "Failed to send message to per-machine process.");
462
463 hr = (HRESULT)dwResult;
464
465LExit:
466 ReleaseBuffer(pbData);
467
468 return hr;
469}
470
471/*******************************************************************
472 ElevationSessionEnd -
473
474*******************************************************************/
475extern "C" HRESULT ElevationSessionEnd(
476 __in HANDLE hPipe,
477 __in BURN_RESUME_MODE resumeMode,
478 __in BOOTSTRAPPER_APPLY_RESTART restart,
479 __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction
480 )
481{
482 HRESULT hr = S_OK;
483 BYTE* pbData = NULL;
484 SIZE_T cbData = 0;
485 DWORD dwResult = 0;
486
487 // serialize message data
488 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)resumeMode);
489 ExitOnFailure(hr, "Failed to write resume mode to message buffer.");
490
491 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)restart);
492 ExitOnFailure(hr, "Failed to write restart enum to message buffer.");
493
494 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)dependencyRegistrationAction);
495 ExitOnFailure(hr, "Failed to write dependency registration action to message buffer.");
496
497 // send message
498 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SESSION_END, pbData, cbData, NULL, NULL, &dwResult);
499 ExitOnFailure(hr, "Failed to send message to per-machine process.");
500
501 hr = (HRESULT)dwResult;
502
503LExit:
504 ReleaseBuffer(pbData);
505
506 return hr;
507}
508
509/*******************************************************************
510 ElevationSaveState -
511
512*******************************************************************/
513HRESULT ElevationSaveState(
514 __in HANDLE hPipe,
515 __in_bcount(cbBuffer) BYTE* pbBuffer,
516 __in SIZE_T cbBuffer
517 )
518{
519 HRESULT hr = S_OK;
520 DWORD dwResult = 0;
521
522 // send message
523 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_SAVE_STATE, pbBuffer, (DWORD)cbBuffer, NULL, NULL, &dwResult);
524 ExitOnFailure(hr, "Failed to send message to per-machine process.");
525
526 hr = (HRESULT)dwResult;
527
528LExit:
529 return hr;
530}
531
532/*******************************************************************
533 ElevationLayoutBundle -
534
535*******************************************************************/
536extern "C" HRESULT ElevationLayoutBundle(
537 __in HANDLE hPipe,
538 __in_z LPCWSTR wzLayoutDirectory,
539 __in_z LPCWSTR wzUnverifiedPath
540 )
541{
542 HRESULT hr = S_OK;
543 BYTE* pbData = NULL;
544 SIZE_T cbData = 0;
545 DWORD dwResult = 0;
546
547 // serialize message data
548 hr = BuffWriteString(&pbData, &cbData, wzLayoutDirectory);
549 ExitOnFailure(hr, "Failed to write layout directory to message buffer.");
550
551 hr = BuffWriteString(&pbData, &cbData, wzUnverifiedPath);
552 ExitOnFailure(hr, "Failed to write payload unverified path to message buffer.");
553
554 // send message
555 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_LAYOUT_BUNDLE, pbData, cbData, NULL, NULL, &dwResult);
556 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_LAYOUT_BUNDLE message to per-machine process.");
557
558 hr = (HRESULT)dwResult;
559
560LExit:
561 ReleaseBuffer(pbData);
562
563 return hr;
564}
565
566/*******************************************************************
567 ElevationCacheOrLayoutPayload -
568
569*******************************************************************/
570extern "C" HRESULT ElevationCacheOrLayoutContainerOrPayload(
571 __in HANDLE hPipe,
572 __in_opt BURN_CONTAINER* pContainer,
573 __in_opt BURN_PACKAGE* pPackage,
574 __in_opt BURN_PAYLOAD* pPayload,
575 __in_z_opt LPCWSTR wzLayoutDirectory,
576 __in_z LPCWSTR wzUnverifiedPath,
577 __in BOOL fMove
578 )
579{
580 HRESULT hr = S_OK;
581 BYTE* pbData = NULL;
582 SIZE_T cbData = 0;
583 DWORD dwResult = 0;
584
585 // serialize message data
586 hr = BuffWriteString(&pbData, &cbData, pContainer ? pContainer->sczId : NULL);
587 ExitOnFailure(hr, "Failed to write container id to message buffer.");
588
589 hr = BuffWriteString(&pbData, &cbData, pPackage ? pPackage->sczId : NULL);
590 ExitOnFailure(hr, "Failed to write package id to message buffer.");
591
592 hr = BuffWriteString(&pbData, &cbData, pPayload ? pPayload->sczKey : NULL);
593 ExitOnFailure(hr, "Failed to write payload id to message buffer.");
594
595 hr = BuffWriteString(&pbData, &cbData, wzLayoutDirectory);
596 ExitOnFailure(hr, "Failed to write layout directory to message buffer.");
597
598 hr = BuffWriteString(&pbData, &cbData, wzUnverifiedPath);
599 ExitOnFailure(hr, "Failed to write unverified path to message buffer.");
600
601 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fMove);
602 ExitOnFailure(hr, "Failed to write move flag to message buffer.");
603
604 // send message
605 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CACHE_OR_LAYOUT_CONTAINER_OR_PAYLOAD, pbData, cbData, NULL, NULL, &dwResult);
606 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CACHE_OR_LAYOUT_CONTAINER_OR_PAYLOAD message to per-machine process.");
607
608 hr = (HRESULT)dwResult;
609
610LExit:
611 ReleaseBuffer(pbData);
612
613 return hr;
614}
615
616/*******************************************************************
617 ElevationCacheCleanup -
618
619*******************************************************************/
620extern "C" HRESULT ElevationCacheCleanup(
621 __in HANDLE hPipe
622 )
623{
624 HRESULT hr = S_OK;
625 DWORD dwResult = 0;
626
627 // send message
628 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP, NULL, 0, NULL, NULL, &dwResult);
629 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP message to per-machine process.");
630
631 hr = (HRESULT)dwResult;
632
633LExit:
634 return hr;
635}
636
637extern "C" HRESULT ElevationProcessDependentRegistration(
638 __in HANDLE hPipe,
639 __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction
640 )
641{
642 HRESULT hr = S_OK;
643 BYTE* pbData = NULL;
644 SIZE_T cbData = 0;
645 DWORD dwResult = 0;
646
647 // serialize message data
648 hr = BuffWriteNumber(&pbData, &cbData, pAction->type);
649 ExitOnFailure(hr, "Failed to write action type to message buffer.");
650
651 hr = BuffWriteString(&pbData, &cbData, pAction->sczBundleId);
652 ExitOnFailure(hr, "Failed to write bundle id to message buffer.");
653
654 hr = BuffWriteString(&pbData, &cbData, pAction->sczDependentProviderKey);
655 ExitOnFailure(hr, "Failed to write dependent provider key to message buffer.");
656
657 // send message
658 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION, pbData, cbData, NULL, NULL, &dwResult);
659 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION message to per-machine process.");
660
661 hr = (HRESULT)dwResult;
662
663LExit:
664 ReleaseBuffer(pbData);
665
666 return hr;
667}
668
669/*******************************************************************
670 ElevationExecuteExePackage -
671
672*******************************************************************/
673extern "C" HRESULT ElevationExecuteExePackage(
674 __in HANDLE hPipe,
675 __in BURN_EXECUTE_ACTION* pExecuteAction,
676 __in BURN_VARIABLES* pVariables,
677 __in BOOL fRollback,
678 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
679 __in LPVOID pvContext,
680 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
681 )
682{
683 HRESULT hr = S_OK;
684 BYTE* pbData = NULL;
685 SIZE_T cbData = 0;
686 BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT context = { };
687 DWORD dwResult = 0;
688
689 // serialize message data
690 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->exePackage.pPackage->sczId);
691 ExitOnFailure(hr, "Failed to write package id to message buffer.");
692
693 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->exePackage.action);
694 ExitOnFailure(hr, "Failed to write action to message buffer.");
695
696 hr = BuffWriteNumber(&pbData, &cbData, fRollback);
697 ExitOnFailure(hr, "Failed to write rollback.");
698
699 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->exePackage.sczIgnoreDependencies);
700 ExitOnFailure(hr, "Failed to write the list of dependencies to ignore to the message buffer.");
701
702 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->exePackage.sczAncestors);
703 ExitOnFailure(hr, "Failed to write the list of ancestors to the message buffer.");
704
705 hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData);
706 ExitOnFailure(hr, "Failed to write variables.");
707
708 // send message
709 context.pfnGenericMessageHandler = pfnGenericMessageHandler;
710 context.pvContext = pvContext;
711
712 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE, pbData, cbData, ProcessGenericExecuteMessages, &context, &dwResult);
713 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE message to per-machine process.");
714
715 hr = ProcessResult(dwResult, pRestart);
716
717LExit:
718 ReleaseBuffer(pbData);
719
720 return hr;
721}
722
723extern "C" HRESULT ElevationMsiBeginTransaction(
724 __in HANDLE hPipe,
725 __in_opt HWND hwndParent,
726 __in LPVOID pvContext
727)
728{
729 UNREFERENCED_PARAMETER(hwndParent);
730 HRESULT hr = S_OK;
731 BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = {};
732 DWORD dwResult = ERROR_SUCCESS;
733
734 context.pvContext = pvContext;
735
736 hr = PipeSendMessage(hPipe, BURN_ELEVATION_TRANSACTION_BEGIN, NULL, 0, NULL, &context, &dwResult);
737 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE message to per-machine process.");
738 ExitOnWin32Error(dwResult, hr, "Failed beginning an elevated MSI transaction");
739
740LExit:
741 return hr;
742}
743
744extern "C" HRESULT ElevationMsiCommitTransaction(
745 __in HANDLE hPipe,
746 __in_opt HWND hwndParent,
747 __in LPVOID pvContext
748)
749{
750 UNREFERENCED_PARAMETER(hwndParent);
751 HRESULT hr = S_OK;
752 BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = {};
753 DWORD dwResult = ERROR_SUCCESS;
754
755 context.pvContext = pvContext;
756
757 hr = PipeSendMessage(hPipe, BURN_ELEVATION_TRANSACTION_COMMIT, NULL, 0, NULL, &context, &dwResult);
758 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE message to per-machine process.");
759 ExitOnWin32Error(dwResult, hr, "Failed committing an elevated MSI transaction");
760
761LExit:
762 return hr;
763}
764
765extern "C" HRESULT ElevationMsiRollbackTransaction(
766 __in HANDLE hPipe,
767 __in_opt HWND hwndParent,
768 __in LPVOID pvContext
769)
770{
771 UNREFERENCED_PARAMETER(hwndParent);
772 HRESULT hr = S_OK;
773 BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = {};
774 DWORD dwResult = ERROR_SUCCESS;
775
776 context.pvContext = pvContext;
777
778 hr = PipeSendMessage(hPipe, BURN_ELEVATION_TRANSACTION_ROLLBACK, NULL, 0, NULL, &context, &dwResult);
779 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE message to per-machine process.");
780 ExitOnWin32Error(dwResult, hr, "Failed rolling back an elevated MSI transaction");
781
782LExit:
783 return hr;
784}
785
786
787
788/*******************************************************************
789 ElevationExecuteMsiPackage -
790
791*******************************************************************/
792extern "C" HRESULT ElevationExecuteMsiPackage(
793 __in HANDLE hPipe,
794 __in_opt HWND hwndParent,
795 __in BURN_EXECUTE_ACTION* pExecuteAction,
796 __in BURN_VARIABLES* pVariables,
797 __in BOOL fRollback,
798 __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
799 __in LPVOID pvContext,
800 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
801 )
802{
803 HRESULT hr = S_OK;
804 BYTE* pbData = NULL;
805 SIZE_T cbData = 0;
806 BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = { };
807 DWORD dwResult = 0;
808
809 // serialize message data
810 // TODO: for patching we might not have a package
811 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msiPackage.pPackage->sczId);
812 ExitOnFailure(hr, "Failed to write package id to message buffer.");
813
814 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)hwndParent);
815 ExitOnFailure(hr, "Failed to write parent hwnd to message buffer.");
816
817 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msiPackage.sczLogPath);
818 ExitOnFailure(hr, "Failed to write package log to message buffer.");
819
820 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.uiLevel);
821 ExitOnFailure(hr, "Failed to write UI level to message buffer.");
822
823 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.action);
824 ExitOnFailure(hr, "Failed to write action to message buffer.");
825
826 // Feature actions.
827 for (DWORD i = 0; i < pExecuteAction->msiPackage.pPackage->Msi.cFeatures; ++i)
828 {
829 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.rgFeatures[i]);
830 ExitOnFailure(hr, "Failed to write feature action to message buffer.");
831 }
832
833 // Slipstream patches actions.
834 for (DWORD i = 0; i < pExecuteAction->msiPackage.pPackage->Msi.cSlipstreamMspPackages; ++i)
835 {
836 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msiPackage.rgSlipstreamPatches[i]);
837 ExitOnFailure(hr, "Failed to write slipstream patch action to message buffer.");
838 }
839
840 hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData);
841 ExitOnFailure(hr, "Failed to write variables.");
842
843 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fRollback);
844 ExitOnFailure(hr, "Failed to write rollback flag to message buffer.");
845
846
847 // send message
848 context.pfnMessageHandler = pfnMessageHandler;
849 context.pvContext = pvContext;
850
851 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE, pbData, cbData, ProcessMsiPackageMessages, &context, &dwResult);
852 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE message to per-machine process.");
853
854 hr = ProcessResult(dwResult, pRestart);
855
856LExit:
857 ReleaseBuffer(pbData);
858
859 return hr;
860}
861
862/*******************************************************************
863 ElevationExecuteMspPackage -
864
865*******************************************************************/
866extern "C" HRESULT ElevationExecuteMspPackage(
867 __in HANDLE hPipe,
868 __in_opt HWND hwndParent,
869 __in BURN_EXECUTE_ACTION* pExecuteAction,
870 __in BURN_VARIABLES* pVariables,
871 __in BOOL fRollback,
872 __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
873 __in LPVOID pvContext,
874 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
875 )
876{
877 HRESULT hr = S_OK;
878 BYTE* pbData = NULL;
879 SIZE_T cbData = 0;
880 BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = { };
881 DWORD dwResult = 0;
882
883 // serialize message data
884 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.pPackage->sczId);
885 ExitOnFailure(hr, "Failed to write package id to message buffer.");
886
887 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)hwndParent);
888 ExitOnFailure(hr, "Failed to write parent hwnd to message buffer.");
889
890 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.sczTargetProductCode);
891 ExitOnFailure(hr, "Failed to write target product code to message buffer.");
892
893 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.sczLogPath);
894 ExitOnFailure(hr, "Failed to write package log to message buffer.");
895
896 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->mspTarget.uiLevel);
897 ExitOnFailure(hr, "Failed to write UI level to message buffer.");
898
899 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->mspTarget.action);
900 ExitOnFailure(hr, "Failed to write action to message buffer.");
901
902 hr = BuffWriteNumber(&pbData, &cbData, pExecuteAction->mspTarget.cOrderedPatches);
903 ExitOnFailure(hr, "Failed to write count of ordered patches to message buffer.");
904
905 for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i)
906 {
907 hr = BuffWriteNumber(&pbData, &cbData, pExecuteAction->mspTarget.rgOrderedPatches[i].dwOrder);
908 ExitOnFailure(hr, "Failed to write ordered patch order to message buffer.");
909
910 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage->sczId);
911 ExitOnFailure(hr, "Failed to write ordered patch id to message buffer.");
912 }
913
914 hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData);
915 ExitOnFailure(hr, "Failed to write variables.");
916
917 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fRollback);
918 ExitOnFailure(hr, "Failed to write rollback flag to message buffer.");
919
920 // send message
921 context.pfnMessageHandler = pfnMessageHandler;
922 context.pvContext = pvContext;
923
924 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE, pbData, cbData, ProcessMsiPackageMessages, &context, &dwResult);
925 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE message to per-machine process.");
926
927 hr = ProcessResult(dwResult, pRestart);
928
929LExit:
930 ReleaseBuffer(pbData);
931
932 return hr;
933}
934
935/*******************************************************************
936 ElevationExecuteMsuPackage -
937
938*******************************************************************/
939extern "C" HRESULT ElevationExecuteMsuPackage(
940 __in HANDLE hPipe,
941 __in BURN_EXECUTE_ACTION* pExecuteAction,
942 __in BOOL fRollback,
943 __in BOOL fStopWusaService,
944 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
945 __in LPVOID pvContext,
946 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
947 )
948{
949 HRESULT hr = S_OK;
950 BYTE* pbData = NULL;
951 SIZE_T cbData = 0;
952 BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT context = { };
953 DWORD dwResult = 0;
954
955 // serialize message data
956 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msuPackage.pPackage->sczId);
957 ExitOnFailure(hr, "Failed to write package id to message buffer.");
958
959 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->msuPackage.sczLogPath);
960 ExitOnFailure(hr, "Failed to write package log to message buffer.");
961
962 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pExecuteAction->msuPackage.action);
963 ExitOnFailure(hr, "Failed to write action to message buffer.");
964
965 hr = BuffWriteNumber(&pbData, &cbData, fRollback);
966 ExitOnFailure(hr, "Failed to write rollback.");
967
968 hr = BuffWriteNumber(&pbData, &cbData, fStopWusaService);
969 ExitOnFailure(hr, "Failed to write StopWusaService.");
970
971 // send message
972 context.pfnGenericMessageHandler = pfnGenericMessageHandler;
973 context.pvContext = pvContext;
974
975 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE, pbData, cbData, ProcessGenericExecuteMessages, &context, &dwResult);
976 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE message to per-machine process.");
977
978 hr = ProcessResult(dwResult, pRestart);
979
980LExit:
981 ReleaseBuffer(pbData);
982
983 return hr;
984}
985
986extern "C" HRESULT ElevationExecutePackageProviderAction(
987 __in HANDLE hPipe,
988 __in BURN_EXECUTE_ACTION* pExecuteAction
989 )
990{
991 HRESULT hr = S_OK;
992 BYTE* pbData = NULL;
993 SIZE_T cbData = 0;
994 DWORD dwResult = 0;
995 BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
996
997 // Serialize the message data.
998 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->packageProvider.pPackage->sczId);
999 ExitOnFailure(hr, "Failed to write package id to message buffer.");
1000
1001 hr = BuffWriteNumber(&pbData, &cbData, pExecuteAction->packageProvider.action);
1002 ExitOnFailure(hr, "Failed to write action to message buffer.");
1003
1004 // Send the message.
1005 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER, pbData, cbData, NULL, NULL, &dwResult);
1006 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER message to per-machine process.");
1007
1008 // Ignore the restart since this action only results in registry writes.
1009 hr = ProcessResult(dwResult, &restart);
1010
1011LExit:
1012 ReleaseBuffer(pbData);
1013
1014 return hr;
1015}
1016
1017extern "C" HRESULT ElevationExecutePackageDependencyAction(
1018 __in HANDLE hPipe,
1019 __in BURN_EXECUTE_ACTION* pExecuteAction
1020 )
1021{
1022 HRESULT hr = S_OK;
1023 BYTE* pbData = NULL;
1024 SIZE_T cbData = 0;
1025 DWORD dwResult = 0;
1026 BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
1027
1028 // Serialize the message data.
1029 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->packageDependency.pPackage->sczId);
1030 ExitOnFailure(hr, "Failed to write package id to message buffer.");
1031
1032 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->packageDependency.sczBundleProviderKey);
1033 ExitOnFailure(hr, "Failed to write bundle dependency key to message buffer.");
1034
1035 hr = BuffWriteNumber(&pbData, &cbData, pExecuteAction->packageDependency.action);
1036 ExitOnFailure(hr, "Failed to write action to message buffer.");
1037
1038 // Send the message.
1039 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY, pbData, cbData, NULL, NULL, &dwResult);
1040 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY message to per-machine process.");
1041
1042 // Ignore the restart since this action only results in registry writes.
1043 hr = ProcessResult(dwResult, &restart);
1044
1045LExit:
1046 ReleaseBuffer(pbData);
1047
1048 return hr;
1049}
1050
1051/*******************************************************************
1052 ElevationLoadCompatiblePackageAction - Load compatible package
1053 information from the referenced package.
1054
1055*******************************************************************/
1056extern "C" HRESULT ElevationLoadCompatiblePackageAction(
1057 __in HANDLE hPipe,
1058 __in BURN_EXECUTE_ACTION* pExecuteAction
1059 )
1060{
1061 HRESULT hr = S_OK;
1062 BYTE* pbData = NULL;
1063 SIZE_T cbData = 0;
1064 DWORD dwResult = 0;
1065 BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
1066
1067 // Serialize message data.
1068 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->compatiblePackage.pReferencePackage->sczId);
1069 ExitOnFailure(hr, "Failed to write package id to message buffer.");
1070
1071 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->compatiblePackage.sczInstalledProductCode);
1072 ExitOnFailure(hr, "Failed to write installed ProductCode to message buffer.");
1073
1074 hr = BuffWriteNumber64(&pbData, &cbData, pExecuteAction->compatiblePackage.qwInstalledVersion);
1075 ExitOnFailure(hr, "Failed to write installed version to message buffer.");
1076
1077 // Send the message.
1078 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_LOAD_COMPATIBLE_PACKAGE, pbData, cbData, NULL, NULL, &dwResult);
1079 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_LOAD_COMPATIBLE_PACKAGE message to per-machine process.");
1080
1081 // Ignore the restart since this action only loads data into memory.
1082 hr = ProcessResult(dwResult, &restart);
1083
1084LExit:
1085 ReleaseBuffer(pbData);
1086
1087 return hr;
1088}
1089
1090/*******************************************************************
1091 ElevationCleanPackage -
1092
1093*******************************************************************/
1094extern "C" HRESULT ElevationCleanPackage(
1095 __in HANDLE hPipe,
1096 __in BURN_PACKAGE* pPackage
1097 )
1098{
1099 HRESULT hr = S_OK;
1100 BYTE* pbData = NULL;
1101 SIZE_T cbData = 0;
1102 DWORD dwResult = 0;
1103
1104 // serialize message data
1105 hr = BuffWriteString(&pbData, &cbData, pPackage->sczId);
1106 ExitOnFailure(hr, "Failed to write clean package id to message buffer.");
1107
1108 // send message
1109 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE, pbData, cbData, NULL, NULL, &dwResult);
1110 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE message to per-machine process.");
1111
1112 hr = (HRESULT)dwResult;
1113
1114LExit:
1115 ReleaseBuffer(pbData);
1116
1117 return hr;
1118}
1119
1120extern "C" HRESULT ElevationLaunchApprovedExe(
1121 __in HANDLE hPipe,
1122 __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe,
1123 __out DWORD* pdwProcessId
1124 )
1125{
1126 HRESULT hr = S_OK;
1127 BYTE* pbData = NULL;
1128 SIZE_T cbData = 0;
1129 DWORD dwResult = 0;
1130 BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT context = { };
1131
1132 // Serialize message data.
1133 hr = BuffWriteString(&pbData, &cbData, pLaunchApprovedExe->sczId);
1134 ExitOnFailure(hr, "Failed to write approved exe id to message buffer.");
1135
1136 hr = BuffWriteString(&pbData, &cbData, pLaunchApprovedExe->sczArguments);
1137 ExitOnFailure(hr, "Failed to write approved exe arguments to message buffer.");
1138
1139 hr = BuffWriteNumber(&pbData, &cbData, pLaunchApprovedExe->dwWaitForInputIdleTimeout);
1140 ExitOnFailure(hr, "Failed to write approved exe WaitForInputIdle timeout to message buffer.");
1141
1142 // Send the message.
1143 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE, pbData, cbData, ProcessLaunchApprovedExeMessages, &context, &dwResult);
1144 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE message to per-machine process.");
1145
1146 hr = (HRESULT)dwResult;
1147 *pdwProcessId = context.dwProcessId;
1148
1149LExit:
1150 ReleaseBuffer(pbData);
1151
1152 return hr;
1153}
1154
1155/*******************************************************************
1156 ElevationChildPumpMessages -
1157
1158*******************************************************************/
1159extern "C" HRESULT ElevationChildPumpMessages(
1160 __in DWORD dwLoggingTlsId,
1161 __in HANDLE hPipe,
1162 __in HANDLE hCachePipe,
1163 __in BURN_APPROVED_EXES* pApprovedExes,
1164 __in BURN_CONTAINERS* pContainers,
1165 __in BURN_PACKAGES* pPackages,
1166 __in BURN_PAYLOADS* pPayloads,
1167 __in BURN_VARIABLES* pVariables,
1168 __in BURN_REGISTRATION* pRegistration,
1169 __in BURN_USER_EXPERIENCE* pUserExperience,
1170 __out HANDLE* phLock,
1171 __out BOOL* pfDisabledAutomaticUpdates,
1172 __out DWORD* pdwChildExitCode,
1173 __out BOOL* pfRestart
1174 )
1175{
1176 HRESULT hr = S_OK;
1177 BURN_ELEVATION_CHILD_MESSAGE_CONTEXT cacheContext = { };
1178 BURN_ELEVATION_CHILD_MESSAGE_CONTEXT context = { };
1179 HANDLE hCacheThread = NULL;
1180 BURN_PIPE_RESULT result = { };
1181
1182 cacheContext.dwLoggingTlsId = dwLoggingTlsId;
1183 cacheContext.hPipe = hCachePipe;
1184 cacheContext.pContainers = pContainers;
1185 cacheContext.pPackages = pPackages;
1186 cacheContext.pPayloads = pPayloads;
1187 cacheContext.pVariables = pVariables;
1188 cacheContext.pRegistration = pRegistration;
1189 cacheContext.pUserExperience = pUserExperience;
1190
1191 context.dwLoggingTlsId = dwLoggingTlsId;
1192 context.hPipe = hPipe;
1193 context.phLock = phLock;
1194 context.pfDisabledAutomaticUpdates = pfDisabledAutomaticUpdates;
1195 context.pApprovedExes = pApprovedExes;
1196 context.pContainers = pContainers;
1197 context.pPackages = pPackages;
1198 context.pPayloads = pPayloads;
1199 context.pVariables = pVariables;
1200 context.pRegistration = pRegistration;
1201 context.pUserExperience = pUserExperience;
1202
1203 hCacheThread = ::CreateThread(NULL, 0, ElevatedChildCacheThreadProc, &cacheContext, 0, NULL);
1204 ExitOnNullWithLastError(hCacheThread, hr, "Failed to create elevated cache thread.");
1205
1206 hr = PipePumpMessages(hPipe, ProcessElevatedChildMessage, &context, &result);
1207 ExitOnFailure(hr, "Failed to pump messages in child process.");
1208
1209 // Wait for the cache thread and verify it gets the right result but don't fail if things
1210 // don't work out.
1211 WaitForElevatedChildCacheThread(hCacheThread, result.dwResult);
1212
1213 *pdwChildExitCode = result.dwResult;
1214 *pfRestart = result.fRestart;
1215
1216LExit:
1217 ReleaseHandle(hCacheThread);
1218
1219 return hr;
1220}
1221
1222extern "C" HRESULT ElevationChildResumeAutomaticUpdates()
1223{
1224 HRESULT hr = S_OK;
1225
1226 LogId(REPORT_STANDARD, MSG_RESUME_AU_STARTING);
1227
1228 hr = WuaResumeAutomaticUpdates();
1229 ExitOnFailure(hr, "Failed to resume automatic updates after pausing them, continuing...");
1230
1231 LogId(REPORT_STANDARD, MSG_RESUME_AU_SUCCEEDED);
1232
1233LExit:
1234 return hr;
1235}
1236
1237// internal function definitions
1238
1239static DWORD WINAPI ElevatedChildCacheThreadProc(
1240 __in LPVOID lpThreadParameter
1241 )
1242{
1243 HRESULT hr = S_OK;
1244 BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext = reinterpret_cast<BURN_ELEVATION_CHILD_MESSAGE_CONTEXT*>(lpThreadParameter);
1245 BOOL fComInitialized = FALSE;
1246 BURN_PIPE_RESULT result = { };
1247
1248 if (!::TlsSetValue(pContext->dwLoggingTlsId, pContext->hPipe))
1249 {
1250 ExitWithLastError(hr, "Failed to set elevated cache pipe into thread local storage for logging.");
1251 }
1252
1253 // initialize COM
1254 hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
1255 ExitOnFailure(hr, "Failed to initialize COM.");
1256 fComInitialized = TRUE;
1257
1258 hr = PipePumpMessages(pContext->hPipe, ProcessElevatedChildCacheMessage, pContext, &result);
1259 ExitOnFailure(hr, "Failed to pump messages in child process.");
1260
1261 hr = (HRESULT)result.dwResult;
1262
1263LExit:
1264 if (fComInitialized)
1265 {
1266 ::CoUninitialize();
1267 }
1268
1269 return (DWORD)hr;
1270}
1271
1272static HRESULT WaitForElevatedChildCacheThread(
1273 __in HANDLE hCacheThread,
1274 __in DWORD dwExpectedExitCode
1275 )
1276{
1277 UNREFERENCED_PARAMETER(dwExpectedExitCode);
1278
1279 HRESULT hr = S_OK;
1280 DWORD dwExitCode = ERROR_SUCCESS;
1281
1282 if (WAIT_OBJECT_0 != ::WaitForSingleObject(hCacheThread, BURN_TIMEOUT))
1283 {
1284 ExitWithLastError(hr, "Failed to wait for cache thread to terminate.");
1285 }
1286
1287 if (!::GetExitCodeThread(hCacheThread, &dwExitCode))
1288 {
1289 ExitWithLastError(hr, "Failed to get cache thread exit code.");
1290 }
1291
1292 AssertSz(dwExitCode == dwExpectedExitCode, "Cache thread should have exited with the expected exit code.");
1293
1294LExit:
1295 return hr;
1296}
1297
1298static HRESULT ProcessGenericExecuteMessages(
1299 __in BURN_PIPE_MESSAGE* pMsg,
1300 __in_opt LPVOID pvContext,
1301 __out DWORD* pdwResult
1302 )
1303{
1304 HRESULT hr = S_OK;
1305 SIZE_T iData = 0;
1306 BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT* pContext = static_cast<BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT*>(pvContext);
1307 LPWSTR sczMessage = NULL;
1308 DWORD cFiles = 0;
1309 LPWSTR* rgwzFiles = NULL;
1310 GENERIC_EXECUTE_MESSAGE message = { };
1311
1312 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.dwAllowedResults);
1313 ExitOnFailure(hr, "Failed to allowed results.");
1314
1315 // Process the message.
1316 switch (pMsg->dwMessage)
1317 {
1318 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS:
1319 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
1320
1321 // read message parameters
1322 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.progress.dwPercentage);
1323 ExitOnFailure(hr, "Failed to progress.");
1324 break;
1325
1326 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR:
1327 message.type = GENERIC_EXECUTE_MESSAGE_ERROR;
1328
1329 // read message parameters
1330 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.error.dwErrorCode);
1331 ExitOnFailure(hr, "Failed to read error code.");
1332
1333 hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &sczMessage);
1334 ExitOnFailure(hr, "Failed to read message.");
1335
1336 message.error.wzMessage = sczMessage;
1337 break;
1338
1339 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE:
1340 message.type = GENERIC_EXECUTE_MESSAGE_FILES_IN_USE;
1341
1342 // read message parameters
1343 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &cFiles);
1344 ExitOnFailure(hr, "Failed to read file count.");
1345
1346 rgwzFiles = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cFiles, TRUE);
1347 ExitOnNull(rgwzFiles, hr, E_OUTOFMEMORY, "Failed to allocate buffer for files in use.");
1348
1349 for (DWORD i = 0; i < cFiles; ++i)
1350 {
1351 hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &rgwzFiles[i]);
1352 ExitOnFailure(hr, "Failed to read file name: %u", i);
1353 }
1354
1355 message.filesInUse.cFiles = cFiles;
1356 message.filesInUse.rgwzFiles = (LPCWSTR*)rgwzFiles;
1357 break;
1358
1359 default:
1360 hr = E_INVALIDARG;
1361 ExitOnRootFailure(hr, "Invalid package message.");
1362 break;
1363 }
1364
1365 // send message
1366 *pdwResult = (DWORD)pContext->pfnGenericMessageHandler(&message, pContext->pvContext);;
1367
1368LExit:
1369 ReleaseStr(sczMessage);
1370
1371 if (rgwzFiles)
1372 {
1373 for (DWORD i = 0; i < cFiles; ++i)
1374 {
1375 ReleaseStr(rgwzFiles[i]);
1376 }
1377 MemFree(rgwzFiles);
1378 }
1379 return hr;
1380}
1381
1382static HRESULT ProcessMsiPackageMessages(
1383 __in BURN_PIPE_MESSAGE* pMsg,
1384 __in_opt LPVOID pvContext,
1385 __out DWORD* pdwResult
1386 )
1387{
1388 HRESULT hr = S_OK;
1389 SIZE_T iData = 0;
1390 WIU_MSI_EXECUTE_MESSAGE message = { };
1391 DWORD cMsiData = 0;
1392 LPWSTR* rgwzMsiData = NULL;
1393 BURN_ELEVATION_MSI_MESSAGE_CONTEXT* pContext = static_cast<BURN_ELEVATION_MSI_MESSAGE_CONTEXT*>(pvContext);
1394 LPWSTR sczMessage = NULL;
1395
1396 // Read MSI extended message data.
1397 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &cMsiData);
1398 ExitOnFailure(hr, "Failed to read MSI data count.");
1399
1400 if (cMsiData)
1401 {
1402 rgwzMsiData = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cMsiData, TRUE);
1403 ExitOnNull(rgwzMsiData, hr, E_OUTOFMEMORY, "Failed to allocate buffer to read MSI data.");
1404
1405 for (DWORD i = 0; i < cMsiData; ++i)
1406 {
1407 hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &rgwzMsiData[i]);
1408 ExitOnFailure(hr, "Failed to read MSI data: %u", i);
1409 }
1410
1411 message.cData = cMsiData;
1412 message.rgwzData = (LPCWSTR*)rgwzMsiData;
1413 }
1414
1415 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, (DWORD*)&message.dwAllowedResults);
1416 ExitOnFailure(hr, "Failed to read UI flags.");
1417
1418 // Process the rest of the message.
1419 switch (pMsg->dwMessage)
1420 {
1421 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS:
1422 // read message parameters
1423 message.type = WIU_MSI_EXECUTE_MESSAGE_PROGRESS;
1424
1425 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.progress.dwPercentage);
1426 ExitOnFailure(hr, "Failed to read progress.");
1427 break;
1428
1429 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR:
1430 // read message parameters
1431 message.type = WIU_MSI_EXECUTE_MESSAGE_ERROR;
1432
1433 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &message.error.dwErrorCode);
1434 ExitOnFailure(hr, "Failed to read error code.");
1435
1436 hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &sczMessage);
1437 ExitOnFailure(hr, "Failed to read message.");
1438 message.error.wzMessage = sczMessage;
1439 break;
1440
1441 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE:
1442 // read message parameters
1443 message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE;
1444
1445 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, (DWORD*)&message.msiMessage.mt);
1446 ExitOnFailure(hr, "Failed to read message type.");
1447
1448 hr = BuffReadString((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &sczMessage);
1449 ExitOnFailure(hr, "Failed to read message.");
1450 message.msiMessage.wzMessage = sczMessage;
1451 break;
1452
1453 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE:
1454 message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE;
1455 message.msiFilesInUse.cFiles = cMsiData;
1456 message.msiFilesInUse.rgwzFiles = (LPCWSTR*)rgwzMsiData;
1457 break;
1458
1459 default:
1460 hr = E_INVALIDARG;
1461 ExitOnRootFailure(hr, "Invalid package message.");
1462 break;
1463 }
1464
1465 // send message
1466 *pdwResult = (DWORD)pContext->pfnMessageHandler(&message, pContext->pvContext);
1467
1468LExit:
1469 ReleaseStr(sczMessage);
1470
1471 if (rgwzMsiData)
1472 {
1473 for (DWORD i = 0; i < cMsiData; ++i)
1474 {
1475 ReleaseStr(rgwzMsiData[i]);
1476 }
1477
1478 MemFree(rgwzMsiData);
1479 }
1480
1481 return hr;
1482}
1483
1484static HRESULT ProcessLaunchApprovedExeMessages(
1485 __in BURN_PIPE_MESSAGE* pMsg,
1486 __in_opt LPVOID pvContext,
1487 __out DWORD* pdwResult
1488 )
1489{
1490 HRESULT hr = S_OK;
1491 SIZE_T iData = 0;
1492 BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT* pContext = static_cast<BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT*>(pvContext);
1493 DWORD dwProcessId = 0;
1494
1495 // Process the message.
1496 switch (pMsg->dwMessage)
1497 {
1498 case BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID:
1499 // read message parameters
1500 hr = BuffReadNumber((BYTE*)pMsg->pvData, pMsg->cbData, &iData, &dwProcessId);
1501 ExitOnFailure(hr, "Failed to read approved exe process id.");
1502 pContext->dwProcessId = dwProcessId;
1503 break;
1504
1505 default:
1506 hr = E_INVALIDARG;
1507 ExitOnRootFailure(hr, "Invalid launch approved exe message.");
1508 break;
1509 }
1510
1511 *pdwResult = static_cast<DWORD>(hr);
1512
1513LExit:
1514 return hr;
1515}
1516
1517static HRESULT ProcessElevatedChildMessage(
1518 __in BURN_PIPE_MESSAGE* pMsg,
1519 __in_opt LPVOID pvContext,
1520 __out DWORD* pdwResult
1521)
1522{
1523 HRESULT hr = S_OK;
1524 BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext = static_cast<BURN_ELEVATION_CHILD_MESSAGE_CONTEXT*>(pvContext);
1525 HRESULT hrResult = S_OK;
1526 DWORD dwPid = 0;
1527
1528 switch (pMsg->dwMessage)
1529 {
1530 case BURN_ELEVATION_TRANSACTION_BEGIN:
1531 hrResult = OnMsiBeginTransaction(pContext);
1532 break;
1533
1534 case BURN_ELEVATION_TRANSACTION_COMMIT:
1535 hrResult = OnMsiCommitTransaction(pContext);
1536 break;
1537
1538 case BURN_ELEVATION_TRANSACTION_ROLLBACK:
1539 hrResult = OnMsiRollbackTransaction(pContext);
1540 break;
1541
1542 case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE:
1543 hrResult = OnApplyInitialize(pContext->pVariables, pContext->pRegistration, pContext->phLock, pContext->pfDisabledAutomaticUpdates, (BYTE*)pMsg->pvData, pMsg->cbData);
1544 break;
1545
1546 case BURN_ELEVATION_MESSAGE_TYPE_APPLY_UNINITIALIZE:
1547 hrResult = OnApplyUninitialize(pContext->phLock);
1548 break;
1549
1550 case BURN_ELEVATION_MESSAGE_TYPE_SESSION_BEGIN:
1551 hrResult = OnSessionBegin(pContext->pRegistration, pContext->pVariables, pContext->pUserExperience, (BYTE*)pMsg->pvData, pMsg->cbData);
1552 break;
1553
1554 case BURN_ELEVATION_MESSAGE_TYPE_SESSION_RESUME:
1555 hrResult = OnSessionResume(pContext->pRegistration, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
1556 break;
1557
1558 case BURN_ELEVATION_MESSAGE_TYPE_SESSION_END:
1559 hrResult = OnSessionEnd(pContext->pRegistration, (BYTE*)pMsg->pvData, pMsg->cbData);
1560 break;
1561
1562 case BURN_ELEVATION_MESSAGE_TYPE_SAVE_STATE:
1563 hrResult = OnSaveState(pContext->pRegistration, (BYTE*)pMsg->pvData, pMsg->cbData);
1564 break;
1565
1566 case BURN_ELEVATION_MESSAGE_TYPE_PROCESS_DEPENDENT_REGISTRATION:
1567 hrResult = OnProcessDependentRegistration(pContext->pRegistration, (BYTE*)pMsg->pvData, pMsg->cbData);
1568 break;
1569
1570 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_EXE_PACKAGE:
1571 hrResult = OnExecuteExePackage(pContext->hPipe, pContext->pPackages, &pContext->pRegistration->relatedBundles, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
1572 break;
1573
1574 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_PACKAGE:
1575 hrResult = OnExecuteMsiPackage(pContext->hPipe, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
1576 break;
1577
1578 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSP_PACKAGE:
1579 hrResult = OnExecuteMspPackage(pContext->hPipe, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
1580 break;
1581
1582 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSU_PACKAGE:
1583 hrResult = OnExecuteMsuPackage(pContext->hPipe, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
1584 break;
1585
1586 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_PROVIDER:
1587 hrResult = OnExecutePackageProviderAction(pContext->pPackages, &pContext->pRegistration->relatedBundles, (BYTE*)pMsg->pvData, pMsg->cbData);
1588 break;
1589
1590 case BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PACKAGE_DEPENDENCY:
1591 hrResult = OnExecutePackageDependencyAction(pContext->pPackages, &pContext->pRegistration->relatedBundles, (BYTE*)pMsg->pvData, pMsg->cbData);
1592 break;
1593
1594 case BURN_ELEVATION_MESSAGE_TYPE_LOAD_COMPATIBLE_PACKAGE:
1595 hrResult = OnLoadCompatiblePackage(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData);
1596 break;
1597
1598 case BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE:
1599 hrResult = OnCleanPackage(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData);
1600 break;
1601
1602 case BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE:
1603 hrResult = OnLaunchApprovedExe(pContext->hPipe, pContext->pApprovedExes, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
1604 break;
1605
1606 default:
1607 hr = E_INVALIDARG;
1608 ExitOnRootFailure(hr, "Unexpected elevated message sent to child process, msg: %u", pMsg->dwMessage);
1609 }
1610
1611 *pdwResult = dwPid ? dwPid : (DWORD)hrResult;
1612
1613LExit:
1614 return hr;
1615}
1616
1617static HRESULT ProcessElevatedChildCacheMessage(
1618 __in BURN_PIPE_MESSAGE* pMsg,
1619 __in_opt LPVOID pvContext,
1620 __out DWORD* pdwResult
1621 )
1622{
1623 HRESULT hr = S_OK;
1624 BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext = static_cast<BURN_ELEVATION_CHILD_MESSAGE_CONTEXT*>(pvContext);
1625 HRESULT hrResult = S_OK;
1626
1627 switch (pMsg->dwMessage)
1628 {
1629 case BURN_ELEVATION_MESSAGE_TYPE_LAYOUT_BUNDLE:
1630 hrResult = OnLayoutBundle(pContext->pRegistration->sczExecutableName, (BYTE*)pMsg->pvData, pMsg->cbData);
1631 break;
1632
1633 case BURN_ELEVATION_MESSAGE_TYPE_CACHE_OR_LAYOUT_CONTAINER_OR_PAYLOAD:
1634 hrResult = OnCacheOrLayoutContainerOrPayload(pContext->pContainers, pContext->pPackages, pContext->pPayloads, (BYTE*)pMsg->pvData, pMsg->cbData);
1635 break;
1636
1637 case BURN_ELEVATION_MESSAGE_TYPE_CACHE_CLEANUP:
1638 OnCacheCleanup(pContext->pRegistration->sczId);
1639 hrResult = S_OK;
1640 break;
1641
1642 case BURN_ELEVATION_MESSAGE_TYPE_CLEAN_PACKAGE:
1643 hrResult = OnCleanPackage(pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData);
1644 break;
1645
1646 default:
1647 hr = E_INVALIDARG;
1648 ExitOnRootFailure(hr, "Unexpected elevated cache message sent to child process, msg: %u", pMsg->dwMessage);
1649 }
1650
1651 *pdwResult = (DWORD)hrResult;
1652
1653LExit:
1654 return hr;
1655}
1656
1657static HRESULT ProcessResult(
1658 __in DWORD dwResult,
1659 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
1660 )
1661{
1662 HRESULT hr = static_cast<HRESULT>(dwResult);
1663 if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == hr)
1664 {
1665 *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
1666 hr = S_OK;
1667 }
1668 else if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED) == hr)
1669 {
1670 *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
1671 hr = S_OK;
1672 }
1673
1674 return hr;
1675}
1676
1677static HRESULT OnMsiBeginTransaction(
1678 __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext
1679)
1680{
1681 UINT uResult = ERROR_SUCCESS;
1682 HRESULT hr = S_OK;
1683
1684 pContext->hMsiTrns = NULL;
1685 pContext->hMsiTrnsEvent = NULL;
1686 uResult = MsiBeginTransaction(L"WiX", 0, &pContext->hMsiTrns, &pContext->hMsiTrnsEvent);
1687 ExitOnWin32Error(uResult, hr, "Failed beginning an MSI transaction");
1688
1689LExit:
1690 return hr;
1691}
1692
1693static HRESULT OnMsiCommitTransaction(
1694 __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext
1695)
1696{
1697 UINT uResult = ERROR_SUCCESS;
1698 HRESULT hr = S_OK;
1699
1700 uResult = MsiEndTransaction(MSITRANSACTIONSTATE_COMMIT);
1701 ExitOnWin32Error(uResult, hr, "Failed committing an MSI transaction");
1702
1703LExit:
1704 pContext->hMsiTrns = NULL;
1705 pContext->hMsiTrnsEvent = NULL;
1706 return hr;
1707}
1708
1709static HRESULT OnMsiRollbackTransaction(
1710 __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext
1711) {
1712 UINT uResult = ERROR_SUCCESS;
1713 HRESULT hr = S_OK;
1714
1715 uResult = MsiEndTransaction(MSITRANSACTIONSTATE_ROLLBACK);
1716 ExitOnWin32Error(uResult, hr, "Failed rolling back an MSI transaction");
1717
1718LExit:
1719 pContext->hMsiTrns = NULL;
1720 pContext->hMsiTrnsEvent = NULL;
1721 return hr;
1722}
1723
1724static HRESULT OnApplyInitialize(
1725 __in BURN_VARIABLES* pVariables,
1726 __in BURN_REGISTRATION* pRegistration,
1727 __in HANDLE* phLock,
1728 __in BOOL* pfDisabledWindowsUpdate,
1729 __in BYTE* pbData,
1730 __in DWORD cbData
1731 )
1732{
1733 HRESULT hr = S_OK;
1734 SIZE_T iData = 0;
1735 DWORD dwAction = 0;
1736 DWORD dwAUAction = 0;
1737 DWORD dwTakeSystemRestorePoint = 0;
1738 LPWSTR sczBundleName = NULL;
1739
1740 // Deserialize message data.
1741 hr = BuffReadNumber(pbData, cbData, &iData, &dwAction);
1742 ExitOnFailure(hr, "Failed to read action.");
1743
1744 hr = BuffReadNumber(pbData, cbData, &iData, &dwAUAction);
1745 ExitOnFailure(hr, "Failed to read update action.");
1746
1747 hr = BuffReadNumber(pbData, cbData, &iData, &dwTakeSystemRestorePoint);
1748 ExitOnFailure(hr, "Failed to read system restore point action.");
1749
1750 hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData);
1751 ExitOnFailure(hr, "Failed to read variables.");
1752
1753 // Initialize.
1754 hr = ApplyLock(TRUE, phLock);
1755 ExitOnFailure(hr, "Failed to acquire lock due to setup in other session.");
1756
1757 // Reset and reload the related bundles.
1758 RelatedBundlesUninitialize(&pRegistration->relatedBundles);
1759
1760 hr = RelatedBundlesInitializeForScope(TRUE, pRegistration, &pRegistration->relatedBundles);
1761 ExitOnFailure(hr, "Failed to initialize per-machine related bundles.");
1762
1763 // Attempt to pause AU with best effort.
1764 if (BURN_AU_PAUSE_ACTION_IFELEVATED == dwAUAction || BURN_AU_PAUSE_ACTION_IFELEVATED_NORESUME == dwAUAction)
1765 {
1766 LogId(REPORT_STANDARD, MSG_PAUSE_AU_STARTING);
1767
1768 hr = WuaPauseAutomaticUpdates();
1769 if (FAILED(hr))
1770 {
1771 LogId(REPORT_STANDARD, MSG_FAILED_PAUSE_AU, hr);
1772 hr = S_OK;
1773 }
1774 else
1775 {
1776 LogId(REPORT_STANDARD, MSG_PAUSE_AU_SUCCEEDED);
1777 if (BURN_AU_PAUSE_ACTION_IFELEVATED == dwAUAction)
1778 {
1779 *pfDisabledWindowsUpdate = TRUE;
1780 }
1781 }
1782 }
1783
1784 if (dwTakeSystemRestorePoint)
1785 {
1786 hr = VariableGetString(pVariables, BURN_BUNDLE_NAME, &sczBundleName);
1787 if (FAILED(hr))
1788 {
1789 hr = S_OK;
1790 ExitFunction();
1791 }
1792
1793 LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_STARTING);
1794
1795 BOOTSTRAPPER_ACTION action = static_cast<BOOTSTRAPPER_ACTION>(dwAction);
1796 SRP_ACTION restoreAction = (BOOTSTRAPPER_ACTION_INSTALL == action) ? SRP_ACTION_INSTALL : (BOOTSTRAPPER_ACTION_UNINSTALL == action) ? SRP_ACTION_UNINSTALL : SRP_ACTION_MODIFY;
1797 hr = SrpCreateRestorePoint(sczBundleName, restoreAction);
1798 if (SUCCEEDED(hr))
1799 {
1800 LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_SUCCEEDED);
1801 }
1802 else if (E_NOTIMPL == hr)
1803 {
1804 LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_DISABLED);
1805 hr = S_OK;
1806 }
1807 else if (FAILED(hr))
1808 {
1809 LogId(REPORT_STANDARD, MSG_SYSTEM_RESTORE_POINT_FAILED, hr);
1810 hr = S_OK;
1811 }
1812 }
1813
1814LExit:
1815 ReleaseStr(sczBundleName);
1816 return hr;
1817}
1818
1819static HRESULT OnApplyUninitialize(
1820 __in HANDLE* phLock
1821 )
1822{
1823 Assert(phLock);
1824
1825 // TODO: end system restore point.
1826
1827 if (*phLock)
1828 {
1829 ::ReleaseMutex(*phLock);
1830 ::CloseHandle(*phLock);
1831 *phLock = NULL;
1832 }
1833
1834 return S_OK;
1835}
1836
1837static HRESULT OnSessionBegin(
1838 __in BURN_REGISTRATION* pRegistration,
1839 __in BURN_VARIABLES* pVariables,
1840 __in BURN_USER_EXPERIENCE* pUserExperience,
1841 __in BYTE* pbData,
1842 __in DWORD cbData
1843 )
1844{
1845 HRESULT hr = S_OK;
1846 SIZE_T iData = 0;
1847 LPWSTR sczEngineWorkingPath = NULL;
1848 DWORD dwRegistrationOperations = 0;
1849 DWORD dwDependencyRegistrationAction = 0;
1850 DWORD64 qwEstimatedSize = 0;
1851
1852 // Deserialize message data.
1853 hr = BuffReadString(pbData, cbData, &iData, &sczEngineWorkingPath);
1854 ExitOnFailure(hr, "Failed to read engine working path.");
1855
1856 hr = BuffReadString(pbData, cbData, &iData, &pRegistration->sczResumeCommandLine);
1857 ExitOnFailure(hr, "Failed to read resume command line.");
1858
1859 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&pRegistration->fDisableResume);
1860 ExitOnFailure(hr, "Failed to read resume flag.");
1861
1862 hr = BuffReadNumber(pbData, cbData, &iData, &dwRegistrationOperations);
1863 ExitOnFailure(hr, "Failed to read registration operations.");
1864
1865 hr = BuffReadNumber(pbData, cbData, &iData, &dwDependencyRegistrationAction);
1866 ExitOnFailure(hr, "Failed to read dependency registration action.");
1867
1868 hr = BuffReadNumber64(pbData, cbData, &iData, &qwEstimatedSize);
1869 ExitOnFailure(hr, "Failed to read estimated size.");
1870
1871 hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData);
1872 ExitOnFailure(hr, "Failed to read variables.");
1873
1874 // Begin session in per-machine process.
1875 hr = RegistrationSessionBegin(sczEngineWorkingPath, pRegistration, pVariables, pUserExperience, dwRegistrationOperations, (BURN_DEPENDENCY_REGISTRATION_ACTION)dwDependencyRegistrationAction, qwEstimatedSize);
1876 ExitOnFailure(hr, "Failed to begin registration session.");
1877
1878LExit:
1879 ReleaseStr(sczEngineWorkingPath);
1880
1881 return hr;
1882}
1883
1884static HRESULT OnSessionResume(
1885 __in BURN_REGISTRATION* pRegistration,
1886 __in BURN_VARIABLES* pVariables,
1887 __in BYTE* pbData,
1888 __in DWORD cbData
1889 )
1890{
1891 HRESULT hr = S_OK;
1892 SIZE_T iData = 0;
1893
1894 // Deserialize message data.
1895 hr = BuffReadString(pbData, cbData, &iData, &pRegistration->sczResumeCommandLine);
1896 ExitOnFailure(hr, "Failed to read resume command line.");
1897
1898 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&pRegistration->fDisableResume);
1899 ExitOnFailure(hr, "Failed to read resume flag.");
1900
1901 hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData);
1902 ExitOnFailure(hr, "Failed to read variables.");
1903
1904 // resume session in per-machine process
1905 hr = RegistrationSessionResume(pRegistration, pVariables);
1906 ExitOnFailure(hr, "Failed to resume registration session.");
1907
1908LExit:
1909 return hr;
1910}
1911
1912static HRESULT OnSessionEnd(
1913 __in BURN_REGISTRATION* pRegistration,
1914 __in BYTE* pbData,
1915 __in DWORD cbData
1916 )
1917{
1918 HRESULT hr = S_OK;
1919 SIZE_T iData = 0;
1920 DWORD dwResumeMode = 0;
1921 DWORD dwRestart = 0;
1922 DWORD dwDependencyRegistrationAction = 0;
1923
1924 // Deserialize message data.
1925 hr = BuffReadNumber(pbData, cbData, &iData, &dwResumeMode);
1926 ExitOnFailure(hr, "Failed to read resume mode enum.");
1927
1928 hr = BuffReadNumber(pbData, cbData, &iData, &dwRestart);
1929 ExitOnFailure(hr, "Failed to read restart enum.");
1930
1931 hr = BuffReadNumber(pbData, cbData, &iData, &dwDependencyRegistrationAction);
1932 ExitOnFailure(hr, "Failed to read dependency registration action.");
1933
1934 // suspend session in per-machine process
1935 hr = RegistrationSessionEnd(pRegistration, (BURN_RESUME_MODE)dwResumeMode, (BOOTSTRAPPER_APPLY_RESTART)dwRestart, (BURN_DEPENDENCY_REGISTRATION_ACTION)dwDependencyRegistrationAction);
1936 ExitOnFailure(hr, "Failed to suspend registration session.");
1937
1938LExit:
1939 return hr;
1940}
1941
1942static HRESULT OnSaveState(
1943 __in BURN_REGISTRATION* pRegistration,
1944 __in BYTE* pbData,
1945 __in DWORD cbData
1946 )
1947{
1948 HRESULT hr = S_OK;
1949
1950 // save state in per-machine process
1951 hr = RegistrationSaveState(pRegistration, pbData, cbData);
1952 ExitOnFailure(hr, "Failed to save state.");
1953
1954LExit:
1955 return hr;
1956}
1957
1958static HRESULT OnLayoutBundle(
1959 __in_z LPCWSTR wzExecutableName,
1960 __in BYTE* pbData,
1961 __in DWORD cbData
1962 )
1963{
1964 HRESULT hr = S_OK;
1965 SIZE_T iData = 0;
1966 LPWSTR sczLayoutDirectory = NULL;
1967 LPWSTR sczUnverifiedPath = NULL;
1968
1969 // Deserialize message data.
1970 hr = BuffReadString(pbData, cbData, &iData, &sczLayoutDirectory);
1971 ExitOnFailure(hr, "Failed to read layout directory.");
1972
1973 hr = BuffReadString(pbData, cbData, &iData, &sczUnverifiedPath);
1974 ExitOnFailure(hr, "Failed to read unverified bundle path.");
1975
1976 // Layout the bundle.
1977 hr = CacheLayoutBundle(wzExecutableName, sczLayoutDirectory, sczUnverifiedPath);
1978 ExitOnFailure(hr, "Failed to layout bundle from: %ls", sczUnverifiedPath);
1979
1980LExit:
1981 ReleaseStr(sczUnverifiedPath);
1982 ReleaseStr(sczLayoutDirectory);
1983
1984 return hr;
1985}
1986
1987static HRESULT OnCacheOrLayoutContainerOrPayload(
1988 __in BURN_CONTAINERS* pContainers,
1989 __in BURN_PACKAGES* pPackages,
1990 __in BURN_PAYLOADS* pPayloads,
1991 __in BYTE* pbData,
1992 __in DWORD cbData
1993 )
1994{
1995 HRESULT hr = S_OK;
1996 SIZE_T iData = 0;
1997 LPWSTR scz = NULL;
1998 BURN_CONTAINER* pContainer = NULL;
1999 BURN_PACKAGE* pPackage = NULL;
2000 BURN_PAYLOAD* pPayload = NULL;
2001 LPWSTR sczLayoutDirectory = NULL;
2002 LPWSTR sczUnverifiedPath = NULL;
2003 BOOL fMove = FALSE;
2004
2005 // Deserialize message data.
2006 hr = BuffReadString(pbData, cbData, &iData, &scz);
2007 ExitOnFailure(hr, "Failed to read package id.");
2008
2009 if (scz && *scz)
2010 {
2011 hr = ContainerFindById(pContainers, scz, &pContainer);
2012 ExitOnFailure(hr, "Failed to find container: %ls", scz);
2013 }
2014
2015 hr = BuffReadString(pbData, cbData, &iData, &scz);
2016 ExitOnFailure(hr, "Failed to read package id.");
2017
2018 if (scz && *scz)
2019 {
2020 hr = PackageFindById(pPackages, scz, &pPackage);
2021 ExitOnFailure(hr, "Failed to find package: %ls", scz);
2022 }
2023
2024 hr = BuffReadString(pbData, cbData, &iData, &scz);
2025 ExitOnFailure(hr, "Failed to read payload id.");
2026
2027 if (scz && *scz)
2028 {
2029 hr = PayloadFindById(pPayloads, scz, &pPayload);
2030 ExitOnFailure(hr, "Failed to find payload: %ls", scz);
2031 }
2032
2033 hr = BuffReadString(pbData, cbData, &iData, &sczLayoutDirectory);
2034 ExitOnFailure(hr, "Failed to read layout directory.");
2035
2036 hr = BuffReadString(pbData, cbData, &iData, &sczUnverifiedPath);
2037 ExitOnFailure(hr, "Failed to read unverified path.");
2038
2039 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fMove);
2040 ExitOnFailure(hr, "Failed to read move flag.");
2041
2042 // Layout payload.
2043 if (sczLayoutDirectory && *sczLayoutDirectory)
2044 {
2045 if (pContainer)
2046 {
2047 Assert(!pPackage);
2048 Assert(!pPayload);
2049
2050 hr = CacheLayoutContainer(pContainer, sczLayoutDirectory, sczUnverifiedPath, fMove);
2051 ExitOnFailure(hr, "Failed to layout container from: %ls to %ls", sczUnverifiedPath, sczLayoutDirectory);
2052 }
2053 else
2054 {
2055 hr = CacheLayoutPayload(pPayload, sczLayoutDirectory, sczUnverifiedPath, fMove);
2056 ExitOnFailure(hr, "Failed to layout payload from: %ls to %ls", sczUnverifiedPath, sczLayoutDirectory);
2057 }
2058 }
2059 else if (pPackage) // complete payload.
2060 {
2061 Assert(!pContainer);
2062
2063 hr = CacheCompletePayload(pPackage->fPerMachine, pPayload, pPackage->sczCacheId, sczUnverifiedPath, fMove);
2064 ExitOnFailure(hr, "Failed to cache payload: %ls", pPayload->sczKey);
2065 }
2066 else
2067 {
2068 hr = E_INVALIDARG;
2069 ExitOnRootFailure(hr, "Invalid data passed to cache or layout payload.");
2070 }
2071
2072LExit:
2073 ReleaseStr(sczUnverifiedPath);
2074 ReleaseStr(sczLayoutDirectory);
2075 ReleaseStr(scz);
2076
2077 return hr;
2078}
2079
2080static void OnCacheCleanup(
2081 __in_z LPCWSTR wzBundleId
2082 )
2083{
2084 CacheCleanup(TRUE, wzBundleId);
2085}
2086
2087static HRESULT OnProcessDependentRegistration(
2088 __in const BURN_REGISTRATION* pRegistration,
2089 __in BYTE* pbData,
2090 __in DWORD cbData
2091 )
2092{
2093 HRESULT hr = S_OK;
2094 SIZE_T iData = 0;
2095 BURN_DEPENDENT_REGISTRATION_ACTION action = { };
2096
2097 // Deserialize message data.
2098 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&action.type);
2099 ExitOnFailure(hr, "Failed to read action type.");
2100
2101 hr = BuffReadString(pbData, cbData, &iData, &action.sczBundleId);
2102 ExitOnFailure(hr, "Failed to read bundle id.");
2103
2104 hr = BuffReadString(pbData, cbData, &iData, &action.sczDependentProviderKey);
2105 ExitOnFailure(hr, "Failed to read dependent provider key.");
2106
2107 // Execute the registration action.
2108 hr = DependencyProcessDependentRegistration(pRegistration, &action);
2109 ExitOnFailure(hr, "Failed to execute dependent registration action for provider key: %ls", action.sczDependentProviderKey);
2110
2111LExit:
2112 // TODO: do the right thing here.
2113 //DependencyUninitializeRegistrationAction(&action);
2114 ReleaseStr(action.sczDependentProviderKey);
2115 ReleaseStr(action.sczBundleId)
2116
2117 return hr;
2118}
2119
2120static HRESULT OnExecuteExePackage(
2121 __in HANDLE hPipe,
2122 __in BURN_PACKAGES* pPackages,
2123 __in BURN_RELATED_BUNDLES* pRelatedBundles,
2124 __in BURN_VARIABLES* pVariables,
2125 __in BYTE* pbData,
2126 __in DWORD cbData
2127 )
2128{
2129 HRESULT hr = S_OK;
2130 SIZE_T iData = 0;
2131 LPWSTR sczPackage = NULL;
2132 DWORD dwRollback = 0;
2133 BURN_EXECUTE_ACTION executeAction = { };
2134 LPWSTR sczIgnoreDependencies = NULL;
2135 LPWSTR sczAncestors = NULL;
2136 BOOTSTRAPPER_APPLY_RESTART exeRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
2137
2138 executeAction.type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE;
2139
2140 // Deserialize message data.
2141 hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2142 ExitOnFailure(hr, "Failed to read EXE package id.");
2143
2144 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.exePackage.action);
2145 ExitOnFailure(hr, "Failed to read action.");
2146
2147 hr = BuffReadNumber(pbData, cbData, &iData, &dwRollback);
2148 ExitOnFailure(hr, "Failed to read rollback.");
2149
2150 hr = BuffReadString(pbData, cbData, &iData, &sczIgnoreDependencies);
2151 ExitOnFailure(hr, "Failed to read the list of dependencies to ignore.");
2152
2153 hr = BuffReadString(pbData, cbData, &iData, &sczAncestors);
2154 ExitOnFailure(hr, "Failed to read the list of ancestors.");
2155
2156 hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData);
2157 ExitOnFailure(hr, "Failed to read variables.");
2158
2159 hr = PackageFindById(pPackages, sczPackage, &executeAction.exePackage.pPackage);
2160 if (E_NOTFOUND == hr)
2161 {
2162 hr = PackageFindRelatedById(pRelatedBundles, sczPackage, &executeAction.exePackage.pPackage);
2163 }
2164 ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
2165
2166 // Pass the list of dependencies to ignore, if any, to the related bundle.
2167 if (sczIgnoreDependencies && *sczIgnoreDependencies)
2168 {
2169 hr = StrAllocString(&executeAction.exePackage.sczIgnoreDependencies, sczIgnoreDependencies, 0);
2170 ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore.");
2171 }
2172
2173 // Pass the list of ancestors, if any, to the related bundle.
2174 if (sczAncestors && *sczAncestors)
2175 {
2176 hr = StrAllocString(&executeAction.exePackage.sczAncestors, sczAncestors, 0);
2177 ExitOnFailure(hr, "Failed to allocate the list of ancestors.");
2178 }
2179
2180 // Execute EXE package.
2181 hr = ExeEngineExecutePackage(&executeAction, pVariables, static_cast<BOOL>(dwRollback), GenericExecuteMessageHandler, hPipe, &exeRestart);
2182 ExitOnFailure(hr, "Failed to execute EXE package.");
2183
2184LExit:
2185 ReleaseStr(sczAncestors);
2186 ReleaseStr(sczIgnoreDependencies);
2187 ReleaseStr(sczPackage);
2188 PlanUninitializeExecuteAction(&executeAction);
2189
2190 if (SUCCEEDED(hr))
2191 {
2192 if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == exeRestart)
2193 {
2194 hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED);
2195 }
2196 else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == exeRestart)
2197 {
2198 hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED);
2199 }
2200 }
2201
2202 return hr;
2203}
2204
2205static HRESULT OnExecuteMsiPackage(
2206 __in HANDLE hPipe,
2207 __in BURN_PACKAGES* pPackages,
2208 __in BURN_VARIABLES* pVariables,
2209 __in BYTE* pbData,
2210 __in DWORD cbData
2211 )
2212{
2213 HRESULT hr = S_OK;
2214 SIZE_T iData = 0;
2215 LPWSTR sczPackage = NULL;
2216 HWND hwndParent = NULL;
2217 BOOL fRollback = 0;
2218 BURN_EXECUTE_ACTION executeAction = { };
2219 BOOTSTRAPPER_APPLY_RESTART msiRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
2220
2221 executeAction.type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE;
2222
2223 // Deserialize message data.
2224 hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2225 ExitOnFailure(hr, "Failed to read MSI package id.");
2226
2227 hr = PackageFindById(pPackages, sczPackage, &executeAction.msiPackage.pPackage);
2228 ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
2229
2230 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&hwndParent);
2231 ExitOnFailure(hr, "Failed to read parent hwnd.");
2232
2233 hr = BuffReadString(pbData, cbData, &iData, &executeAction.msiPackage.sczLogPath);
2234 ExitOnFailure(hr, "Failed to read package log.");
2235
2236 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.uiLevel);
2237 ExitOnFailure(hr, "Failed to read UI level.");
2238
2239 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.action);
2240 ExitOnFailure(hr, "Failed to read action.");
2241
2242 // Read feature actions.
2243 if (executeAction.msiPackage.pPackage->Msi.cFeatures)
2244 {
2245 executeAction.msiPackage.rgFeatures = (BOOTSTRAPPER_FEATURE_ACTION*)MemAlloc(executeAction.msiPackage.pPackage->Msi.cFeatures * sizeof(BOOTSTRAPPER_FEATURE_ACTION), TRUE);
2246 ExitOnNull(executeAction.msiPackage.rgFeatures, hr, E_OUTOFMEMORY, "Failed to allocate memory for feature actions.");
2247
2248 for (DWORD i = 0; i < executeAction.msiPackage.pPackage->Msi.cFeatures; ++i)
2249 {
2250 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.rgFeatures[i]);
2251 ExitOnFailure(hr, "Failed to read feature action.");
2252 }
2253 }
2254
2255 // Read slipstream patches actions.
2256 if (executeAction.msiPackage.pPackage->Msi.cSlipstreamMspPackages)
2257 {
2258 executeAction.msiPackage.rgSlipstreamPatches = (BOOTSTRAPPER_ACTION_STATE*)MemAlloc(executeAction.msiPackage.pPackage->Msi.cSlipstreamMspPackages * sizeof(BOOTSTRAPPER_ACTION_STATE), TRUE);
2259 ExitOnNull(executeAction.msiPackage.rgSlipstreamPatches, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream patch actions.");
2260
2261 for (DWORD i = 0; i < executeAction.msiPackage.pPackage->Msi.cSlipstreamMspPackages; ++i)
2262 {
2263 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.msiPackage.rgSlipstreamPatches[i]);
2264 ExitOnFailure(hr, "Failed to read slipstream action.");
2265 }
2266 }
2267
2268 hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData);
2269 ExitOnFailure(hr, "Failed to read variables.");
2270
2271 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fRollback);
2272 ExitOnFailure(hr, "Failed to read rollback flag.");
2273
2274 // Execute MSI package.
2275 hr = MsiEngineExecutePackage(hwndParent, &executeAction, pVariables, fRollback, MsiExecuteMessageHandler, hPipe, &msiRestart);
2276 ExitOnFailure(hr, "Failed to execute MSI package.");
2277
2278LExit:
2279 ReleaseStr(sczPackage);
2280 PlanUninitializeExecuteAction(&executeAction);
2281
2282 if (SUCCEEDED(hr))
2283 {
2284 if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == msiRestart)
2285 {
2286 hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED);
2287 }
2288 else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == msiRestart)
2289 {
2290 hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED);
2291 }
2292 }
2293
2294 return hr;
2295}
2296
2297static HRESULT OnExecuteMspPackage(
2298 __in HANDLE hPipe,
2299 __in BURN_PACKAGES* pPackages,
2300 __in BURN_VARIABLES* pVariables,
2301 __in BYTE* pbData,
2302 __in DWORD cbData
2303 )
2304{
2305 HRESULT hr = S_OK;
2306 SIZE_T iData = 0;
2307 LPWSTR sczPackage = NULL;
2308 HWND hwndParent = NULL;
2309 BOOL fRollback = 0;
2310 BURN_EXECUTE_ACTION executeAction = { };
2311 BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
2312
2313 executeAction.type = BURN_EXECUTE_ACTION_TYPE_MSP_TARGET;
2314
2315 // Deserialize message data.
2316 hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2317 ExitOnFailure(hr, "Failed to read MSP package id.");
2318
2319 hr = PackageFindById(pPackages, sczPackage, &executeAction.mspTarget.pPackage);
2320 ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
2321
2322 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&hwndParent);
2323 ExitOnFailure(hr, "Failed to read parent hwnd.");
2324
2325 executeAction.mspTarget.fPerMachineTarget = TRUE; // we're in the elevated process, clearly we're targeting a per-machine product.
2326
2327 hr = BuffReadString(pbData, cbData, &iData, &executeAction.mspTarget.sczTargetProductCode);
2328 ExitOnFailure(hr, "Failed to read target product code.");
2329
2330 hr = BuffReadString(pbData, cbData, &iData, &executeAction.mspTarget.sczLogPath);
2331 ExitOnFailure(hr, "Failed to read package log.");
2332
2333 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.uiLevel);
2334 ExitOnFailure(hr, "Failed to read UI level.");
2335
2336 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.action);
2337 ExitOnFailure(hr, "Failed to read action.");
2338
2339 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.mspTarget.cOrderedPatches);
2340 ExitOnFailure(hr, "Failed to read count of ordered patches.");
2341
2342 if (executeAction.mspTarget.cOrderedPatches)
2343 {
2344 executeAction.mspTarget.rgOrderedPatches = (BURN_ORDERED_PATCHES*)MemAlloc(executeAction.mspTarget.cOrderedPatches * sizeof(BURN_ORDERED_PATCHES), TRUE);
2345 ExitOnNull(executeAction.mspTarget.rgOrderedPatches, hr, E_OUTOFMEMORY, "Failed to allocate memory for ordered patches.");
2346
2347 for (DWORD i = 0; i < executeAction.mspTarget.cOrderedPatches; ++i)
2348 {
2349 hr = BuffReadNumber(pbData, cbData, &iData, &executeAction.mspTarget.rgOrderedPatches[i].dwOrder);
2350 ExitOnFailure(hr, "Failed to read ordered patch order number.");
2351
2352 hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2353 ExitOnFailure(hr, "Failed to read ordered patch package id.");
2354
2355 hr = PackageFindById(pPackages, sczPackage, &executeAction.mspTarget.rgOrderedPatches[i].pPackage);
2356 ExitOnFailure(hr, "Failed to find ordered patch package: %ls", sczPackage);
2357 }
2358 }
2359
2360 hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData);
2361 ExitOnFailure(hr, "Failed to read variables.");
2362
2363 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fRollback);
2364 ExitOnFailure(hr, "Failed to read rollback flag.");
2365
2366 // Execute MSP package.
2367 hr = MspEngineExecutePackage(hwndParent, &executeAction, pVariables, fRollback, MsiExecuteMessageHandler, hPipe, &restart);
2368 ExitOnFailure(hr, "Failed to execute MSP package.");
2369
2370LExit:
2371 ReleaseStr(sczPackage);
2372 PlanUninitializeExecuteAction(&executeAction);
2373
2374 if (SUCCEEDED(hr))
2375 {
2376 if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == restart)
2377 {
2378 hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED);
2379 }
2380 else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart)
2381 {
2382 hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED);
2383 }
2384 }
2385
2386 return hr;
2387}
2388
2389static HRESULT OnExecuteMsuPackage(
2390 __in HANDLE hPipe,
2391 __in BURN_PACKAGES* pPackages,
2392 __in BURN_VARIABLES* pVariables,
2393 __in BYTE* pbData,
2394 __in DWORD cbData
2395 )
2396{
2397 HRESULT hr = S_OK;
2398 SIZE_T iData = 0;
2399 LPWSTR sczPackage = NULL;
2400 DWORD dwRollback = 0;
2401 DWORD dwStopWusaService = 0;
2402 BURN_EXECUTE_ACTION executeAction = { };
2403 BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
2404
2405 executeAction.type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE;
2406
2407 // Deserialize message data.
2408 hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2409 ExitOnFailure(hr, "Failed to read MSU package id.");
2410
2411 hr = BuffReadString(pbData, cbData, &iData, &executeAction.msuPackage.sczLogPath);
2412 ExitOnFailure(hr, "Failed to read package log.");
2413
2414 hr = BuffReadNumber(pbData, cbData, &iData, reinterpret_cast<DWORD*>(&executeAction.msuPackage.action));
2415 ExitOnFailure(hr, "Failed to read action.");
2416
2417 hr = BuffReadNumber(pbData, cbData, &iData, &dwRollback);
2418 ExitOnFailure(hr, "Failed to read rollback.");
2419
2420 hr = BuffReadNumber(pbData, cbData, &iData, &dwStopWusaService);
2421 ExitOnFailure(hr, "Failed to read StopWusaService.");
2422
2423 hr = PackageFindById(pPackages, sczPackage, &executeAction.msuPackage.pPackage);
2424 ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
2425
2426 // execute MSU package
2427 hr = MsuEngineExecutePackage(&executeAction, pVariables, static_cast<BOOL>(dwRollback), static_cast<BOOL>(dwStopWusaService), GenericExecuteMessageHandler, hPipe, &restart);
2428 ExitOnFailure(hr, "Failed to execute MSU package.");
2429
2430LExit:
2431 ReleaseStr(sczPackage);
2432 PlanUninitializeExecuteAction(&executeAction);
2433
2434 if (SUCCEEDED(hr))
2435 {
2436 if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == restart)
2437 {
2438 hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED);
2439 }
2440 else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart)
2441 {
2442 hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED);
2443 }
2444 }
2445
2446 return hr;
2447}
2448
2449static HRESULT OnExecutePackageProviderAction(
2450 __in BURN_PACKAGES* pPackages,
2451 __in BURN_RELATED_BUNDLES* pRelatedBundles,
2452 __in BYTE* pbData,
2453 __in DWORD cbData
2454 )
2455{
2456 HRESULT hr = S_OK;
2457 SIZE_T iData = 0;
2458 LPWSTR sczPackage = NULL;
2459 BURN_EXECUTE_ACTION executeAction = { };
2460
2461 executeAction.type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER;
2462
2463 // Deserialize the message data.
2464 hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2465 ExitOnFailure(hr, "Failed to read package id from message buffer.");
2466
2467 hr = BuffReadNumber(pbData, cbData, &iData, reinterpret_cast<DWORD*>(&executeAction.packageProvider.action));
2468 ExitOnFailure(hr, "Failed to read action.");
2469
2470 // Find the package again.
2471 hr = PackageFindById(pPackages, sczPackage, &executeAction.packageProvider.pPackage);
2472 if (E_NOTFOUND == hr)
2473 {
2474 hr = PackageFindRelatedById(pRelatedBundles, sczPackage, &executeAction.packageProvider.pPackage);
2475 }
2476 ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
2477
2478 // Execute the package provider action.
2479 hr = DependencyExecutePackageProviderAction(&executeAction);
2480 ExitOnFailure(hr, "Failed to execute package provider action.");
2481
2482LExit:
2483 ReleaseStr(sczPackage);
2484 PlanUninitializeExecuteAction(&executeAction);
2485
2486 return hr;
2487}
2488
2489static HRESULT OnExecutePackageDependencyAction(
2490 __in BURN_PACKAGES* pPackages,
2491 __in BURN_RELATED_BUNDLES* pRelatedBundles,
2492 __in BYTE* pbData,
2493 __in DWORD cbData
2494 )
2495{
2496 HRESULT hr = S_OK;
2497 SIZE_T iData = 0;
2498 LPWSTR sczPackage = NULL;
2499 BURN_EXECUTE_ACTION executeAction = { };
2500
2501 executeAction.type = BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY;
2502
2503 // Deserialize the message data.
2504 hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2505 ExitOnFailure(hr, "Failed to read package id from message buffer.");
2506
2507 hr = BuffReadString(pbData, cbData, &iData, &executeAction.packageDependency.sczBundleProviderKey);
2508 ExitOnFailure(hr, "Failed to read bundle dependency key from message buffer.");
2509
2510 hr = BuffReadNumber(pbData, cbData, &iData, reinterpret_cast<DWORD*>(&executeAction.packageDependency.action));
2511 ExitOnFailure(hr, "Failed to read action.");
2512
2513 // Find the package again.
2514 hr = PackageFindById(pPackages, sczPackage, &executeAction.packageDependency.pPackage);
2515 if (E_NOTFOUND == hr)
2516 {
2517 hr = PackageFindRelatedById(pRelatedBundles, sczPackage, &executeAction.packageDependency.pPackage);
2518 }
2519 ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
2520
2521 // Execute the package dependency action.
2522 hr = DependencyExecutePackageDependencyAction(TRUE, &executeAction);
2523 ExitOnFailure(hr, "Failed to execute package dependency action.");
2524
2525LExit:
2526 ReleaseStr(sczPackage);
2527 PlanUninitializeExecuteAction(&executeAction);
2528
2529 return hr;
2530}
2531
2532static HRESULT OnLoadCompatiblePackage(
2533 __in BURN_PACKAGES* pPackages,
2534 __in BYTE* pbData,
2535 __in DWORD cbData
2536 )
2537{
2538 HRESULT hr = S_OK;
2539 SIZE_T iData = 0;
2540 LPWSTR sczPackage = NULL;
2541 BURN_EXECUTE_ACTION executeAction = { };
2542
2543 executeAction.type = BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE;
2544
2545 // Deserialize the message data.
2546 hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2547 ExitOnFailure(hr, "Failed to read package id from message buffer.");
2548
2549 // Find the reference package.
2550 hr = PackageFindById(pPackages, sczPackage, &executeAction.compatiblePackage.pReferencePackage);
2551 ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
2552
2553 hr = BuffReadString(pbData, cbData, &iData, &executeAction.compatiblePackage.sczInstalledProductCode);
2554 ExitOnFailure(hr, "Failed to read installed ProductCode from message buffer.");
2555
2556 hr = BuffReadNumber64(pbData, cbData, &iData, &executeAction.compatiblePackage.qwInstalledVersion);
2557 ExitOnFailure(hr, "Failed to read installed version from message buffer.");
2558
2559 // Copy the installed data to the reference package.
2560 hr = StrAllocString(&executeAction.compatiblePackage.pReferencePackage->Msi.sczInstalledProductCode, executeAction.compatiblePackage.sczInstalledProductCode, 0);
2561 ExitOnFailure(hr, "Failed to copy installed ProductCode.");
2562
2563 executeAction.compatiblePackage.pReferencePackage->Msi.qwInstalledVersion = executeAction.compatiblePackage.qwInstalledVersion;
2564
2565 // Load the compatible package and add it to the list.
2566 hr = MsiEngineAddCompatiblePackage(pPackages, executeAction.compatiblePackage.pReferencePackage, NULL);
2567 ExitOnFailure(hr, "Failed to load compatible package.");
2568
2569LExit:
2570 ReleaseStr(sczPackage);
2571 PlanUninitializeExecuteAction(&executeAction);
2572
2573 return hr;
2574}
2575
2576static int GenericExecuteMessageHandler(
2577 __in GENERIC_EXECUTE_MESSAGE* pMessage,
2578 __in LPVOID pvContext
2579 )
2580{
2581 HRESULT hr = S_OK;
2582 int nResult = IDOK;
2583 HANDLE hPipe = (HANDLE)pvContext;
2584 BYTE* pbData = NULL;
2585 SIZE_T cbData = 0;
2586 DWORD dwMessage = 0;
2587
2588 hr = BuffWriteNumber(&pbData, &cbData, pMessage->dwAllowedResults);
2589 ExitOnFailure(hr, "Failed to write UI flags.");
2590
2591 switch(pMessage->type)
2592 {
2593 case GENERIC_EXECUTE_MESSAGE_PROGRESS:
2594 // serialize message data
2595 hr = BuffWriteNumber(&pbData, &cbData, pMessage->progress.dwPercentage);
2596 ExitOnFailure(hr, "Failed to write progress percentage to message buffer.");
2597
2598 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS;
2599 break;
2600
2601 case GENERIC_EXECUTE_MESSAGE_ERROR:
2602 // serialize message data
2603 hr = BuffWriteNumber(&pbData, &cbData, pMessage->error.dwErrorCode);
2604 ExitOnFailure(hr, "Failed to write error code to message buffer.");
2605
2606 hr = BuffWriteString(&pbData, &cbData, pMessage->error.wzMessage);
2607 ExitOnFailure(hr, "Failed to write message to message buffer.");
2608
2609 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR;
2610 break;
2611
2612 case GENERIC_EXECUTE_MESSAGE_FILES_IN_USE:
2613 hr = BuffWriteNumber(&pbData, &cbData, pMessage->filesInUse.cFiles);
2614 ExitOnFailure(hr, "Failed to count of files in use to message buffer.");
2615
2616 for (DWORD i = 0; i < pMessage->filesInUse.cFiles; ++i)
2617 {
2618 hr = BuffWriteString(&pbData, &cbData, pMessage->filesInUse.rgwzFiles[i]);
2619 ExitOnFailure(hr, "Failed to write file in use to message buffer.");
2620 }
2621
2622 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE;
2623 break;
2624 }
2625
2626 // send message
2627 hr = PipeSendMessage(hPipe, dwMessage, pbData, cbData, NULL, NULL, reinterpret_cast<DWORD*>(&nResult));
2628 ExitOnFailure(hr, "Failed to send message to per-user process.");
2629
2630LExit:
2631 ReleaseBuffer(pbData);
2632
2633 return nResult;
2634}
2635
2636static int MsiExecuteMessageHandler(
2637 __in WIU_MSI_EXECUTE_MESSAGE* pMessage,
2638 __in_opt LPVOID pvContext
2639 )
2640{
2641 HRESULT hr = S_OK;
2642 int nResult = IDOK;
2643 HANDLE hPipe = (HANDLE)pvContext;
2644 BYTE* pbData = NULL;
2645 SIZE_T cbData = 0;
2646 DWORD dwMessage = 0;
2647
2648 // Always send any extra data via the struct first.
2649 hr = BuffWriteNumber(&pbData, &cbData, pMessage->cData);
2650 ExitOnFailure(hr, "Failed to write MSI data count to message buffer.");
2651
2652 for (DWORD i = 0; i < pMessage->cData; ++i)
2653 {
2654 hr = BuffWriteString(&pbData, &cbData, pMessage->rgwzData[i]);
2655 ExitOnFailure(hr, "Failed to write MSI data to message buffer.");
2656 }
2657
2658 hr = BuffWriteNumber(&pbData, &cbData, pMessage->dwAllowedResults);
2659 ExitOnFailure(hr, "Failed to write UI flags.");
2660
2661 switch (pMessage->type)
2662 {
2663 case WIU_MSI_EXECUTE_MESSAGE_PROGRESS:
2664 // serialize message data
2665 hr = BuffWriteNumber(&pbData, &cbData, pMessage->progress.dwPercentage);
2666 ExitOnFailure(hr, "Failed to write progress percentage to message buffer.");
2667
2668 // set message id
2669 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_PROGRESS;
2670 break;
2671
2672 case WIU_MSI_EXECUTE_MESSAGE_ERROR:
2673 // serialize message data
2674 hr = BuffWriteNumber(&pbData, &cbData, pMessage->error.dwErrorCode);
2675 ExitOnFailure(hr, "Failed to write error code to message buffer.");
2676
2677 hr = BuffWriteString(&pbData, &cbData, pMessage->error.wzMessage);
2678 ExitOnFailure(hr, "Failed to write message to message buffer.");
2679
2680 // set message id
2681 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_ERROR;
2682 break;
2683
2684 case WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE:
2685 // serialize message data
2686 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)pMessage->msiMessage.mt);
2687 ExitOnFailure(hr, "Failed to write MSI message type to message buffer.");
2688
2689 hr = BuffWriteString(&pbData, &cbData, pMessage->msiMessage.wzMessage);
2690 ExitOnFailure(hr, "Failed to write message to message buffer.");
2691
2692 // set message id
2693 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_MSI_MESSAGE;
2694 break;
2695
2696 case WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE:
2697 // NOTE: we do not serialize other message data here because all the "files in use" are in the data above.
2698
2699 // set message id
2700 dwMessage = BURN_ELEVATION_MESSAGE_TYPE_EXECUTE_FILES_IN_USE;
2701 break;
2702
2703 default:
2704 hr = E_UNEXPECTED;
2705 ExitOnFailure(hr, "Invalid message type: %d", pMessage->type);
2706 }
2707
2708 // send message
2709 hr = PipeSendMessage(hPipe, dwMessage, pbData, cbData, NULL, NULL, (DWORD*)&nResult);
2710 ExitOnFailure(hr, "Failed to send message to per-machine process.");
2711
2712LExit:
2713 ReleaseBuffer(pbData);
2714
2715 return nResult;
2716}
2717
2718static HRESULT OnCleanPackage(
2719 __in BURN_PACKAGES* pPackages,
2720 __in BYTE* pbData,
2721 __in DWORD cbData
2722 )
2723{
2724 HRESULT hr = S_OK;
2725 SIZE_T iData = 0;
2726 LPWSTR sczPackage = NULL;
2727 BURN_PACKAGE* pPackage = NULL;
2728
2729 // Deserialize message data.
2730 hr = BuffReadString(pbData, cbData, &iData, &sczPackage);
2731 ExitOnFailure(hr, "Failed to read package id.");
2732
2733 hr = PackageFindById(pPackages, sczPackage, &pPackage);
2734 ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
2735
2736 // Remove the package from the cache.
2737 hr = CacheRemovePackage(TRUE, pPackage->sczId, pPackage->sczCacheId);
2738 ExitOnFailure(hr, "Failed to remove from cache package: %ls", pPackage->sczId);
2739
2740LExit:
2741 ReleaseStr(sczPackage);
2742 return hr;
2743}
2744
2745static HRESULT OnLaunchApprovedExe(
2746 __in HANDLE hPipe,
2747 __in BURN_APPROVED_EXES* pApprovedExes,
2748 __in BURN_VARIABLES* pVariables,
2749 __in BYTE* pbData,
2750 __in DWORD cbData
2751 )
2752{
2753 HRESULT hr = S_OK;
2754 SIZE_T iData = 0;
2755 BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe = NULL;
2756 BURN_APPROVED_EXE* pApprovedExe = NULL;
2757 REGSAM samDesired = KEY_QUERY_VALUE;
2758 HKEY hKey = NULL;
2759 DWORD dwProcessId = 0;
2760 BYTE* pbSendData = NULL;
2761 SIZE_T cbSendData = 0;
2762 DWORD dwResult = 0;
2763
2764 pLaunchApprovedExe = (BURN_LAUNCH_APPROVED_EXE*)MemAlloc(sizeof(BURN_LAUNCH_APPROVED_EXE), TRUE);
2765
2766 // Deserialize message data.
2767 hr = BuffReadString(pbData, cbData, &iData, &pLaunchApprovedExe->sczId);
2768 ExitOnFailure(hr, "Failed to read approved exe id.");
2769
2770 hr = BuffReadString(pbData, cbData, &iData, &pLaunchApprovedExe->sczArguments);
2771 ExitOnFailure(hr, "Failed to read approved exe arguments.");
2772
2773 hr = BuffReadNumber(pbData, cbData, &iData, &pLaunchApprovedExe->dwWaitForInputIdleTimeout);
2774 ExitOnFailure(hr, "Failed to read approved exe WaitForInputIdle timeout.");
2775
2776 hr = ApprovedExesFindById(pApprovedExes, pLaunchApprovedExe->sczId, &pApprovedExe);
2777 ExitOnFailure(hr, "The per-user process requested unknown approved exe with id: %ls", pLaunchApprovedExe->sczId);
2778
2779 LogId(REPORT_STANDARD, MSG_LAUNCH_APPROVED_EXE_SEARCH, pApprovedExe->sczKey, pApprovedExe->sczValueName ? pApprovedExe->sczValueName : L"", pApprovedExe->fWin64 ? L"yes" : L"no");
2780
2781 if (pApprovedExe->fWin64)
2782 {
2783 samDesired |= KEY_WOW64_64KEY;
2784 }
2785
2786 hr = RegOpen(HKEY_LOCAL_MACHINE, pApprovedExe->sczKey, samDesired, &hKey);
2787 ExitOnFailure(hr, "Failed to open the registry key for the approved exe path.");
2788
2789 hr = RegReadString(hKey, pApprovedExe->sczValueName, &pLaunchApprovedExe->sczExecutablePath);
2790 ExitOnFailure(hr, "Failed to read the value for the approved exe path.");
2791
2792 hr = ApprovedExesVerifySecureLocation(pVariables, pLaunchApprovedExe);
2793 ExitOnFailure(hr, "Failed to verify the executable path is in a secure location: %ls", pLaunchApprovedExe->sczExecutablePath);
2794 if (S_FALSE == hr)
2795 {
2796 LogStringLine(REPORT_STANDARD, "The executable path is not in a secure location: %ls", pLaunchApprovedExe->sczExecutablePath);
2797 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED));
2798 }
2799
2800 hr = ApprovedExesLaunch(pVariables, pLaunchApprovedExe, &dwProcessId);
2801 ExitOnFailure(hr, "Failed to launch approved exe: %ls", pLaunchApprovedExe->sczExecutablePath);
2802
2803 //send process id over pipe
2804 hr = BuffWriteNumber(&pbSendData, &cbSendData, dwProcessId);
2805 ExitOnFailure(hr, "Failed to write the approved exe process id to message buffer.");
2806
2807 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID, pbSendData, cbSendData, NULL, NULL, &dwResult);
2808 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_LAUNCH_APPROVED_EXE_PROCESSID message to per-user process.");
2809
2810LExit:
2811 ReleaseBuffer(pbSendData);
2812 ApprovedExesUninitializeLaunch(pLaunchApprovedExe);
2813 return hr;
2814}
diff --git a/src/engine/elevation.h b/src/engine/elevation.h
new file mode 100644
index 00000000..d82d9b1c
--- /dev/null
+++ b/src/engine/elevation.h
@@ -0,0 +1,178 @@
1#pragma once
2// 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.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9
10// Parent (per-user process) side functions.
11HRESULT ElevationElevate(
12 __in BURN_ENGINE_STATE* pEngineState,
13 __in_opt HWND hwndParent
14 );
15HRESULT ElevationApplyInitialize(
16 __in HANDLE hPipe,
17 __in BURN_VARIABLES* pVariables,
18 __in BOOTSTRAPPER_ACTION action,
19 __in BURN_AU_PAUSE_ACTION auAction,
20 __in BOOL fTakeSystemRestorePoint
21 );
22HRESULT ElevationApplyUninitialize(
23 __in HANDLE hPipe
24 );
25HRESULT ElevationSessionBegin(
26 __in HANDLE hPipe,
27 __in_z LPCWSTR wzEngineWorkingPath,
28 __in_z LPCWSTR wzResumeCommandLine,
29 __in BOOL fDisableResume,
30 __in BURN_VARIABLES* pVariables,
31 __in DWORD dwRegistrationOperations,
32 __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction,
33 __in DWORD64 qwEstimatedSize
34 );
35HRESULT ElevationSessionResume(
36 __in HANDLE hPipe,
37 __in_z LPCWSTR wzResumeCommandLine,
38 __in BOOL fDisableResume,
39 __in BURN_VARIABLES* pVariables
40 );
41HRESULT ElevationSessionEnd(
42 __in HANDLE hPipe,
43 __in BURN_RESUME_MODE resumeMode,
44 __in BOOTSTRAPPER_APPLY_RESTART restart,
45 __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction
46 );
47HRESULT ElevationSaveState(
48 __in HANDLE hPipe,
49 __in_bcount(cbBuffer) BYTE* pbBuffer,
50 __in SIZE_T cbBuffer
51 );
52HRESULT ElevationLayoutBundle(
53 __in HANDLE hPipe,
54 __in_z LPCWSTR wzLayoutDirectory,
55 __in_z LPCWSTR wzUnverifiedPath
56 );
57HRESULT ElevationCacheOrLayoutContainerOrPayload(
58 __in HANDLE hPipe,
59 __in_opt BURN_CONTAINER* pContainer,
60 __in_opt BURN_PACKAGE* pPackage,
61 __in_opt BURN_PAYLOAD* pPayload,
62 __in_z_opt LPCWSTR wzLayoutDirectory,
63 __in_z LPCWSTR wzUnverifiedPath,
64 __in BOOL fMove
65 );
66HRESULT ElevationCacheCleanup(
67 __in HANDLE hPipe
68 );
69HRESULT ElevationProcessDependentRegistration(
70 __in HANDLE hPipe,
71 __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction
72 );
73HRESULT ElevationExecuteExePackage(
74 __in HANDLE hPipe,
75 __in BURN_EXECUTE_ACTION* pExecuteAction,
76 __in BURN_VARIABLES* pVariables,
77 __in BOOL fRollback,
78 __in PFN_GENERICMESSAGEHANDLER pfnGenericExecuteProgress,
79 __in LPVOID pvContext,
80 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
81 );
82HRESULT ElevationExecuteMsiPackage(
83 __in HANDLE hPipe,
84 __in_opt HWND hwndParent,
85 __in BURN_EXECUTE_ACTION* pExecuteAction,
86 __in BURN_VARIABLES* pVariables,
87 __in BOOL fRollback,
88 __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
89 __in LPVOID pvContext,
90 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
91 );
92HRESULT ElevationExecuteMspPackage(
93 __in HANDLE hPipe,
94 __in_opt HWND hwndParent,
95 __in BURN_EXECUTE_ACTION* pExecuteAction,
96 __in BURN_VARIABLES* pVariables,
97 __in BOOL fRollback,
98 __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
99 __in LPVOID pvContext,
100 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
101 );
102HRESULT ElevationExecuteMsuPackage(
103 __in HANDLE hPipe,
104 __in BURN_EXECUTE_ACTION* pExecuteAction,
105 __in BOOL fRollback,
106 __in BOOL fStopWusaService,
107 __in PFN_GENERICMESSAGEHANDLER pfnGenericExecuteProgress,
108 __in LPVOID pvContext,
109 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
110 );
111HRESULT ElevationExecutePackageProviderAction(
112 __in HANDLE hPipe,
113 __in BURN_EXECUTE_ACTION* pExecuteAction
114 );
115HRESULT ElevationExecutePackageDependencyAction(
116 __in HANDLE hPipe,
117 __in BURN_EXECUTE_ACTION* pExecuteAction
118 );
119HRESULT ElevationLoadCompatiblePackageAction(
120 __in HANDLE hPipe,
121 __in BURN_EXECUTE_ACTION* pExecuteAction
122 );
123HRESULT ElevationLaunchElevatedChild(
124 __in HANDLE hPipe,
125 __in BURN_PACKAGE* pPackage,
126 __in LPCWSTR wzPipeName,
127 __in LPCWSTR wzPipeToken,
128 __out DWORD* pdwChildPid
129 );
130HRESULT ElevationCleanPackage(
131 __in HANDLE hPipe,
132 __in BURN_PACKAGE* pPackage
133 );
134HRESULT ElevationLaunchApprovedExe(
135 __in HANDLE hPipe,
136 __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe,
137 __out DWORD* pdwProcessId
138 );
139
140// Child (per-machine process) side functions.
141HRESULT ElevationChildPumpMessages(
142 __in DWORD dwLoggingTlsId,
143 __in HANDLE hPipe,
144 __in HANDLE hCachePipe,
145 __in BURN_APPROVED_EXES* pApprovedExes,
146 __in BURN_CONTAINERS* pContainers,
147 __in BURN_PACKAGES* pPackages,
148 __in BURN_PAYLOADS* pPayloads,
149 __in BURN_VARIABLES* pVariables,
150 __in BURN_REGISTRATION* pRegistration,
151 __in BURN_USER_EXPERIENCE* pUserExperience,
152 __out HANDLE* phLock,
153 __out BOOL* pfDisabledAutomaticUpdates,
154 __out DWORD* pdwChildExitCode,
155 __out BOOL* pfRestart
156 );
157HRESULT ElevationChildResumeAutomaticUpdates();
158
159
160HRESULT ElevationMsiBeginTransaction(
161 __in HANDLE hPipe,
162 __in_opt HWND hwndParent,
163 __in LPVOID pvContext
164);
165HRESULT ElevationMsiCommitTransaction(
166 __in HANDLE hPipe,
167 __in_opt HWND hwndParent,
168 __in LPVOID pvContext
169);
170HRESULT ElevationMsiRollbackTransaction(
171 __in HANDLE hPipe,
172 __in_opt HWND hwndParent,
173 __in LPVOID pvContext
174);
175
176#ifdef __cplusplus
177}
178#endif
diff --git a/src/engine/embedded.cpp b/src/engine/embedded.cpp
new file mode 100644
index 00000000..09666980
--- /dev/null
+++ b/src/engine/embedded.cpp
@@ -0,0 +1,197 @@
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#include "precomp.h"
4
5
6// struct
7
8struct BURN_EMBEDDED_CALLBACK_CONTEXT
9{
10 PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler;
11 LPVOID pvContext;
12};
13
14// internal function declarations
15
16static HRESULT ProcessEmbeddedMessages(
17 __in BURN_PIPE_MESSAGE* pMsg,
18 __in_opt LPVOID pvContext,
19 __out DWORD* pdwResult
20 );
21static HRESULT OnEmbeddedErrorMessage(
22 __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler,
23 __in LPVOID pvContext,
24 __in_bcount(cbData) BYTE* pbData,
25 __in DWORD cbData,
26 __out DWORD* pdwResult
27 );
28static HRESULT OnEmbeddedProgress(
29 __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler,
30 __in LPVOID pvContext,
31 __in_bcount(cbData) BYTE* pbData,
32 __in DWORD cbData,
33 __out DWORD* pdwResult
34 );
35
36// function definitions
37
38/*******************************************************************
39 EmbeddedLaunchChildProcess -
40
41*******************************************************************/
42extern "C" HRESULT EmbeddedRunBundle(
43 __in LPCWSTR wzExecutablePath,
44 __in LPCWSTR wzArguments,
45 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
46 __in LPVOID pvContext,
47 __out DWORD* pdwExitCode
48 )
49{
50 HRESULT hr = S_OK;
51 DWORD dwCurrentProcessId = ::GetCurrentProcessId();
52 HANDLE hCreatedPipesEvent = NULL;
53 LPWSTR sczCommand = NULL;
54 STARTUPINFOW si = { };
55 PROCESS_INFORMATION pi = { };
56 BURN_PIPE_RESULT result = { };
57
58 BURN_PIPE_CONNECTION connection = { };
59 PipeConnectionInitialize(&connection);
60
61 BURN_EMBEDDED_CALLBACK_CONTEXT context = { };
62 context.pfnGenericMessageHandler = pfnGenericMessageHandler;
63 context.pvContext = pvContext;
64
65 hr = PipeCreateNameAndSecret(&connection.sczName, &connection.sczSecret);
66 ExitOnFailure(hr, "Failed to create embedded pipe name and client token.");
67
68 hr = PipeCreatePipes(&connection, FALSE, &hCreatedPipesEvent);
69 ExitOnFailure(hr, "Failed to create embedded pipe.");
70
71 hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls %ls %ls %u", wzArguments, BURN_COMMANDLINE_SWITCH_EMBEDDED, connection.sczName, connection.sczSecret, dwCurrentProcessId);
72 ExitOnFailure(hr, "Failed to allocate embedded command.");
73
74 if (!::CreateProcessW(wzExecutablePath, sczCommand, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
75 {
76 ExitWithLastError(hr, "Failed to create embedded process at path: %ls", wzExecutablePath);
77 }
78
79 connection.dwProcessId = ::GetProcessId(pi.hProcess);
80 connection.hProcess = pi.hProcess;
81 pi.hProcess = NULL;
82
83 hr = PipeWaitForChildConnect(&connection);
84 ExitOnFailure(hr, "Failed to wait for embedded process to connect to pipe.");
85
86 hr = PipePumpMessages(connection.hPipe, ProcessEmbeddedMessages, &context, &result);
87 ExitOnFailure(hr, "Failed to process messages from embedded message.");
88
89 // Get the return code from the embedded process.
90 hr = ProcWaitForCompletion(connection.hProcess, INFINITE, pdwExitCode);
91 ExitOnFailure(hr, "Failed to wait for embedded executable: %ls", wzExecutablePath);
92
93LExit:
94 ReleaseHandle(pi.hThread);
95 ReleaseHandle(pi.hProcess);
96
97 StrSecureZeroFreeString(sczCommand);
98 ReleaseHandle(hCreatedPipesEvent);
99 PipeConnectionUninitialize(&connection);
100
101 return hr;
102}
103
104
105// internal function definitions
106
107static HRESULT ProcessEmbeddedMessages(
108 __in BURN_PIPE_MESSAGE* pMsg,
109 __in_opt LPVOID pvContext,
110 __out DWORD* pdwResult
111 )
112{
113 HRESULT hr = S_OK;
114 BURN_EMBEDDED_CALLBACK_CONTEXT* pContext = static_cast<BURN_EMBEDDED_CALLBACK_CONTEXT*>(pvContext);
115 DWORD dwResult = 0;
116
117 // Process the message.
118 switch (pMsg->dwMessage)
119 {
120 case BURN_EMBEDDED_MESSAGE_TYPE_ERROR:
121 hr = OnEmbeddedErrorMessage(pContext->pfnGenericMessageHandler, pContext->pvContext, static_cast<BYTE*>(pMsg->pvData), pMsg->cbData, &dwResult);
122 ExitOnFailure(hr, "Failed to process embedded error message.");
123 break;
124
125 case BURN_EMBEDDED_MESSAGE_TYPE_PROGRESS:
126 hr = OnEmbeddedProgress(pContext->pfnGenericMessageHandler, pContext->pvContext, static_cast<BYTE*>(pMsg->pvData), pMsg->cbData, &dwResult);
127 ExitOnFailure(hr, "Failed to process embedded progress message.");
128 break;
129
130 default:
131 hr = E_INVALIDARG;
132 ExitOnRootFailure(hr, "Unexpected embedded message sent to child process, msg: %u", pMsg->dwMessage);
133 }
134
135 *pdwResult = dwResult;
136
137LExit:
138 return hr;
139}
140
141static HRESULT OnEmbeddedErrorMessage(
142 __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler,
143 __in LPVOID pvContext,
144 __in_bcount(cbData) BYTE* pbData,
145 __in DWORD cbData,
146 __out DWORD* pdwResult
147 )
148{
149 HRESULT hr = S_OK;
150 DWORD iData = 0;
151 GENERIC_EXECUTE_MESSAGE message = { };
152 LPWSTR sczMessage = NULL;
153
154 message.type = GENERIC_EXECUTE_MESSAGE_ERROR;
155
156 hr = BuffReadNumber(pbData, cbData, &iData, &message.error.dwErrorCode);
157 ExitOnFailure(hr, "Failed to read error code from buffer.");
158
159 hr = BuffReadString(pbData, cbData, &iData, &sczMessage);
160 ExitOnFailure(hr, "Failed to read error message from buffer.");
161
162 message.error.wzMessage = sczMessage;
163
164 hr = BuffReadNumber(pbData, cbData, &iData, &message.dwAllowedResults);
165 ExitOnFailure(hr, "Failed to read UI hint from buffer.");
166
167 *pdwResult = (DWORD)pfnMessageHandler(&message, pvContext);
168
169LExit:
170 ReleaseStr(sczMessage);
171
172 return hr;
173}
174
175static HRESULT OnEmbeddedProgress(
176 __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler,
177 __in LPVOID pvContext,
178 __in_bcount(cbData) BYTE* pbData,
179 __in DWORD cbData,
180 __out DWORD* pdwResult
181 )
182{
183 HRESULT hr = S_OK;
184 DWORD iData = 0;
185 GENERIC_EXECUTE_MESSAGE message = { };
186
187 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
188 message.dwAllowedResults = MB_OKCANCEL;
189
190 hr = BuffReadNumber(pbData, cbData, &iData, &message.progress.dwPercentage);
191 ExitOnFailure(hr, "Failed to read progress from buffer.");
192
193 *pdwResult = (DWORD)pfnMessageHandler(&message, pvContext);
194
195LExit:
196 return hr;
197}
diff --git a/src/engine/embedded.h b/src/engine/embedded.h
new file mode 100644
index 00000000..08adeae0
--- /dev/null
+++ b/src/engine/embedded.h
@@ -0,0 +1,27 @@
1#pragma once
2// 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.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9typedef enum _BURN_EMBEDDED_MESSAGE_TYPE
10{
11 BURN_EMBEDDED_MESSAGE_TYPE_UNKNOWN,
12 BURN_EMBEDDED_MESSAGE_TYPE_ERROR,
13 BURN_EMBEDDED_MESSAGE_TYPE_PROGRESS,
14} BURN_EMBEDDED_MESSAGE_TYPE;
15
16
17HRESULT EmbeddedRunBundle(
18 __in LPCWSTR wzExecutablePath,
19 __in LPCWSTR wzArguments,
20 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
21 __in LPVOID pvContext,
22 __out DWORD* pdwExitCode
23 );
24
25#ifdef __cplusplus
26}
27#endif
diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp
new file mode 100644
index 00000000..3c0f09c9
--- /dev/null
+++ b/src/engine/engine.cpp
@@ -0,0 +1,889 @@
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#include "precomp.h"
4
5
6// constants
7
8const DWORD RESTART_RETRIES = 10;
9
10// internal function declarations
11
12static HRESULT InitializeEngineState(
13 __in BURN_ENGINE_STATE* pEngineState,
14 __in HANDLE hEngineFile
15 );
16static void UninitializeEngineState(
17 __in BURN_ENGINE_STATE* pEngineState
18 );
19static HRESULT RunUntrusted(
20 __in LPCWSTR wzCommandLine,
21 __in BURN_ENGINE_STATE* pEngineState
22 );
23static HRESULT RunNormal(
24 __in HINSTANCE hInstance,
25 __in BURN_ENGINE_STATE* pEngineState
26 );
27static HRESULT RunElevated(
28 __in HINSTANCE hInstance,
29 __in LPCWSTR wzCommandLine,
30 __in BURN_ENGINE_STATE* pEngineState
31 );
32static HRESULT RunEmbedded(
33 __in HINSTANCE hInstance,
34 __in BURN_ENGINE_STATE* pEngineState
35 );
36static HRESULT RunRunOnce(
37 __in const BURN_REGISTRATION* pRegistration,
38 __in int nCmdShow
39 );
40static HRESULT RunApplication(
41 __in BURN_ENGINE_STATE* pEngineState,
42 __out BOOL* pfReloadApp
43 );
44static HRESULT ProcessMessage(
45 __in BURN_ENGINE_STATE* pEngineState,
46 __in const MSG* pmsg
47 );
48static HRESULT DAPI RedirectLoggingOverPipe(
49 __in_z LPCSTR szString,
50 __in_opt LPVOID pvContext
51 );
52static HRESULT Restart();
53
54
55// function definitions
56
57extern "C" BOOL EngineInCleanRoom(
58 __in_z_opt LPCWSTR wzCommandLine
59 )
60{
61 // Be very careful with the functions you call from here.
62 // This function will be called before ::SetDefaultDllDirectories()
63 // has been called so dependencies outside of kernel32.dll are
64 // very likely to introduce DLL hijacking opportunities.
65
66 static DWORD cchCleanRoomSwitch = lstrlenW(BURN_COMMANDLINE_SWITCH_CLEAN_ROOM);
67
68 // This check is wholly dependent on the clean room command line switch being
69 // present at the beginning of the command line. Since Burn is the only thing
70 // that should be setting this command line option, that is in our control.
71 BOOL fInCleanRoom = (wzCommandLine &&
72 (wzCommandLine[0] == L'-' || wzCommandLine[0] == L'/') &&
73 CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzCommandLine + 1, cchCleanRoomSwitch, BURN_COMMANDLINE_SWITCH_CLEAN_ROOM, cchCleanRoomSwitch) &&
74 wzCommandLine[1 + cchCleanRoomSwitch] == L'='
75 );
76
77 return fInCleanRoom;
78}
79
80extern "C" HRESULT EngineRun(
81 __in HINSTANCE hInstance,
82 __in HANDLE hEngineFile,
83 __in_z_opt LPCWSTR wzCommandLine,
84 __in int nCmdShow,
85 __out DWORD* pdwExitCode
86 )
87{
88 HRESULT hr = S_OK;
89 BOOL fComInitialized = FALSE;
90 BOOL fLogInitialized = FALSE;
91 BOOL fCrypInitialized = FALSE;
92 BOOL fRegInitialized = FALSE;
93 BOOL fWiuInitialized = FALSE;
94 BOOL fXmlInitialized = FALSE;
95 OSVERSIONINFOEXW ovix = { };
96 LPWSTR sczExePath = NULL;
97 BOOL fRunNormal = FALSE;
98 BOOL fRestart = FALSE;
99
100 BURN_ENGINE_STATE engineState = { };
101
102 // Always initialize logging first
103 LogInitialize(::GetModuleHandleW(NULL));
104 fLogInitialized = TRUE;
105
106 // Ensure that log contains approriate level of information
107#ifdef _DEBUG
108 LogSetLevel(REPORT_DEBUG, FALSE);
109#else
110 LogSetLevel(REPORT_VERBOSE, FALSE); // FALSE means don't write an additional text line to the log saying the level changed
111#endif
112
113 hr = AppParseCommandLine(wzCommandLine, &engineState.argc, &engineState.argv);
114 ExitOnFailure(hr, "Failed to parse command line.");
115
116 hr = InitializeEngineState(&engineState, hEngineFile);
117 ExitOnFailure(hr, "Failed to initialize engine state.");
118
119 engineState.command.nCmdShow = nCmdShow;
120
121 // initialize platform layer
122 PlatformInitialize();
123
124 // initialize COM
125 hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
126 ExitOnFailure(hr, "Failed to initialize COM.");
127 fComInitialized = TRUE;
128
129 // Initialize dutil.
130 hr = CrypInitialize();
131 ExitOnFailure(hr, "Failed to initialize Cryputil.");
132 fCrypInitialized = TRUE;
133
134 hr = RegInitialize();
135 ExitOnFailure(hr, "Failed to initialize Regutil.");
136 fRegInitialized = TRUE;
137
138 hr = WiuInitialize();
139 ExitOnFailure(hr, "Failed to initialize Wiutil.");
140 fWiuInitialized = TRUE;
141
142 hr = XmlInitialize();
143 ExitOnFailure(hr, "Failed to initialize XML util.");
144 fXmlInitialized = TRUE;
145
146 ovix.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
147 if (!::GetVersionExW((LPOSVERSIONINFOW)&ovix))
148 {
149 ExitWithLastError(hr, "Failed to get OS info.");
150 }
151
152 PathForCurrentProcess(&sczExePath, NULL); // Ignore failure.
153 LogId(REPORT_STANDARD, MSG_BURN_INFO, szVerMajorMinorBuild, ovix.dwMajorVersion, ovix.dwMinorVersion, ovix.dwBuildNumber, ovix.wServicePackMajor, sczExePath);
154 ReleaseNullStr(sczExePath);
155
156 // initialize core
157 hr = CoreInitialize(&engineState);
158 ExitOnFailure(hr, "Failed to initialize core.");
159
160 // Select run mode.
161 switch (engineState.mode)
162 {
163 case BURN_MODE_UNTRUSTED:
164 hr = RunUntrusted(wzCommandLine, &engineState);
165 ExitOnFailure(hr, "Failed to run untrusted mode.");
166 break;
167
168 case BURN_MODE_NORMAL:
169 fRunNormal = TRUE;
170
171 hr = RunNormal(hInstance, &engineState);
172 ExitOnFailure(hr, "Failed to run per-user mode.");
173 break;
174
175 case BURN_MODE_ELEVATED:
176 hr = RunElevated(hInstance, wzCommandLine, &engineState);
177 ExitOnFailure(hr, "Failed to run per-machine mode.");
178 break;
179
180 case BURN_MODE_EMBEDDED:
181 fRunNormal = TRUE;
182
183 hr = RunEmbedded(hInstance, &engineState);
184 ExitOnFailure(hr, "Failed to run embedded mode.");
185 break;
186
187 case BURN_MODE_RUNONCE:
188 hr = RunRunOnce(&engineState.registration, nCmdShow);
189 ExitOnFailure(hr, "Failed to run RunOnce mode.");
190 break;
191
192 default:
193 hr = E_UNEXPECTED;
194 ExitOnFailure(hr, "Invalid run mode.");
195 }
196
197 // set exit code and remember if we are supposed to restart.
198 *pdwExitCode = engineState.userExperience.dwExitCode;
199 fRestart = engineState.fRestart;
200
201LExit:
202 ReleaseStr(sczExePath);
203
204 // If anything went wrong but the log was never open, try to open a "failure" log
205 // and that will dump anything captured in the log memory buffer to the log.
206 if (FAILED(hr) && BURN_LOGGING_STATE_CLOSED == engineState.log.state)
207 {
208 LoggingOpenFailed();
209 }
210
211 UserExperienceRemove(&engineState.userExperience);
212
213 CacheRemoveWorkingFolder(engineState.registration.sczId);
214 CacheUninitialize();
215
216 // If this is a related bundle (but not an update) suppress restart and return the standard restart error code.
217 if (fRestart && BOOTSTRAPPER_RELATION_NONE != engineState.command.relationType && BOOTSTRAPPER_RELATION_UPDATE != engineState.command.relationType)
218 {
219 LogId(REPORT_STANDARD, MSG_RESTART_ABORTED, LoggingRelationTypeToString(engineState.command.relationType));
220
221 fRestart = FALSE;
222 hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED);
223 }
224
225 UninitializeEngineState(&engineState);
226
227 if (fXmlInitialized)
228 {
229 XmlUninitialize();
230 }
231
232 if (fWiuInitialized)
233 {
234 WiuUninitialize();
235 }
236
237 if (fRegInitialized)
238 {
239 RegUninitialize();
240 }
241
242 if (fCrypInitialized)
243 {
244 CrypUninitialize();
245 }
246
247 if (fComInitialized)
248 {
249 ::CoUninitialize();
250 }
251
252 if (fRunNormal)
253 {
254 LogId(REPORT_STANDARD, MSG_EXITING, FAILED(hr) ? (int)hr : *pdwExitCode, LoggingBoolToString(fRestart));
255
256 if (fRestart)
257 {
258 LogId(REPORT_STANDARD, MSG_RESTARTING);
259 }
260 }
261
262 if (fLogInitialized)
263 {
264 LogClose(FALSE);
265 }
266
267 if (fRestart)
268 {
269 Restart();
270 }
271
272 if (fLogInitialized)
273 {
274 LogUninitialize(FALSE);
275 }
276
277 return hr;
278}
279
280
281// internal function definitions
282
283static HRESULT InitializeEngineState(
284 __in BURN_ENGINE_STATE* pEngineState,
285 __in HANDLE hEngineFile
286 )
287{
288 HRESULT hr = S_OK;
289 LPCWSTR wzParam = NULL;
290 HANDLE hSectionFile = hEngineFile;
291 HANDLE hSourceEngineFile = INVALID_HANDLE_VALUE;
292
293 pEngineState->automaticUpdates = BURN_AU_PAUSE_ACTION_IFELEVATED;
294 pEngineState->dwElevatedLoggingTlsId = TLS_OUT_OF_INDEXES;
295 ::InitializeCriticalSection(&pEngineState->csActive);
296 ::InitializeCriticalSection(&pEngineState->userExperience.csEngineActive);
297 PipeConnectionInitialize(&pEngineState->companionConnection);
298 PipeConnectionInitialize(&pEngineState->embeddedConnection);
299
300 for (int i = 0; i < pEngineState->argc; ++i)
301 {
302 if (pEngineState->argv[i][0] == L'-')
303 {
304 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &pEngineState->argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED), BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED)))
305 {
306 wzParam = &pEngineState->argv[i][2 + lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED)];
307 if (L'=' != wzParam[-1] || L'\0' == wzParam[0])
308 {
309 ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED);
310 }
311
312 hr = StrStringToUInt32(wzParam, 0, reinterpret_cast<UINT*>(&hSourceEngineFile));
313 ExitOnFailure(hr, "Failed to parse file handle: '%ls'", (wzParam));
314 }
315 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &pEngineState->argv[i][1], lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF), BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF, lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF)))
316 {
317 wzParam = &pEngineState->argv[i][2 + lstrlenW(BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF)];
318 if (L'=' != wzParam[-1] || L'\0' == wzParam[0])
319 {
320 ExitOnRootFailure(hr = E_INVALIDARG, "Missing required parameter for switch: %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF);
321 }
322
323 hr = StrStringToUInt32(wzParam, 0, reinterpret_cast<UINT*>(&hSectionFile));
324 ExitOnFailure(hr, "Failed to parse file handle: '%ls'", (wzParam));
325 }
326 }
327 }
328
329 hr = SectionInitialize(&pEngineState->section, hSectionFile, hSourceEngineFile);
330 ExitOnFailure(hr, "Failed to initialize engine section.");
331
332LExit:
333 return hr;
334}
335
336static void UninitializeEngineState(
337 __in BURN_ENGINE_STATE* pEngineState
338 )
339{
340 if (pEngineState->argv)
341 {
342 AppFreeCommandLineArgs(pEngineState->argv);
343 }
344
345 ReleaseStr(pEngineState->sczIgnoreDependencies);
346
347 PipeConnectionUninitialize(&pEngineState->embeddedConnection);
348 PipeConnectionUninitialize(&pEngineState->companionConnection);
349 ReleaseStr(pEngineState->sczBundleEngineWorkingPath)
350
351 ReleaseHandle(pEngineState->hMessageWindowThread);
352
353 ::DeleteCriticalSection(&pEngineState->userExperience.csEngineActive);
354 UserExperienceUninitialize(&pEngineState->userExperience);
355
356 ApprovedExesUninitialize(&pEngineState->approvedExes);
357 UpdateUninitialize(&pEngineState->update);
358 VariablesUninitialize(&pEngineState->variables);
359 SearchesUninitialize(&pEngineState->searches);
360 RegistrationUninitialize(&pEngineState->registration);
361 PayloadsUninitialize(&pEngineState->payloads);
362 PackagesUninitialize(&pEngineState->packages);
363 CatalogUninitialize(&pEngineState->catalogs);
364 SectionUninitialize(&pEngineState->section);
365 ContainersUninitialize(&pEngineState->containers);
366
367 ReleaseStr(pEngineState->command.wzLayoutDirectory);
368 ReleaseStr(pEngineState->command.wzCommandLine);
369
370 ReleaseStr(pEngineState->log.sczExtension);
371 ReleaseStr(pEngineState->log.sczPrefix);
372 ReleaseStr(pEngineState->log.sczPath);
373 ReleaseStr(pEngineState->log.sczPathVariable);
374
375 if (TLS_OUT_OF_INDEXES != pEngineState->dwElevatedLoggingTlsId)
376 {
377 ::TlsFree(pEngineState->dwElevatedLoggingTlsId);
378 }
379
380 ::DeleteCriticalSection(&pEngineState->csActive);
381
382 // clear struct
383 memset(pEngineState, 0, sizeof(BURN_ENGINE_STATE));
384}
385
386static HRESULT RunUntrusted(
387 __in LPCWSTR wzCommandLine,
388 __in BURN_ENGINE_STATE* pEngineState
389 )
390{
391 HRESULT hr = S_OK;
392 LPWSTR sczCurrentProcessPath = NULL;
393 LPWSTR wzCleanRoomBundlePath = NULL;
394 LPWSTR sczCachedCleanRoomBundlePath = NULL;
395 LPWSTR sczParameters = NULL;
396 LPWSTR sczFullCommandLine = NULL;
397 STARTUPINFOW si = { };
398 PROCESS_INFORMATION pi = { };
399 HANDLE hFileAttached = NULL;
400 HANDLE hFileSelf = NULL;
401 HANDLE hProcess = NULL;
402
403 hr = PathForCurrentProcess(&sczCurrentProcessPath, NULL);
404 ExitOnFailure(hr, "Failed to get path for current process.");
405
406 BOOL fRunningFromCache = CacheBundleRunningFromCache();
407
408 // If we're running from the package cache, we're in a secure
409 // folder (DLLs cannot be inserted here for hijacking purposes)
410 // so just launch the current process's path as the clean room
411 // process. Technically speaking, we'd be able to skip creating
412 // a clean room process at all (since we're already running from
413 // a secure folder) but it makes the code that only wants to run
414 // in clean room more complicated if we don't launch an explicit
415 // clean room process.
416 if (fRunningFromCache)
417 {
418 wzCleanRoomBundlePath = sczCurrentProcessPath;
419 }
420 else
421 {
422 hr = CacheBundleToCleanRoom(&pEngineState->userExperience.payloads, &pEngineState->section, &sczCachedCleanRoomBundlePath);
423 ExitOnFailure(hr, "Failed to cache to clean room.");
424
425 wzCleanRoomBundlePath = sczCachedCleanRoomBundlePath;
426 }
427
428 // The clean room switch must always be at the front of the command line so
429 // the EngineInCleanRoom function will operate correctly.
430 hr = StrAllocFormatted(&sczParameters, L"-%ls=\"%ls\"", BURN_COMMANDLINE_SWITCH_CLEAN_ROOM, sczCurrentProcessPath);
431 ExitOnFailure(hr, "Failed to allocate parameters for unelevated process.");
432
433 // Send a file handle for the child Burn process to access the attached container.
434 hr = CoreAppendFileHandleAttachedToCommandLine(pEngineState->section.hEngineFile, &hFileAttached, &sczParameters);
435 ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED);
436
437 // Grab a file handle for the child Burn process.
438 hr = CoreAppendFileHandleSelfToCommandLine(wzCleanRoomBundlePath, &hFileSelf, &sczParameters, NULL);
439 ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF);
440
441 hr = StrAllocFormattedSecure(&sczParameters, L"%ls %ls", sczParameters, wzCommandLine);
442 ExitOnFailure(hr, "Failed to append original command line.");
443
444#ifdef ENABLE_UNELEVATE
445 // TODO: Pass file handle to unelevated process if this ever gets reenabled.
446 if (!pEngineState->fDisableUnelevate)
447 {
448 // Try to launch unelevated and if that fails for any reason, we'll launch our process normally (even though that may make it elevated).
449 hr = ProcExecuteAsInteractiveUser(wzCleanRoomBundlePath, sczParameters, &hProcess);
450 }
451#endif
452
453 if (!hProcess)
454 {
455 hr = StrAllocFormattedSecure(&sczFullCommandLine, L"\"%ls\" %ls", wzCleanRoomBundlePath, sczParameters);
456 ExitOnFailure(hr, "Failed to allocate full command-line.");
457
458 si.cb = sizeof(si);
459 si.wShowWindow = static_cast<WORD>(pEngineState->command.nCmdShow);
460 if (!::CreateProcessW(wzCleanRoomBundlePath, sczFullCommandLine, NULL, NULL, TRUE, 0, 0, NULL, &si, &pi))
461 {
462 ExitWithLastError(hr, "Failed to launch clean room process: %ls", sczFullCommandLine);
463 }
464
465 hProcess = pi.hProcess;
466 pi.hProcess = NULL;
467 }
468
469 hr = ProcWaitForCompletion(hProcess, INFINITE, &pEngineState->userExperience.dwExitCode);
470 ExitOnFailure(hr, "Failed to wait for clean room process: %ls", wzCleanRoomBundlePath);
471
472LExit:
473 ReleaseHandle(pi.hThread);
474 ReleaseFileHandle(hFileSelf);
475 ReleaseFileHandle(hFileAttached);
476 ReleaseHandle(hProcess);
477 StrSecureZeroFreeString(sczFullCommandLine);
478 StrSecureZeroFreeString(sczParameters);
479 ReleaseStr(sczCachedCleanRoomBundlePath);
480 ReleaseStr(sczCurrentProcessPath);
481
482 return hr;
483}
484
485static HRESULT RunNormal(
486 __in HINSTANCE hInstance,
487 __in BURN_ENGINE_STATE* pEngineState
488 )
489{
490 HRESULT hr = S_OK;
491 HANDLE hPipesCreatedEvent = NULL;
492 BOOL fContinueExecution = TRUE;
493 BOOL fReloadApp = FALSE;
494
495 // Initialize logging.
496 hr = LoggingOpen(&pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->registration.sczDisplayName);
497 ExitOnFailure(hr, "Failed to open log.");
498
499 // Ensure we're on a supported operating system.
500 hr = ConditionGlobalCheck(&pEngineState->variables, &pEngineState->condition, pEngineState->command.display, pEngineState->registration.sczDisplayName, &pEngineState->userExperience.dwExitCode, &fContinueExecution);
501 ExitOnFailure(hr, "Failed to check global conditions");
502
503 if (!fContinueExecution)
504 {
505 LogId(REPORT_STANDARD, MSG_FAILED_CONDITION_CHECK);
506
507 // If the block told us to abort, abort!
508 ExitFunction1(hr = S_OK);
509 }
510
511 if (pEngineState->userExperience.fSplashScreen && BOOTSTRAPPER_DISPLAY_NONE < pEngineState->command.display)
512 {
513 SplashScreenCreate(hInstance, NULL, &pEngineState->command.hwndSplashScreen);
514 }
515
516 // Create a top-level window to handle system messages.
517 hr = UiCreateMessageWindow(hInstance, pEngineState);
518 ExitOnFailure(hr, "Failed to create the message window.");
519
520 // Query registration state.
521 hr = CoreQueryRegistration(pEngineState);
522 ExitOnFailure(hr, "Failed to query registration.");
523
524 // Set some built-in variables before loading the BA.
525 hr = PlanSetVariables(pEngineState->command.action, &pEngineState->variables);
526 ExitOnFailure(hr, "Failed to set action variables.");
527
528 hr = RegistrationSetVariables(&pEngineState->registration, &pEngineState->variables);
529 ExitOnFailure(hr, "Failed to set registration variables.");
530
531 // If a layout directory was specified on the command-line, set it as a well-known variable.
532 if (pEngineState->command.wzLayoutDirectory && *pEngineState->command.wzLayoutDirectory)
533 {
534 hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_LAYOUT_DIRECTORY, pEngineState->command.wzLayoutDirectory, FALSE);
535 ExitOnFailure(hr, "Failed to set layout directory variable to value provided from command-line.");
536 }
537
538 do
539 {
540 fReloadApp = FALSE;
541
542 hr = RunApplication(pEngineState, &fReloadApp);
543 ExitOnFailure(hr, "Failed while running ");
544 } while (fReloadApp);
545
546LExit:
547 // If the message window is still around, close it.
548 UiCloseMessageWindow(pEngineState);
549
550 VariablesDump(&pEngineState->variables);
551
552 // end per-machine process if running
553 if (INVALID_HANDLE_VALUE != pEngineState->companionConnection.hPipe)
554 {
555 PipeTerminateChildProcess(&pEngineState->companionConnection, pEngineState->userExperience.dwExitCode, FALSE);
556 }
557
558 // If the splash screen is still around, close it.
559 if (::IsWindow(pEngineState->command.hwndSplashScreen))
560 {
561 ::PostMessageW(pEngineState->command.hwndSplashScreen, WM_CLOSE, 0, 0);
562 }
563
564 ReleaseHandle(hPipesCreatedEvent);
565
566 return hr;
567}
568
569static HRESULT RunElevated(
570 __in HINSTANCE hInstance,
571 __in LPCWSTR /*wzCommandLine*/,
572 __in BURN_ENGINE_STATE* pEngineState
573 )
574{
575 HRESULT hr = S_OK;
576 HANDLE hLock = NULL;
577 BOOL fDisabledAutomaticUpdates = FALSE;
578
579 // connect to per-user process
580 hr = PipeChildConnect(&pEngineState->companionConnection, TRUE);
581 ExitOnFailure(hr, "Failed to connect to unelevated process.");
582
583 // Set up the thread local storage to store the correct pipe to communicate logging then
584 // override logging to write over the pipe.
585 pEngineState->dwElevatedLoggingTlsId = ::TlsAlloc();
586 if (TLS_OUT_OF_INDEXES == pEngineState->dwElevatedLoggingTlsId)
587 {
588 ExitWithLastError(hr, "Failed to allocate thread local storage for logging.");
589 }
590
591 if (!::TlsSetValue(pEngineState->dwElevatedLoggingTlsId, pEngineState->companionConnection.hPipe))
592 {
593 ExitWithLastError(hr, "Failed to set elevated pipe into thread local storage for logging.");
594 }
595
596 LogRedirect(RedirectLoggingOverPipe, pEngineState);
597
598 // Create a top-level window to prevent shutting down the elevated process.
599 hr = UiCreateMessageWindow(hInstance, pEngineState);
600 ExitOnFailure(hr, "Failed to create the message window.");
601
602 SrpInitialize(TRUE);
603
604 // Pump messages from parent process.
605 hr = ElevationChildPumpMessages(pEngineState->dwElevatedLoggingTlsId, pEngineState->companionConnection.hPipe, pEngineState->companionConnection.hCachePipe, &pEngineState->approvedExes, &pEngineState->containers, &pEngineState->packages, &pEngineState->payloads, &pEngineState->variables, &pEngineState->registration, &pEngineState->userExperience, &hLock, &fDisabledAutomaticUpdates, &pEngineState->userExperience.dwExitCode, &pEngineState->fRestart);
606 LogRedirect(NULL, NULL); // reset logging so the next failure gets written to "log buffer" for the failure log.
607 ExitOnFailure(hr, "Failed to pump messages from parent process.");
608
609LExit:
610 LogRedirect(NULL, NULL); // we're done talking to the child so always reset logging now.
611
612 // If the message window is still around, close it.
613 UiCloseMessageWindow(pEngineState);
614
615 if (fDisabledAutomaticUpdates)
616 {
617 ElevationChildResumeAutomaticUpdates();
618 }
619
620 if (hLock)
621 {
622 ::ReleaseMutex(hLock);
623 ::CloseHandle(hLock);
624 }
625
626 return hr;
627}
628
629static HRESULT RunEmbedded(
630 __in HINSTANCE hInstance,
631 __in BURN_ENGINE_STATE* pEngineState
632 )
633{
634 HRESULT hr = S_OK;
635
636 // Disable system restore since the parent bundle may have done it.
637 pEngineState->fDisableSystemRestore = TRUE;
638
639 // Connect to parent process.
640 hr = PipeChildConnect(&pEngineState->embeddedConnection, FALSE);
641 ExitOnFailure(hr, "Failed to connect to parent of embedded process.");
642
643 // Do not register the bundle to automatically restart if embedded.
644 if (BOOTSTRAPPER_DISPLAY_EMBEDDED == pEngineState->command.display)
645 {
646 pEngineState->registration.fDisableResume = TRUE;
647 }
648
649 // Now run the application like normal.
650 hr = RunNormal(hInstance, pEngineState);
651 ExitOnFailure(hr, "Failed to run bootstrapper application embedded.");
652
653LExit:
654 return hr;
655}
656
657static HRESULT RunRunOnce(
658 __in const BURN_REGISTRATION* pRegistration,
659 __in int nCmdShow
660 )
661{
662 HRESULT hr = S_OK;
663 LPWSTR sczNewCommandLine = NULL;
664 LPWSTR sczBurnPath = NULL;
665 HANDLE hProcess = NULL;
666
667 hr = RegistrationGetResumeCommandLine(pRegistration, &sczNewCommandLine);
668 ExitOnFailure(hr, "Unable to get resume command line from the registry");
669
670 // and re-launch
671 hr = PathForCurrentProcess(&sczBurnPath, NULL);
672 ExitOnFailure(hr, "Failed to get current process path.");
673
674 hr = ProcExec(sczBurnPath, 0 < sczNewCommandLine ? sczNewCommandLine : L"", nCmdShow, &hProcess);
675 ExitOnFailure(hr, "Failed to re-launch bundle process after RunOnce: %ls", sczBurnPath);
676
677LExit:
678 ReleaseHandle(hProcess);
679 ReleaseStr(sczNewCommandLine);
680 ReleaseStr(sczBurnPath);
681
682 return hr;
683}
684
685static HRESULT RunApplication(
686 __in BURN_ENGINE_STATE* pEngineState,
687 __out BOOL* pfReloadApp
688 )
689{
690 HRESULT hr = S_OK;
691 BOOTSTRAPPER_ENGINE_CONTEXT engineContext = { };
692 BOOL fStartupCalled = FALSE;
693 BOOL fRet = FALSE;
694 MSG msg = { };
695 BOOTSTRAPPER_SHUTDOWN_ACTION shutdownAction = BOOTSTRAPPER_SHUTDOWN_ACTION_NONE;
696
697 ::PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
698
699 // Setup the bootstrapper engine.
700 engineContext.dwThreadId = ::GetCurrentThreadId();
701 engineContext.pEngineState = pEngineState;
702
703 // Load the bootstrapper application.
704 hr = UserExperienceLoad(&pEngineState->userExperience, &engineContext, &pEngineState->command);
705 ExitOnFailure(hr, "Failed to load BA.");
706
707 fStartupCalled = TRUE;
708 hr = UserExperienceOnStartup(&pEngineState->userExperience);
709 ExitOnFailure(hr, "Failed to start bootstrapper application.");
710
711 // Enter the message pump.
712 while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
713 {
714 if (-1 == fRet)
715 {
716 hr = E_UNEXPECTED;
717 ExitOnRootFailure(hr, "Unexpected return value from message pump.");
718 }
719 else
720 {
721 ProcessMessage(pEngineState, &msg);
722 }
723 }
724
725 // Get exit code.
726 pEngineState->userExperience.dwExitCode = (DWORD)msg.wParam;
727
728LExit:
729 if (fStartupCalled)
730 {
731 UserExperienceOnShutdown(&pEngineState->userExperience, &shutdownAction);
732 if (BOOTSTRAPPER_SHUTDOWN_ACTION_RESTART == shutdownAction)
733 {
734 LogId(REPORT_STANDARD, MSG_BA_REQUESTED_RESTART, LoggingBoolToString(pEngineState->fRestart));
735 pEngineState->fRestart = TRUE;
736 }
737 else if (BOOTSTRAPPER_SHUTDOWN_ACTION_RELOAD_BOOTSTRAPPER == shutdownAction)
738 {
739 LogId(REPORT_STANDARD, MSG_BA_REQUESTED_RELOAD);
740 *pfReloadApp = TRUE;
741 }
742 }
743
744 // Unload BA.
745 UserExperienceUnload(&pEngineState->userExperience);
746
747 return hr;
748}
749
750static HRESULT ProcessMessage(
751 __in BURN_ENGINE_STATE* pEngineState,
752 __in const MSG* pmsg
753 )
754{
755 HRESULT hr = S_OK;
756
757 switch (pmsg->message)
758 {
759 case WM_BURN_DETECT:
760 hr = CoreDetect(pEngineState, reinterpret_cast<HWND>(pmsg->lParam));
761 break;
762
763 case WM_BURN_PLAN:
764 hr = CorePlan(pEngineState, static_cast<BOOTSTRAPPER_ACTION>(pmsg->lParam));
765 break;
766
767 case WM_BURN_ELEVATE:
768 hr = CoreElevate(pEngineState, reinterpret_cast<HWND>(pmsg->lParam));
769 break;
770
771 case WM_BURN_APPLY:
772 hr = CoreApply(pEngineState, reinterpret_cast<HWND>(pmsg->lParam));
773 break;
774
775 case WM_BURN_LAUNCH_APPROVED_EXE:
776 hr = CoreLaunchApprovedExe(pEngineState, reinterpret_cast<BURN_LAUNCH_APPROVED_EXE*>(pmsg->lParam));
777 break;
778
779 case WM_BURN_QUIT:
780 hr = CoreQuit(pEngineState, static_cast<int>(pmsg->wParam));
781 break;
782 }
783
784 return hr;
785}
786
787static HRESULT DAPI RedirectLoggingOverPipe(
788 __in_z LPCSTR szString,
789 __in_opt LPVOID pvContext
790 )
791{
792 static BOOL s_fCurrentlyLoggingToPipe = FALSE;
793
794 HRESULT hr = S_OK;
795 BURN_ENGINE_STATE* pEngineState = static_cast<BURN_ENGINE_STATE*>(pvContext);
796 BOOL fStartedLogging = FALSE;
797 HANDLE hPipe = INVALID_HANDLE_VALUE;
798 BYTE* pbData = NULL;
799 SIZE_T cbData = 0;
800 DWORD dwResult = 0;
801
802 // Prevent this function from being called recursively.
803 if (s_fCurrentlyLoggingToPipe)
804 {
805 ExitFunction();
806 }
807
808 s_fCurrentlyLoggingToPipe = TRUE;
809 fStartedLogging = TRUE;
810
811 // Make sure the current thread set the pipe in TLS.
812 hPipe = ::TlsGetValue(pEngineState->dwElevatedLoggingTlsId);
813 if (!hPipe || INVALID_HANDLE_VALUE == hPipe)
814 {
815 hr = HRESULT_FROM_WIN32(ERROR_PIPE_NOT_CONNECTED);
816 ExitFunction();
817 }
818
819 // Do not log or use ExitOnFailure() macro here because they will be discarded
820 // by the recursive block at the top of this function.
821 hr = BuffWriteStringAnsi(&pbData, &cbData, szString);
822 if (SUCCEEDED(hr))
823 {
824 hr = PipeSendMessage(hPipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_LOG), pbData, cbData, NULL, NULL, &dwResult);
825 if (SUCCEEDED(hr))
826 {
827 hr = (HRESULT)dwResult;
828 }
829 }
830
831LExit:
832 ReleaseBuffer(pbData);
833
834 // We started logging so remember to say we are no longer logging.
835 if (fStartedLogging)
836 {
837 s_fCurrentlyLoggingToPipe = FALSE;
838 }
839
840 return hr;
841}
842
843static HRESULT Restart()
844{
845 HRESULT hr = S_OK;
846 HANDLE hProcessToken = NULL;
847 TOKEN_PRIVILEGES priv = { };
848 DWORD dwRetries = 0;
849
850 if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hProcessToken))
851 {
852 ExitWithLastError(hr, "Failed to get process token.");
853 }
854
855 priv.PrivilegeCount = 1;
856 priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
857 if (!::LookupPrivilegeValueW(NULL, L"SeShutdownPrivilege", &priv.Privileges[0].Luid))
858 {
859 ExitWithLastError(hr, "Failed to get shutdown privilege LUID.");
860 }
861
862 if (!::AdjustTokenPrivileges(hProcessToken, FALSE, &priv, sizeof(TOKEN_PRIVILEGES), NULL, 0))
863 {
864 ExitWithLastError(hr, "Failed to adjust token to add shutdown privileges.");
865 }
866
867 do
868 {
869 hr = S_OK;
870
871 // Wait a second to let the companion process (assuming we did an elevated install) to get to the
872 // point where it too is thinking about restarting the computer. Only one will schedule the restart
873 // but both will have their log files closed and otherwise be ready to exit.
874 //
875 // On retry, we'll also wait a second to let the OS try to get to a place where the restart can
876 // be initiated.
877 ::Sleep(1000);
878
879 if (!vpfnInitiateSystemShutdownExW(NULL, NULL, 0, FALSE, TRUE, SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_INSTALLATION | SHTDN_REASON_FLAG_PLANNED))
880 {
881 hr = HRESULT_FROM_WIN32(::GetLastError());
882 }
883 } while (dwRetries++ < RESTART_RETRIES && (HRESULT_FROM_WIN32(ERROR_MACHINE_LOCKED) == hr || HRESULT_FROM_WIN32(ERROR_NOT_READY) == hr));
884 ExitOnRootFailure(hr, "Failed to schedule restart.");
885
886LExit:
887 ReleaseHandle(hProcessToken);
888 return hr;
889}
diff --git a/src/engine/engine.mc b/src/engine/engine.mc
new file mode 100644
index 00000000..fb2dd6e9
--- /dev/null
+++ b/src/engine/engine.mc
@@ -0,0 +1,901 @@
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
4MessageIdTypedef=DWORD
5
6LanguageNames=(English=0x409:MSG00409)
7
8
9; // message definitions
10
11; // MessageId=#
12; // Severity=Success
13; // SymbolicName=MSG_SUCCESS
14; // Language=English
15; // Success %1.
16; // .
17;
18; // MessageId=#
19; // Severity=Warning
20; // SymbolicName=MSG_WARNING
21; // Language=English
22; // Warning %1.
23; // .
24;
25; // MessageId=#
26; // Severity=Error
27; // SymbolicName=MSG_ERROR
28; // Language=English
29; // Error %1.
30; // .
31
32MessageId=1
33Severity=Success
34SymbolicName=MSG_BURN_INFO
35Language=English
36Burn v%1!hs!, Windows v%2!d!.%3!d! (Build %4!d!: Service Pack %5!d!), path: %6!ls!
37.
38
39MessageId=2
40Severity=Warning
41SymbolicName=MSG_BURN_UNKNOWN_PRIVATE_SWITCH
42Language=English
43Unknown burn internal command-line switch encountered: '%1!ls!'.
44.
45
46MessageId=3
47Severity=Success
48SymbolicName=MSG_BURN_RUN_BY_RELATED_BUNDLE
49Language=English
50This bundle is being run by a related bundle as type '%1!hs!'.
51.
52
53MessageId=4
54Severity=Success
55SymbolicName=MSG_BA_REQUESTED_RESTART
56Language=English
57Bootstrapper application requested restart at shutdown. Planned to restart already: %1!hs!.
58.
59
60MessageId=5
61Severity=Warning
62SymbolicName=MSG_RESTARTING
63Language=English
64Restarting computer...
65=======================================
66.
67
68MessageId=6
69Severity=Success
70SymbolicName=MSG_BA_REQUESTED_RELOAD
71Language=English
72Bootstrapper application requested to be reloaded.
73.
74
75MessageId=7
76Severity=Success
77SymbolicName=MSG_EXITING
78Language=English
79Exit code: 0x%1!x!, restarting: %2!hs!
80.
81
82MessageId=8
83Severity=Warning
84SymbolicName=MSG_RESTART_ABORTED
85Language=English
86Preventing requested restart because bundle is related: '%1!hs!'. Returning restart requested to parent bundle.
87.
88
89MessageId=9
90Severity=Success
91SymbolicName=MSG_BURN_COMMAND_LINE
92Language=English
93Command Line: '%1!ls!'
94.
95
96MessageId=10
97Severity=Success
98SymbolicName=MSG_LAUNCH_ELEVATED_ENGINE_STARTING
99Language=English
100Launching elevated engine process.
101.
102
103MessageId=11
104Severity=Success
105SymbolicName=MSG_LAUNCH_ELEVATED_ENGINE_SUCCESS
106Language=English
107Launched elevated engine process.
108.
109
110MessageId=12
111Severity=Success
112SymbolicName=MSG_CONNECT_TO_ELEVATED_ENGINE_SUCCESS
113Language=English
114Connected to elevated engine.
115.
116
117MessageId=51
118Severity=Error
119SymbolicName=MSG_FAILED_PARSE_CONDITION
120Language=English
121Error %1!hs!. Failed to parse condition %2!ls!. Unexpected symbol at position %3!hs!
122.
123
124MessageId=52
125Severity=Success
126SymbolicName=MSG_CONDITION_RESULT
127Language=English
128Condition '%1!ls!' evaluates to %2!hs!.
129.
130
131MessageId=53
132Severity=Error
133SymbolicName=MSG_FAILED_CONDITION_CHECK
134Language=English
135Bundle global condition check didn't succeed - aborting without loading application.
136.
137
138MessageId=54
139Severity=Error
140SymbolicName=MSG_PAYLOAD_FILE_NOT_PRESENT
141Language=English
142Failed to resolve source for file: %2!ls!, error: %1!ls!.
143.
144
145MessageId=55
146Severity=Warning
147SymbolicName=MSG_CANNOT_LOAD_STATE_FILE
148Language=English
149Could not load or read state file: %2!ls!, error: 0x%1!x!.
150.
151
152MessageId=56
153Severity=Error
154SymbolicName=MSG_USER_CANCELED
155Language=English
156Application canceled operation: %2!ls!, error: %1!ls!
157.
158
159MessageId=100
160Severity=Success
161SymbolicName=MSG_DETECT_BEGIN
162Language=English
163Detect begin, %1!u! packages
164.
165
166MessageId=101
167Severity=Success
168SymbolicName=MSG_DETECTED_PACKAGE
169Language=English
170Detected package: %1!ls!, state: %2!hs!, cached: %3!hs!
171.
172
173MessageId=102
174Severity=Success
175SymbolicName=MSG_DETECTED_RELATED_BUNDLE
176Language=English
177Detected related bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!hs!, operation: %5!hs!
178.
179
180MessageId=103
181Severity=Success
182SymbolicName=MSG_DETECTED_RELATED_PACKAGE
183Language=English
184Detected related package: %1!ls!, scope: %2!hs!, version: %3!hs!, language: %4!u! operation: %5!hs!
185.
186
187MessageId=104
188Severity=Success
189SymbolicName=MSG_DETECTED_MSI_FEATURE
190Language=English
191Detected package: %1!ls!, feature: %2!ls!, state: %3!hs!
192.
193
194MessageId=105
195Severity=Success
196SymbolicName=MSG_DETECTED_MSP_TARGET
197Language=English
198Detected package: %1!ls! target: %2!ls!, state: %3!hs!
199.
200
201MessageId=106
202Severity=Success
203SymbolicName=MSG_DETECT_CALCULATE_PATCH_APPLICABILITY
204Language=English
205Calculating patch applicability for target product code: %1!ls!, context: %2!hs!
206.
207
208MessageId=107
209Severity=Success
210SymbolicName=MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE
211Language=English
212Detected forward compatible bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!hs!, enabled: %5!hs!
213.
214
215MessageId=108
216Severity=Success
217SymbolicName=MSG_DETECTED_COMPATIBLE_PACKAGE_FROM_PROVIDER
218Language=English
219Detected compatible package: %1!ls!, provider: %2!ls!, installed: %3!ls!, version: %4!ls!, chained: %5!ls!
220.
221
222MessageId=120
223Severity=Warning
224SymbolicName=MSG_DETECT_PACKAGE_NOT_FULLY_CACHED
225Language=English
226Detected partially cached package: %1!ls!, invalid payload: %2!ls!, reason: 0x%3!x!
227.
228
229MessageId=121
230Severity=Warning
231SymbolicName=MSG_DETECT_FAILED_CALCULATE_PATCH_APPLICABILITY
232Language=English
233Could not calculate patch applicability for target product code: %1!ls!, context: %2!hs!, reason: 0x%3!x!
234.
235
236MessageId=151
237Severity=Error
238SymbolicName=MSG_FAILED_DETECT_PACKAGE
239Language=English
240Detect failed for package: %2!ls!, error: %1!ls!
241.
242
243MessageId=152
244Severity=Error
245SymbolicName=MSG_FAILED_READ_RELATED_PACKAGE_LANGUAGE
246Language=English
247Detected related package: %2!ls!, but failed to read language: %3!hs!, error: 0x%1!x!
248.
249
250MessageId=170
251Severity=Warning
252SymbolicName=MSG_DETECT_BAD_PRODUCT_CONFIGURATION
253Language=English
254Detected bad configuration for product: %1!ls!
255.
256
257MessageId=199
258Severity=Success
259SymbolicName=MSG_DETECT_COMPLETE
260Language=English
261Detect complete, result: 0x%1!x!
262.
263
264MessageId=200
265Severity=Success
266SymbolicName=MSG_PLAN_BEGIN
267Language=English
268Plan begin, %1!u! packages, action: %2!hs!
269.
270
271MessageId=201
272Severity=Success
273SymbolicName=MSG_PLANNED_PACKAGE
274Language=English
275Planned package: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs!, cache: %7!hs!, uncache: %8!hs!, dependency: %9!hs!
276.
277
278MessageId=202
279Severity=Success
280SymbolicName=MSG_PLANNED_BUNDLE_UX_CHANGED_REQUEST
281Language=English
282Planned bundle: %1!ls!, ba requested state: %2!hs! over default: %3!hs!
283.
284
285MessageId=203
286Severity=Success
287SymbolicName=MSG_PLANNED_MSI_FEATURE
288Language=English
289Planned feature: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute action: %5!hs!, rollback action: %6!hs!
290.
291
292MessageId=204
293Severity=Success
294SymbolicName=MSG_PLAN_MSI_FEATURES
295Language=English
296Plan %1!u! msi features for package: %2!ls!
297.
298
299MessageId=205
300Severity=Warning
301SymbolicName=MSG_PLAN_SKIP_PATCH_ACTION
302Language=English
303Plan %5!hs! skipped patch: %1!ls!, action: %2!hs! because chained target package: %3!ls! being uninstalled
304.
305
306MessageId=206
307Severity=Warning
308SymbolicName=MSG_PLAN_SKIP_SLIPSTREAM_ACTION
309Language=English
310Plan %5!hs! skipped patch: %1!ls!, action: %2!hs! because slipstreamed into chained target package: %3!ls!, action: %4!hs!
311.
312
313MessageId=207
314Severity=Success
315SymbolicName=MSG_PLANNED_RELATED_BUNDLE
316Language=English
317Planned related bundle: %1!ls!, type: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs!, dependency: %7!hs!
318.
319
320MessageId=208
321Severity=Warning
322SymbolicName=MSG_PLAN_DISABLING_ROLLBACK_NO_CACHE
323Language=English
324Plan disabled rollback for package: %1!ls!, due to incomplete cache: %2!hs!, original rollback action: %3!hs!
325.
326
327MessageId=209
328Severity=Warning
329SymbolicName=MSG_PLAN_SKIPPED_PROVIDER_KEY_REMOVAL
330Language=English
331Plan skipped removal of provider key: %1!ls! because it is registered to a different bundle: %2!ls!
332.
333
334MessageId=210
335Severity=Warning
336SymbolicName=MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS
337Language=English
338Plan skipped due to %1!u! remaining dependents
339.
340
341MessageId=211
342Severity=Success
343SymbolicName=MSG_PLANNED_UPGRADE_BUNDLE
344Language=English
345Planned upgrade bundle: %1!ls!, default requested: %2!hs!, ba requested: %3!hs!, execute: %4!hs!, rollback: %5!hs!, dependency: %6!hs!
346.
347
348MessageId=212
349Severity=Success
350SymbolicName=MSG_PLANNED_FORWARD_COMPATIBLE_BUNDLE
351Language=English
352Planned forward compatible bundle: %1!ls!, default requested: %2!hs!, ba requested: %3!hs!, execute: %4!hs!, rollback: %5!hs!, dependency: %6!hs!
353.
354
355MessageId=213
356Severity=Success
357SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_DEPENDENT
358Language=English
359Plan skipped related bundle: %1!ls!, type: %2!hs!, because it was dependent and the current bundle is being executed as type: %3!hs!.
360.
361
362MessageId=214
363Severity=Success
364SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_SCHEDULED
365Language=English
366Plan skipped related bundle: %1!ls!, type: %2!hs!, because it was previously scheduled.
367.
368
369MessageId=215
370Severity=Success
371SymbolicName=MSG_PLANNED_ORPHAN_PACKAGE_FROM_PROVIDER
372Language=English
373Will remove orphan package: %1!ls!, installed: %2!ls!, chained: %3!ls!
374.
375
376MessageId=216
377Severity=Success
378SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_EMBEDDED_BUNDLE_NEWER
379Language=English
380Plan skipped related bundle: %1!ls!, type: %2!hs!, provider key: %3!ls!, because an embedded bundle with the same provider key is being installed.
381.
382
383MessageId=217
384Severity=Success
385SymbolicName=MSG_PLAN_SKIPPED_DEPENDENT_BUNDLE_REPAIR
386Language=English
387Plan skipped dependent bundle repair: %1!ls!, type: %2!hs!, because no packages are being executed during this uninstall operation.
388.
389
390MessageId=299
391Severity=Success
392SymbolicName=MSG_PLAN_COMPLETE
393Language=English
394Plan complete, result: 0x%1!x!
395.
396
397MessageId=300
398Severity=Success
399SymbolicName=MSG_APPLY_BEGIN
400Language=English
401Apply begin
402.
403
404MessageId=301
405Severity=Success
406SymbolicName=MSG_APPLYING_PACKAGE
407Language=English
408Applying %1!hs! package: %2!ls!, action: %3!hs!, path: %4!ls!, arguments: '%5!ls!'
409.
410
411MessageId=302
412Severity=Success
413SymbolicName=MSG_ACQUIRED_PAYLOAD
414Language=English
415Acquired payload: %1!ls! to working path: %2!ls! from: %4!ls!.
416.
417
418MessageId=304
419Severity=Success
420SymbolicName=MSG_VERIFIED_EXISTING_PAYLOAD
421Language=English
422Verified existing payload: %1!ls! at path: %2!ls!.
423.
424
425MessageId=305
426Severity=Success
427SymbolicName=MSG_VERIFIED_ACQUIRED_PAYLOAD
428Language=English
429Verified acquired payload: %1!ls! at path: %2!ls!, %3!hs! to: %4!ls!.
430.
431
432MessageId=306
433Severity=Success
434SymbolicName=MSG_APPLYING_PATCH_PACKAGE
435Language=English
436Applying package: %1!ls!, target: %5!ls!, action: %2!hs!, path: %3!ls!, arguments: '%4!ls!'
437.
438
439MessageId=307
440Severity=Warning
441SymbolicName=MSG_ATTEMPTED_UNINSTALL_ABSENT_PACKAGE
442Language=English
443Attempted to uninstall absent package: %1!ls!. Continuing...
444.
445
446MessageId=308
447Severity=Warning
448SymbolicName=MSG_FAILED_PAUSE_AU
449Language=English
450Automatic updates could not be paused due to error: 0x%1!x!. Continuing...
451.
452
453MessageId=309
454Severity=Warning
455SymbolicName=MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE
456Language=English
457Skipping apply of package: %1!ls! due to cache error: 0x%2!x!. Continuing...
458.
459
460MessageId=310
461Severity=Error
462SymbolicName=MSG_FAILED_VERIFY_PAYLOAD
463Language=English
464Failed to verify payload: %2!ls! at path: %3!ls!, error: %1!ls!. Deleting file.
465.
466
467MessageId=311
468Severity=Error
469SymbolicName=MSG_FAILED_ACQUIRE_CONTAINER
470Language=English
471Failed to acquire container: %2!ls! to working path: %3!ls!, error: %1!ls!.
472.
473
474MessageId=312
475Severity=Error
476SymbolicName=MSG_FAILED_EXTRACT_CONTAINER
477Language=English
478Failed to extract payloads from container: %2!ls! to working path: %3!ls!, error: %1!ls!.
479.
480
481MessageId=313
482Severity=Error
483SymbolicName=MSG_FAILED_ACQUIRE_PAYLOAD
484Language=English
485Failed to acquire payload: %2!ls! to working path: %3!ls!, error: %1!ls!.
486.
487
488MessageId=314
489Severity=Error
490SymbolicName=MSG_FAILED_CACHE_PAYLOAD
491Language=English
492Failed to cache payload: %2!ls! from working path: %3!ls!, error: %1!ls!.
493.
494
495MessageId=315
496Severity=Error
497SymbolicName=MSG_FAILED_LAYOUT_BUNDLE
498Language=English
499Failed to layout bundle: %2!ls! to layout directory: %3!ls!, error: %1!ls!.
500.
501
502MessageId=316
503Severity=Error
504SymbolicName=MSG_FAILED_LAYOUT_CONTAINER
505Language=English
506Failed to layout container: %2!ls! to layout directory: %3!ls!, error: %1!ls!.
507.
508
509
510MessageId=317
511Severity=Error
512SymbolicName=MSG_FAILED_LAYOUT_PAYLOAD
513Language=English
514Failed to layout payload: %2!ls! to layout directory: %3!ls!, error: %1!ls!.
515.
516
517MessageId=318
518Severity=Success
519SymbolicName=MSG_ROLLBACK_PACKAGE_SKIPPED
520Language=English
521Skipped rollback of package: %1!ls!, action: %2!hs!, already: %3!hs!
522.
523
524MessageId=319
525Severity=Success
526SymbolicName=MSG_APPLY_COMPLETED_PACKAGE
527Language=English
528Applied %1!hs! package: %2!ls!, result: 0x%3!x!, restart: %4!hs!
529.
530
531MessageId=320
532Severity=Success
533SymbolicName=MSG_DEPENDENCY_BUNDLE_REGISTER
534Language=English
535Registering bundle dependency provider: %1!ls!, version: %2!ls!
536.
537
538MessageId=321
539Severity=Warning
540SymbolicName=MSG_DEPENDENCY_PACKAGE_SKIP_NOPROVIDERS
541Language=English
542Skipping dependency registration on package with no dependency providers: %1!ls!
543.
544
545MessageId=322
546Severity=Warning
547SymbolicName=MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE
548Language=English
549Skipping cross-scope dependency registration on package: %1!ls!, bundle scope: %2!hs!, package scope: %3!hs!
550.
551
552MessageId=323
553Severity=Success
554SymbolicName=MSG_DEPENDENCY_PACKAGE_REGISTER
555Language=English
556Registering package dependency provider: %1!ls!, version: %2!ls!, package: %3!ls!
557.
558
559MessageId=324
560Severity=Warning
561SymbolicName=MSG_DEPENDENCY_PACKAGE_SKIP_MISSING
562Language=English
563Skipping dependency registration on missing package provider: %1!ls!, package: %2!ls!
564.
565
566MessageId=325
567Severity=Success
568SymbolicName=MSG_DEPENDENCY_PACKAGE_REGISTER_DEPENDENCY
569Language=English
570Registering dependency: %1!ls! on package provider: %2!ls!, package: %3!ls!
571.
572
573MessageId=326
574Severity=Success
575SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY
576Language=English
577Removed dependency: %1!ls! on package provider: %2!ls!, package %3!ls!
578.
579
580MessageId=327
581Severity=Warning
582SymbolicName=MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS
583Language=English
584Will not uninstall package: %1!ls!, found dependents: %2!d!
585.
586
587MessageId=328
588Severity=Warning
589SymbolicName=MSG_DEPENDENCY_PACKAGE_DEPENDENT
590Language=English
591Found dependent: %1!ls!, name: %2!ls!
592.
593
594MessageId=329
595Severity=Success
596SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED
597Language=English
598Removed package dependency provider: %1!ls!, package: %2!ls!
599.
600
601MessageId=330
602Severity=Success
603SymbolicName=MSG_DEPENDENCY_BUNDLE_UNREGISTERED
604Language=English
605Removed bundle dependency provider: %1!ls!
606.
607
608MessageId=331
609Severity=Warning
610SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY_FAILED
611Language=English
612Could not remove dependency: %1!ls! on package provider: %2!ls!, package %3!ls!, error: 0x%4!x!
613.
614
615MessageId=332
616Severity=Warning
617SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED_FAILED
618Language=English
619Could not remove package dependency provider: %1!ls!, package: %2!ls!, error: 0x%3!x!
620.
621
622MessageId=333
623Severity=Warning
624SymbolicName=MSG_DEPENDENCY_BUNDLE_UNREGISTERED_FAILED
625Language=English
626Could not remove bundle dependency provider: %1!ls!, error: 0x%2!x!
627.
628
629MessageId=335
630Severity=Success
631SymbolicName=MSG_ACQUIRE_BUNDLE_PAYLOAD
632Language=English
633Acquiring bundle payload: %2!ls!, %3!hs! from: %4!ls!
634.
635
636MessageId=336
637Severity=Success
638SymbolicName=MSG_ACQUIRE_CONTAINER
639Language=English
640Acquiring container: %1!ls!, %3!hs! from: %4!ls!
641.
642
643MessageId=337
644Severity=Success
645SymbolicName=MSG_ACQUIRE_CONTAINER_PAYLOAD
646Language=English
647Acquiring container: %1!ls!, payload: %2!ls!, %3!hs! from: %4!ls!
648.
649
650MessageId=338
651Severity=Success
652SymbolicName=MSG_ACQUIRE_PACKAGE_PAYLOAD
653Language=English
654Acquiring package: %1!ls!, payload: %2!ls!, %3!hs! from: %4!ls!
655.
656
657MessageId=340
658Severity=Warning
659SymbolicName=MSG_PROMPT_BUNDLE_PAYLOAD_SOURCE
660Language=English
661Prompt for source of bundle payload: %2!ls!, path: %3!ls!
662.
663
664MessageId=341
665Severity=Warning
666SymbolicName=MSG_PROMPT_CONTAINER_SOURCE
667Language=English
668Prompt for source of container: %1!ls!, path: %3!ls!
669.
670
671MessageId=342
672Severity=Warning
673SymbolicName=MSG_PROMPT_CONTAINER_PAYLOAD_SOURCE
674Language=English
675Prompt for source of container: %1!ls!, payload: %2!ls!, path: %3!ls!
676.
677
678MessageId=343
679Severity=Warning
680SymbolicName=MSG_PROMPT_PACKAGE_PAYLOAD_SOURCE
681Language=English
682Prompt for source of package: %1!ls!, payload: %2!ls!, path: %3!ls!
683.
684
685MessageId=348
686Severity=Warning
687SymbolicName=MSG_APPLY_RETRYING_PACKAGE
688Language=English
689Application requested retry of package: %1!ls!, encountered error: 0x%2!x!. Retrying...
690.
691
692MessageId=349
693Severity=Warning
694SymbolicName=MSG_APPLY_RETRYING_PAYLOAD
695Language=English
696Application requested retry of payload: %2!ls!, encountered error: %1!ls!. Retrying...
697.
698
699MessageId=350
700Severity=Warning
701SymbolicName=MSG_APPLY_CONTINUING_NONVITAL_PACKAGE
702Language=English
703Applied non-vital package: %1!ls!, encountered error: 0x%2!x!. Continuing...
704.
705
706MessageId=351
707Severity=Success
708SymbolicName=MSG_UNCACHE_PACKAGE
709Language=English
710Removing cached package: %1!ls!, from path: %2!ls!
711.
712
713MessageId=352
714Severity=Success
715SymbolicName=MSG_UNCACHE_BUNDLE
716Language=English
717Removing cached bundle: %1!ls!, from path: %2!ls!
718.
719
720MessageId=353
721Severity=Warning
722SymbolicName=MSG_UNABLE_UNCACHE_PACKAGE
723Language=English
724Unable to remove cached package: %1!ls!, from path: %2!ls!, reason: 0x%3!x!. Continuing...
725.
726
727MessageId=354
728Severity=Warning
729SymbolicName=MSG_UNABLE_UNCACHE_BUNDLE
730Language=English
731Unable to remove cached bundle: %1!ls!, from path: %2!ls!, reason: 0x%3!x!. Continuing...
732.
733
734MessageId=355
735Severity=Warning
736SymbolicName=MSG_SOURCELIST_REGISTER
737Language=English
738Unable to register source directory: %1!ls!, product: %2!ls!, reason: 0x%3!x!. Continuing...
739.
740
741MessageId=358
742Severity=Success
743SymbolicName=MSG_PAUSE_AU_STARTING
744Language=English
745Pausing automatic updates.
746.
747
748MessageId=359
749Severity=Success
750SymbolicName=MSG_PAUSE_AU_SUCCEEDED
751Language=English
752Paused automatic updates.
753.
754
755MessageId=360
756Severity=Success
757SymbolicName=MSG_SYSTEM_RESTORE_POINT_STARTING
758Language=English
759Creating a system restore point.
760.
761
762MessageId=361
763Severity=Success
764SymbolicName=MSG_SYSTEM_RESTORE_POINT_SUCCEEDED
765Language=English
766Created a system restore point.
767.
768
769MessageId=362
770Severity=Success
771SymbolicName=MSG_SYSTEM_RESTORE_POINT_DISABLED
772Language=English
773System restore disabled, system restore point not created.
774.
775
776MessageId=363
777Severity=Warning
778SymbolicName=MSG_SYSTEM_RESTORE_POINT_FAILED
779Language=English
780Could not create system restore point, error: 0x%1!x!. Continuing...
781.
782
783MessageId=370
784Severity=Success
785SymbolicName=MSG_SESSION_BEGIN
786Language=English
787Session begin, registration key: %1!ls!, options: 0x%2!x!, disable resume: %3!hs!
788.
789
790MessageId=371
791Severity=Success
792SymbolicName=MSG_SESSION_UPDATE
793Language=English
794Updating session, registration key: %1!ls!, resume: %2!hs!, restart initiated: %3!hs!, disable resume: %4!hs!
795.
796
797MessageId=372
798Severity=Success
799SymbolicName=MSG_SESSION_END
800Language=English
801Session end, registration key: %1!ls!, resume: %2!hs!, restart: %3!hs!, disable resume: %4!hs!
802.
803
804MessageId=380
805Severity=Warning
806SymbolicName=MSG_APPLY_SKIPPED
807Language=English
808Apply skipped, no planned actions
809.
810
811MessageId=381
812Severity=Warning
813SymbolicName=MSG_APPLY_CANCEL_IGNORED_DURING_ROLLBACK
814Language=English
815Ignoring application request to cancel from %1!ls! during rollback.
816.
817
818MessageId=399
819Severity=Success
820SymbolicName=MSG_APPLY_COMPLETE
821Language=English
822Apply complete, result: 0x%1!x!, restart: %2!hs!, ba requested restart: %3!hs!
823.
824
825MessageId=400
826Severity=Success
827SymbolicName=MSG_SYSTEM_SHUTDOWN
828Language=English
829Received system request to shut down the process: critical: %1!hs!, elevated: %2!hs!, allowed: %3!hs!
830.
831
832MessageId=410
833Severity=Success
834SymbolicName=MSG_VARIABLE_DUMP
835Language=English
836Variable: %1!ls!
837.
838
839MessageId=420
840Severity=Success
841SymbolicName=MSG_RESUME_AU_STARTING
842Language=English
843Resuming automatic updates.
844.
845
846MessageId=421
847Severity=Success
848SymbolicName=MSG_RESUME_AU_SUCCEEDED
849Language=English
850Resumed automatic updates.
851.
852
853MessageId=500
854Severity=Success
855SymbolicName=MSG_QUIT
856Language=English
857Shutting down, exit code: 0x%1!x!
858.
859
860MessageId=501
861Severity=Warning
862SymbolicName=MSG_STATE_NOT_SAVED
863Language=English
864The state file could not be saved, error: 0x%1!x!. Continuing...
865.
866
867MessageId=600
868Severity=Success
869SymbolicName=MSG_LAUNCH_APPROVED_EXE_BEGIN
870Language=English
871LaunchApprovedExe begin, id: %1!ls!
872.
873
874MessageId=601
875Severity=Success
876SymbolicName=MSG_LAUNCH_APPROVED_EXE_SEARCH
877Language=English
878Searching registry for approved exe path, key: %1!ls!, value: '%2!ls!', win64: %3!ls!
879.
880
881MessageId=602
882Severity=Success
883SymbolicName=MSG_LAUNCHING_APPROVED_EXE
884Language=English
885Launching approved exe, path: '%1!ls!', 'command: %2!ls!'
886.
887
888MessageId=699
889Severity=Success
890SymbolicName=MSG_LAUNCH_APPROVED_EXE_COMPLETE
891Language=English
892LaunchApprovedExe complete, result: 0x%1!x!, processId: %2!lu!
893.
894
895MessageId=700
896Severity=Success
897SymbolicName=MSG_MSI_PROPERTY_CONDITION_FAILED
898Language=English
899Skipping MSI property '%1!ls!' because condition '%2!ls!' evaluates to %3!hs!.
900.
901
diff --git a/src/engine/exeengine.cpp b/src/engine/exeengine.cpp
new file mode 100644
index 00000000..71540d5d
--- /dev/null
+++ b/src/engine/exeengine.cpp
@@ -0,0 +1,820 @@
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#include "precomp.h"
4
5
6// internal function declarations
7
8static HRESULT HandleExitCode(
9 __in BURN_PACKAGE* pPackage,
10 __in DWORD dwExitCode,
11 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
12 );
13static HRESULT ParseCommandLineArgumentsFromXml(
14 __in IXMLDOMNode* pixnExePackage,
15 __in BURN_PACKAGE* pPackage
16 );
17static HRESULT ParseExitCodesFromXml(
18 __in IXMLDOMNode* pixnExePackage,
19 __in BURN_PACKAGE* pPackage
20 );
21
22
23// function definitions
24
25extern "C" HRESULT ExeEngineParsePackageFromXml(
26 __in IXMLDOMNode* pixnExePackage,
27 __in BURN_PACKAGE* pPackage
28 )
29{
30 HRESULT hr = S_OK;
31 IXMLDOMNodeList* pixnNodes = NULL;
32 IXMLDOMNode* pixnNode = NULL;
33 LPWSTR scz = NULL;
34
35 // @DetectCondition
36 hr = XmlGetAttributeEx(pixnExePackage, L"DetectCondition", &pPackage->Exe.sczDetectCondition);
37 ExitOnFailure(hr, "Failed to get @DetectCondition.");
38
39 // @InstallArguments
40 hr = XmlGetAttributeEx(pixnExePackage, L"InstallArguments", &pPackage->Exe.sczInstallArguments);
41 ExitOnFailure(hr, "Failed to get @InstallArguments.");
42
43 // @UninstallArguments
44 hr = XmlGetAttributeEx(pixnExePackage, L"UninstallArguments", &pPackage->Exe.sczUninstallArguments);
45 ExitOnFailure(hr, "Failed to get @UninstallArguments.");
46
47 // @RepairArguments
48 hr = XmlGetAttributeEx(pixnExePackage, L"RepairArguments", &pPackage->Exe.sczRepairArguments);
49 ExitOnFailure(hr, "Failed to get @RepairArguments.");
50
51 // @Repairable
52 hr = XmlGetYesNoAttribute(pixnExePackage, L"Repairable", &pPackage->Exe.fRepairable);
53 if (E_NOTFOUND != hr)
54 {
55 ExitOnFailure(hr, "Failed to get @Repairable.");
56 }
57
58 // @Protocol
59 hr = XmlGetAttributeEx(pixnExePackage, L"Protocol", &scz);
60 if (SUCCEEDED(hr))
61 {
62 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"burn", -1))
63 {
64 pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_BURN;
65 }
66 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"netfx4", -1))
67 {
68 pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_NETFX4;
69 }
70 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"none", -1))
71 {
72 pPackage->Exe.protocol = BURN_EXE_PROTOCOL_TYPE_NONE;
73 }
74 else
75 {
76 hr = E_UNEXPECTED;
77 ExitOnFailure(hr, "Invalid protocol type: %ls", scz);
78 }
79 }
80 else if (E_NOTFOUND != hr)
81 {
82 ExitOnFailure(hr, "Failed to get @Protocol.");
83 }
84
85 hr = ParseExitCodesFromXml(pixnExePackage, pPackage);
86 ExitOnFailure(hr, "Failed to parse exit codes.");
87
88 hr = ParseCommandLineArgumentsFromXml(pixnExePackage, pPackage);
89 ExitOnFailure(hr, "Failed to parse command lines.");
90
91LExit:
92 ReleaseObject(pixnNodes);
93 ReleaseObject(pixnNode);
94 ReleaseStr(scz);
95
96 return hr;
97}
98
99extern "C" void ExeEnginePackageUninitialize(
100 __in BURN_PACKAGE* pPackage
101 )
102{
103 ReleaseStr(pPackage->Exe.sczDetectCondition);
104 ReleaseStr(pPackage->Exe.sczInstallArguments);
105 ReleaseStr(pPackage->Exe.sczRepairArguments);
106 ReleaseStr(pPackage->Exe.sczUninstallArguments);
107 ReleaseStr(pPackage->Exe.sczIgnoreDependencies);
108 ReleaseStr(pPackage->Exe.sczAncestors);
109 //ReleaseStr(pPackage->Exe.sczProgressSwitch);
110 ReleaseMem(pPackage->Exe.rgExitCodes);
111
112 // free command-line arguments
113 if (pPackage->Exe.rgCommandLineArguments)
114 {
115 for (DWORD i = 0; i < pPackage->Exe.cCommandLineArguments; ++i)
116 {
117 BURN_EXE_COMMAND_LINE_ARGUMENT* pCommandLineArgument = &pPackage->Exe.rgCommandLineArguments[i];
118 ReleaseStr(pCommandLineArgument->sczInstallArgument);
119 ReleaseStr(pCommandLineArgument->sczUninstallArgument);
120 ReleaseStr(pCommandLineArgument->sczRepairArgument);
121 ReleaseStr(pCommandLineArgument->sczCondition);
122 }
123 MemFree(pPackage->Exe.rgCommandLineArguments);
124 }
125
126 // clear struct
127 memset(&pPackage->Exe, 0, sizeof(pPackage->Exe));
128}
129
130extern "C" HRESULT ExeEngineDetectPackage(
131 __in BURN_PACKAGE* pPackage,
132 __in BURN_VARIABLES* pVariables
133 )
134{
135 HRESULT hr = S_OK;
136 BOOL fDetected = FALSE;
137
138 // evaluate detect condition
139 if (pPackage->Exe.sczDetectCondition && *pPackage->Exe.sczDetectCondition)
140 {
141 hr = ConditionEvaluate(pVariables, pPackage->Exe.sczDetectCondition, &fDetected);
142 ExitOnFailure(hr, "Failed to evaluate executable package detect condition.");
143 }
144
145 // update detect state
146 pPackage->currentState = fDetected ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
147
148LExit:
149 return hr;
150}
151
152//
153// PlanCalculate - calculates the execute and rollback state for the requested package state.
154//
155extern "C" HRESULT ExeEnginePlanCalculatePackage(
156 __in BURN_PACKAGE* pPackage,
157 __out_opt BOOL* pfBARequestedCache
158 )
159{
160 HRESULT hr = S_OK;
161 //BOOL fCondition = FALSE;
162 //BOOTSTRAPPER_PACKAGE_STATE expected = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN;
163 BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE;
164 BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
165 BOOL fBARequestedCache = FALSE;
166
167 //// evaluate rollback install condition
168 //if (pPackage->sczRollbackInstallCondition)
169 //{
170 // hr = ConditionEvaluate(pVariables, pPackage->sczRollbackInstallCondition, &fCondition);
171 // ExitOnFailure(hr, "Failed to evaluate rollback install condition.");
172
173 // expected = fCondition ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
174 //}
175
176 // execute action
177 switch (pPackage->currentState)
178 {
179 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
180 switch (pPackage->requested)
181 {
182 case BOOTSTRAPPER_REQUEST_STATE_PRESENT:
183 execute = pPackage->Exe.fPseudoBundle ? BOOTSTRAPPER_ACTION_STATE_INSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
184 break;
185 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
186 execute = pPackage->Exe.fRepairable ? BOOTSTRAPPER_ACTION_STATE_REPAIR : BOOTSTRAPPER_ACTION_STATE_NONE;
187 break;
188 case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough;
189 case BOOTSTRAPPER_REQUEST_STATE_CACHE:
190 execute = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
191 break;
192 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT:
193 execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
194 break;
195 default:
196 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
197 break;
198 }
199 break;
200
201 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
202 switch (pPackage->requested)
203 {
204 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
205 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
206 execute = BOOTSTRAPPER_ACTION_STATE_INSTALL;
207 break;
208 case BOOTSTRAPPER_REQUEST_STATE_CACHE:
209 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
210 fBARequestedCache = TRUE;
211 break;
212 default:
213 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
214 break;
215 }
216 break;
217
218 default:
219 hr = E_INVALIDARG;
220 ExitOnRootFailure(hr, "Invalid package current state: %d.", pPackage->currentState);
221 }
222
223 // Calculate the rollback action if there is an execute action.
224 if (BOOTSTRAPPER_ACTION_STATE_NONE != execute)
225 {
226 switch (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN != pPackage->expected ? pPackage->expected : pPackage->currentState)
227 {
228 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
229 switch (pPackage->requested)
230 {
231 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
232 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
233 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
234 break;
235 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough;
236 case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
237 rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL;
238 break;
239 default:
240 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
241 break;
242 }
243 break;
244
245 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
246 switch (pPackage->requested)
247 {
248 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
249 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
250 rollback = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
251 break;
252 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough;
253 case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
254 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
255 break;
256 default:
257 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
258 break;
259 }
260 break;
261
262 default:
263 hr = E_INVALIDARG;
264 ExitOnRootFailure(hr, "Invalid package expected state.");
265 }
266 }
267
268 // return values
269 pPackage->execute = execute;
270 pPackage->rollback = rollback;
271
272 if (pfBARequestedCache)
273 {
274 *pfBARequestedCache = fBARequestedCache;
275 }
276
277LExit:
278 return hr;
279}
280
281//
282// PlanAdd - adds the calculated execute and rollback actions for the package.
283//
284extern "C" HRESULT ExeEnginePlanAddPackage(
285 __in_opt DWORD *pdwInsertSequence,
286 __in BURN_PACKAGE* pPackage,
287 __in BURN_PLAN* pPlan,
288 __in BURN_LOGGING* pLog,
289 __in BURN_VARIABLES* pVariables,
290 __in_opt HANDLE hCacheEvent,
291 __in BOOL fPlanPackageCacheRollback
292 )
293{
294 HRESULT hr = S_OK;
295 BURN_EXECUTE_ACTION* pAction = NULL;
296
297 // add wait for cache
298 if (hCacheEvent)
299 {
300 hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent, fPlanPackageCacheRollback);
301 ExitOnFailure(hr, "Failed to plan package cache syncpoint");
302 }
303
304 hr = DependencyPlanPackage(pdwInsertSequence, pPackage, pPlan);
305 ExitOnFailure(hr, "Failed to plan package dependency actions.");
306
307 // add execute action
308 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute)
309 {
310 if (NULL != pdwInsertSequence)
311 {
312 hr = PlanInsertExecuteAction(*pdwInsertSequence, pPlan, &pAction);
313 ExitOnFailure(hr, "Failed to insert execute action.");
314 }
315 else
316 {
317 hr = PlanAppendExecuteAction(pPlan, &pAction);
318 ExitOnFailure(hr, "Failed to append execute action.");
319 }
320
321 pAction->type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE;
322 pAction->exePackage.pPackage = pPackage;
323 pAction->exePackage.fFireAndForget = (BOOTSTRAPPER_ACTION_UPDATE_REPLACE == pPlan->action);
324 pAction->exePackage.action = pPackage->execute;
325
326 if (pPackage->Exe.sczIgnoreDependencies)
327 {
328 hr = StrAllocString(&pAction->exePackage.sczIgnoreDependencies, pPackage->Exe.sczIgnoreDependencies, 0);
329 ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore.");
330 }
331
332 if (pPackage->Exe.sczAncestors)
333 {
334 hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.sczAncestors, 0);
335 ExitOnFailure(hr, "Failed to allocate the list of ancestors.");
336 }
337
338 LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, NULL); // ignore errors.
339 }
340
341 // add rollback action
342 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)
343 {
344 hr = PlanAppendRollbackAction(pPlan, &pAction);
345 ExitOnFailure(hr, "Failed to append rollback action.");
346
347 pAction->type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE;
348 pAction->exePackage.pPackage = pPackage;
349 pAction->exePackage.action = pPackage->rollback;
350
351 if (pPackage->Exe.sczIgnoreDependencies)
352 {
353 hr = StrAllocString(&pAction->exePackage.sczIgnoreDependencies, pPackage->Exe.sczIgnoreDependencies, 0);
354 ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore.");
355 }
356
357 if (pPackage->Exe.sczAncestors)
358 {
359 hr = StrAllocString(&pAction->exePackage.sczAncestors, pPackage->Exe.sczAncestors, 0);
360 ExitOnFailure(hr, "Failed to allocate the list of ancestors.");
361 }
362
363 LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, NULL); // ignore errors.
364 }
365
366LExit:
367 return hr;
368}
369
370extern "C" HRESULT ExeEngineExecutePackage(
371 __in BURN_EXECUTE_ACTION* pExecuteAction,
372 __in BURN_VARIABLES* pVariables,
373 __in BOOL fRollback,
374 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
375 __in LPVOID pvContext,
376 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
377 )
378{
379 HRESULT hr = S_OK;
380 WCHAR wzCurrentDirectory[MAX_PATH] = { };
381 BOOL fChangedCurrentDirectory = FALSE;
382 int nResult = IDNOACTION;
383 LPCWSTR wzArguments = NULL;
384 LPWSTR sczArguments = NULL;
385 LPWSTR sczArgumentsFormatted = NULL;
386 LPWSTR sczArgumentsObfuscated = NULL;
387 LPWSTR sczCachedDirectory = NULL;
388 LPWSTR sczExecutablePath = NULL;
389 LPWSTR sczCommand = NULL;
390 LPWSTR sczCommandObfuscated = NULL;
391 HANDLE hExecutableFile = INVALID_HANDLE_VALUE;
392 STARTUPINFOW si = { };
393 PROCESS_INFORMATION pi = { };
394 DWORD dwExitCode = 0;
395 GENERIC_EXECUTE_MESSAGE message = { };
396
397 // get cached executable path
398 hr = CacheGetCompletedPath(pExecuteAction->exePackage.pPackage->fPerMachine, pExecuteAction->exePackage.pPackage->sczCacheId, &sczCachedDirectory);
399 ExitOnFailure(hr, "Failed to get cached path for package: %ls", pExecuteAction->exePackage.pPackage->sczId);
400
401 // Best effort to set the execute package cache folder and action variables.
402 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE);
403 VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->exePackage.action, TRUE);
404
405 hr = PathConcat(sczCachedDirectory, pExecuteAction->exePackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczExecutablePath);
406 ExitOnFailure(hr, "Failed to build executable path.");
407
408 // pick arguments
409 switch (pExecuteAction->exePackage.action)
410 {
411 case BOOTSTRAPPER_ACTION_STATE_INSTALL:
412 wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczInstallArguments;
413 break;
414
415 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
416 wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczUninstallArguments;
417 break;
418
419 case BOOTSTRAPPER_ACTION_STATE_REPAIR:
420 wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczRepairArguments;
421 break;
422
423 default:
424 hr = E_INVALIDARG;
425 ExitOnFailure(hr, "Invalid Exe package action: %d.", pExecuteAction->exePackage.action);
426 }
427
428 // now add optional arguments
429 hr = StrAllocString(&sczArguments, wzArguments && *wzArguments ? wzArguments : L"", 0);
430 ExitOnFailure(hr, "Failed to copy package arguments.");
431
432 for (DWORD i = 0; i < pExecuteAction->exePackage.pPackage->Exe.cCommandLineArguments; ++i)
433 {
434 BURN_EXE_COMMAND_LINE_ARGUMENT* commandLineArgument = &pExecuteAction->exePackage.pPackage->Exe.rgCommandLineArguments[i];
435 BOOL fCondition = FALSE;
436
437 hr = ConditionEvaluate(pVariables, commandLineArgument->sczCondition, &fCondition);
438 ExitOnFailure(hr, "Failed to evaluate executable package command-line condition.");
439
440 if (fCondition)
441 {
442 hr = StrAllocConcat(&sczArguments, L" ", 0);
443 ExitOnFailure(hr, "Failed to separate command-line arguments.");
444
445 switch (pExecuteAction->exePackage.action)
446 {
447 case BOOTSTRAPPER_ACTION_STATE_INSTALL:
448 hr = StrAllocConcat(&sczArguments, commandLineArgument->sczInstallArgument, 0);
449 ExitOnFailure(hr, "Failed to get command-line argument for install.");
450 break;
451
452 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
453 hr = StrAllocConcat(&sczArguments, commandLineArgument->sczUninstallArgument, 0);
454 ExitOnFailure(hr, "Failed to get command-line argument for uninstall.");
455 break;
456
457 case BOOTSTRAPPER_ACTION_STATE_REPAIR:
458 hr = StrAllocConcat(&sczArguments, commandLineArgument->sczRepairArgument, 0);
459 ExitOnFailure(hr, "Failed to get command-line argument for repair.");
460 break;
461
462 default:
463 hr = E_INVALIDARG;
464 ExitOnFailure(hr, "Invalid Exe package action: %d.", pExecuteAction->exePackage.action);
465 }
466 }
467 }
468
469 // build command
470 if (0 < lstrlenW(sczArguments))
471 {
472 hr = VariableFormatString(pVariables, sczArguments, &sczArgumentsFormatted, NULL);
473 ExitOnFailure(hr, "Failed to format argument string.");
474
475 hr = StrAllocFormattedSecure(&sczCommand, L"\"%ls\" %s", sczExecutablePath, sczArgumentsFormatted);
476 ExitOnFailure(hr, "Failed to create executable command.");
477
478 hr = VariableFormatStringObfuscated(pVariables, sczArguments, &sczArgumentsObfuscated, NULL);
479 ExitOnFailure(hr, "Failed to format obfuscated argument string.");
480
481 hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\" %s", sczExecutablePath, sczArgumentsObfuscated);
482 }
483 else
484 {
485 hr = StrAllocFormatted(&sczCommand, L"\"%ls\"", sczExecutablePath);
486 ExitOnFailure(hr, "Failed to create executable command.");
487
488 hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\"", sczExecutablePath);
489 }
490 ExitOnFailure(hr, "Failed to create obfuscated executable command.");
491
492 if (pExecuteAction->exePackage.pPackage->Exe.fSupportsAncestors)
493 {
494 // Add the list of dependencies to ignore, if any, to the burn command line.
495 if (pExecuteAction->exePackage.sczIgnoreDependencies && BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol)
496 {
497 hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies);
498 ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line.");
499
500 hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies);
501 ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the obfuscated command line.");
502 }
503
504 // Add the list of ancestors, if any, to the burn command line.
505 if (pExecuteAction->exePackage.sczAncestors)
506 {
507 hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors);
508 ExitOnFailure(hr, "Failed to append the list of ancestors to the command line.");
509
510 hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors);
511 ExitOnFailure(hr, "Failed to append the list of ancestors to the obfuscated command line.");
512 }
513 }
514
515 if (BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol)
516 {
517 hr = CoreAppendFileHandleSelfToCommandLine(sczExecutablePath, &hExecutableFile, &sczCommand, &sczCommandObfuscated);
518 ExitOnFailure(hr, "Failed to append %ls", BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF);
519 }
520
521 // Log before we add the secret pipe name and client token for embedded processes.
522 LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->exePackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->exePackage.action), sczExecutablePath, sczCommandObfuscated);
523
524 if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol)
525 {
526 hr = EmbeddedRunBundle(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode);
527 ExitOnFailure(hr, "Failed to run bundle as embedded from path: %ls", sczExecutablePath);
528 }
529 else if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_NETFX4 == pExecuteAction->exePackage.pPackage->Exe.protocol)
530 {
531 hr = NetFxRunChainer(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode);
532 ExitOnFailure(hr, "Failed to run netfx chainer: %ls", sczExecutablePath);
533 }
534 else // create and wait for the executable process while sending fake progress to allow cancel.
535 {
536 // Make the cache location of the executable the current directory to help those executables
537 // that expect stuff to be relative to them.
538 if (::GetCurrentDirectoryW(countof(wzCurrentDirectory), wzCurrentDirectory))
539 {
540 fChangedCurrentDirectory = ::SetCurrentDirectoryW(sczCachedDirectory);
541 }
542
543 si.cb = sizeof(si); // TODO: hookup the stdin/stdout/stderr pipes for logging purposes?
544 if (!::CreateProcessW(sczExecutablePath, sczCommand, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
545 {
546 ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", sczExecutablePath);
547 }
548
549 if (pExecuteAction->exePackage.fFireAndForget)
550 {
551 ::WaitForInputIdle(pi.hProcess, 5000);
552 ExitFunction();
553 }
554
555 do
556 {
557 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
558 message.dwAllowedResults = MB_OKCANCEL;
559 message.progress.dwPercentage = 50;
560 nResult = pfnGenericMessageHandler(&message, pvContext);
561 hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE);
562 ExitOnRootFailure(hr, "Bootstrapper application aborted during EXE progress.");
563
564 hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode);
565 if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr)
566 {
567 ExitOnFailure(hr, "Failed to wait for executable to complete: %ls", sczExecutablePath);
568 }
569 } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr);
570 }
571
572 hr = HandleExitCode(pExecuteAction->exePackage.pPackage, dwExitCode, pRestart);
573 ExitOnRootFailure(hr, "Process returned error: 0x%x", dwExitCode);
574
575LExit:
576 if (fChangedCurrentDirectory)
577 {
578 ::SetCurrentDirectoryW(wzCurrentDirectory);
579 }
580
581 StrSecureZeroFreeString(sczArguments);
582 StrSecureZeroFreeString(sczArgumentsFormatted);
583 ReleaseStr(sczArgumentsObfuscated);
584 ReleaseStr(sczCachedDirectory);
585 ReleaseStr(sczExecutablePath);
586 StrSecureZeroFreeString(sczCommand);
587 ReleaseStr(sczCommandObfuscated);
588
589 ReleaseHandle(pi.hThread);
590 ReleaseHandle(pi.hProcess);
591 ReleaseFileHandle(hExecutableFile);
592
593 // Best effort to clear the execute package cache folder and action variables.
594 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE);
595 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE);
596
597 return hr;
598}
599
600
601// internal helper functions
602
603static HRESULT ParseExitCodesFromXml(
604 __in IXMLDOMNode* pixnExePackage,
605 __in BURN_PACKAGE* pPackage
606 )
607{
608 HRESULT hr = S_OK;
609 IXMLDOMNodeList* pixnNodes = NULL;
610 IXMLDOMNode* pixnNode = NULL;
611 DWORD cNodes = 0;
612 LPWSTR scz = NULL;
613
614 // select exit code nodes
615 hr = XmlSelectNodes(pixnExePackage, L"ExitCode", &pixnNodes);
616 ExitOnFailure(hr, "Failed to select exit code nodes.");
617
618 // get exit code node count
619 hr = pixnNodes->get_length((long*) &cNodes);
620 ExitOnFailure(hr, "Failed to get exit code node count.");
621
622 if (cNodes)
623 {
624 // allocate memory for exit codes
625 pPackage->Exe.rgExitCodes = (BURN_EXE_EXIT_CODE*) MemAlloc(sizeof(BURN_EXE_EXIT_CODE) * cNodes, TRUE);
626 ExitOnNull(pPackage->Exe.rgExitCodes, hr, E_OUTOFMEMORY, "Failed to allocate memory for exit code structs.");
627
628 pPackage->Exe.cExitCodes = cNodes;
629
630 // parse package elements
631 for (DWORD i = 0; i < cNodes; ++i)
632 {
633 BURN_EXE_EXIT_CODE* pExitCode = &pPackage->Exe.rgExitCodes[i];
634
635 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
636 ExitOnFailure(hr, "Failed to get next node.");
637
638 // @Type
639 hr = XmlGetAttributeNumber(pixnNode, L"Type", (DWORD*)&pExitCode->type);
640 ExitOnFailure(hr, "Failed to get @Type.");
641
642 // @Code
643 hr = XmlGetAttributeEx(pixnNode, L"Code", &scz);
644 ExitOnFailure(hr, "Failed to get @Code.");
645
646 if (L'*' == scz[0])
647 {
648 pExitCode->fWildcard = TRUE;
649 }
650 else
651 {
652 hr = StrStringToUInt32(scz, 0, (UINT*) &pExitCode->dwCode);
653 ExitOnFailure(hr, "Failed to parse @Code value: %ls", scz);
654 }
655
656 // prepare next iteration
657 ReleaseNullObject(pixnNode);
658 }
659 }
660
661 hr = S_OK;
662
663LExit:
664 ReleaseObject(pixnNodes);
665 ReleaseObject(pixnNode);
666 ReleaseStr(scz);
667
668 return hr;
669}
670
671static HRESULT ParseCommandLineArgumentsFromXml(
672 __in IXMLDOMNode* pixnExePackage,
673 __in BURN_PACKAGE* pPackage
674 )
675{
676 HRESULT hr = S_OK;
677 IXMLDOMNodeList* pixnNodes = NULL;
678 IXMLDOMNode* pixnNode = NULL;
679 DWORD cNodes = 0;
680 LPWSTR scz = NULL;
681
682 // Select command-line argument nodes.
683 hr = XmlSelectNodes(pixnExePackage, L"CommandLine", &pixnNodes);
684 ExitOnFailure(hr, "Failed to select command-line argument nodes.");
685
686 // Get command-line argument node count.
687 hr = pixnNodes->get_length((long*) &cNodes);
688 ExitOnFailure(hr, "Failed to get command-line argument count.");
689
690 if (cNodes)
691 {
692 pPackage->Exe.rgCommandLineArguments = (BURN_EXE_COMMAND_LINE_ARGUMENT*) MemAlloc(sizeof(BURN_EXE_COMMAND_LINE_ARGUMENT) * cNodes, TRUE);
693 ExitOnNull(pPackage->Exe.rgCommandLineArguments, hr, E_OUTOFMEMORY, "Failed to allocate memory for command-line argument structs.");
694
695 pPackage->Exe.cCommandLineArguments = cNodes;
696
697 // Parse command-line argument elements.
698 for (DWORD i = 0; i < cNodes; ++i)
699 {
700 BURN_EXE_COMMAND_LINE_ARGUMENT* pCommandLineArgument = &pPackage->Exe.rgCommandLineArguments[i];
701
702 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
703 ExitOnFailure(hr, "Failed to get next command-line argument node.");
704
705 // @InstallArgument
706 hr = XmlGetAttributeEx(pixnNode, L"InstallArgument", &pCommandLineArgument->sczInstallArgument);
707 ExitOnFailure(hr, "Failed to get @InstallArgument.");
708
709 // @UninstallArgument
710 hr = XmlGetAttributeEx(pixnNode, L"UninstallArgument", &pCommandLineArgument->sczUninstallArgument);
711 ExitOnFailure(hr, "Failed to get @UninstallArgument.");
712
713 // @RepairArgument
714 hr = XmlGetAttributeEx(pixnNode, L"RepairArgument", &pCommandLineArgument->sczRepairArgument);
715 ExitOnFailure(hr, "Failed to get @RepairArgument.");
716
717 // @Condition
718 hr = XmlGetAttributeEx(pixnNode, L"Condition", &pCommandLineArgument->sczCondition);
719 ExitOnFailure(hr, "Failed to get @Condition.");
720
721 // Prepare next iteration.
722 ReleaseNullObject(pixnNode);
723 }
724 }
725
726 hr = S_OK;
727
728LExit:
729 ReleaseObject(pixnNodes);
730 ReleaseObject(pixnNode);
731 ReleaseStr(scz);
732
733 return hr;
734}
735
736static HRESULT HandleExitCode(
737 __in BURN_PACKAGE* pPackage,
738 __in DWORD dwExitCode,
739 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
740 )
741{
742 HRESULT hr = S_OK;
743 BURN_EXE_EXIT_CODE_TYPE typeCode = BURN_EXE_EXIT_CODE_TYPE_NONE;
744
745 for (DWORD i = 0; i < pPackage->Exe.cExitCodes; ++i)
746 {
747 BURN_EXE_EXIT_CODE* pExitCode = &pPackage->Exe.rgExitCodes[i];
748
749 // If this is a wildcard, use the last one we come across.
750 if (pExitCode->fWildcard)
751 {
752 typeCode = pExitCode->type;
753 }
754 else if (dwExitCode == pExitCode->dwCode) // If we have an exact match on the error code use that and stop looking.
755 {
756 typeCode = pExitCode->type;
757 break;
758 }
759 }
760
761 // If we didn't find a matching code then treat 0 as success, the standard restarts codes as restarts
762 // and everything else as an error.
763 if (BURN_EXE_EXIT_CODE_TYPE_NONE == typeCode)
764 {
765 if (0 == dwExitCode)
766 {
767 typeCode = BURN_EXE_EXIT_CODE_TYPE_SUCCESS;
768 }
769 else if (ERROR_SUCCESS_REBOOT_REQUIRED == dwExitCode ||
770 HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == static_cast<HRESULT>(dwExitCode) ||
771 ERROR_SUCCESS_RESTART_REQUIRED == dwExitCode ||
772 HRESULT_FROM_WIN32(ERROR_SUCCESS_RESTART_REQUIRED) == static_cast<HRESULT>(dwExitCode))
773 {
774 typeCode = BURN_EXE_EXIT_CODE_TYPE_SCHEDULE_REBOOT;
775 }
776 else if (ERROR_SUCCESS_REBOOT_INITIATED == dwExitCode ||
777 HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED) == static_cast<HRESULT>(dwExitCode))
778 {
779 typeCode = BURN_EXE_EXIT_CODE_TYPE_FORCE_REBOOT;
780 }
781 else
782 {
783 typeCode = BURN_EXE_EXIT_CODE_TYPE_ERROR;
784 }
785 }
786
787 switch (typeCode)
788 {
789 case BURN_EXE_EXIT_CODE_TYPE_SUCCESS:
790 *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
791 hr = S_OK;
792 break;
793
794 case BURN_EXE_EXIT_CODE_TYPE_ERROR:
795 *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
796 hr = HRESULT_FROM_WIN32(dwExitCode);
797 if (SUCCEEDED(hr))
798 {
799 hr = E_FAIL;
800 }
801 break;
802
803 case BURN_EXE_EXIT_CODE_TYPE_SCHEDULE_REBOOT:
804 *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
805 hr = S_OK;
806 break;
807
808 case BURN_EXE_EXIT_CODE_TYPE_FORCE_REBOOT:
809 *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
810 hr = S_OK;
811 break;
812
813 default:
814 hr = E_UNEXPECTED;
815 break;
816 }
817
818//LExit:
819 return hr;
820}
diff --git a/src/engine/exeengine.h b/src/engine/exeengine.h
new file mode 100644
index 00000000..402e51ed
--- /dev/null
+++ b/src/engine/exeengine.h
@@ -0,0 +1,48 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// function declarations
11
12HRESULT ExeEngineParsePackageFromXml(
13 __in IXMLDOMNode* pixnExePackage,
14 __in BURN_PACKAGE* pPackage
15 );
16void ExeEnginePackageUninitialize(
17 __in BURN_PACKAGE* pPackage
18 );
19HRESULT ExeEngineDetectPackage(
20 __in BURN_PACKAGE* pPackage,
21 __in BURN_VARIABLES* pVariables
22 );
23HRESULT ExeEnginePlanCalculatePackage(
24 __in BURN_PACKAGE* pPackage,
25 __out_opt BOOL* pfBARequestedCache
26 );
27HRESULT ExeEnginePlanAddPackage(
28 __in_opt DWORD *pdwInsertSequence,
29 __in BURN_PACKAGE* pPackage,
30 __in BURN_PLAN* pPlan,
31 __in BURN_LOGGING* pLog,
32 __in BURN_VARIABLES* pVariables,
33 __in_opt HANDLE hCacheEvent,
34 __in BOOL fPlanPackageCacheRollback
35 );
36HRESULT ExeEngineExecutePackage(
37 __in BURN_EXECUTE_ACTION* pExecuteAction,
38 __in BURN_VARIABLES* pVariables,
39 __in BOOL fRollback,
40 __in PFN_GENERICMESSAGEHANDLER pfnGenericExecuteProgress,
41 __in LPVOID pvContext,
42 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
43 );
44
45
46#if defined(__cplusplus)
47}
48#endif
diff --git a/src/engine/inc/engine.h b/src/engine/inc/engine.h
new file mode 100644
index 00000000..808bb91a
--- /dev/null
+++ b/src/engine/inc/engine.h
@@ -0,0 +1,27 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// function declarations
11
12BOOL EngineInCleanRoom(
13 __in_z_opt LPCWSTR wzCommandLine
14 );
15
16HRESULT EngineRun(
17 __in HINSTANCE hInstance,
18 __in HANDLE hEngineFile,
19 __in_z_opt LPCWSTR wzCommandLine,
20 __in int nCmdShow,
21 __out DWORD* pdwExitCode
22 );
23
24
25#if defined(__cplusplus)
26}
27#endif
diff --git a/src/engine/logging.cpp b/src/engine/logging.cpp
new file mode 100644
index 00000000..818532ad
--- /dev/null
+++ b/src/engine/logging.cpp
@@ -0,0 +1,683 @@
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#include "precomp.h"
4
5
6static DWORD vdwPackageSequence = 0;
7static const DWORD LOG_OPEN_RETRY_COUNT = 3;
8static const DWORD LOG_OPEN_RETRY_WAIT = 2000;
9static CONST LPWSTR LOG_FAILED_EVENT_LOG_MESSAGE = L"Burn Engine Fatal Error: failed to open log file.";
10
11// structs
12
13
14
15// internal function declarations
16
17static void CheckLoggingPolicy(
18 __out DWORD *pdwAttributes
19 );
20static HRESULT GetNonSessionSpecificTempFolder(
21 __deref_out_z LPWSTR* psczNonSessionTempFolder
22 );
23
24
25// function definitions
26
27extern "C" HRESULT LoggingOpen(
28 __in BURN_LOGGING* pLog,
29 __in BURN_VARIABLES* pVariables,
30 __in BOOTSTRAPPER_DISPLAY display,
31 __in_z LPCWSTR wzBundleName
32 )
33{
34 HRESULT hr = S_OK;
35 LPWSTR sczLoggingBaseFolder = NULL;
36
37 // Check if the logging policy is set and configure the logging appropriately.
38 CheckLoggingPolicy(&pLog->dwAttributes);
39
40 if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_VERBOSE || pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG)
41 {
42 if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG)
43 {
44 LogSetLevel(REPORT_DEBUG, FALSE);
45 }
46 else if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_VERBOSE)
47 {
48 LogSetLevel(REPORT_VERBOSE, FALSE);
49 }
50
51 if ((!pLog->sczPath || !*pLog->sczPath) && (!pLog->sczPrefix || !*pLog->sczPrefix))
52 {
53 PathCreateTimeBasedTempFile(NULL, L"Setup", NULL, L"log", &pLog->sczPath, NULL);
54 }
55 }
56
57 // Open the log approriately.
58 if (pLog->sczPath && *pLog->sczPath)
59 {
60 DWORD cRetry = 0;
61
62 hr = DirGetCurrent(&sczLoggingBaseFolder);
63 ExitOnFailure(hr, "Failed to get current directory.");
64
65 // Try pretty hard to open the log file when appending.
66 do
67 {
68 if (0 < cRetry)
69 {
70 ::Sleep(LOG_OPEN_RETRY_WAIT);
71 }
72
73 hr = LogOpen(sczLoggingBaseFolder, pLog->sczPath, NULL, NULL, pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_APPEND, FALSE, &pLog->sczPath);
74 if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_APPEND && HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION) == hr)
75 {
76 ++cRetry;
77 }
78 } while (cRetry > 0 && cRetry <= LOG_OPEN_RETRY_COUNT);
79
80 if (FAILED(hr))
81 {
82 // Log is not open, so note that.
83 LogDisable();
84 pLog->state = BURN_LOGGING_STATE_DISABLED;
85
86 if (pLog->dwAttributes & BURN_LOGGING_ATTRIBUTE_APPEND)
87 {
88 // If appending, ignore the failure and continue.
89 hr = S_OK;
90 }
91 else // specifically tried to create a log file so show an error if appropriate and bail.
92 {
93 HRESULT hrOriginal = hr;
94
95 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_LOG_FAILURE);
96 SplashScreenDisplayError(display, wzBundleName, hr);
97
98 ExitOnFailure(hrOriginal, "Failed to open log: %ls", pLog->sczPath);
99 }
100 }
101 else
102 {
103 pLog->state = BURN_LOGGING_STATE_OPEN;
104 }
105 }
106 else if (pLog->sczPrefix && *pLog->sczPrefix)
107 {
108 hr = GetNonSessionSpecificTempFolder(&sczLoggingBaseFolder);
109 ExitOnFailure(hr, "Failed to get non-session specific TEMP folder.");
110
111 // Best effort to open default logging.
112 hr = LogOpen(sczLoggingBaseFolder, pLog->sczPrefix, NULL, pLog->sczExtension, FALSE, FALSE, &pLog->sczPath);
113 if (FAILED(hr))
114 {
115 LogDisable();
116 pLog->state = BURN_LOGGING_STATE_DISABLED;
117
118 hr = S_OK;
119 }
120 else
121 {
122 pLog->state = BURN_LOGGING_STATE_OPEN;
123 }
124 }
125 else // no logging enabled.
126 {
127 LogDisable();
128 pLog->state = BURN_LOGGING_STATE_DISABLED;
129 }
130
131 // If the log was opened, write the header info and update the prefix and extension to match
132 // the log name so future logs are opened with the same pattern.
133 if (BURN_LOGGING_STATE_OPEN == pLog->state)
134 {
135 LPCWSTR wzExtension = PathExtension(pLog->sczPath);
136 if (wzExtension && *wzExtension)
137 {
138 hr = StrAllocString(&pLog->sczPrefix, pLog->sczPath, wzExtension - pLog->sczPath);
139 ExitOnFailure(hr, "Failed to copy log path to prefix.");
140
141 hr = StrAllocString(&pLog->sczExtension, wzExtension + 1, 0);
142 ExitOnFailure(hr, "Failed to copy log extension to extension.");
143 }
144 else
145 {
146 hr = StrAllocString(&pLog->sczPrefix, pLog->sczPath, 0);
147 ExitOnFailure(hr, "Failed to copy full log path to prefix.");
148 }
149
150 if (pLog->sczPathVariable && *pLog->sczPathVariable)
151 {
152 VariableSetString(pVariables, pLog->sczPathVariable, pLog->sczPath, FALSE); // Ignore failure.
153 }
154 }
155
156LExit:
157 ReleaseStr(sczLoggingBaseFolder);
158
159 return hr;
160}
161
162extern "C" void LoggingOpenFailed()
163{
164 HRESULT hr = S_OK;
165 HANDLE hEventLog = NULL;
166 LPCWSTR* lpStrings = const_cast<LPCWSTR*>(&LOG_FAILED_EVENT_LOG_MESSAGE);
167 WORD wNumStrings = 1;
168
169 hr = LogOpen(NULL, L"Setup", L"_Failed", L"txt", FALSE, FALSE, NULL);
170 if (SUCCEEDED(hr))
171 {
172 ExitFunction();
173 }
174
175 // If opening the "failure" log failed, then attempt to record that in the Application event log.
176 hEventLog = ::OpenEventLogW(NULL, L"Application");
177 ExitOnNullWithLastError(hEventLog, hr, "Failed to open Application event log");
178
179 hr = ::ReportEventW(hEventLog, EVENTLOG_ERROR_TYPE, 1, 1, NULL, wNumStrings, 0, lpStrings, NULL);
180 ExitOnNullWithLastError(hEventLog, hr, "Failed to write event log entry");
181
182LExit:
183 if (hEventLog)
184 {
185 ::CloseEventLog(hEventLog);
186 }
187}
188
189extern "C" void LoggingIncrementPackageSequence()
190{
191 ++vdwPackageSequence;
192}
193
194extern "C" HRESULT LoggingSetPackageVariable(
195 __in BURN_PACKAGE* pPackage,
196 __in_z_opt LPCWSTR wzSuffix,
197 __in BOOL fRollback,
198 __in BURN_LOGGING* pLog,
199 __in BURN_VARIABLES* pVariables,
200 __out_opt LPWSTR* psczLogPath
201 )
202{
203 HRESULT hr = S_OK;
204 LPWSTR sczLogPath = NULL;
205
206 // Make sure that no package log files are created when logging has been disabled via Log element.
207 if (BURN_LOGGING_STATE_DISABLED == pLog->state)
208 {
209 if (psczLogPath)
210 {
211 *psczLogPath = NULL;
212 }
213
214 ExitFunction();
215 }
216
217 if ((!fRollback && pPackage->sczLogPathVariable && *pPackage->sczLogPathVariable) ||
218 (fRollback && pPackage->sczRollbackLogPathVariable && *pPackage->sczRollbackLogPathVariable))
219 {
220 hr = StrAllocFormatted(&sczLogPath, L"%ls%hs%ls_%03u_%ls%ls.%ls", pLog->sczPrefix, wzSuffix && *wzSuffix ? "_" : "", wzSuffix && *wzSuffix ? wzSuffix : L"", vdwPackageSequence, pPackage->sczId, fRollback ? L"_rollback" : L"", pLog->sczExtension);
221 ExitOnFailure(hr, "Failed to allocate path for package log.");
222
223 hr = VariableSetString(pVariables, fRollback ? pPackage->sczRollbackLogPathVariable : pPackage->sczLogPathVariable, sczLogPath, FALSE);
224 ExitOnFailure(hr, "Failed to set log path into variable.");
225
226 if (psczLogPath)
227 {
228 hr = StrAllocString(psczLogPath, sczLogPath, 0);
229 ExitOnFailure(hr, "Failed to copy package log path.");
230 }
231 }
232
233LExit:
234 ReleaseStr(sczLogPath);
235
236 return hr;
237}
238
239extern "C" LPCSTR LoggingBurnActionToString(
240 __in BOOTSTRAPPER_ACTION action
241 )
242{
243 switch (action)
244 {
245 case BOOTSTRAPPER_ACTION_UNKNOWN:
246 return "Unknown";
247 case BOOTSTRAPPER_ACTION_HELP:
248 return "Help";
249 case BOOTSTRAPPER_ACTION_LAYOUT:
250 return "Layout";
251 case BOOTSTRAPPER_ACTION_CACHE:
252 return "Cache";
253 case BOOTSTRAPPER_ACTION_UNINSTALL:
254 return "Uninstall";
255 case BOOTSTRAPPER_ACTION_INSTALL:
256 return "Install";
257 case BOOTSTRAPPER_ACTION_MODIFY:
258 return "Modify";
259 case BOOTSTRAPPER_ACTION_REPAIR:
260 return "Repair";
261 case BOOTSTRAPPER_ACTION_UPDATE_REPLACE:
262 return "UpdateReplace";
263 case BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED:
264 return "UpdateReplaceEmbedded";
265 default:
266 return "Invalid";
267 }
268}
269
270extern "C" LPCSTR LoggingActionStateToString(
271 __in BOOTSTRAPPER_ACTION_STATE actionState
272 )
273{
274 switch (actionState)
275 {
276 case BOOTSTRAPPER_ACTION_STATE_NONE:
277 return "None";
278 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
279 return "Uninstall";
280 case BOOTSTRAPPER_ACTION_STATE_INSTALL:
281 return "Install";
282 case BOOTSTRAPPER_ACTION_STATE_ADMIN_INSTALL:
283 return "AdminInstall";
284 case BOOTSTRAPPER_ACTION_STATE_MODIFY:
285 return "Modify";
286 case BOOTSTRAPPER_ACTION_STATE_REPAIR:
287 return "Repair";
288 case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE:
289 return "MinorUpgrade";
290 case BOOTSTRAPPER_ACTION_STATE_MAJOR_UPGRADE:
291 return "MajorUpgrade";
292 case BOOTSTRAPPER_ACTION_STATE_PATCH:
293 return "Patch";
294 default:
295 return "Invalid";
296 }
297}
298
299extern "C" LPCSTR LoggingDependencyActionToString(
300 BURN_DEPENDENCY_ACTION action
301 )
302{
303 switch (action)
304 {
305 case BURN_DEPENDENCY_ACTION_NONE:
306 return "None";
307 case BURN_DEPENDENCY_ACTION_REGISTER:
308 return "Register";
309 case BURN_DEPENDENCY_ACTION_UNREGISTER:
310 return "Unregister";
311 default:
312 return "Invalid";
313 }
314}
315
316extern "C" LPCSTR LoggingBoolToString(
317 __in BOOL f
318 )
319{
320 if (f)
321 {
322 return "Yes";
323 }
324
325 return "No";
326}
327
328extern "C" LPCSTR LoggingTrueFalseToString(
329 __in BOOL f
330 )
331{
332 if (f)
333 {
334 return "true";
335 }
336
337 return "false";
338}
339
340extern "C" LPCSTR LoggingPackageStateToString(
341 __in BOOTSTRAPPER_PACKAGE_STATE packageState
342 )
343{
344 switch (packageState)
345 {
346 case BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN:
347 return "Unknown";
348 case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE:
349 return "Obsolete";
350 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
351 return "Absent";
352 case BOOTSTRAPPER_PACKAGE_STATE_CACHED:
353 return "Cached";
354 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
355 return "Present";
356 case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
357 return "Superseded";
358 default:
359 return "Invalid";
360 }
361}
362
363extern "C" LPCSTR LoggingCacheStateToString(
364 __in BURN_CACHE_STATE cacheState
365 )
366{
367 switch (cacheState)
368 {
369 case BURN_CACHE_STATE_NONE:
370 return "None";
371 case BURN_CACHE_STATE_PARTIAL:
372 return "Partial";
373 case BURN_CACHE_STATE_COMPLETE:
374 return "Complete";
375 default:
376 return "Invalid";
377 }
378}
379
380extern "C" LPCSTR LoggingMsiFeatureStateToString(
381 __in BOOTSTRAPPER_FEATURE_STATE featureState
382 )
383{
384 switch (featureState)
385 {
386 case BOOTSTRAPPER_FEATURE_STATE_UNKNOWN:
387 return "Unknown";
388 case BOOTSTRAPPER_FEATURE_STATE_ABSENT:
389 return "Absent";
390 case BOOTSTRAPPER_FEATURE_STATE_ADVERTISED:
391 return "Advertised";
392 case BOOTSTRAPPER_FEATURE_STATE_LOCAL:
393 return "Local";
394 case BOOTSTRAPPER_FEATURE_STATE_SOURCE:
395 return "Source";
396 default:
397 return "Invalid";
398 }
399}
400
401extern "C" LPCSTR LoggingMsiFeatureActionToString(
402 __in BOOTSTRAPPER_FEATURE_ACTION featureAction
403 )
404{
405 switch (featureAction)
406 {
407 case BOOTSTRAPPER_FEATURE_ACTION_NONE:
408 return "None";
409 case BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL:
410 return "AddLocal";
411 case BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE:
412 return "AddSource";
413 case BOOTSTRAPPER_FEATURE_ACTION_ADDDEFAULT:
414 return "AddDefault";
415 case BOOTSTRAPPER_FEATURE_ACTION_REINSTALL:
416 return "Reinstall";
417 case BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE:
418 return "Advertise";
419 case BOOTSTRAPPER_FEATURE_ACTION_REMOVE:
420 return "Remove";
421 default:
422 return "Invalid";
423 }
424}
425
426extern "C" LPCSTR LoggingMsiInstallContext(
427 __in MSIINSTALLCONTEXT context
428 )
429{
430 switch (context)
431 {
432 case MSIINSTALLCONTEXT_ALL:
433 return "All";
434 case MSIINSTALLCONTEXT_ALLUSERMANAGED:
435 return "AllUserManaged";
436 case MSIINSTALLCONTEXT_MACHINE:
437 return "Machine";
438 case MSIINSTALLCONTEXT_NONE:
439 return "None";
440 case MSIINSTALLCONTEXT_USERMANAGED:
441 return "UserManaged";
442 case MSIINSTALLCONTEXT_USERUNMANAGED:
443 return "UserUnmanaged";
444 default:
445 return "Invalid";
446 }
447}
448
449extern "C" LPCSTR LoggingPerMachineToString(
450 __in BOOL fPerMachine
451 )
452{
453 if (fPerMachine)
454 {
455 return "PerMachine";
456 }
457
458 return "PerUser";
459}
460
461extern "C" LPCSTR LoggingRestartToString(
462 __in BOOTSTRAPPER_APPLY_RESTART restart
463 )
464{
465 switch (restart)
466 {
467 case BOOTSTRAPPER_APPLY_RESTART_NONE:
468 return "None";
469 case BOOTSTRAPPER_APPLY_RESTART_REQUIRED:
470 return "Required";
471 case BOOTSTRAPPER_APPLY_RESTART_INITIATED:
472 return "Initiated";
473 default:
474 return "Invalid";
475 }
476}
477
478extern "C" LPCSTR LoggingResumeModeToString(
479 __in BURN_RESUME_MODE resumeMode
480 )
481{
482 switch (resumeMode)
483 {
484 case BURN_RESUME_MODE_NONE:
485 return "None";
486 case BURN_RESUME_MODE_ACTIVE:
487 return "Active";
488 case BURN_RESUME_MODE_SUSPEND:
489 return "Suspend";
490 case BURN_RESUME_MODE_ARP:
491 return "ARP";
492 case BURN_RESUME_MODE_REBOOT_PENDING:
493 return "Reboot Pending";
494 default:
495 return "Invalid";
496 }
497}
498
499extern "C" LPCSTR LoggingRelationTypeToString(
500 __in BOOTSTRAPPER_RELATION_TYPE type
501 )
502{
503 switch (type)
504 {
505 case BOOTSTRAPPER_RELATION_NONE:
506 return "None";
507 case BOOTSTRAPPER_RELATION_DETECT:
508 return "Detect";
509 case BOOTSTRAPPER_RELATION_UPGRADE:
510 return "Upgrade";
511 case BOOTSTRAPPER_RELATION_ADDON:
512 return "Addon";
513 case BOOTSTRAPPER_RELATION_PATCH:
514 return "Patch";
515 case BOOTSTRAPPER_RELATION_DEPENDENT:
516 return "Dependent";
517 case BOOTSTRAPPER_RELATION_UPDATE:
518 return "Update";
519 default:
520 return "Invalid";
521 }
522}
523
524extern "C" LPCSTR LoggingRelatedOperationToString(
525 __in BOOTSTRAPPER_RELATED_OPERATION operation
526 )
527{
528 switch (operation)
529 {
530 case BOOTSTRAPPER_RELATED_OPERATION_NONE:
531 return "None";
532 case BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE:
533 return "Downgrade";
534 case BOOTSTRAPPER_RELATED_OPERATION_MINOR_UPDATE:
535 return "MinorUpdate";
536 case BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE:
537 return "MajorUpgrade";
538 case BOOTSTRAPPER_RELATED_OPERATION_REMOVE:
539 return "Remove";
540 case BOOTSTRAPPER_RELATED_OPERATION_INSTALL:
541 return "Install";
542 case BOOTSTRAPPER_RELATED_OPERATION_REPAIR:
543 return "Repair";
544 default:
545 return "Invalid";
546 }
547}
548
549extern "C" LPCSTR LoggingRequestStateToString(
550 __in BOOTSTRAPPER_REQUEST_STATE requestState
551 )
552{
553 switch (requestState)
554 {
555 case BOOTSTRAPPER_REQUEST_STATE_NONE:
556 return "None";
557 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT:
558 return "ForceAbsent";
559 case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
560 return "Absent";
561 case BOOTSTRAPPER_REQUEST_STATE_CACHE:
562 return "Cache";
563 case BOOTSTRAPPER_REQUEST_STATE_PRESENT:
564 return "Present";
565 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
566 return "Repair";
567 default:
568 return "Invalid";
569 }
570}
571
572extern "C" LPCSTR LoggingRollbackOrExecute(
573 __in BOOL fRollback
574 )
575{
576 return fRollback ? "rollback" : "execute";
577}
578
579extern "C" LPWSTR LoggingStringOrUnknownIfNull(
580 __in LPCWSTR wz
581 )
582{
583 return wz ? wz : L"Unknown";
584}
585
586// Note: this function is not thread safe.
587extern "C" LPCSTR LoggingVersionToString(
588 __in DWORD64 dw64Version
589 )
590{
591 static CHAR szVersion[40] = { 0 };
592 HRESULT hr = S_OK;
593
594 hr = ::StringCchPrintfA(szVersion, countof(szVersion), "%I64u.%I64u.%I64u.%I64u", dw64Version >> 48 & 0xFFFF, dw64Version >> 32 & 0xFFFF, dw64Version >> 16 & 0xFFFF, dw64Version & 0xFFFF);
595 if (FAILED(hr))
596 {
597 memset(szVersion, 0, sizeof(szVersion));
598 }
599
600 return szVersion;
601}
602
603
604// internal function declarations
605
606static void CheckLoggingPolicy(
607 __out DWORD *pdwAttributes
608 )
609{
610 HRESULT hr = S_OK;
611 HKEY hk = NULL;
612 LPWSTR sczLoggingPolicy = NULL;
613
614 hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Policies\\Microsoft\\Windows\\Installer", KEY_READ, &hk);
615 if (SUCCEEDED(hr))
616 {
617 hr = RegReadString(hk, L"Logging", &sczLoggingPolicy);
618 if (SUCCEEDED(hr))
619 {
620 LPCWSTR wz = sczLoggingPolicy;
621 while (*wz)
622 {
623 if (L'v' == *wz || L'V' == *wz)
624 {
625 *pdwAttributes |= BURN_LOGGING_ATTRIBUTE_VERBOSE;
626 }
627 else if (L'x' == *wz || L'X' == *wz)
628 {
629 *pdwAttributes |= BURN_LOGGING_ATTRIBUTE_EXTRADEBUG;
630 }
631
632 ++wz;
633 }
634 }
635 }
636
637 ReleaseStr(sczLoggingPolicy);
638 ReleaseRegKey(hk);
639}
640
641static HRESULT GetNonSessionSpecificTempFolder(
642 __deref_out_z LPWSTR* psczNonSessionTempFolder
643 )
644{
645 HRESULT hr = S_OK;
646 WCHAR wzTempFolder[MAX_PATH] = { };
647 DWORD cchTempFolder = 0;
648 DWORD dwSessionId = 0;
649 LPWSTR sczSessionId = 0;
650 DWORD cchSessionId = 0;
651
652 if (!::GetTempPathW(countof(wzTempFolder), wzTempFolder))
653 {
654 ExitWithLastError(hr, "Failed to get temp folder.");
655 }
656
657 hr = ::StringCchLengthW(wzTempFolder, countof(wzTempFolder), reinterpret_cast<size_t*>(&cchTempFolder));
658 ExitOnFailure(hr, "Failed to get length of temp folder.");
659
660 // If our session id is in the TEMP path then remove that part so we get the non-session
661 // specific temporary folder.
662 if (::ProcessIdToSessionId(::GetCurrentProcessId(), &dwSessionId))
663 {
664 hr = StrAllocFormatted(&sczSessionId, L"%u\\", dwSessionId);
665 ExitOnFailure(hr, "Failed to format session id as a string.");
666
667 hr = ::StringCchLengthW(sczSessionId, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cchSessionId));
668 ExitOnFailure(hr, "Failed to get length of session id string.");
669
670 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzTempFolder + cchTempFolder - cchSessionId, cchSessionId, sczSessionId, cchSessionId))
671 {
672 cchTempFolder -= cchSessionId;
673 }
674 }
675
676 hr = StrAllocString(psczNonSessionTempFolder, wzTempFolder, cchTempFolder);
677 ExitOnFailure(hr, "Failed to copy temp folder.");
678
679LExit:
680 ReleaseStr(sczSessionId);
681
682 return hr;
683}
diff --git a/src/engine/logging.h b/src/engine/logging.h
new file mode 100644
index 00000000..cea4d31d
--- /dev/null
+++ b/src/engine/logging.h
@@ -0,0 +1,144 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// constants
11
12enum BURN_LOGGING_STATE
13{
14 BURN_LOGGING_STATE_CLOSED,
15 BURN_LOGGING_STATE_OPEN,
16 BURN_LOGGING_STATE_DISABLED,
17};
18
19enum BURN_LOGGING_ATTRIBUTE
20{
21 BURN_LOGGING_ATTRIBUTE_APPEND = 0x1,
22 BURN_LOGGING_ATTRIBUTE_VERBOSE = 0x2,
23 BURN_LOGGING_ATTRIBUTE_EXTRADEBUG = 0x4,
24};
25
26
27// structs
28
29typedef struct _BURN_LOGGING
30{
31 BURN_LOGGING_STATE state;
32 LPWSTR sczPathVariable;
33
34 DWORD dwAttributes;
35 LPWSTR sczPath;
36 LPWSTR sczPrefix;
37 LPWSTR sczExtension;
38} BURN_LOGGING;
39
40
41
42// function declarations
43
44HRESULT LoggingOpen(
45 __in BURN_LOGGING* pLog,
46 __in BURN_VARIABLES* pVariables,
47 __in BOOTSTRAPPER_DISPLAY display,
48 __in_z LPCWSTR wzBundleName
49 );
50
51void LoggingOpenFailed();
52
53void LoggingIncrementPackageSequence();
54
55HRESULT LoggingSetPackageVariable(
56 __in BURN_PACKAGE* pPackage,
57 __in_z_opt LPCWSTR wzSuffix,
58 __in BOOL fRollback,
59 __in BURN_LOGGING* pLog,
60 __in BURN_VARIABLES* pVariables,
61 __out_opt LPWSTR* psczLogPath
62 );
63
64LPCSTR LoggingBurnActionToString(
65 __in BOOTSTRAPPER_ACTION action
66 );
67
68LPCSTR LoggingActionStateToString(
69 __in BOOTSTRAPPER_ACTION_STATE actionState
70 );
71
72LPCSTR LoggingDependencyActionToString(
73 BURN_DEPENDENCY_ACTION action
74 );
75
76LPCSTR LoggingBoolToString(
77 __in BOOL f
78 );
79
80LPCSTR LoggingTrueFalseToString(
81 __in BOOL f
82 );
83
84LPCSTR LoggingPackageStateToString(
85 __in BOOTSTRAPPER_PACKAGE_STATE packageState
86 );
87
88LPCSTR LoggingCacheStateToString(
89 __in BURN_CACHE_STATE cacheState
90 );
91
92LPCSTR LoggingMsiFeatureStateToString(
93 __in BOOTSTRAPPER_FEATURE_STATE featureState
94 );
95
96LPCSTR LoggingMsiFeatureActionToString(
97 __in BOOTSTRAPPER_FEATURE_ACTION featureAction
98 );
99
100LPCSTR LoggingMsiInstallContext(
101 __in MSIINSTALLCONTEXT context
102 );
103
104LPCSTR LoggingPerMachineToString(
105 __in BOOL fPerMachine
106 );
107
108LPCSTR LoggingRestartToString(
109 __in BOOTSTRAPPER_APPLY_RESTART restart
110 );
111
112LPCSTR LoggingResumeModeToString(
113 __in BURN_RESUME_MODE resumeMode
114 );
115
116LPCSTR LoggingRelationTypeToString(
117 __in BOOTSTRAPPER_RELATION_TYPE type
118 );
119
120LPCSTR LoggingRelatedOperationToString(
121 __in BOOTSTRAPPER_RELATED_OPERATION operation
122 );
123
124LPCSTR LoggingRequestStateToString(
125 __in BOOTSTRAPPER_REQUEST_STATE requestState
126 );
127
128LPCSTR LoggingRollbackOrExecute(
129 __in BOOL fRollback
130 );
131
132LPWSTR LoggingStringOrUnknownIfNull(
133 __in LPCWSTR wz
134 );
135
136// Note: this function is not thread safe.
137LPCSTR LoggingVersionToString(
138 __in DWORD64 dw64Version
139 );
140
141
142#if defined(__cplusplus)
143}
144#endif
diff --git a/src/engine/manifest.cpp b/src/engine/manifest.cpp
new file mode 100644
index 00000000..c2214a89
--- /dev/null
+++ b/src/engine/manifest.cpp
@@ -0,0 +1,125 @@
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#include "precomp.h"
4
5
6// function definitions
7
8extern "C" HRESULT ManifestLoadXmlFromBuffer(
9 __in_bcount(cbBuffer) BYTE* pbBuffer,
10 __in SIZE_T cbBuffer,
11 __in BURN_ENGINE_STATE* pEngineState
12 )
13{
14 HRESULT hr = S_OK;
15 IXMLDOMDocument* pixdDocument = NULL;
16 IXMLDOMElement* pixeBundle = NULL;
17 IXMLDOMNode* pixnLog = NULL;
18 IXMLDOMNode* pixnChain = NULL;
19
20 // load xml document
21 hr = XmlLoadDocumentFromBuffer(pbBuffer, cbBuffer, &pixdDocument);
22 ExitOnFailure(hr, "Failed to load manifest as XML document.");
23
24 // get bundle element
25 hr = pixdDocument->get_documentElement(&pixeBundle);
26 ExitOnFailure(hr, "Failed to get bundle element.");
27
28 // parse the log element, if present.
29 hr = XmlSelectSingleNode(pixeBundle, L"Log", &pixnLog);
30 ExitOnFailure(hr, "Failed to get Log element.");
31
32 if (S_OK == hr)
33 {
34 hr = XmlGetAttributeEx(pixnLog, L"PathVariable", &pEngineState->log.sczPathVariable);
35 if (E_NOTFOUND != hr)
36 {
37 ExitOnFailure(hr, "Failed to get Log/@PathVariable.");
38 }
39
40 hr = XmlGetAttributeEx(pixnLog, L"Prefix", &pEngineState->log.sczPrefix);
41 ExitOnFailure(hr, "Failed to get Log/@Prefix attribute.");
42
43 hr = XmlGetAttributeEx(pixnLog, L"Extension", &pEngineState->log.sczExtension);
44 ExitOnFailure(hr, "Failed to get Log/@Extension attribute.");
45 }
46
47 // get the chain element
48 hr = XmlSelectSingleNode(pixeBundle, L"Chain", &pixnChain);
49 ExitOnFailure(hr, "Failed to get chain element.");
50
51 if (S_OK == hr)
52 {
53 // parse disable rollback
54 hr = XmlGetYesNoAttribute(pixnChain, L"DisableRollback", &pEngineState->fDisableRollback);
55 if (E_NOTFOUND != hr)
56 {
57 ExitOnFailure(hr, "Failed to get Chain/@DisableRollback");
58 }
59
60 // parse disable system restore
61 hr = XmlGetYesNoAttribute(pixnChain, L"DisableSystemRestore", &pEngineState->fDisableSystemRestore);
62 if (E_NOTFOUND != hr)
63 {
64 ExitOnFailure(hr, "Failed to get Chain/@DisableSystemRestore");
65 }
66
67 // parse parallel cache
68 hr = XmlGetYesNoAttribute(pixnChain, L"ParallelCache", &pEngineState->fParallelCacheAndExecute);
69 if (E_NOTFOUND != hr)
70 {
71 ExitOnFailure(hr, "Failed to get Chain/@ParallelCache");
72 }
73 }
74
75 // parse built-in condition
76 hr = ConditionGlobalParseFromXml(&pEngineState->condition, pixeBundle);
77 ExitOnFailure(hr, "Failed to parse global condition.");
78
79 // parse variables
80 hr = VariablesParseFromXml(&pEngineState->variables, pixeBundle);
81 ExitOnFailure(hr, "Failed to parse variables.");
82
83 // parse searches
84 hr = SearchesParseFromXml(&pEngineState->searches, pixeBundle); // TODO: Modularization
85 ExitOnFailure(hr, "Failed to parse searches.");
86
87 // parse user experience
88 hr = UserExperienceParseFromXml(&pEngineState->userExperience, pixeBundle);
89 ExitOnFailure(hr, "Failed to parse user experience.");
90
91 // parse catalog files
92 hr = CatalogsParseFromXml(&pEngineState->catalogs, pixeBundle);
93 ExitOnFailure(hr, "Failed to parse catalog files.");
94
95 // parse registration
96 hr = RegistrationParseFromXml(&pEngineState->registration, pixeBundle);
97 ExitOnFailure(hr, "Failed to parse registration.");
98
99 // parse update
100 hr = UpdateParseFromXml(&pEngineState->update, pixeBundle);
101 ExitOnFailure(hr, "Failed to parse update.");
102
103 // parse containers
104 hr = ContainersParseFromXml(&pEngineState->section, &pEngineState->containers, pixeBundle);
105 ExitOnFailure(hr, "Failed to parse containers.");
106
107 // parse payloads
108 hr = PayloadsParseFromXml(&pEngineState->payloads, &pEngineState->containers, &pEngineState->catalogs, pixeBundle);
109 ExitOnFailure(hr, "Failed to parse payloads.");
110
111 // parse packages
112 hr = PackagesParseFromXml(&pEngineState->packages, &pEngineState->payloads, pixeBundle);
113 ExitOnFailure(hr, "Failed to parse packages.");
114
115 // parse approved exes for elevation
116 hr = ApprovedExesParseFromXml(&pEngineState->approvedExes, pixeBundle);
117 ExitOnFailure(hr, "Failed to parse approved exes.");
118
119LExit:
120 ReleaseObject(pixnChain);
121 ReleaseObject(pixnLog);
122 ReleaseObject(pixeBundle);
123 ReleaseObject(pixdDocument);
124 return hr;
125}
diff --git a/src/engine/manifest.h b/src/engine/manifest.h
new file mode 100644
index 00000000..6e535d60
--- /dev/null
+++ b/src/engine/manifest.h
@@ -0,0 +1,23 @@
1#pragma once
2// 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.
3
4
5interface IBurnPayload; // forward declare.
6
7#if defined(__cplusplus)
8extern "C" {
9#endif
10
11
12// function declarations
13
14HRESULT ManifestLoadXmlFromBuffer(
15 __in_bcount(cbBuffer) BYTE* pbBuffer,
16 __in SIZE_T cbBuffer,
17 __in BURN_ENGINE_STATE* pEngineState
18 );
19
20
21#if defined(__cplusplus)
22}
23#endif
diff --git a/src/engine/msiengine.cpp b/src/engine/msiengine.cpp
new file mode 100644
index 00000000..17571ac5
--- /dev/null
+++ b/src/engine/msiengine.cpp
@@ -0,0 +1,1910 @@
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#include "precomp.h"
4
5
6// constants
7
8
9// structs
10
11
12
13// internal function declarations
14
15static HRESULT ParseRelatedMsiFromXml(
16 __in IXMLDOMNode* pixnRelatedMsi,
17 __in BURN_RELATED_MSI* pRelatedMsi
18 );
19static HRESULT EvaluateActionStateConditions(
20 __in BURN_VARIABLES* pVariables,
21 __in_z_opt LPCWSTR sczAddLocalCondition,
22 __in_z_opt LPCWSTR sczAddSourceCondition,
23 __in_z_opt LPCWSTR sczAdvertiseCondition,
24 __out BOOTSTRAPPER_FEATURE_STATE* pState
25 );
26static HRESULT CalculateFeatureAction(
27 __in BOOTSTRAPPER_FEATURE_STATE currentState,
28 __in BOOTSTRAPPER_FEATURE_STATE requestedState,
29 __in BOOL fRepair,
30 __out BOOTSTRAPPER_FEATURE_ACTION* pFeatureAction,
31 __inout BOOL* pfDelta
32 );
33static HRESULT EscapePropertyArgumentString(
34 __in LPCWSTR wzProperty,
35 __inout_z LPWSTR* psczEscapedValue,
36 __in BOOL fZeroOnRealloc
37 );
38static HRESULT ConcatFeatureActionProperties(
39 __in BURN_PACKAGE* pPackage,
40 __in BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions,
41 __inout_z LPWSTR* psczArguments
42 );
43static HRESULT ConcatPatchProperty(
44 __in BURN_PACKAGE* pPackage,
45 __in_opt BOOTSTRAPPER_ACTION_STATE* rgSlipstreamPatchActions,
46 __inout_z LPWSTR* psczArguments
47 );
48static void RegisterSourceDirectory(
49 __in BURN_PACKAGE* pPackage,
50 __in_z LPCWSTR wzCacheDirectory
51 );
52
53
54// function definitions
55
56extern "C" HRESULT MsiEngineParsePackageFromXml(
57 __in IXMLDOMNode* pixnMsiPackage,
58 __in BURN_PACKAGE* pPackage
59 )
60{
61 HRESULT hr = S_OK;
62 IXMLDOMNodeList* pixnNodes = NULL;
63 IXMLDOMNode* pixnNode = NULL;
64 DWORD cNodes = 0;
65 LPWSTR scz = NULL;
66
67 // @ProductCode
68 hr = XmlGetAttributeEx(pixnMsiPackage, L"ProductCode", &pPackage->Msi.sczProductCode);
69 ExitOnFailure(hr, "Failed to get @ProductCode.");
70
71 // @Language
72 hr = XmlGetAttributeNumber(pixnMsiPackage, L"Language", &pPackage->Msi.dwLanguage);
73 ExitOnFailure(hr, "Failed to get @Language.");
74
75 // @Version
76 hr = XmlGetAttributeEx(pixnMsiPackage, L"Version", &scz);
77 ExitOnFailure(hr, "Failed to get @Version.");
78
79 hr = FileVersionFromStringEx(scz, 0, &pPackage->Msi.qwVersion);
80 ExitOnFailure(hr, "Failed to parse @Version: %ls", scz);
81
82 // @DisplayInternalUI
83 hr = XmlGetYesNoAttribute(pixnMsiPackage, L"DisplayInternalUI", &pPackage->Msi.fDisplayInternalUI);
84 ExitOnFailure(hr, "Failed to get @DisplayInternalUI.");
85
86 // @UpgradeCode
87 hr = XmlGetAttributeEx(pixnMsiPackage, L"UpgradeCode", &pPackage->Msi.sczUpgradeCode);
88 if (E_NOTFOUND != hr)
89 {
90 ExitOnFailure(hr, "Failed to get @UpgradeCode.");
91 }
92
93 // select feature nodes
94 hr = XmlSelectNodes(pixnMsiPackage, L"MsiFeature", &pixnNodes);
95 ExitOnFailure(hr, "Failed to select feature nodes.");
96
97 // get feature node count
98 hr = pixnNodes->get_length((long*)&cNodes);
99 ExitOnFailure(hr, "Failed to get feature node count.");
100
101 if (cNodes)
102 {
103 // allocate memory for features
104 pPackage->Msi.rgFeatures = (BURN_MSIFEATURE*)MemAlloc(sizeof(BURN_MSIFEATURE) * cNodes, TRUE);
105 ExitOnNull(pPackage->Msi.rgFeatures, hr, E_OUTOFMEMORY, "Failed to allocate memory for MSI feature structs.");
106
107 pPackage->Msi.cFeatures = cNodes;
108
109 // parse feature elements
110 for (DWORD i = 0; i < cNodes; ++i)
111 {
112 BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
113
114 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
115 ExitOnFailure(hr, "Failed to get next node.");
116
117 // @Id
118 hr = XmlGetAttributeEx(pixnNode, L"Id", &pFeature->sczId);
119 ExitOnFailure(hr, "Failed to get @Id.");
120
121 // @AddLocalCondition
122 hr = XmlGetAttributeEx(pixnNode, L"AddLocalCondition", &pFeature->sczAddLocalCondition);
123 if (E_NOTFOUND != hr)
124 {
125 ExitOnFailure(hr, "Failed to get @AddLocalCondition.");
126 }
127
128 // @AddSourceCondition
129 hr = XmlGetAttributeEx(pixnNode, L"AddSourceCondition", &pFeature->sczAddSourceCondition);
130 if (E_NOTFOUND != hr)
131 {
132 ExitOnFailure(hr, "Failed to get @AddSourceCondition.");
133 }
134
135 // @AdvertiseCondition
136 hr = XmlGetAttributeEx(pixnNode, L"AdvertiseCondition", &pFeature->sczAdvertiseCondition);
137 if (E_NOTFOUND != hr)
138 {
139 ExitOnFailure(hr, "Failed to get @AdvertiseCondition.");
140 }
141
142 // @RollbackAddLocalCondition
143 hr = XmlGetAttributeEx(pixnNode, L"RollbackAddLocalCondition", &pFeature->sczRollbackAddLocalCondition);
144 if (E_NOTFOUND != hr)
145 {
146 ExitOnFailure(hr, "Failed to get @RollbackAddLocalCondition.");
147 }
148
149 // @RollbackAddSourceCondition
150 hr = XmlGetAttributeEx(pixnNode, L"RollbackAddSourceCondition", &pFeature->sczRollbackAddSourceCondition);
151 if (E_NOTFOUND != hr)
152 {
153 ExitOnFailure(hr, "Failed to get @RollbackAddSourceCondition.");
154 }
155
156 // @RollbackAdvertiseCondition
157 hr = XmlGetAttributeEx(pixnNode, L"RollbackAdvertiseCondition", &pFeature->sczRollbackAdvertiseCondition);
158 if (E_NOTFOUND != hr)
159 {
160 ExitOnFailure(hr, "Failed to get @RollbackAdvertiseCondition.");
161 }
162
163 // prepare next iteration
164 ReleaseNullObject(pixnNode);
165 }
166 }
167
168 ReleaseNullObject(pixnNodes); // done with the MsiFeature elements.
169
170 hr = MsiEngineParsePropertiesFromXml(pixnMsiPackage, &pPackage->Msi.rgProperties, &pPackage->Msi.cProperties);
171 ExitOnFailure(hr, "Failed to parse properties from XML.");
172
173 // select related MSI nodes
174 hr = XmlSelectNodes(pixnMsiPackage, L"RelatedPackage", &pixnNodes);
175 ExitOnFailure(hr, "Failed to select related MSI nodes.");
176
177 // get related MSI node count
178 hr = pixnNodes->get_length((long*)&cNodes);
179 ExitOnFailure(hr, "Failed to get related MSI node count.");
180
181 if (cNodes)
182 {
183 // allocate memory for related MSIs
184 pPackage->Msi.rgRelatedMsis = (BURN_RELATED_MSI*)MemAlloc(sizeof(BURN_RELATED_MSI) * cNodes, TRUE);
185 ExitOnNull(pPackage->Msi.rgRelatedMsis, hr, E_OUTOFMEMORY, "Failed to allocate memory for related MSI structs.");
186
187 pPackage->Msi.cRelatedMsis = cNodes;
188
189 // parse related MSI elements
190 for (DWORD i = 0; i < cNodes; ++i)
191 {
192 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
193 ExitOnFailure(hr, "Failed to get next node.");
194
195 // parse related MSI element
196 hr = ParseRelatedMsiFromXml(pixnNode, &pPackage->Msi.rgRelatedMsis[i]);
197 ExitOnFailure(hr, "Failed to parse related MSI element.");
198
199 // prepare next iteration
200 ReleaseNullObject(pixnNode);
201 }
202 }
203
204 ReleaseNullObject(pixnNodes); // done with the RelatedPackage elements.
205
206 // Select slipstream MSP nodes.
207 hr = XmlSelectNodes(pixnMsiPackage, L"SlipstreamMsp", &pixnNodes);
208 ExitOnFailure(hr, "Failed to select related MSI nodes.");
209
210 hr = pixnNodes->get_length((long*)&cNodes);
211 ExitOnFailure(hr, "Failed to get related MSI node count.");
212
213 if (cNodes)
214 {
215 pPackage->Msi.rgpSlipstreamMspPackages = reinterpret_cast<BURN_PACKAGE**>(MemAlloc(sizeof(BURN_PACKAGE*) * cNodes, TRUE));
216 ExitOnNull(pPackage->Msi.rgpSlipstreamMspPackages, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream MSP packages.");
217
218 pPackage->Msi.rgsczSlipstreamMspPackageIds = reinterpret_cast<LPWSTR*>(MemAlloc(sizeof(LPWSTR*) * cNodes, TRUE));
219 ExitOnNull(pPackage->Msi.rgsczSlipstreamMspPackageIds, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream MSP ids.");
220
221 pPackage->Msi.cSlipstreamMspPackages = cNodes;
222
223 // Parse slipstream MSP Ids.
224 for (DWORD i = 0; i < cNodes; ++i)
225 {
226 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
227 ExitOnFailure(hr, "Failed to get next slipstream MSP node.");
228
229 hr = XmlGetAttributeEx(pixnNode, L"Id", pPackage->Msi.rgsczSlipstreamMspPackageIds + i);
230 ExitOnFailure(hr, "Failed to parse slipstream MSP ids.");
231
232 ReleaseNullObject(pixnNode);
233 }
234 }
235
236 hr = S_OK;
237
238LExit:
239 ReleaseObject(pixnNodes);
240 ReleaseObject(pixnNode);
241 ReleaseStr(scz);
242
243 return hr;
244}
245
246extern "C" HRESULT MsiEngineParsePropertiesFromXml(
247 __in IXMLDOMNode* pixnPackage,
248 __out BURN_MSIPROPERTY** prgProperties,
249 __out DWORD* pcProperties
250 )
251{
252 HRESULT hr = S_OK;
253 IXMLDOMNodeList* pixnNodes = NULL;
254 IXMLDOMNode* pixnNode = NULL;
255 DWORD cNodes = 0;
256
257 BURN_MSIPROPERTY* pProperties = NULL;
258
259 // select property nodes
260 hr = XmlSelectNodes(pixnPackage, L"MsiProperty", &pixnNodes);
261 ExitOnFailure(hr, "Failed to select property nodes.");
262
263 // get property node count
264 hr = pixnNodes->get_length((long*)&cNodes);
265 ExitOnFailure(hr, "Failed to get property node count.");
266
267 if (cNodes)
268 {
269 // allocate memory for properties
270 pProperties = (BURN_MSIPROPERTY*)MemAlloc(sizeof(BURN_MSIPROPERTY) * cNodes, TRUE);
271 ExitOnNull(pProperties, hr, E_OUTOFMEMORY, "Failed to allocate memory for MSI property structs.");
272
273 // parse property elements
274 for (DWORD i = 0; i < cNodes; ++i)
275 {
276 BURN_MSIPROPERTY* pProperty = &pProperties[i];
277
278 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
279 ExitOnFailure(hr, "Failed to get next node.");
280
281 // @Id
282 hr = XmlGetAttributeEx(pixnNode, L"Id", &pProperty->sczId);
283 ExitOnFailure(hr, "Failed to get @Id.");
284
285 // @Value
286 hr = XmlGetAttributeEx(pixnNode, L"Value", &pProperty->sczValue);
287 ExitOnFailure(hr, "Failed to get @Value.");
288
289 // @RollbackValue
290 hr = XmlGetAttributeEx(pixnNode, L"RollbackValue", &pProperty->sczRollbackValue);
291 if (E_NOTFOUND != hr)
292 {
293 ExitOnFailure(hr, "Failed to get @RollbackValue.");
294 }
295
296 // @Condition
297 hr = XmlGetAttributeEx(pixnNode, L"Condition", &pProperty->sczCondition);
298 if (E_NOTFOUND != hr)
299 {
300 ExitOnFailure(hr, "Failed to get @Condition.");
301 }
302
303 // prepare next iteration
304 ReleaseNullObject(pixnNode);
305 }
306 }
307
308 *pcProperties = cNodes;
309 *prgProperties = pProperties;
310 pProperties = NULL;
311
312 hr = S_OK;
313
314LExit:
315 ReleaseNullObject(pixnNodes);
316 ReleaseMem(pProperties);
317
318 return hr;
319}
320
321extern "C" void MsiEnginePackageUninitialize(
322 __in BURN_PACKAGE* pPackage
323 )
324{
325 ReleaseStr(pPackage->Msi.sczProductCode);
326 ReleaseStr(pPackage->Msi.sczUpgradeCode);
327 ReleaseStr(pPackage->Msi.sczInstalledProductCode);
328
329 // free features
330 if (pPackage->Msi.rgFeatures)
331 {
332 for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
333 {
334 BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
335
336 ReleaseStr(pFeature->sczId);
337 ReleaseStr(pFeature->sczAddLocalCondition);
338 ReleaseStr(pFeature->sczAddSourceCondition);
339 ReleaseStr(pFeature->sczAdvertiseCondition);
340 ReleaseStr(pFeature->sczRollbackAddLocalCondition);
341 ReleaseStr(pFeature->sczRollbackAddSourceCondition);
342 ReleaseStr(pFeature->sczRollbackAdvertiseCondition);
343 }
344 MemFree(pPackage->Msi.rgFeatures);
345 }
346
347 // free properties
348 if (pPackage->Msi.rgProperties)
349 {
350 for (DWORD i = 0; i < pPackage->Msi.cProperties; ++i)
351 {
352 BURN_MSIPROPERTY* pProperty = &pPackage->Msi.rgProperties[i];
353
354 ReleaseStr(pProperty->sczId);
355 ReleaseStr(pProperty->sczValue);
356 ReleaseStr(pProperty->sczRollbackValue);
357 ReleaseStr(pProperty->sczCondition);
358 }
359 MemFree(pPackage->Msi.rgProperties);
360 }
361
362 // free related MSIs
363 if (pPackage->Msi.rgRelatedMsis)
364 {
365 for (DWORD i = 0; i < pPackage->Msi.cRelatedMsis; ++i)
366 {
367 BURN_RELATED_MSI* pRelatedMsi = &pPackage->Msi.rgRelatedMsis[i];
368
369 ReleaseStr(pRelatedMsi->sczUpgradeCode);
370 ReleaseMem(pRelatedMsi->rgdwLanguages);
371 }
372 MemFree(pPackage->Msi.rgRelatedMsis);
373 }
374
375 // free slipstream MSPs
376 if (pPackage->Msi.rgsczSlipstreamMspPackageIds)
377 {
378 for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i)
379 {
380 ReleaseStr(pPackage->Msi.rgsczSlipstreamMspPackageIds[i]);
381 }
382
383 MemFree(pPackage->Msi.rgsczSlipstreamMspPackageIds);
384 }
385
386 if (pPackage->Msi.rgpSlipstreamMspPackages)
387 {
388 MemFree(pPackage->Msi.rgpSlipstreamMspPackages);
389 }
390
391 // clear struct
392 memset(&pPackage->Msi, 0, sizeof(pPackage->Msi));
393}
394
395extern "C" HRESULT MsiEngineDetectPackage(
396 __in BURN_PACKAGE* pPackage,
397 __in BURN_USER_EXPERIENCE* pUserExperience
398 )
399{
400 Trace(REPORT_STANDARD, "Detecting MSI package 0x%p", pPackage);
401
402 HRESULT hr = S_OK;
403 LPWSTR sczInstalledVersion = NULL;
404 LPWSTR sczInstalledLanguage = NULL;
405 LPWSTR sczInstalledProductCode = NULL;
406 LPWSTR sczInstalledProviderKey = NULL;
407 INSTALLSTATE installState = INSTALLSTATE_UNKNOWN;
408 BOOTSTRAPPER_RELATED_OPERATION operation = BOOTSTRAPPER_RELATED_OPERATION_NONE;
409 BOOTSTRAPPER_RELATED_OPERATION relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE;
410 WCHAR wzProductCode[MAX_GUID_CHARS + 1] = { };
411 DWORD64 qwVersion = 0;
412 UINT uLcid = 0;
413 BOOL fPerMachine = FALSE;
414
415 // detect self by product code
416 // TODO: what to do about MSIINSTALLCONTEXT_USERMANAGED?
417 hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
418 if (SUCCEEDED(hr))
419 {
420 hr = FileVersionFromStringEx(sczInstalledVersion, 0, &pPackage->Msi.qwInstalledVersion);
421 ExitOnFailure(hr, "Failed to convert version: %ls to DWORD64 for ProductCode: %ls", sczInstalledVersion, pPackage->Msi.sczProductCode);
422
423 // compare versions
424 if (pPackage->Msi.qwVersion < pPackage->Msi.qwInstalledVersion)
425 {
426 operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE;
427 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED;
428 }
429 else
430 {
431 if (pPackage->Msi.qwVersion > pPackage->Msi.qwInstalledVersion)
432 {
433 operation = BOOTSTRAPPER_RELATED_OPERATION_MINOR_UPDATE;
434 }
435
436 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT;
437 }
438
439 // Report related MSI package to BA.
440 if (BOOTSTRAPPER_RELATED_OPERATION_NONE != operation)
441 {
442 LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_PACKAGE, pPackage->Msi.sczProductCode, LoggingPerMachineToString(pPackage->fPerMachine), LoggingVersionToString(pPackage->Msi.qwInstalledVersion), pPackage->Msi.dwLanguage, LoggingRelatedOperationToString(operation));
443
444 hr = UserExperienceOnDetectRelatedMsiPackage(pUserExperience, pPackage->sczId, pPackage->Msi.sczUpgradeCode, pPackage->Msi.sczProductCode, pPackage->fPerMachine, pPackage->Msi.qwInstalledVersion, operation);
445 ExitOnRootFailure(hr, "BA aborted detect related MSI package.");
446 }
447 }
448 else if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr || HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) == hr) // package not present.
449 {
450 // Check for newer, compatible packages based on a fixed provider key.
451 hr = DependencyDetectProviderKeyPackageId(pPackage, &sczInstalledProviderKey, &sczInstalledProductCode);
452 if (SUCCEEDED(hr))
453 {
454 hr = WiuGetProductInfoEx(sczInstalledProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
455 if (SUCCEEDED(hr))
456 {
457 hr = FileVersionFromStringEx(sczInstalledVersion, 0, &qwVersion);
458 ExitOnFailure(hr, "Failed to convert version: %ls to DWORD64 for ProductCode: %ls", sczInstalledVersion, sczInstalledProductCode);
459
460 if (pPackage->Msi.qwVersion < qwVersion)
461 {
462 LogId(REPORT_STANDARD, MSG_DETECTED_COMPATIBLE_PACKAGE_FROM_PROVIDER, pPackage->sczId, sczInstalledProviderKey, sczInstalledProductCode, sczInstalledVersion, pPackage->Msi.sczProductCode);
463
464 hr = UserExperienceOnDetectCompatibleMsiPackage(pUserExperience, pPackage->sczId, sczInstalledProductCode, qwVersion);
465 ExitOnRootFailure(hr, "BA aborted detect compatible MSI package.");
466
467 hr = StrAllocString(&pPackage->Msi.sczInstalledProductCode, sczInstalledProductCode, 0);
468 ExitOnFailure(hr, "Failed to copy the installed ProductCode to the package.");
469
470 pPackage->Msi.qwInstalledVersion = qwVersion;
471 pPackage->Msi.fCompatibleInstalled = TRUE;
472 }
473 }
474 }
475
476 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
477 hr = S_OK;
478 }
479 else
480 {
481 ExitOnFailure(hr, "Failed to get product information for ProductCode: %ls", pPackage->Msi.sczProductCode);
482 }
483
484 // detect related packages by upgrade code
485 for (DWORD i = 0; i < pPackage->Msi.cRelatedMsis; ++i)
486 {
487 BURN_RELATED_MSI* pRelatedMsi = &pPackage->Msi.rgRelatedMsis[i];
488
489 for (DWORD iProduct = 0; ; ++iProduct)
490 {
491 // get product
492 hr = WiuEnumRelatedProducts(pRelatedMsi->sczUpgradeCode, iProduct, wzProductCode);
493 if (E_NOMOREITEMS == hr)
494 {
495 hr = S_OK;
496 break;
497 }
498 ExitOnFailure(hr, "Failed to enum related products.");
499
500 // If we found ourselves, skip because saying that a package is related to itself is nonsensical.
501 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pPackage->Msi.sczProductCode, -1, wzProductCode, -1))
502 {
503 continue;
504 }
505
506 // get product version
507 hr = WiuGetProductInfoEx(wzProductCode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
508 if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr && HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) != hr)
509 {
510 ExitOnFailure(hr, "Failed to get version for product in user unmanaged context: %ls", wzProductCode);
511 fPerMachine = FALSE;
512 }
513 else
514 {
515 hr = WiuGetProductInfoEx(wzProductCode, NULL, MSIINSTALLCONTEXT_MACHINE, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
516 if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr && HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) != hr)
517 {
518 ExitOnFailure(hr, "Failed to get version for product in machine context: %ls", wzProductCode);
519 fPerMachine = TRUE;
520 }
521 else
522 {
523 hr = S_OK;
524 continue;
525 }
526 }
527
528 hr = FileVersionFromStringEx(sczInstalledVersion, 0, &qwVersion);
529 ExitOnFailure(hr, "Failed to convert version: %ls to DWORD64 for ProductCode: %ls", sczInstalledVersion, wzProductCode);
530
531 // compare versions
532 if (pRelatedMsi->fMinProvided && (pRelatedMsi->fMinInclusive ? (qwVersion < pRelatedMsi->qwMinVersion) : (qwVersion <= pRelatedMsi->qwMinVersion)))
533 {
534 continue;
535 }
536
537 if (pRelatedMsi->fMaxProvided && (pRelatedMsi->fMaxInclusive ? (qwVersion > pRelatedMsi->qwMaxVersion) : (qwVersion >= pRelatedMsi->qwMaxVersion)))
538 {
539 continue;
540 }
541
542 // Filter by language if necessary.
543 uLcid = 0; // always reset the found language.
544 if (pRelatedMsi->cLanguages)
545 {
546 // If there is a language to get, convert it into an LCID.
547 hr = WiuGetProductInfoEx(wzProductCode, NULL, fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_LANGUAGE, &sczInstalledLanguage);
548 if (SUCCEEDED(hr))
549 {
550 hr = StrStringToUInt32(sczInstalledLanguage, 0, &uLcid);
551 }
552
553 // Ignore related product where we can't read the language.
554 if (FAILED(hr))
555 {
556 LogErrorId(hr, MSG_FAILED_READ_RELATED_PACKAGE_LANGUAGE, wzProductCode, sczInstalledLanguage, NULL);
557
558 hr = S_OK;
559 continue;
560 }
561
562 BOOL fMatchedLcid = FALSE;
563 for (DWORD iLanguage = 0; iLanguage < pRelatedMsi->cLanguages; ++iLanguage)
564 {
565 if (uLcid == pRelatedMsi->rgdwLanguages[iLanguage])
566 {
567 fMatchedLcid = TRUE;
568 break;
569 }
570 }
571
572 // Skip the product if the language did not meet the inclusive/exclusive criteria.
573 if ((pRelatedMsi->fLangInclusive && !fMatchedLcid) || (!pRelatedMsi->fLangInclusive && fMatchedLcid))
574 {
575 continue;
576 }
577 }
578
579 // If this is a detect-only related package and we're not installed yet, then we'll assume a downgrade
580 // would take place since that is the overwhelmingly common use of detect-only related packages. If
581 // not detect-only then it's easy; we're clearly doing a major upgrade.
582 if (pRelatedMsi->fOnlyDetect)
583 {
584 // If we've already detected a major upgrade that trumps any guesses that the detect is a downgrade
585 // or even something else.
586 if (BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE == operation)
587 {
588 relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE;
589 }
590 // It can't be a downgrade if the upgrade codes aren't the same.
591 else if (BOOTSTRAPPER_PACKAGE_STATE_ABSENT == pPackage->currentState &&
592 pPackage->Msi.sczUpgradeCode && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pPackage->Msi.sczUpgradeCode, -1, pRelatedMsi->sczUpgradeCode, -1))
593 {
594 relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE;
595 operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE;
596 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE;
597 }
598 else // we're already on the machine so the detect-only *must* be for detection purposes only.
599 {
600 relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE;
601 }
602 }
603 else
604 {
605 relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE;
606 operation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE;
607 }
608
609 LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_PACKAGE, wzProductCode, LoggingPerMachineToString(fPerMachine), LoggingVersionToString(qwVersion), uLcid, LoggingRelatedOperationToString(relatedMsiOperation));
610
611 // Pass to BA.
612 hr = UserExperienceOnDetectRelatedMsiPackage(pUserExperience, pPackage->sczId, pRelatedMsi->sczUpgradeCode, wzProductCode, fPerMachine, qwVersion, relatedMsiOperation);
613 ExitOnRootFailure(hr, "BA aborted detect related MSI package.");
614 }
615 }
616
617 // detect features
618 if (pPackage->Msi.cFeatures)
619 {
620 for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
621 {
622 BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
623
624 // Try to detect features state if the product is present on the machine.
625 if (BOOTSTRAPPER_PACKAGE_STATE_PRESENT <= pPackage->currentState)
626 {
627 hr = WiuQueryFeatureState(pPackage->Msi.sczProductCode, pFeature->sczId, &installState);
628 ExitOnFailure(hr, "Failed to query feature state.");
629
630 if (INSTALLSTATE_UNKNOWN == installState) // in case of an upgrade a feature could be removed.
631 {
632 installState = INSTALLSTATE_ABSENT;
633 }
634 }
635 else // MSI not installed then the features can't be either.
636 {
637 installState = INSTALLSTATE_ABSENT;
638 }
639
640 // set current state
641 switch (installState)
642 {
643 case INSTALLSTATE_ABSENT:
644 pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_ABSENT;
645 break;
646 case INSTALLSTATE_ADVERTISED:
647 pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_ADVERTISED;
648 break;
649 case INSTALLSTATE_LOCAL:
650 pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_LOCAL;
651 break;
652 case INSTALLSTATE_SOURCE:
653 pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_SOURCE;
654 break;
655 default:
656 hr = E_UNEXPECTED;
657 ExitOnRootFailure(hr, "Invalid state value.");
658 }
659
660 // Pass to BA.
661 hr = UserExperienceOnDetectMsiFeature(pUserExperience, pPackage->sczId, pFeature->sczId, pFeature->currentState);
662 ExitOnRootFailure(hr, "BA aborted detect MSI feature.");
663 }
664 }
665
666LExit:
667 ReleaseStr(sczInstalledProviderKey);
668 ReleaseStr(sczInstalledProductCode);
669 ReleaseStr(sczInstalledLanguage);
670 ReleaseStr(sczInstalledVersion);
671
672 return hr;
673}
674
675//
676// PlanCalculate - calculates the execute and rollback state for the requested package state.
677//
678extern "C" HRESULT MsiEnginePlanCalculatePackage(
679 __in BURN_PACKAGE* pPackage,
680 __in BURN_VARIABLES* pVariables,
681 __in BURN_USER_EXPERIENCE* pUserExperience,
682 __out BOOL* pfBARequestedCache
683 )
684{
685 Trace(REPORT_STANDARD, "Planning MSI package 0x%p", pPackage);
686
687 HRESULT hr = S_OK;
688 DWORD64 qwVersion = pPackage->Msi.qwVersion;
689 DWORD64 qwInstalledVersion = pPackage->Msi.qwInstalledVersion;
690 BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE;
691 BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
692 BOOL fFeatureActionDelta = FALSE;
693 BOOL fRollbackFeatureActionDelta = FALSE;
694 BOOL fBARequestedCache = FALSE;
695
696 if (pPackage->Msi.cFeatures)
697 {
698 // If the package is present and we're repairing it.
699 BOOL fRepairingPackage = (BOOTSTRAPPER_PACKAGE_STATE_CACHED < pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested);
700
701 LogId(REPORT_STANDARD, MSG_PLAN_MSI_FEATURES, pPackage->Msi.cFeatures, pPackage->sczId);
702
703 // plan features
704 for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
705 {
706 BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
707 BOOTSTRAPPER_FEATURE_STATE defaultFeatureRequestedState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN;
708 BOOTSTRAPPER_FEATURE_STATE featureRequestedState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN;
709 BOOTSTRAPPER_FEATURE_STATE featureExpectedState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN;
710
711 // Evaluate feature conditions.
712 hr = EvaluateActionStateConditions(pVariables, pFeature->sczAddLocalCondition, pFeature->sczAddSourceCondition, pFeature->sczAdvertiseCondition, &defaultFeatureRequestedState);
713 ExitOnFailure(hr, "Failed to evaluate requested state conditions.");
714
715 hr = EvaluateActionStateConditions(pVariables, pFeature->sczRollbackAddLocalCondition, pFeature->sczRollbackAddSourceCondition, pFeature->sczRollbackAdvertiseCondition, &featureExpectedState);
716 ExitOnFailure(hr, "Failed to evaluate expected state conditions.");
717
718 // Remember the default feature requested state so the engine doesn't get blamed for planning the wrong thing if the BA changes it.
719 featureRequestedState = defaultFeatureRequestedState;
720
721 // Send plan MSI feature message to BA.
722 hr = UserExperienceOnPlanMsiFeature(pUserExperience, pPackage->sczId, pFeature->sczId, &featureRequestedState);
723 ExitOnRootFailure(hr, "BA aborted plan MSI feature.");
724
725 // Calculate feature actions.
726 hr = CalculateFeatureAction(pFeature->currentState, featureRequestedState, fRepairingPackage, &pFeature->execute, &fFeatureActionDelta);
727 ExitOnFailure(hr, "Failed to calculate execute feature state.");
728
729 hr = CalculateFeatureAction(featureRequestedState, BOOTSTRAPPER_FEATURE_ACTION_NONE == pFeature->execute ? featureExpectedState : pFeature->currentState, FALSE, &pFeature->rollback, &fRollbackFeatureActionDelta);
730 ExitOnFailure(hr, "Failed to calculate rollback feature state.");
731
732 LogId(REPORT_STANDARD, MSG_PLANNED_MSI_FEATURE, pFeature->sczId, LoggingMsiFeatureStateToString(pFeature->currentState), LoggingMsiFeatureStateToString(defaultFeatureRequestedState), LoggingMsiFeatureStateToString(featureRequestedState), LoggingMsiFeatureActionToString(pFeature->execute), LoggingMsiFeatureActionToString(pFeature->rollback));
733 }
734 }
735
736 // execute action
737 switch (pPackage->currentState)
738 {
739 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough;
740 case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
741 if (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested)
742 {
743 // Take a look at the version and determine if this is a potential
744 // minor upgrade (same ProductCode newer ProductVersion), otherwise,
745 // there is a newer version so no work necessary.
746 if (qwVersion > qwInstalledVersion)
747 {
748 execute = BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE;
749 }
750 else if (BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested)
751 {
752 execute = BOOTSTRAPPER_ACTION_STATE_REPAIR;
753 }
754 else
755 {
756 execute = fFeatureActionDelta ? BOOTSTRAPPER_ACTION_STATE_MODIFY : BOOTSTRAPPER_ACTION_STATE_NONE;
757 }
758 }
759 else if ((BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_CACHE == pPackage->requested) &&
760 pPackage->fUninstallable) // removing a package that can be removed.
761 {
762 execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
763 }
764 else if (BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested)
765 {
766 execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
767 }
768 else
769 {
770 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
771 }
772 break;
773
774 case BOOTSTRAPPER_PACKAGE_STATE_CACHED:
775 switch (pPackage->requested)
776 {
777 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
778 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
779 execute = BOOTSTRAPPER_ACTION_STATE_INSTALL;
780 break;
781
782 default:
783 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
784 break;
785 }
786 break;
787
788 case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough;
789 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
790 switch (pPackage->requested)
791 {
792 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
793 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
794 execute = BOOTSTRAPPER_ACTION_STATE_INSTALL;
795 break;
796
797 case BOOTSTRAPPER_REQUEST_STATE_CACHE:
798 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
799 fBARequestedCache = TRUE;
800 break;
801
802 default:
803 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
804 break;
805 }
806 break;
807
808 default:
809 hr = E_INVALIDARG;
810 ExitOnRootFailure(hr, "Invalid package current state result encountered during plan: %d", pPackage->currentState);
811 }
812
813 // Calculate the rollback action if there is an execute action.
814 if (BOOTSTRAPPER_ACTION_STATE_NONE != execute)
815 {
816 switch (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN != pPackage->expected ? pPackage->expected : pPackage->currentState)
817 {
818 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough;
819 case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
820 switch (pPackage->requested)
821 {
822 case BOOTSTRAPPER_REQUEST_STATE_PRESENT:
823 rollback = fRollbackFeatureActionDelta ? BOOTSTRAPPER_ACTION_STATE_MODIFY : BOOTSTRAPPER_ACTION_STATE_NONE;
824 break;
825 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
826 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
827 break;
828 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough;
829 case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
830 rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL;
831 break;
832 default:
833 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
834 break;
835 }
836 break;
837
838 case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough;
839 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough;
840 case BOOTSTRAPPER_PACKAGE_STATE_CACHED:
841 // If we requested to put the package on the machine then remove the package during rollback
842 // if the package is uninstallable.
843 if ((BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested) &&
844 pPackage->fUninstallable)
845 {
846 rollback = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
847 }
848 else
849 {
850 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
851 }
852 break;
853
854 default:
855 hr = E_INVALIDARG;
856 ExitOnRootFailure(hr, "Invalid package detection result encountered.");
857 }
858 }
859
860 // return values
861 pPackage->execute = execute;
862 pPackage->rollback = rollback;
863
864 if (pfBARequestedCache)
865 {
866 *pfBARequestedCache = fBARequestedCache;
867 }
868
869LExit:
870 return hr;
871}
872
873//
874// PlanAdd - adds the calculated execute and rollback actions for the package.
875//
876extern "C" HRESULT MsiEnginePlanAddPackage(
877 __in BOOTSTRAPPER_DISPLAY display,
878 __in BURN_PACKAGE* pPackage,
879 __in BURN_PLAN* pPlan,
880 __in BURN_LOGGING* pLog,
881 __in BURN_VARIABLES* pVariables,
882 __in_opt HANDLE hCacheEvent,
883 __in BOOL fPlanPackageCacheRollback
884 )
885{
886 HRESULT hr = S_OK;
887 BURN_EXECUTE_ACTION* pAction = NULL;
888 BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions = NULL;
889 BOOTSTRAPPER_FEATURE_ACTION* rgRollbackFeatureActions = NULL;
890
891 if (pPackage->Msi.cFeatures)
892 {
893 // Allocate and populate array for feature actions.
894 rgFeatureActions = (BOOTSTRAPPER_FEATURE_ACTION*)MemAlloc(sizeof(BOOTSTRAPPER_FEATURE_ACTION) * pPackage->Msi.cFeatures, TRUE);
895 ExitOnNull(rgFeatureActions, hr, E_OUTOFMEMORY, "Failed to allocate memory for feature actions.");
896
897 rgRollbackFeatureActions = (BOOTSTRAPPER_FEATURE_ACTION*)MemAlloc(sizeof(BOOTSTRAPPER_FEATURE_ACTION) * pPackage->Msi.cFeatures, TRUE);
898 ExitOnNull(rgRollbackFeatureActions, hr, E_OUTOFMEMORY, "Failed to allocate memory for rollback feature actions.");
899
900 for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
901 {
902 BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
903
904 // calculate feature actions
905 rgFeatureActions[i] = pFeature->execute;
906 rgRollbackFeatureActions[i] = pFeature->rollback;
907 }
908 }
909
910 // add wait for cache
911 if (hCacheEvent)
912 {
913 hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent, fPlanPackageCacheRollback);
914 ExitOnFailure(hr, "Failed to plan package cache syncpoint");
915 }
916
917 hr = DependencyPlanPackage(NULL, pPackage, pPlan);
918 ExitOnFailure(hr, "Failed to plan package dependency actions.");
919
920 // add rollback action
921 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)
922 {
923 hr = PlanAppendRollbackAction(pPlan, &pAction);
924 ExitOnFailure(hr, "Failed to append rollback action.");
925
926 pAction->type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE;
927 pAction->msiPackage.pPackage = pPackage;
928 pAction->msiPackage.action = pPackage->rollback;
929 pAction->msiPackage.uiLevel = MsiEngineCalculateInstallUiLevel(pPackage->Msi.fDisplayInternalUI, display, pAction->msiPackage.action);
930 pAction->msiPackage.rgFeatures = rgRollbackFeatureActions;
931 rgRollbackFeatureActions = NULL;
932
933 LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, &pAction->msiPackage.sczLogPath); // ignore errors.
934 pAction->msiPackage.dwLoggingAttributes = pLog->dwAttributes;
935
936 // Plan a checkpoint between rollback and execute so that we always attempt
937 // rollback in the case that the MSI was not able to rollback itself (e.g.
938 // user pushes cancel after InstallFinalize).
939 hr = PlanExecuteCheckpoint(pPlan);
940 ExitOnFailure(hr, "Failed to append execute checkpoint.");
941 }
942
943 // add execute action
944 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute)
945 {
946 hr = PlanAppendExecuteAction(pPlan, &pAction);
947 ExitOnFailure(hr, "Failed to append execute action.");
948
949 pAction->type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE;
950 pAction->msiPackage.pPackage = pPackage;
951 pAction->msiPackage.action = pPackage->execute;
952 pAction->msiPackage.uiLevel = MsiEngineCalculateInstallUiLevel(pPackage->Msi.fDisplayInternalUI, display, pAction->msiPackage.action);
953 pAction->msiPackage.rgFeatures = rgFeatureActions;
954 rgFeatureActions = NULL;
955
956 LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, &pAction->msiPackage.sczLogPath); // ignore errors.
957 pAction->msiPackage.dwLoggingAttributes = pLog->dwAttributes;
958 }
959
960 // Update any slipstream patches' state.
961 for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i)
962 {
963 BURN_PACKAGE* pMspPackage = pPackage->Msi.rgpSlipstreamMspPackages[i];
964 AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches.");
965
966 MspEngineSlipstreamUpdateState(pMspPackage, pPackage->execute, pPackage->rollback);
967 }
968
969LExit:
970 ReleaseMem(rgFeatureActions);
971 ReleaseMem(rgRollbackFeatureActions);
972
973 return hr;
974}
975
976extern "C" HRESULT MsiEngineAddCompatiblePackage(
977 __in BURN_PACKAGES* pPackages,
978 __in const BURN_PACKAGE* pPackage,
979 __out_opt BURN_PACKAGE** ppCompatiblePackage
980 )
981{
982 Assert(BURN_PACKAGE_TYPE_MSI == pPackage->type);
983
984 HRESULT hr = S_OK;
985 BURN_PACKAGE* pCompatiblePackage = NULL;
986 LPWSTR sczInstalledVersion = NULL;
987
988 // Allocate enough memory all at once so pointers to packages within
989 // aren't invalidated if we otherwise reallocated.
990 hr = PackageEnsureCompatiblePackagesArray(pPackages);
991 ExitOnFailure(hr, "Failed to allocate memory for compatible MSI package.");
992
993 pCompatiblePackage = pPackages->rgCompatiblePackages + pPackages->cCompatiblePackages;
994 ++pPackages->cCompatiblePackages;
995
996 pCompatiblePackage->type = BURN_PACKAGE_TYPE_MSI;
997
998 // Read in the compatible ProductCode if not already available.
999 if (pPackage->Msi.sczInstalledProductCode)
1000 {
1001 hr = StrAllocString(&pCompatiblePackage->Msi.sczProductCode, pPackage->Msi.sczInstalledProductCode, 0);
1002 ExitOnFailure(hr, "Failed to copy installed ProductCode to compatible package.");
1003 }
1004 else
1005 {
1006 hr = DependencyDetectProviderKeyPackageId(pPackage, NULL, &pCompatiblePackage->Msi.sczProductCode);
1007 ExitOnFailure(hr, "Failed to detect compatible package from provider key.");
1008 }
1009
1010 // Read in the compatible ProductVersion if not already available.
1011 if (pPackage->Msi.qwInstalledVersion)
1012 {
1013 pCompatiblePackage->Msi.qwVersion = pPackage->Msi.qwInstalledVersion;
1014
1015 hr = FileVersionToStringEx(pCompatiblePackage->Msi.qwVersion, &sczInstalledVersion);
1016 ExitOnFailure(hr, "Failed to format version number string.");
1017 }
1018 else
1019 {
1020 hr = WiuGetProductInfoEx(pCompatiblePackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
1021 ExitOnFailure(hr, "Failed to read version from compatible package.");
1022
1023 hr = FileVersionFromStringEx(sczInstalledVersion, 0, &pCompatiblePackage->Msi.qwVersion);
1024 ExitOnFailure(hr, "Failed to convert version: %ls to DWORD64 for ProductCode: %ls", sczInstalledVersion, pCompatiblePackage->Msi.sczProductCode);
1025 }
1026
1027 // For now, copy enough information to support uninstalling the newer, compatible package.
1028 hr = StrAllocString(&pCompatiblePackage->sczId, pCompatiblePackage->Msi.sczProductCode, 0);
1029 ExitOnFailure(hr, "Failed to copy installed ProductCode as compatible package ID.");
1030
1031 pCompatiblePackage->fPerMachine = pPackage->fPerMachine;
1032 pCompatiblePackage->fUninstallable = pPackage->fUninstallable;
1033 pCompatiblePackage->cacheType = pPackage->cacheType;
1034
1035 // Removing compatible packages is best effort.
1036 pCompatiblePackage->fVital = FALSE;
1037
1038 // Format a suitable log path variable from the original package.
1039 hr = StrAllocFormatted(&pCompatiblePackage->sczLogPathVariable, L"%ls_Compatible", pPackage->sczLogPathVariable);
1040 ExitOnFailure(hr, "Failed to format log path variable for compatible package.");
1041
1042 // Use the default cache ID generation from the binder.
1043 hr = StrAllocFormatted(&pCompatiblePackage->sczCacheId, L"%lsv%ls", pCompatiblePackage->sczId, sczInstalledVersion);
1044 ExitOnFailure(hr, "Failed to format cache ID for compatible package.");
1045
1046 pCompatiblePackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT;
1047 pCompatiblePackage->cache = BURN_CACHE_STATE_PARTIAL; // Cannot know if it's complete or not.
1048
1049 // Copy all the providers to ensure no dependents.
1050 if (pPackage->cDependencyProviders)
1051 {
1052 pCompatiblePackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER) * pPackage->cDependencyProviders, TRUE);
1053 ExitOnNull(pCompatiblePackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate for compatible package providers.");
1054
1055 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
1056 {
1057 BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders + i;
1058 BURN_DEPENDENCY_PROVIDER* pCompatibleProvider = pCompatiblePackage->rgDependencyProviders + i;
1059
1060 // Only need to copy the key for uninstall.
1061 hr = StrAllocString(&pCompatibleProvider->sczKey, pProvider->sczKey, 0);
1062 ExitOnFailure(hr, "Failed to copy the compatible provider key.");
1063
1064 // Assume the package version is the same as the provider version.
1065 hr = StrAllocString(&pCompatibleProvider->sczVersion, sczInstalledVersion, 0);
1066 ExitOnFailure(hr, "Failed to copy the compatible provider version.");
1067
1068 // Assume provider keys are similarly authored for this package.
1069 pCompatibleProvider->fImported = pProvider->fImported;
1070 }
1071
1072 pCompatiblePackage->cDependencyProviders = pPackage->cDependencyProviders;
1073 }
1074
1075 pCompatiblePackage->type = BURN_PACKAGE_TYPE_MSI;
1076 pCompatiblePackage->Msi.fDisplayInternalUI = pPackage->Msi.fDisplayInternalUI;
1077
1078 if (ppCompatiblePackage)
1079 {
1080 *ppCompatiblePackage = pCompatiblePackage;
1081 }
1082
1083LExit:
1084 ReleaseStr(sczInstalledVersion);
1085
1086 return hr;
1087}
1088
1089extern "C" HRESULT MsiEngineExecutePackage(
1090 __in_opt HWND hwndParent,
1091 __in BURN_EXECUTE_ACTION* pExecuteAction,
1092 __in BURN_VARIABLES* pVariables,
1093 __in BOOL fRollback,
1094 __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
1095 __in LPVOID pvContext,
1096 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
1097 )
1098{
1099 HRESULT hr = S_OK;
1100 WIU_MSI_EXECUTE_CONTEXT context = { };
1101 WIU_RESTART restart = WIU_RESTART_NONE;
1102
1103 LPWSTR sczInstalledVersion = NULL;
1104 LPWSTR sczCachedDirectory = NULL;
1105 LPWSTR sczMsiPath = NULL;
1106 LPWSTR sczProperties = NULL;
1107 LPWSTR sczObfuscatedProperties = NULL;
1108
1109 // During rollback, if the package is already in the rollback state we expect don't
1110 // touch it again.
1111 if (fRollback)
1112 {
1113 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pExecuteAction->msiPackage.action)
1114 {
1115 hr = WiuGetProductInfoEx(pExecuteAction->msiPackage.pPackage->Msi.sczProductCode, NULL, pExecuteAction->msiPackage.pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
1116 if (FAILED(hr)) // package not present.
1117 {
1118 LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pExecuteAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), LoggingPackageStateToString(BOOTSTRAPPER_PACKAGE_STATE_ABSENT));
1119
1120 hr = S_OK;
1121 ExitFunction();
1122 }
1123 }
1124 else if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->msiPackage.action)
1125 {
1126 hr = WiuGetProductInfoEx(pExecuteAction->msiPackage.pPackage->Msi.sczProductCode, NULL, pExecuteAction->msiPackage.pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
1127 if (SUCCEEDED(hr)) // package present.
1128 {
1129 LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pExecuteAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), LoggingPackageStateToString(BOOTSTRAPPER_PACKAGE_STATE_PRESENT));
1130
1131 hr = S_OK;
1132 ExitFunction();
1133 }
1134
1135 hr = S_OK;
1136 }
1137 }
1138
1139 // Default to "verbose" logging and set extra debug mode only if explicitly required.
1140 DWORD dwLogMode = WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE;
1141
1142 if (pExecuteAction->msiPackage.dwLoggingAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG)
1143 {
1144 dwLogMode |= INSTALLLOGMODE_EXTRADEBUG;
1145 }
1146
1147 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL != pExecuteAction->msiPackage.action)
1148 {
1149 // get cached MSI path
1150 hr = CacheGetCompletedPath(pExecuteAction->msiPackage.pPackage->fPerMachine, pExecuteAction->msiPackage.pPackage->sczCacheId, &sczCachedDirectory);
1151 ExitOnFailure(hr, "Failed to get cached path for package: %ls", pExecuteAction->msiPackage.pPackage->sczId);
1152
1153 // Best effort to set the execute package cache folder variable.
1154 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE);
1155
1156 hr = PathConcat(sczCachedDirectory, pExecuteAction->msiPackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczMsiPath);
1157 ExitOnFailure(hr, "Failed to build MSI path.");
1158 }
1159
1160 // Best effort to set the execute package action variable.
1161 VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->msiPackage.action, TRUE);
1162
1163 // Wire up the external UI handler and logging.
1164 hr = WiuInitializeExternalUI(pfnMessageHandler, pExecuteAction->msiPackage.uiLevel, hwndParent, pvContext, fRollback, &context);
1165 ExitOnFailure(hr, "Failed to initialize external UI handler.");
1166
1167 if (pExecuteAction->msiPackage.sczLogPath && *pExecuteAction->msiPackage.sczLogPath)
1168 {
1169 hr = WiuEnableLog(dwLogMode, pExecuteAction->msiPackage.sczLogPath, 0);
1170 ExitOnFailure(hr, "Failed to enable logging for package: %ls to: %ls", pExecuteAction->msiPackage.pPackage->sczId, pExecuteAction->msiPackage.sczLogPath);
1171 }
1172
1173 // set up properties
1174 hr = MsiEngineConcatProperties(pExecuteAction->msiPackage.pPackage->Msi.rgProperties, pExecuteAction->msiPackage.pPackage->Msi.cProperties, pVariables, fRollback, &sczProperties, FALSE);
1175 ExitOnFailure(hr, "Failed to add properties to argument string.");
1176
1177 hr = MsiEngineConcatProperties(pExecuteAction->msiPackage.pPackage->Msi.rgProperties, pExecuteAction->msiPackage.pPackage->Msi.cProperties, pVariables, fRollback, &sczObfuscatedProperties, TRUE);
1178 ExitOnFailure(hr, "Failed to add obfuscated properties to argument string.");
1179
1180 // add feature action properties
1181 hr = ConcatFeatureActionProperties(pExecuteAction->msiPackage.pPackage, pExecuteAction->msiPackage.rgFeatures, &sczProperties);
1182 ExitOnFailure(hr, "Failed to add feature action properties to argument string.");
1183
1184 hr = ConcatFeatureActionProperties(pExecuteAction->msiPackage.pPackage, pExecuteAction->msiPackage.rgFeatures, &sczObfuscatedProperties);
1185 ExitOnFailure(hr, "Failed to add feature action properties to obfuscated argument string.");
1186
1187 // add slipstream patch properties
1188 hr = ConcatPatchProperty(pExecuteAction->msiPackage.pPackage, pExecuteAction->msiPackage.rgSlipstreamPatches, &sczProperties);
1189 ExitOnFailure(hr, "Failed to add patch properties to argument string.");
1190
1191 hr = ConcatPatchProperty(pExecuteAction->msiPackage.pPackage, pExecuteAction->msiPackage.rgSlipstreamPatches, &sczObfuscatedProperties);
1192 ExitOnFailure(hr, "Failed to add patch properties to obfuscated argument string.");
1193
1194 LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), sczMsiPath, sczObfuscatedProperties ? sczObfuscatedProperties : L"");
1195
1196 //
1197 // Do the actual action.
1198 //
1199 switch (pExecuteAction->msiPackage.action)
1200 {
1201 case BOOTSTRAPPER_ACTION_STATE_ADMIN_INSTALL:
1202 hr = StrAllocConcatSecure(&sczProperties, L" ACTION=ADMIN", 0);
1203 ExitOnFailure(hr, "Failed to add ADMIN property on admin install.");
1204 __fallthrough;
1205
1206 case BOOTSTRAPPER_ACTION_STATE_MAJOR_UPGRADE: __fallthrough;
1207 case BOOTSTRAPPER_ACTION_STATE_INSTALL:
1208 hr = StrAllocConcatSecure(&sczProperties, L" REBOOT=ReallySuppress", 0);
1209 ExitOnFailure(hr, "Failed to add reboot suppression property on install.");
1210
1211 hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart);
1212 ExitOnFailure(hr, "Failed to install MSI package.");
1213
1214 RegisterSourceDirectory(pExecuteAction->msiPackage.pPackage, sczMsiPath);
1215 break;
1216
1217 case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE:
1218 // If feature selection is not enabled, then reinstall the existing features to ensure they get
1219 // updated.
1220 if (0 == pExecuteAction->msiPackage.pPackage->Msi.cFeatures)
1221 {
1222 hr = StrAllocConcatSecure(&sczProperties, L" REINSTALL=ALL", 0);
1223 ExitOnFailure(hr, "Failed to add reinstall all property on minor upgrade.");
1224 }
1225
1226 hr = StrAllocConcatSecure(&sczProperties, L" REINSTALLMODE=\"vomus\" REBOOT=ReallySuppress", 0);
1227 ExitOnFailure(hr, "Failed to add reinstall mode and reboot suppression properties on minor upgrade.");
1228
1229 hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart);
1230 ExitOnFailure(hr, "Failed to perform minor upgrade of MSI package.");
1231
1232 RegisterSourceDirectory(pExecuteAction->msiPackage.pPackage, sczMsiPath);
1233 break;
1234
1235 case BOOTSTRAPPER_ACTION_STATE_MODIFY: __fallthrough;
1236 case BOOTSTRAPPER_ACTION_STATE_REPAIR:
1237 {
1238 LPCWSTR wzReinstallAll = (BOOTSTRAPPER_ACTION_STATE_MODIFY == pExecuteAction->msiPackage.action ||
1239 pExecuteAction->msiPackage.pPackage->Msi.cFeatures) ? L"" : L" REINSTALL=ALL";
1240 LPCWSTR wzReinstallMode = (BOOTSTRAPPER_ACTION_STATE_MODIFY == pExecuteAction->msiPackage.action) ? L"o" : L"e";
1241
1242 hr = StrAllocFormattedSecure(&sczProperties, L"%ls%ls REINSTALLMODE=\"cmus%ls\" REBOOT=ReallySuppress", sczProperties ? sczProperties : L"", wzReinstallAll, wzReinstallMode);
1243 ExitOnFailure(hr, "Failed to add reinstall mode and reboot suppression properties on repair.");
1244 }
1245
1246 // Ignore all dependencies, since the Burn engine already performed the check.
1247 hr = StrAllocFormattedSecure(&sczProperties, L"%ls %ls=ALL", sczProperties, DEPENDENCY_IGNOREDEPENDENCIES);
1248 ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties.");
1249
1250 hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart);
1251 ExitOnFailure(hr, "Failed to run maintenance mode for MSI package.");
1252 break;
1253
1254 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
1255 hr = StrAllocConcatSecure(&sczProperties, L" REBOOT=ReallySuppress", 0);
1256 ExitOnFailure(hr, "Failed to add reboot suppression property on uninstall.");
1257
1258 // Ignore all dependencies, since the Burn engine already performed the check.
1259 hr = StrAllocFormattedSecure(&sczProperties, L"%ls %ls=ALL", sczProperties, DEPENDENCY_IGNOREDEPENDENCIES);
1260 ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties.");
1261
1262 hr = WiuConfigureProductEx(pExecuteAction->msiPackage.pPackage->Msi.sczProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_ABSENT, sczProperties, &restart);
1263 if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr)
1264 {
1265 LogId(REPORT_STANDARD, MSG_ATTEMPTED_UNINSTALL_ABSENT_PACKAGE, pExecuteAction->msiPackage.pPackage->sczId);
1266 hr = S_OK;
1267 }
1268 ExitOnFailure(hr, "Failed to uninstall MSI package.");
1269 break;
1270 }
1271
1272LExit:
1273 WiuUninitializeExternalUI(&context);
1274
1275 StrSecureZeroFreeString(sczProperties);
1276 ReleaseStr(sczObfuscatedProperties);
1277 ReleaseStr(sczMsiPath);
1278 ReleaseStr(sczCachedDirectory);
1279 ReleaseStr(sczInstalledVersion);
1280
1281 switch (restart)
1282 {
1283 case WIU_RESTART_NONE:
1284 *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
1285 break;
1286
1287 case WIU_RESTART_REQUIRED:
1288 *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
1289 break;
1290
1291 case WIU_RESTART_INITIATED:
1292 *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
1293 break;
1294 }
1295
1296 // Best effort to clear the execute package cache folder and action variables.
1297 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE);
1298 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE);
1299
1300 return hr;
1301}
1302
1303// The contents of psczProperties may be sensitive, should keep encrypted and SecureZeroFree.
1304extern "C" HRESULT MsiEngineConcatProperties(
1305 __in_ecount(cProperties) BURN_MSIPROPERTY* rgProperties,
1306 __in DWORD cProperties,
1307 __in BURN_VARIABLES* pVariables,
1308 __in BOOL fRollback,
1309 __deref_out_z LPWSTR* psczProperties,
1310 __in BOOL fObfuscateHiddenVariables
1311 )
1312{
1313 HRESULT hr = S_OK;
1314 LPWSTR sczValue = NULL;
1315 LPWSTR sczEscapedValue = NULL;
1316 LPWSTR sczProperty = NULL;
1317
1318 for (DWORD i = 0; i < cProperties; ++i)
1319 {
1320 BURN_MSIPROPERTY* pProperty = &rgProperties[i];
1321
1322 if (pProperty->sczCondition && *pProperty->sczCondition)
1323 {
1324 BOOL fCondition = FALSE;
1325
1326 hr = ConditionEvaluate(pVariables, pProperty->sczCondition, &fCondition);
1327 if (FAILED(hr) || !fCondition)
1328 {
1329 LogId(REPORT_VERBOSE, MSG_MSI_PROPERTY_CONDITION_FAILED, pProperty->sczId, pProperty->sczCondition, LoggingTrueFalseToString(fCondition));
1330 continue;
1331 }
1332 }
1333
1334 // format property value
1335 if (fObfuscateHiddenVariables)
1336 {
1337 hr = VariableFormatStringObfuscated(pVariables, (fRollback && pProperty->sczRollbackValue) ? pProperty->sczRollbackValue : pProperty->sczValue, &sczValue, NULL);
1338 }
1339 else
1340 {
1341 hr = VariableFormatString(pVariables, (fRollback && pProperty->sczRollbackValue) ? pProperty->sczRollbackValue : pProperty->sczValue, &sczValue, NULL);
1342 ExitOnFailure(hr, "Failed to format property value.");
1343 }
1344 ExitOnFailure(hr, "Failed to format property value.");
1345
1346 // escape property value
1347 hr = EscapePropertyArgumentString(sczValue, &sczEscapedValue, !fObfuscateHiddenVariables);
1348 ExitOnFailure(hr, "Failed to escape string.");
1349
1350 // build part
1351 hr = VariableStrAllocFormatted(!fObfuscateHiddenVariables, &sczProperty, L" %s%=\"%s\"", pProperty->sczId, sczEscapedValue);
1352 ExitOnFailure(hr, "Failed to format property string part.");
1353
1354 // append to property string
1355 hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, psczProperties, sczProperty, 0);
1356 ExitOnFailure(hr, "Failed to append property string part.");
1357 }
1358
1359LExit:
1360 StrSecureZeroFreeString(sczValue);
1361 StrSecureZeroFreeString(sczEscapedValue);
1362 StrSecureZeroFreeString(sczProperty);
1363 return hr;
1364}
1365
1366extern "C" INSTALLUILEVEL MsiEngineCalculateInstallUiLevel(
1367 __in BOOL fDisplayInternalUI,
1368 __in BOOTSTRAPPER_DISPLAY display,
1369 __in BOOTSTRAPPER_ACTION_STATE actionState
1370 )
1371{
1372 // Assume there will be no internal UI displayed.
1373 INSTALLUILEVEL uiLevel = static_cast<INSTALLUILEVEL>(INSTALLUILEVEL_NONE | INSTALLUILEVEL_SOURCERESONLY);
1374
1375 // suppress internal UI during uninstall to mimic ARP and "msiexec /x" behavior
1376 if (fDisplayInternalUI && BOOTSTRAPPER_ACTION_STATE_UNINSTALL != actionState && BOOTSTRAPPER_ACTION_STATE_REPAIR != actionState)
1377 {
1378 switch (display)
1379 {
1380 case BOOTSTRAPPER_DISPLAY_FULL:
1381 uiLevel = INSTALLUILEVEL_FULL;
1382 break;
1383
1384 case BOOTSTRAPPER_DISPLAY_PASSIVE:
1385 uiLevel = INSTALLUILEVEL_REDUCED;
1386 break;
1387 }
1388 }
1389
1390 return uiLevel;
1391}
1392
1393
1394// internal helper functions
1395
1396static HRESULT ParseRelatedMsiFromXml(
1397 __in IXMLDOMNode* pixnRelatedMsi,
1398 __in BURN_RELATED_MSI* pRelatedMsi
1399 )
1400{
1401 HRESULT hr = S_OK;
1402 IXMLDOMNodeList* pixnNodes = NULL;
1403 IXMLDOMNode* pixnNode = NULL;
1404 DWORD cNodes = 0;
1405 LPWSTR scz = NULL;
1406
1407 // @Id
1408 hr = XmlGetAttributeEx(pixnRelatedMsi, L"Id", &pRelatedMsi->sczUpgradeCode);
1409 ExitOnFailure(hr, "Failed to get @Id.");
1410
1411 // @MinVersion
1412 hr = XmlGetAttributeEx(pixnRelatedMsi, L"MinVersion", &scz);
1413 if (E_NOTFOUND != hr)
1414 {
1415 ExitOnFailure(hr, "Failed to get @MinVersion.");
1416
1417 hr = FileVersionFromStringEx(scz, 0, &pRelatedMsi->qwMinVersion);
1418 ExitOnFailure(hr, "Failed to parse @MinVersion: %ls", scz);
1419
1420 // flag that we have a min version
1421 pRelatedMsi->fMinProvided = TRUE;
1422
1423 // @MinInclusive
1424 hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"MinInclusive", &pRelatedMsi->fMinInclusive);
1425 ExitOnFailure(hr, "Failed to get @MinInclusive.");
1426 }
1427
1428 // @MaxVersion
1429 hr = XmlGetAttributeEx(pixnRelatedMsi, L"MaxVersion", &scz);
1430 if (E_NOTFOUND != hr)
1431 {
1432 ExitOnFailure(hr, "Failed to get @MaxVersion.");
1433
1434 hr = FileVersionFromStringEx(scz, 0, &pRelatedMsi->qwMaxVersion);
1435 ExitOnFailure(hr, "Failed to parse @MaxVersion: %ls", scz);
1436
1437 // flag that we have a max version
1438 pRelatedMsi->fMaxProvided = TRUE;
1439
1440 // @MaxInclusive
1441 hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"MaxInclusive", &pRelatedMsi->fMaxInclusive);
1442 ExitOnFailure(hr, "Failed to get @MaxInclusive.");
1443 }
1444
1445 // @OnlyDetect
1446 hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"OnlyDetect", &pRelatedMsi->fOnlyDetect);
1447 ExitOnFailure(hr, "Failed to get @OnlyDetect.");
1448
1449 // select language nodes
1450 hr = XmlSelectNodes(pixnRelatedMsi, L"Language", &pixnNodes);
1451 ExitOnFailure(hr, "Failed to select language nodes.");
1452
1453 // get language node count
1454 hr = pixnNodes->get_length((long*)&cNodes);
1455 ExitOnFailure(hr, "Failed to get language node count.");
1456
1457 if (cNodes)
1458 {
1459 // @LangInclusive
1460 hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"LangInclusive", &pRelatedMsi->fLangInclusive);
1461 ExitOnFailure(hr, "Failed to get @LangInclusive.");
1462
1463 // allocate memory for language IDs
1464 pRelatedMsi->rgdwLanguages = (DWORD*)MemAlloc(sizeof(DWORD) * cNodes, TRUE);
1465 ExitOnNull(pRelatedMsi->rgdwLanguages, hr, E_OUTOFMEMORY, "Failed to allocate memory for language IDs.");
1466
1467 pRelatedMsi->cLanguages = cNodes;
1468
1469 // parse language elements
1470 for (DWORD i = 0; i < cNodes; ++i)
1471 {
1472 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
1473 ExitOnFailure(hr, "Failed to get next node.");
1474
1475 // @Id
1476 hr = XmlGetAttributeNumber(pixnNode, L"Id", &pRelatedMsi->rgdwLanguages[i]);
1477 ExitOnFailure(hr, "Failed to get Language/@Id.");
1478
1479 // prepare next iteration
1480 ReleaseNullObject(pixnNode);
1481 }
1482 }
1483
1484 hr = S_OK;
1485
1486LExit:
1487 ReleaseObject(pixnNodes);
1488 ReleaseObject(pixnNode);
1489 ReleaseStr(scz);
1490
1491 return hr;
1492}
1493
1494static HRESULT EvaluateActionStateConditions(
1495 __in BURN_VARIABLES* pVariables,
1496 __in_z_opt LPCWSTR sczAddLocalCondition,
1497 __in_z_opt LPCWSTR sczAddSourceCondition,
1498 __in_z_opt LPCWSTR sczAdvertiseCondition,
1499 __out BOOTSTRAPPER_FEATURE_STATE* pState
1500 )
1501{
1502 HRESULT hr = S_OK;
1503 BOOL fCondition = FALSE;
1504
1505 // if no condition was set, return no feature state
1506 if (!sczAddLocalCondition && !sczAddSourceCondition && !sczAdvertiseCondition)
1507 {
1508 *pState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN;
1509 ExitFunction();
1510 }
1511
1512 if (sczAddLocalCondition)
1513 {
1514 hr = ConditionEvaluate(pVariables, sczAddLocalCondition, &fCondition);
1515 ExitOnFailure(hr, "Failed to evaluate add local condition.");
1516
1517 if (fCondition)
1518 {
1519 *pState = BOOTSTRAPPER_FEATURE_STATE_LOCAL;
1520 ExitFunction();
1521 }
1522 }
1523
1524 if (sczAddSourceCondition)
1525 {
1526 hr = ConditionEvaluate(pVariables, sczAddSourceCondition, &fCondition);
1527 ExitOnFailure(hr, "Failed to evaluate add source condition.");
1528
1529 if (fCondition)
1530 {
1531 *pState = BOOTSTRAPPER_FEATURE_STATE_SOURCE;
1532 ExitFunction();
1533 }
1534 }
1535
1536 if (sczAdvertiseCondition)
1537 {
1538 hr = ConditionEvaluate(pVariables, sczAdvertiseCondition, &fCondition);
1539 ExitOnFailure(hr, "Failed to evaluate advertise condition.");
1540
1541 if (fCondition)
1542 {
1543 *pState = BOOTSTRAPPER_FEATURE_STATE_ADVERTISED;
1544 ExitFunction();
1545 }
1546 }
1547
1548 // if no condition was true, set to absent
1549 *pState = BOOTSTRAPPER_FEATURE_STATE_ABSENT;
1550
1551LExit:
1552 return hr;
1553}
1554
1555static HRESULT CalculateFeatureAction(
1556 __in BOOTSTRAPPER_FEATURE_STATE currentState,
1557 __in BOOTSTRAPPER_FEATURE_STATE requestedState,
1558 __in BOOL fRepair,
1559 __out BOOTSTRAPPER_FEATURE_ACTION* pFeatureAction,
1560 __inout BOOL* pfDelta
1561 )
1562{
1563 HRESULT hr = S_OK;
1564
1565 *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_NONE;
1566 switch (requestedState)
1567 {
1568 case BOOTSTRAPPER_FEATURE_STATE_UNKNOWN:
1569 *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_NONE;
1570 break;
1571
1572 case BOOTSTRAPPER_FEATURE_STATE_ABSENT:
1573 if (BOOTSTRAPPER_FEATURE_STATE_ABSENT != currentState)
1574 {
1575 *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REMOVE;
1576 }
1577 break;
1578
1579 case BOOTSTRAPPER_FEATURE_STATE_ADVERTISED:
1580 if (BOOTSTRAPPER_FEATURE_STATE_ADVERTISED != currentState)
1581 {
1582 *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE;
1583 }
1584 else if (fRepair)
1585 {
1586 *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REINSTALL;
1587 }
1588 break;
1589
1590 case BOOTSTRAPPER_FEATURE_STATE_LOCAL:
1591 if (BOOTSTRAPPER_FEATURE_STATE_LOCAL != currentState)
1592 {
1593 *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL;
1594 }
1595 else if (fRepair)
1596 {
1597 *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REINSTALL;
1598 }
1599 break;
1600
1601 case BOOTSTRAPPER_FEATURE_STATE_SOURCE:
1602 if (BOOTSTRAPPER_FEATURE_STATE_SOURCE != currentState)
1603 {
1604 *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE;
1605 }
1606 else if (fRepair)
1607 {
1608 *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REINSTALL;
1609 }
1610 break;
1611
1612 default:
1613 hr = E_UNEXPECTED;
1614 ExitOnRootFailure(hr, "Invalid state value.");
1615 }
1616
1617 if (BOOTSTRAPPER_FEATURE_ACTION_NONE != *pFeatureAction)
1618 {
1619 *pfDelta = TRUE;
1620 }
1621
1622LExit:
1623 return hr;
1624}
1625
1626static HRESULT EscapePropertyArgumentString(
1627 __in LPCWSTR wzProperty,
1628 __inout_z LPWSTR* psczEscapedValue,
1629 __in BOOL fZeroOnRealloc
1630 )
1631{
1632 HRESULT hr = S_OK;
1633 DWORD cch = 0;
1634 DWORD cchEscape = 0;
1635 LPCWSTR wzSource = NULL;
1636 LPWSTR wzTarget = NULL;
1637
1638 // count characters to escape
1639 wzSource = wzProperty;
1640 while (*wzSource)
1641 {
1642 ++cch;
1643 if (L'\"' == *wzSource)
1644 {
1645 ++cchEscape;
1646 }
1647 ++wzSource;
1648 }
1649
1650 // allocate target buffer
1651 hr = VariableStrAlloc(fZeroOnRealloc, psczEscapedValue, cch + cchEscape + 1); // character count, plus escape character count, plus null terminator
1652 ExitOnFailure(hr, "Failed to allocate string buffer.");
1653
1654 // write to target buffer
1655 wzSource = wzProperty;
1656 wzTarget = *psczEscapedValue;
1657 while (*wzSource)
1658 {
1659 *wzTarget = *wzSource;
1660 if (L'\"' == *wzTarget)
1661 {
1662 ++wzTarget;
1663 *wzTarget = L'\"';
1664 }
1665
1666 ++wzSource;
1667 ++wzTarget;
1668 }
1669
1670 *wzTarget = L'\0'; // add null terminator
1671
1672LExit:
1673 return hr;
1674}
1675
1676static HRESULT ConcatFeatureActionProperties(
1677 __in BURN_PACKAGE* pPackage,
1678 __in BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions,
1679 __inout_z LPWSTR* psczArguments
1680 )
1681{
1682 HRESULT hr = S_OK;
1683 LPWSTR scz = NULL;
1684 LPWSTR sczAddLocal = NULL;
1685 LPWSTR sczAddSource = NULL;
1686 LPWSTR sczAddDefault = NULL;
1687 LPWSTR sczReinstall = NULL;
1688 LPWSTR sczAdvertise = NULL;
1689 LPWSTR sczRemove = NULL;
1690
1691 // features
1692 for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
1693 {
1694 BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
1695
1696 switch (rgFeatureActions[i])
1697 {
1698 case BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL:
1699 if (sczAddLocal)
1700 {
1701 hr = StrAllocConcat(&sczAddLocal, L",", 0);
1702 ExitOnFailure(hr, "Failed to concat separator.");
1703 }
1704 hr = StrAllocConcat(&sczAddLocal, pFeature->sczId, 0);
1705 ExitOnFailure(hr, "Failed to concat feature.");
1706 break;
1707
1708 case BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE:
1709 if (sczAddSource)
1710 {
1711 hr = StrAllocConcat(&sczAddSource, L",", 0);
1712 ExitOnFailure(hr, "Failed to concat separator.");
1713 }
1714 hr = StrAllocConcat(&sczAddSource, pFeature->sczId, 0);
1715 ExitOnFailure(hr, "Failed to concat feature.");
1716 break;
1717
1718 case BOOTSTRAPPER_FEATURE_ACTION_ADDDEFAULT:
1719 if (sczAddDefault)
1720 {
1721 hr = StrAllocConcat(&sczAddDefault, L",", 0);
1722 ExitOnFailure(hr, "Failed to concat separator.");
1723 }
1724 hr = StrAllocConcat(&sczAddDefault, pFeature->sczId, 0);
1725 ExitOnFailure(hr, "Failed to concat feature.");
1726 break;
1727
1728 case BOOTSTRAPPER_FEATURE_ACTION_REINSTALL:
1729 if (sczReinstall)
1730 {
1731 hr = StrAllocConcat(&sczReinstall, L",", 0);
1732 ExitOnFailure(hr, "Failed to concat separator.");
1733 }
1734 hr = StrAllocConcat(&sczReinstall, pFeature->sczId, 0);
1735 ExitOnFailure(hr, "Failed to concat feature.");
1736 break;
1737
1738 case BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE:
1739 if (sczAdvertise)
1740 {
1741 hr = StrAllocConcat(&sczAdvertise, L",", 0);
1742 ExitOnFailure(hr, "Failed to concat separator.");
1743 }
1744 hr = StrAllocConcat(&sczAdvertise, pFeature->sczId, 0);
1745 ExitOnFailure(hr, "Failed to concat feature.");
1746 break;
1747
1748 case BOOTSTRAPPER_FEATURE_ACTION_REMOVE:
1749 if (sczRemove)
1750 {
1751 hr = StrAllocConcat(&sczRemove, L",", 0);
1752 ExitOnFailure(hr, "Failed to concat separator.");
1753 }
1754 hr = StrAllocConcat(&sczRemove, pFeature->sczId, 0);
1755 ExitOnFailure(hr, "Failed to concat feature.");
1756 break;
1757 }
1758 }
1759
1760 if (sczAddLocal)
1761 {
1762 hr = StrAllocFormatted(&scz, L" ADDLOCAL=\"%s\"", sczAddLocal, 0);
1763 ExitOnFailure(hr, "Failed to format ADDLOCAL string.");
1764
1765 hr = StrAllocConcatSecure(psczArguments, scz, 0);
1766 ExitOnFailure(hr, "Failed to concat argument string.");
1767 }
1768
1769 if (sczAddSource)
1770 {
1771 hr = StrAllocFormatted(&scz, L" ADDSOURCE=\"%s\"", sczAddSource, 0);
1772 ExitOnFailure(hr, "Failed to format ADDSOURCE string.");
1773
1774 hr = StrAllocConcatSecure(psczArguments, scz, 0);
1775 ExitOnFailure(hr, "Failed to concat argument string.");
1776 }
1777
1778 if (sczAddDefault)
1779 {
1780 hr = StrAllocFormatted(&scz, L" ADDDEFAULT=\"%s\"", sczAddDefault, 0);
1781 ExitOnFailure(hr, "Failed to format ADDDEFAULT string.");
1782
1783 hr = StrAllocConcatSecure(psczArguments, scz, 0);
1784 ExitOnFailure(hr, "Failed to concat argument string.");
1785 }
1786
1787 if (sczReinstall)
1788 {
1789 hr = StrAllocFormatted(&scz, L" REINSTALL=\"%s\"", sczReinstall, 0);
1790 ExitOnFailure(hr, "Failed to format REINSTALL string.");
1791
1792 hr = StrAllocConcatSecure(psczArguments, scz, 0);
1793 ExitOnFailure(hr, "Failed to concat argument string.");
1794 }
1795
1796 if (sczAdvertise)
1797 {
1798 hr = StrAllocFormatted(&scz, L" ADVERTISE=\"%s\"", sczAdvertise, 0);
1799 ExitOnFailure(hr, "Failed to format ADVERTISE string.");
1800
1801 hr = StrAllocConcatSecure(psczArguments, scz, 0);
1802 ExitOnFailure(hr, "Failed to concat argument string.");
1803 }
1804
1805 if (sczRemove)
1806 {
1807 hr = StrAllocFormatted(&scz, L" REMOVE=\"%s\"", sczRemove, 0);
1808 ExitOnFailure(hr, "Failed to format REMOVE string.");
1809
1810 hr = StrAllocConcatSecure(psczArguments, scz, 0);
1811 ExitOnFailure(hr, "Failed to concat argument string.");
1812 }
1813
1814LExit:
1815 ReleaseStr(scz);
1816 ReleaseStr(sczAddLocal);
1817 ReleaseStr(sczAddSource);
1818 ReleaseStr(sczAddDefault);
1819 ReleaseStr(sczReinstall);
1820 ReleaseStr(sczAdvertise);
1821 ReleaseStr(sczRemove);
1822
1823 return hr;
1824}
1825
1826static HRESULT ConcatPatchProperty(
1827 __in BURN_PACKAGE* pPackage,
1828 __in_opt BOOTSTRAPPER_ACTION_STATE* rgSlipstreamPatchActions,
1829 __inout_z LPWSTR* psczArguments
1830 )
1831{
1832 HRESULT hr = S_OK;
1833 LPWSTR sczCachedDirectory = NULL;
1834 LPWSTR sczMspPath = NULL;
1835 LPWSTR sczPatches = NULL;
1836
1837 // If there are slipstream patch actions, build up their patch action.
1838 if (rgSlipstreamPatchActions)
1839 {
1840 for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i)
1841 {
1842 BURN_PACKAGE* pMspPackage = pPackage->Msi.rgpSlipstreamMspPackages[i];
1843 AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches.");
1844
1845 BOOTSTRAPPER_ACTION_STATE patchExecuteAction = rgSlipstreamPatchActions[i];
1846 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < patchExecuteAction)
1847 {
1848 hr = CacheGetCompletedPath(pMspPackage->fPerMachine, pMspPackage->sczCacheId, &sczCachedDirectory);
1849 ExitOnFailure(hr, "Failed to get cached path for MSP package: %ls", pMspPackage->sczId);
1850
1851 hr = PathConcat(sczCachedDirectory, pMspPackage->rgPayloads[0].pPayload->sczFilePath, &sczMspPath);
1852 ExitOnFailure(hr, "Failed to build MSP path.");
1853
1854 if (!sczPatches)
1855 {
1856 hr = StrAllocConcat(&sczPatches, L" PATCH=\"", 0);
1857 ExitOnFailure(hr, "Failed to prefix with PATCH property.");
1858 }
1859 else
1860 {
1861 hr = StrAllocConcat(&sczPatches, L";", 0);
1862 ExitOnFailure(hr, "Failed to semi-colon delimit patches.");
1863 }
1864
1865 hr = StrAllocConcat(&sczPatches, sczMspPath, 0);
1866 ExitOnFailure(hr, "Failed to append patch path.");
1867 }
1868 }
1869
1870 if (sczPatches)
1871 {
1872 hr = StrAllocConcat(&sczPatches, L"\"", 0);
1873 ExitOnFailure(hr, "Failed to close the quoted PATCH property.");
1874
1875 hr = StrAllocConcatSecure(psczArguments, sczPatches, 0);
1876 ExitOnFailure(hr, "Failed to append PATCH property.");
1877 }
1878 }
1879
1880LExit:
1881 ReleaseStr(sczMspPath);
1882 ReleaseStr(sczCachedDirectory);
1883 ReleaseStr(sczPatches);
1884 return hr;
1885}
1886
1887static void RegisterSourceDirectory(
1888 __in BURN_PACKAGE* pPackage,
1889 __in_z LPCWSTR wzMsiPath
1890 )
1891{
1892 HRESULT hr = S_OK;
1893 LPWSTR sczMsiDirectory = NULL;
1894 MSIINSTALLCONTEXT dwContext = pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED;
1895
1896 hr = PathGetDirectory(wzMsiPath, &sczMsiDirectory);
1897 ExitOnFailure(hr, "Failed to get directory for path: %ls", wzMsiPath);
1898
1899 hr = WiuSourceListAddSourceEx(pPackage->Msi.sczProductCode, NULL, dwContext, MSICODE_PRODUCT, sczMsiDirectory, 1);
1900 if (FAILED(hr))
1901 {
1902 LogId(REPORT_VERBOSE, MSG_SOURCELIST_REGISTER, sczMsiDirectory, pPackage->Msi.sczProductCode, hr);
1903 ExitFunction();
1904 }
1905
1906LExit:
1907 ReleaseStr(sczMsiDirectory);
1908
1909 return;
1910}
diff --git a/src/engine/msiengine.h b/src/engine/msiengine.h
new file mode 100644
index 00000000..2a8ebfd5
--- /dev/null
+++ b/src/engine/msiengine.h
@@ -0,0 +1,73 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// function declarations
11
12HRESULT MsiEngineParsePackageFromXml(
13 __in IXMLDOMNode* pixnBundle,
14 __in BURN_PACKAGE* pPackage
15 );
16HRESULT MsiEngineParsePropertiesFromXml(
17 __in IXMLDOMNode* pixnPackage,
18 __out BURN_MSIPROPERTY** prgProperties,
19 __out DWORD* pcProperties
20 );
21void MsiEnginePackageUninitialize(
22 __in BURN_PACKAGE* pPackage
23 );
24HRESULT MsiEngineDetectPackage(
25 __in BURN_PACKAGE* pPackage,
26 __in BURN_USER_EXPERIENCE* pUserExperience
27 );
28HRESULT MsiEnginePlanCalculatePackage(
29 __in BURN_PACKAGE* pPackage,
30 __in BURN_VARIABLES* pVariables,
31 __in BURN_USER_EXPERIENCE* pUserExperience,
32 __out_opt BOOL* pfBARequestedCache
33 );
34HRESULT MsiEnginePlanAddPackage(
35 __in BOOTSTRAPPER_DISPLAY display,
36 __in BURN_PACKAGE* pPackage,
37 __in BURN_PLAN* pPlan,
38 __in BURN_LOGGING* pLog,
39 __in BURN_VARIABLES* pVariables,
40 __in_opt HANDLE hCacheEvent,
41 __in BOOL fPlanPackageCacheRollback
42 );
43HRESULT MsiEngineAddCompatiblePackage(
44 __in BURN_PACKAGES* pPackages,
45 __in const BURN_PACKAGE* pPackage,
46 __out_opt BURN_PACKAGE** ppCompatiblePackage
47 );
48HRESULT MsiEngineExecutePackage(
49 __in_opt HWND hwndParent,
50 __in BURN_EXECUTE_ACTION* pExecuteAction,
51 __in BURN_VARIABLES* pVariables,
52 __in BOOL fRollback,
53 __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
54 __in LPVOID pvContext,
55 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
56 );
57HRESULT MsiEngineConcatProperties(
58 __in_ecount(cProperties) BURN_MSIPROPERTY* rgProperties,
59 __in DWORD cProperties,
60 __in BURN_VARIABLES* pVariables,
61 __in BOOL fRollback,
62 __deref_out_z LPWSTR* psczProperties,
63 __in BOOL fObfuscateHiddenVariables
64 );
65INSTALLUILEVEL MsiEngineCalculateInstallUiLevel(
66 __in BOOL fDisplayInternalUI,
67 __in BOOTSTRAPPER_DISPLAY display,
68 __in BOOTSTRAPPER_ACTION_STATE actionState
69 );
70
71#if defined(__cplusplus)
72}
73#endif
diff --git a/src/engine/mspengine.cpp b/src/engine/mspengine.cpp
new file mode 100644
index 00000000..463799e6
--- /dev/null
+++ b/src/engine/mspengine.cpp
@@ -0,0 +1,980 @@
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#include "precomp.h"
4
5
6// constants
7
8
9// structs
10
11struct POSSIBLE_TARGETPRODUCT
12{
13 WCHAR wzProductCode[39];
14 LPWSTR pszLocalPackage;
15 MSIINSTALLCONTEXT context;
16};
17
18// internal function declarations
19
20static HRESULT GetPossibleTargetProductCodes(
21 __in BURN_PACKAGES* pPackages,
22 __deref_inout_ecount_opt(*pcPossibleTargetProductCodes) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProductCodes,
23 __inout DWORD* pcPossibleTargetProductCodes
24 );
25static HRESULT AddPossibleTargetProduct(
26 __in STRINGDICT_HANDLE sdUniquePossibleTargetProductCodes,
27 __in_z LPCWSTR wzPossibleTargetProductCode,
28 __in MSIINSTALLCONTEXT context,
29 __deref_inout_ecount_opt(*pcPossibleTargetProducts) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProducts,
30 __inout DWORD* pcPossibleTargetProducts
31 );
32static HRESULT AddDetectedTargetProduct(
33 __in BURN_PACKAGES* pPackages,
34 __in BURN_PACKAGE* pPackage,
35 __in DWORD dwOrder,
36 __in_z LPCWSTR wzProductCode,
37 __in MSIINSTALLCONTEXT context
38 );
39static void DeterminePatchChainedTarget(
40 __in BURN_PACKAGES* pPackages,
41 __in BURN_PACKAGE* pMspPackage,
42 __in LPCWSTR wzTargetProductCode,
43 __out BURN_PACKAGE** ppChainedTargetPackage,
44 __out BOOL* pfSlipstreamed
45 );
46static HRESULT PlanTargetProduct(
47 __in BOOTSTRAPPER_DISPLAY display,
48 __in BOOL fRollback,
49 __in BURN_PLAN* pPlan,
50 __in BURN_LOGGING* pLog,
51 __in BURN_VARIABLES* pVariables,
52 __in BOOTSTRAPPER_ACTION_STATE actionState,
53 __in BURN_PACKAGE* pPackage,
54 __in BURN_MSPTARGETPRODUCT* pTargetProduct,
55 __in_opt HANDLE hCacheEvent
56 );
57
58
59// function definitions
60
61extern "C" HRESULT MspEngineParsePackageFromXml(
62 __in IXMLDOMNode* pixnMspPackage,
63 __in BURN_PACKAGE* pPackage
64 )
65{
66 HRESULT hr = S_OK;
67
68 // @PatchCode
69 hr = XmlGetAttributeEx(pixnMspPackage, L"PatchCode", &pPackage->Msp.sczPatchCode);
70 ExitOnFailure(hr, "Failed to get @PatchCode.");
71
72 // @PatchXml
73 hr = XmlGetAttributeEx(pixnMspPackage, L"PatchXml", &pPackage->Msp.sczApplicabilityXml);
74 ExitOnFailure(hr, "Failed to get @PatchXml.");
75
76 // @DisplayInternalUI
77 hr = XmlGetYesNoAttribute(pixnMspPackage, L"DisplayInternalUI", &pPackage->Msp.fDisplayInternalUI);
78 ExitOnFailure(hr, "Failed to get @DisplayInternalUI.");
79
80 // Read properties.
81 hr = MsiEngineParsePropertiesFromXml(pixnMspPackage, &pPackage->Msp.rgProperties, &pPackage->Msp.cProperties);
82 ExitOnFailure(hr, "Failed to parse properties from XML.");
83
84LExit:
85
86 return hr;
87}
88
89extern "C" void MspEnginePackageUninitialize(
90 __in BURN_PACKAGE* pPackage
91 )
92{
93 ReleaseStr(pPackage->Msp.sczPatchCode);
94 ReleaseStr(pPackage->Msp.sczApplicabilityXml);
95
96 // free properties
97 if (pPackage->Msp.rgProperties)
98 {
99 for (DWORD i = 0; i < pPackage->Msp.cProperties; ++i)
100 {
101 BURN_MSIPROPERTY* pProperty = &pPackage->Msp.rgProperties[i];
102
103 ReleaseStr(pProperty->sczId);
104 ReleaseStr(pProperty->sczValue);
105 ReleaseStr(pProperty->sczRollbackValue);
106 }
107 MemFree(pPackage->Msp.rgProperties);
108 }
109
110 // free target products
111 ReleaseMem(pPackage->Msp.rgTargetProducts);
112
113 // clear struct
114 memset(&pPackage->Msp, 0, sizeof(pPackage->Msp));
115}
116
117extern "C" HRESULT MspEngineDetectInitialize(
118 __in BURN_PACKAGES* pPackages
119 )
120{
121 AssertSz(pPackages->cPatchInfo, "MspEngineDetectInitialize() should only be called if there are MSP packages.");
122
123 HRESULT hr = S_OK;
124 POSSIBLE_TARGETPRODUCT* rgPossibleTargetProducts = NULL;
125 DWORD cPossibleTargetProducts = 0;
126
127#ifdef DEBUG
128 // All patch info should be initialized to zero.
129 for (DWORD i = 0; i < pPackages->cPatchInfo; ++i)
130 {
131 BURN_PACKAGE* pPackage = pPackages->rgPatchInfoToPackage[i];
132 Assert(!pPackage->Msp.cTargetProductCodes);
133 Assert(!pPackage->Msp.rgTargetProducts);
134 }
135#endif
136
137 // Figure out which product codes to target on the machine. In the worst case all products on the machine
138 // will be returned.
139 hr = GetPossibleTargetProductCodes(pPackages, &rgPossibleTargetProducts, &cPossibleTargetProducts);
140 ExitOnFailure(hr, "Failed to get possible target product codes.");
141
142 // Loop through possible target products, testing the collective patch applicability against each product in
143 // the appropriate context. Store the result with the appropriate patch package.
144 for (DWORD iSearch = 0; iSearch < cPossibleTargetProducts; ++iSearch)
145 {
146 const POSSIBLE_TARGETPRODUCT* pPossibleTargetProduct = rgPossibleTargetProducts + iSearch;
147
148 LogId(REPORT_STANDARD, MSG_DETECT_CALCULATE_PATCH_APPLICABILITY, pPossibleTargetProduct->wzProductCode, LoggingMsiInstallContext(pPossibleTargetProduct->context));
149
150 if (pPossibleTargetProduct->pszLocalPackage)
151 {
152 // Ignores current machine state to determine just patch applicability.
153 // Superseded and obsolesced patches will be planned separately.
154 hr = WiuDetermineApplicablePatches(pPossibleTargetProduct->pszLocalPackage, pPackages->rgPatchInfo, pPackages->cPatchInfo);
155 }
156 else
157 {
158 hr = WiuDeterminePatchSequence(pPossibleTargetProduct->wzProductCode, NULL, pPossibleTargetProduct->context, pPackages->rgPatchInfo, pPackages->cPatchInfo);
159 }
160
161 if (SUCCEEDED(hr))
162 {
163 for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo)
164 {
165 if (ERROR_SUCCESS == pPackages->rgPatchInfo[iPatchInfo].uStatus)
166 {
167 BURN_PACKAGE* pMspPackage = pPackages->rgPatchInfoToPackage[iPatchInfo];
168 Assert(BURN_PACKAGE_TYPE_MSP == pMspPackage->type);
169
170 // Note that we do add superseded and obsolete MSP packages. Package Detect and Plan will sort them out later.
171 hr = AddDetectedTargetProduct(pPackages, pMspPackage, pPackages->rgPatchInfo[iPatchInfo].dwOrder, pPossibleTargetProduct->wzProductCode, pPossibleTargetProduct->context);
172 ExitOnFailure(hr, "Failed to add target product code to package: %ls", pMspPackage->sczId);
173 }
174 // TODO: should we log something for this error case?
175 }
176 }
177 else
178 {
179 LogId(REPORT_STANDARD, MSG_DETECT_FAILED_CALCULATE_PATCH_APPLICABILITY, pPossibleTargetProduct->wzProductCode, LoggingMsiInstallContext(pPossibleTargetProduct->context), hr);
180 }
181
182 hr = S_OK; // always reset so we test all possible target products.
183 }
184
185LExit:
186 if (rgPossibleTargetProducts)
187 {
188 for (DWORD i = 0; i < cPossibleTargetProducts; ++i)
189 {
190 ReleaseStr(rgPossibleTargetProducts[i].pszLocalPackage);
191 }
192 MemFree(rgPossibleTargetProducts);
193 }
194
195 return hr;
196}
197
198extern "C" HRESULT MspEngineDetectPackage(
199 __in BURN_PACKAGE* pPackage,
200 __in BURN_USER_EXPERIENCE* pUserExperience
201 )
202{
203 HRESULT hr = S_OK;
204 LPWSTR sczState = NULL;
205
206 if (0 == pPackage->Msp.cTargetProductCodes)
207 {
208 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
209 }
210 else
211 {
212 // Start the package state at the the highest state then loop through all the
213 // target product codes and end up setting the current state to the lowest
214 // package state applied to the the target product codes.
215 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED;
216
217 for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i)
218 {
219 BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i;
220
221 hr = WiuGetPatchInfoEx(pPackage->Msp.sczPatchCode, pTargetProduct->wzTargetProductCode, NULL, pTargetProduct->context, INSTALLPROPERTY_PATCHSTATE, &sczState);
222 if (SUCCEEDED(hr))
223 {
224 switch (*sczState)
225 {
226 case '1':
227 pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT;
228 break;
229
230 case '2':
231 pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED;
232 break;
233
234 case '4':
235 pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE;
236 break;
237
238 default:
239 pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
240 break;
241 }
242 }
243 else if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PATCH) == hr)
244 {
245 pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
246 hr = S_OK;
247 }
248 ExitOnFailure(hr, "Failed to get patch information for patch code: %ls, target product code: %ls", pPackage->Msp.sczPatchCode, pTargetProduct->wzTargetProductCode);
249
250 if (pPackage->currentState > pTargetProduct->patchPackageState)
251 {
252 pPackage->currentState = pTargetProduct->patchPackageState;
253 }
254
255 hr = UserExperienceOnDetectTargetMsiPackage(pUserExperience, pPackage->sczId, pTargetProduct->wzTargetProductCode, pTargetProduct->patchPackageState);
256 ExitOnRootFailure(hr, "BA aborted detect target MSI package.");
257 }
258 }
259
260LExit:
261 ReleaseStr(sczState);
262
263 return hr;
264}
265
266//
267// PlanCalculate - calculates the execute and rollback state for the requested package state.
268//
269extern "C" HRESULT MspEnginePlanCalculatePackage(
270 __in BURN_PACKAGE* pPackage,
271 __in BURN_USER_EXPERIENCE* pUserExperience,
272 __out BOOL* pfBARequestedCache
273 )
274{
275 HRESULT hr = S_OK;
276 BOOL fBARequestedCache = FALSE;
277
278 for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i)
279 {
280 BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i;
281
282 BOOTSTRAPPER_REQUEST_STATE requested = pPackage->requested;
283 BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE;
284 BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
285
286 hr = UserExperienceOnPlanTargetMsiPackage(pUserExperience, pPackage->sczId, pTargetProduct->wzTargetProductCode, &requested);
287 ExitOnRootFailure(hr, "BA aborted plan target MSI package.");
288
289 // Calculate the execute action.
290 switch (pTargetProduct->patchPackageState)
291 {
292 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
293 switch (requested)
294 {
295 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
296 execute = BOOTSTRAPPER_ACTION_STATE_REPAIR;
297 break;
298
299 case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough;
300 case BOOTSTRAPPER_REQUEST_STATE_CACHE:
301 execute = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
302 break;
303
304 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT:
305 execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
306 break;
307
308 default:
309 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
310 break;
311 }
312 break;
313
314 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
315 switch (requested)
316 {
317 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
318 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
319 execute = BOOTSTRAPPER_ACTION_STATE_INSTALL;
320 break;
321
322 case BOOTSTRAPPER_REQUEST_STATE_CACHE:
323 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
324 fBARequestedCache = TRUE;
325 break;
326
327 default:
328 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
329 break;
330 }
331 break;
332 }
333
334 // Calculate the rollback action if there is an execute action.
335 if (BOOTSTRAPPER_ACTION_STATE_NONE != execute)
336 {
337 switch (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN != pPackage->expected ? pPackage->expected : pPackage->currentState)
338 {
339 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
340 switch (requested)
341 {
342 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough;
343 case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
344 rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL;
345 break;
346
347 default:
348 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
349 break;
350 }
351 break;
352
353 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: __fallthrough;
354 case BOOTSTRAPPER_PACKAGE_STATE_CACHED:
355 switch (requested)
356 {
357 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
358 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
359 rollback = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
360 break;
361
362 default:
363 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
364 break;
365 }
366 break;
367
368 default:
369 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
370 break;
371 }
372 }
373
374 pTargetProduct->execute = execute;
375 pTargetProduct->rollback = rollback;
376
377 // The highest aggregate action state found will be returned.
378 if (pPackage->execute < execute)
379 {
380 pPackage->execute = execute;
381 }
382
383 if (pPackage->rollback < rollback)
384 {
385 pPackage->rollback = rollback;
386 }
387 }
388
389 if (pfBARequestedCache)
390 {
391 *pfBARequestedCache = fBARequestedCache;
392 }
393
394LExit:
395
396 return hr;
397}
398
399//
400// PlanAdd - adds the calculated execute and rollback actions for the package.
401//
402extern "C" HRESULT MspEnginePlanAddPackage(
403 __in BOOTSTRAPPER_DISPLAY display,
404 __in BURN_PACKAGE* pPackage,
405 __in BURN_PLAN* pPlan,
406 __in BURN_LOGGING* pLog,
407 __in BURN_VARIABLES* pVariables,
408 __in_opt HANDLE hCacheEvent,
409 __in BOOL fPlanPackageCacheRollback
410 )
411{
412 HRESULT hr = S_OK;
413
414 // TODO: need to handle the case where this patch adds itself to an earlier patch's list of target products. That would
415 // essentially bump this patch earlier in the plan and we need to make sure this patch is downloaded.
416 // add wait for cache
417 if (hCacheEvent)
418 {
419 hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent, fPlanPackageCacheRollback);
420 ExitOnFailure(hr, "Failed to plan package cache syncpoint");
421 }
422
423 hr = DependencyPlanPackage(NULL, pPackage, pPlan);
424 ExitOnFailure(hr, "Failed to plan package dependency actions.");
425
426 // Plan the actions for each target product code.
427 for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i)
428 {
429 BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i;
430
431 // If the dependency manager changed the action state for the patch, change the target product actions.
432 if (pPackage->fDependencyManagerWasHere)
433 {
434 pTargetProduct->execute = pPackage->execute;
435 pTargetProduct->rollback = pPackage->rollback;
436 }
437
438 if (BOOTSTRAPPER_ACTION_STATE_NONE != pTargetProduct->execute)
439 {
440 hr = PlanTargetProduct(display, FALSE, pPlan, pLog, pVariables, pTargetProduct->execute, pPackage, pTargetProduct, hCacheEvent);
441 ExitOnFailure(hr, "Failed to plan target product.");
442 }
443
444 if (BOOTSTRAPPER_ACTION_STATE_NONE != pTargetProduct->rollback)
445 {
446 hr = PlanTargetProduct(display, TRUE, pPlan, pLog, pVariables, pTargetProduct->rollback, pPackage, pTargetProduct, hCacheEvent);
447 ExitOnFailure(hr, "Failed to plan rollack target product.");
448 }
449 }
450
451LExit:
452
453 return hr;
454}
455
456extern "C" HRESULT MspEngineExecutePackage(
457 __in_opt HWND hwndParent,
458 __in BURN_EXECUTE_ACTION* pExecuteAction,
459 __in BURN_VARIABLES* pVariables,
460 __in BOOL fRollback,
461 __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
462 __in LPVOID pvContext,
463 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
464 )
465{
466 HRESULT hr = S_OK;
467 INSTALLUILEVEL uiLevel = pExecuteAction->mspTarget.pPackage->Msp.fDisplayInternalUI ? INSTALLUILEVEL_DEFAULT : static_cast<INSTALLUILEVEL>(INSTALLUILEVEL_NONE | INSTALLUILEVEL_SOURCERESONLY);
468 WIU_MSI_EXECUTE_CONTEXT context = { };
469 WIU_RESTART restart = WIU_RESTART_NONE;
470
471 LPWSTR sczCachedDirectory = NULL;
472 LPWSTR sczMspPath = NULL;
473 LPWSTR sczPatches = NULL;
474 LPWSTR sczProperties = NULL;
475 LPWSTR sczObfuscatedProperties = NULL;
476
477 // default to "verbose" logging
478 DWORD dwLogMode = WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE;
479
480 // get cached MSP paths
481 for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i)
482 {
483 LPCWSTR wzAppend = NULL;
484 BURN_PACKAGE* pMspPackage = pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage;
485 AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Invalid package type added to ordered patches.");
486
487 if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->mspTarget.action)
488 {
489 hr = CacheGetCompletedPath(pMspPackage->fPerMachine, pMspPackage->sczCacheId, &sczCachedDirectory);
490 ExitOnFailure(hr, "Failed to get cached path for MSP package: %ls", pMspPackage->sczId);
491
492 // TODO: Figure out if this makes sense -- the variable is set to the last patch's path only
493 // Best effort to set the execute package cache folder variable.
494 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE);
495
496 hr = PathConcat(sczCachedDirectory, pMspPackage->rgPayloads[0].pPayload->sczFilePath, &sczMspPath);
497 ExitOnFailure(hr, "Failed to build MSP path.");
498
499 wzAppend = sczMspPath;
500 }
501 else // uninstall
502 {
503 wzAppend = pMspPackage->Msp.sczPatchCode;
504 }
505
506 if (NULL != sczPatches)
507 {
508 hr = StrAllocConcat(&sczPatches, L";", 0);
509 ExitOnFailure(hr, "Failed to semi-colon delimit patches.");
510 }
511
512 hr = StrAllocConcat(&sczPatches, wzAppend, 0);
513 ExitOnFailure(hr, "Failed to append patch.");
514 }
515
516 // Best effort to set the execute package action variable.
517 VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->mspTarget.action, TRUE);
518
519 // Wire up the external UI handler and logging.
520 hr = WiuInitializeExternalUI(pfnMessageHandler, uiLevel, hwndParent, pvContext, fRollback, &context);
521 ExitOnFailure(hr, "Failed to initialize external UI handler.");
522
523 //if (BURN_LOGGING_LEVEL_DEBUG == logLevel)
524 //{
525 // dwLogMode | INSTALLLOGMODE_EXTRADEBUG;
526 //}
527
528 if (pExecuteAction->mspTarget.sczLogPath && *pExecuteAction->mspTarget.sczLogPath)
529 {
530 hr = WiuEnableLog(dwLogMode, pExecuteAction->mspTarget.sczLogPath, 0);
531 ExitOnFailure(hr, "Failed to enable logging for package: %ls to: %ls", pExecuteAction->mspTarget.pPackage->sczId, pExecuteAction->mspTarget.sczLogPath);
532 }
533
534 // set up properties
535 hr = MsiEngineConcatProperties(pExecuteAction->mspTarget.pPackage->Msp.rgProperties, pExecuteAction->mspTarget.pPackage->Msp.cProperties, pVariables, fRollback, &sczProperties, FALSE);
536 ExitOnFailure(hr, "Failed to add properties to argument string.");
537
538 hr = MsiEngineConcatProperties(pExecuteAction->mspTarget.pPackage->Msp.rgProperties, pExecuteAction->mspTarget.pPackage->Msp.cProperties, pVariables, fRollback, &sczObfuscatedProperties, TRUE);
539 ExitOnFailure(hr, "Failed to add properties to obfuscated argument string.");
540
541 LogId(REPORT_STANDARD, MSG_APPLYING_PATCH_PACKAGE, pExecuteAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pExecuteAction->mspTarget.action), sczPatches, sczObfuscatedProperties, pExecuteAction->mspTarget.sczTargetProductCode);
542
543 //
544 // Do the actual action.
545 //
546 switch (pExecuteAction->mspTarget.action)
547 {
548 case BOOTSTRAPPER_ACTION_STATE_INSTALL: __fallthrough;
549 case BOOTSTRAPPER_ACTION_STATE_REPAIR:
550 hr = StrAllocConcatSecure(&sczProperties, L" PATCH=\"", 0);
551 ExitOnFailure(hr, "Failed to add PATCH property on install.");
552
553 hr = StrAllocConcatSecure(&sczProperties, sczPatches, 0);
554 ExitOnFailure(hr, "Failed to add patches to PATCH property on install.");
555
556 hr = StrAllocConcatSecure(&sczProperties, L"\" REBOOT=ReallySuppress", 0);
557 ExitOnFailure(hr, "Failed to add reboot suppression property on install.");
558
559 hr = WiuConfigureProductEx(pExecuteAction->mspTarget.sczTargetProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_DEFAULT, sczProperties, &restart);
560 ExitOnFailure(hr, "Failed to install MSP package.");
561 break;
562
563 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
564 hr = StrAllocConcatSecure(&sczProperties, L" REBOOT=ReallySuppress", 0);
565 ExitOnFailure(hr, "Failed to add reboot suppression property on uninstall.");
566
567 // Ignore all dependencies, since the Burn engine already performed the check.
568 hr = StrAllocFormattedSecure(&sczProperties, L"%ls %ls=ALL", sczProperties, DEPENDENCY_IGNOREDEPENDENCIES);
569 ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties.");
570
571 hr = WiuRemovePatches(sczPatches, pExecuteAction->mspTarget.sczTargetProductCode, sczProperties, &restart);
572 ExitOnFailure(hr, "Failed to uninstall MSP package.");
573 break;
574 }
575
576LExit:
577 WiuUninitializeExternalUI(&context);
578
579 ReleaseStr(sczCachedDirectory);
580 ReleaseStr(sczMspPath);
581 StrSecureZeroFreeString(sczProperties);
582 ReleaseStr(sczObfuscatedProperties);
583 ReleaseStr(sczPatches);
584
585 switch (restart)
586 {
587 case WIU_RESTART_NONE:
588 *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
589 break;
590
591 case WIU_RESTART_REQUIRED:
592 *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
593 break;
594
595 case WIU_RESTART_INITIATED:
596 *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
597 break;
598 }
599
600 // Best effort to clear the execute package cache folder and action variables.
601 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE);
602 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE);
603
604 return hr;
605}
606
607extern "C" void MspEngineSlipstreamUpdateState(
608 __in BURN_PACKAGE* pPackage,
609 __in BOOTSTRAPPER_ACTION_STATE execute,
610 __in BOOTSTRAPPER_ACTION_STATE rollback
611 )
612{
613 Assert(BURN_PACKAGE_TYPE_MSP == pPackage->type);
614
615 // If the dependency manager set our state then that means something else
616 // is dependent on our package. That trumps whatever the slipstream update
617 // state might set.
618 if (!pPackage->fDependencyManagerWasHere)
619 {
620 // The highest aggregate action state found will be returned.
621 if (pPackage->execute < execute)
622 {
623 pPackage->execute = execute;
624 }
625
626 if (pPackage->rollback < rollback)
627 {
628 pPackage->rollback = rollback;
629 }
630 }
631}
632
633
634// internal helper functions
635
636static HRESULT GetPossibleTargetProductCodes(
637 __in BURN_PACKAGES* pPackages,
638 __deref_inout_ecount_opt(*pcPossibleTargetProducts) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProducts,
639 __inout DWORD* pcPossibleTargetProducts
640 )
641{
642 HRESULT hr = S_OK;
643 STRINGDICT_HANDLE sdUniquePossibleTargetProductCodes = NULL;
644 BOOL fCheckAll = FALSE;
645 WCHAR wzPossibleTargetProductCode[MAX_GUID_CHARS + 1];
646
647 // Use a dictionary to ensure we capture unique product codes. Otherwise, we could end up
648 // doing patch applicability for the same product code multiple times and that would confuse
649 // everything down stream.
650 hr = DictCreateStringList(&sdUniquePossibleTargetProductCodes, 5, DICT_FLAG_NONE);
651 ExitOnFailure(hr, "Failed to create unique target product codes.");
652
653 // If the patches target a specific set of product/upgrade codes, search only those. This
654 // should be much faster than searching all packages on the machine.
655 if (pPackages->rgPatchTargetCodes)
656 {
657 for (DWORD i = 0; i < pPackages->cPatchTargetCodes; ++i)
658 {
659 BURN_PATCH_TARGETCODE* pTargetCode = pPackages->rgPatchTargetCodes + i;
660
661 // If targeting a product, add the unique product code to the list.
662 if (BURN_PATCH_TARGETCODE_TYPE_PRODUCT == pTargetCode->type)
663 {
664 hr = AddPossibleTargetProduct(sdUniquePossibleTargetProductCodes, pTargetCode->sczTargetCode, MSIINSTALLCONTEXT_NONE, prgPossibleTargetProducts, pcPossibleTargetProducts);
665 ExitOnFailure(hr, "Failed to add product code to possible target product codes.");
666 }
667 else if (BURN_PATCH_TARGETCODE_TYPE_UPGRADE == pTargetCode->type)
668 {
669 // Enumerate all unique related products to the target upgrade code.
670 for (DWORD iProduct = 0; SUCCEEDED(hr); ++iProduct)
671 {
672 hr = WiuEnumRelatedProducts(pTargetCode->sczTargetCode, iProduct, wzPossibleTargetProductCode);
673 if (SUCCEEDED(hr))
674 {
675 hr = AddPossibleTargetProduct(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode, MSIINSTALLCONTEXT_NONE, prgPossibleTargetProducts, pcPossibleTargetProducts);
676 ExitOnFailure(hr, "Failed to add upgrade product code to possible target product codes.");
677 }
678 else if (E_BADCONFIGURATION == hr)
679 {
680 // Skip product's with bad configuration and continue.
681 LogId(REPORT_STANDARD, MSG_DETECT_BAD_PRODUCT_CONFIGURATION, wzPossibleTargetProductCode);
682
683 hr = S_OK;
684 }
685 }
686
687 if (E_NOMOREITEMS == hr)
688 {
689 hr = S_OK;
690 }
691 ExitOnFailure(hr, "Failed to enumerate all products to patch related to upgrade code: %ls", pTargetCode->sczTargetCode);
692 }
693 else
694 {
695 // The element does not target a specific product.
696 fCheckAll = TRUE;
697
698 break;
699 }
700 }
701 }
702 else
703 {
704 fCheckAll = TRUE;
705 }
706
707 // One or more of the patches do not target a specific product so search everything on the machine.
708 if (fCheckAll)
709 {
710 for (DWORD iProduct = 0; SUCCEEDED(hr); ++iProduct)
711 {
712 MSIINSTALLCONTEXT context = MSIINSTALLCONTEXT_NONE;
713
714 hr = WiuEnumProductsEx(NULL, NULL, MSIINSTALLCONTEXT_ALL, iProduct, wzPossibleTargetProductCode, &context, NULL, NULL);
715 if (SUCCEEDED(hr))
716 {
717 hr = AddPossibleTargetProduct(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode, context, prgPossibleTargetProducts, pcPossibleTargetProducts);
718 ExitOnFailure(hr, "Failed to add product code to search product codes.");
719 }
720 else if (E_BADCONFIGURATION == hr)
721 {
722 // Skip products with bad configuration and continue.
723 LogId(REPORT_STANDARD, MSG_DETECT_BAD_PRODUCT_CONFIGURATION, wzPossibleTargetProductCode);
724
725 hr = S_OK;
726 }
727 }
728
729 if (E_NOMOREITEMS == hr)
730 {
731 hr = S_OK;
732 }
733 ExitOnFailure(hr, "Failed to enumerate all products on the machine for patches applicability.");
734 }
735
736LExit:
737 ReleaseDict(sdUniquePossibleTargetProductCodes);
738
739 return hr;
740}
741
742static HRESULT AddPossibleTargetProduct(
743 __in STRINGDICT_HANDLE sdUniquePossibleTargetProductCodes,
744 __in_z LPCWSTR wzPossibleTargetProductCode,
745 __in MSIINSTALLCONTEXT context,
746 __deref_inout_ecount_opt(*pcPossibleTargetProducts) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProducts,
747 __inout DWORD* pcPossibleTargetProducts
748 )
749{
750 HRESULT hr = S_OK;
751 LPWSTR pszLocalPackage = NULL;
752
753 // Only add this possible target code if we haven't queried for it already.
754 if (E_NOTFOUND == DictKeyExists(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode))
755 {
756 // If the install context is not known, ask the Windows Installer for it. If we can't get the context
757 // then bail.
758 if (MSIINSTALLCONTEXT_NONE == context)
759 {
760 hr = WiuEnumProductsEx(wzPossibleTargetProductCode, NULL, MSIINSTALLCONTEXT_ALL, 0, NULL, &context, NULL, NULL);
761 if (FAILED(hr))
762 {
763 ExitFunction1(hr = S_OK);
764 }
765 }
766
767 hr = DictAddKey(sdUniquePossibleTargetProductCodes, wzPossibleTargetProductCode);
768 ExitOnFailure(hr, "Failed to add possible target code to unique product codes.");
769
770 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgPossibleTargetProducts), *pcPossibleTargetProducts + 1, sizeof(POSSIBLE_TARGETPRODUCT), 3);
771 ExitOnFailure(hr, "Failed to grow array of possible target products.");
772
773 POSSIBLE_TARGETPRODUCT *const pPossibleTargetProduct = *prgPossibleTargetProducts + *pcPossibleTargetProducts;
774
775 hr = ::StringCchCopyW(pPossibleTargetProduct->wzProductCode, countof(pPossibleTargetProduct->wzProductCode), wzPossibleTargetProductCode);
776 ExitOnFailure(hr, "Failed to copy possible target product code.");
777
778 // Attempt to get the local package path so we can more quickly determine patch applicability later.
779 hr = WiuGetProductInfoEx(wzPossibleTargetProductCode, NULL, context, INSTALLPROPERTY_LOCALPACKAGE, &pszLocalPackage);
780 if (SUCCEEDED(hr))
781 {
782 pPossibleTargetProduct->pszLocalPackage = pszLocalPackage;
783 pszLocalPackage = NULL;
784 }
785 else
786 {
787 // Will instead call MsiDeterminePatchSequence later.
788 hr = S_OK;
789 }
790
791 pPossibleTargetProduct->context = context;
792
793 ++(*pcPossibleTargetProducts);
794 }
795
796LExit:
797 ReleaseStr(pszLocalPackage);
798
799 return hr;
800}
801
802static HRESULT AddDetectedTargetProduct(
803 __in BURN_PACKAGES* pPackages,
804 __in BURN_PACKAGE* pPackage,
805 __in DWORD dwOrder,
806 __in_z LPCWSTR wzProductCode,
807 __in MSIINSTALLCONTEXT context
808 )
809{
810 HRESULT hr = S_OK;
811
812 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPackage->Msp.rgTargetProducts), pPackage->Msp.cTargetProductCodes + 1, sizeof(BURN_MSPTARGETPRODUCT), 5);
813 ExitOnFailure(hr, "Failed to ensure enough target product codes were allocated.");
814
815 hr = ::StringCchCopyW(pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].wzTargetProductCode, countof(pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].wzTargetProductCode), wzProductCode);
816 ExitOnFailure(hr, "Failed to copy target product code.");
817
818 DeterminePatchChainedTarget(pPackages, pPackage, wzProductCode,
819 &pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].pChainedTargetPackage,
820 &pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].fSlipstream);
821
822 pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].context = context;
823 pPackage->Msp.rgTargetProducts[pPackage->Msp.cTargetProductCodes].dwOrder = dwOrder;
824 ++pPackage->Msp.cTargetProductCodes;
825
826LExit:
827 return hr;
828}
829
830static void DeterminePatchChainedTarget(
831 __in BURN_PACKAGES* pPackages,
832 __in BURN_PACKAGE* pMspPackage,
833 __in LPCWSTR wzTargetProductCode,
834 __out BURN_PACKAGE** ppChainedTargetPackage,
835 __out BOOL* pfSlipstreamed
836 )
837{
838 BURN_PACKAGE* pTargetMsiPackage = NULL;
839 BOOL fSlipstreamed = FALSE;
840
841 for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage)
842 {
843 BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage;
844
845 if (BURN_PACKAGE_TYPE_MSI == pPackage->type && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzTargetProductCode, -1, pPackage->Msi.sczProductCode, -1))
846 {
847 pTargetMsiPackage = pPackage;
848
849 for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j)
850 {
851 BURN_PACKAGE* pSlipstreamMsp = pPackage->Msi.rgpSlipstreamMspPackages[j];
852 if (pSlipstreamMsp == pMspPackage)
853 {
854 AssertSz(!fSlipstreamed, "An MSP should only show up as a slipstreamed patch in an MSI once.");
855 fSlipstreamed = TRUE;
856 break;
857 }
858 }
859
860 break;
861 }
862 }
863
864 *ppChainedTargetPackage = pTargetMsiPackage;
865 *pfSlipstreamed = fSlipstreamed;
866
867 return;
868}
869
870static HRESULT PlanTargetProduct(
871 __in BOOTSTRAPPER_DISPLAY display,
872 __in BOOL fRollback,
873 __in BURN_PLAN* pPlan,
874 __in BURN_LOGGING* pLog,
875 __in BURN_VARIABLES* pVariables,
876 __in BOOTSTRAPPER_ACTION_STATE actionState,
877 __in BURN_PACKAGE* pPackage,
878 __in BURN_MSPTARGETPRODUCT* pTargetProduct,
879 __in_opt HANDLE hCacheEvent
880 )
881{
882 HRESULT hr = S_OK;
883 BURN_EXECUTE_ACTION* rgActions = fRollback ? pPlan->rgRollbackActions : pPlan->rgExecuteActions;
884 DWORD cActions = fRollback ? pPlan->cRollbackActions : pPlan->cExecuteActions;
885 BURN_EXECUTE_ACTION* pAction = NULL;
886 DWORD dwInsertSequence = 0;
887
888 // Try to find another MSP action with the exact same action (install or uninstall) targeting
889 // the same product in the same machine context (per-user or per-machine).
890 for (DWORD i = 0; i < cActions; ++i)
891 {
892 pAction = rgActions + i;
893
894 if (BURN_EXECUTE_ACTION_TYPE_MSP_TARGET == pAction->type &&
895 pAction->mspTarget.action == actionState &&
896 pAction->mspTarget.fPerMachineTarget == (MSIINSTALLCONTEXT_MACHINE == pTargetProduct->context) &&
897 CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pAction->mspTarget.sczTargetProductCode, -1, pTargetProduct->wzTargetProductCode, -1))
898 {
899 dwInsertSequence = i;
900 break;
901 }
902
903 pAction = NULL;
904 }
905
906 // If we didn't find an MSP target action already updating the product, create a new action.
907 if (!pAction)
908 {
909 if (fRollback)
910 {
911 hr = PlanAppendRollbackAction(pPlan, &pAction);
912 }
913 else
914 {
915 hr = PlanAppendExecuteAction(pPlan, &pAction);
916 }
917 ExitOnFailure(hr, "Failed to plan action for target product.");
918
919 pAction->type = BURN_EXECUTE_ACTION_TYPE_MSP_TARGET;
920 pAction->mspTarget.action = actionState;
921 pAction->mspTarget.pPackage = pPackage;
922 pAction->mspTarget.fPerMachineTarget = (MSIINSTALLCONTEXT_MACHINE == pTargetProduct->context);
923 pAction->mspTarget.uiLevel = MsiEngineCalculateInstallUiLevel(pPackage->Msp.fDisplayInternalUI, display, pAction->mspTarget.action);
924 pAction->mspTarget.pChainedTargetPackage = pTargetProduct->pChainedTargetPackage;
925 pAction->mspTarget.fSlipstream = pTargetProduct->fSlipstream;
926 hr = StrAllocString(&pAction->mspTarget.sczTargetProductCode, pTargetProduct->wzTargetProductCode, 0);
927 ExitOnFailure(hr, "Failed to copy target product code.");
928
929 // If this is a per-machine target product, then the plan needs to be per-machine as well.
930 if (pAction->mspTarget.fPerMachineTarget)
931 {
932 pPlan->fPerMachine = TRUE;
933 }
934
935 LoggingSetPackageVariable(pPackage, pAction->mspTarget.sczTargetProductCode, fRollback, pLog, pVariables, &pAction->mspTarget.sczLogPath); // ignore errors.
936 }
937 else
938 {
939 if (!fRollback && hCacheEvent)
940 {
941 // Since a previouse MSP target action is being updated with the new MSP,
942 // insert a wait syncpoint to before this action since we need to cache the current MSI before using it.
943 BURN_EXECUTE_ACTION* pWaitSyncPointAction = NULL;
944 hr = PlanInsertExecuteAction(dwInsertSequence, pPlan, &pWaitSyncPointAction);
945 ExitOnFailure(hr, "Failed to insert execute action.");
946
947 pWaitSyncPointAction->type = BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT;
948 pWaitSyncPointAction->syncpoint.hEvent = hCacheEvent;
949
950 // Since we inserted an action before the MSP target action that we will be updating, need to update the pointer.
951 pAction = pPlan->rgExecuteActions + (dwInsertSequence + 1);
952 }
953 }
954
955 // Add our target product to the array and sort based on their order determined during detection.
956 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pAction->mspTarget.rgOrderedPatches), pAction->mspTarget.cOrderedPatches + 1, sizeof(BURN_ORDERED_PATCHES), 2);
957 ExitOnFailure(hr, "Failed grow array of ordered patches.");
958
959 pAction->mspTarget.rgOrderedPatches[pAction->mspTarget.cOrderedPatches].dwOrder = pTargetProduct->dwOrder;
960 pAction->mspTarget.rgOrderedPatches[pAction->mspTarget.cOrderedPatches].pPackage = pPackage;
961 ++pAction->mspTarget.cOrderedPatches;
962
963 // Insertion sort to keep the patches ordered.
964 for (DWORD i = pAction->mspTarget.cOrderedPatches - 1; i > 0; --i)
965 {
966 if (pAction->mspTarget.rgOrderedPatches[i].dwOrder < pAction->mspTarget.rgOrderedPatches[i - 1].dwOrder)
967 {
968 BURN_ORDERED_PATCHES temp = pAction->mspTarget.rgOrderedPatches[i - 1];
969 pAction->mspTarget.rgOrderedPatches[i - 1] = pAction->mspTarget.rgOrderedPatches[i];
970 pAction->mspTarget.rgOrderedPatches[i] = temp;
971 }
972 else // no swap necessary, we're done.
973 {
974 break;
975 }
976 }
977
978LExit:
979 return hr;
980}
diff --git a/src/engine/mspengine.h b/src/engine/mspengine.h
new file mode 100644
index 00000000..9f585720
--- /dev/null
+++ b/src/engine/mspengine.h
@@ -0,0 +1,67 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// constants
11
12
13// structures
14
15
16// typedefs
17
18
19// function declarations
20
21HRESULT MspEngineParsePackageFromXml(
22 __in IXMLDOMNode* pixnBundle,
23 __in BURN_PACKAGE* pPackage
24 );
25void MspEnginePackageUninitialize(
26 __in BURN_PACKAGE* pPackage
27 );
28HRESULT MspEngineDetectInitialize(
29 __in BURN_PACKAGES* pPackages
30 );
31HRESULT MspEngineDetectPackage(
32 __in BURN_PACKAGE* pPackage,
33 __in BURN_USER_EXPERIENCE* pUserExperience
34 );
35HRESULT MspEnginePlanCalculatePackage(
36 __in BURN_PACKAGE* pPackage,
37 __in BURN_USER_EXPERIENCE* pUserExperience,
38 __out_opt BOOL* pfBARequestedCache
39 );
40HRESULT MspEnginePlanAddPackage(
41 __in BOOTSTRAPPER_DISPLAY display,
42 __in BURN_PACKAGE* pPackage,
43 __in BURN_PLAN* pPlan,
44 __in BURN_LOGGING* pLog,
45 __in BURN_VARIABLES* pVariables,
46 __in_opt HANDLE hCacheEvent,
47 __in BOOL fPlanPackageCacheRollback
48 );
49HRESULT MspEngineExecutePackage(
50 __in_opt HWND hwndParent,
51 __in BURN_EXECUTE_ACTION* pExecuteAction,
52 __in BURN_VARIABLES* pVariables,
53 __in BOOL fRollback,
54 __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
55 __in LPVOID pvContext,
56 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
57 );
58void MspEngineSlipstreamUpdateState(
59 __in BURN_PACKAGE* pMspPackage,
60 __in BOOTSTRAPPER_ACTION_STATE execute,
61 __in BOOTSTRAPPER_ACTION_STATE rollback
62 );
63
64
65#if defined(__cplusplus)
66}
67#endif
diff --git a/src/engine/msuengine.cpp b/src/engine/msuengine.cpp
new file mode 100644
index 00000000..3818e932
--- /dev/null
+++ b/src/engine/msuengine.cpp
@@ -0,0 +1,513 @@
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#include "precomp.h"
4
5
6// constants
7
8#define WU_S_REBOOT_REQUIRED 0x00240005L
9#define WU_S_ALREADY_INSTALLED 0x00240006L
10
11
12// function definitions
13static HRESULT EnsureWUServiceEnabled(
14 __in BOOL fStopWusaService,
15 __out SC_HANDLE* pschWu,
16 __out BOOL* pfPreviouslyDisabled
17 );
18static HRESULT SetServiceStartType(
19 __in SC_HANDLE sch,
20 __in DWORD stratType
21 );
22static HRESULT StopWUService(
23 __in SC_HANDLE schWu
24 );
25
26
27extern "C" HRESULT MsuEngineParsePackageFromXml(
28 __in IXMLDOMNode* pixnMsuPackage,
29 __in BURN_PACKAGE* pPackage
30 )
31{
32 HRESULT hr = S_OK;
33
34 // @KB
35 hr = XmlGetAttributeEx(pixnMsuPackage, L"KB", &pPackage->Msu.sczKB);
36 ExitOnFailure(hr, "Failed to get @KB.");
37
38 // @DetectCondition
39 hr = XmlGetAttributeEx(pixnMsuPackage, L"DetectCondition", &pPackage->Msu.sczDetectCondition);
40 ExitOnFailure(hr, "Failed to get @DetectCondition.");
41
42LExit:
43 return hr;
44}
45
46extern "C" void MsuEnginePackageUninitialize(
47 __in BURN_PACKAGE* pPackage
48 )
49{
50 ReleaseNullStr(pPackage->Msu.sczKB);
51 ReleaseNullStr(pPackage->Msu.sczDetectCondition);
52}
53
54extern "C" HRESULT MsuEngineDetectPackage(
55 __in BURN_PACKAGE* pPackage,
56 __in BURN_VARIABLES* pVariables
57 )
58{
59 HRESULT hr = S_OK;
60 BOOL fDetected = FALSE;
61
62 // evaluate detect condition
63 if (pPackage->Msu.sczDetectCondition && *pPackage->Msu.sczDetectCondition)
64 {
65 hr = ConditionEvaluate(pVariables, pPackage->Msu.sczDetectCondition, &fDetected);
66 ExitOnFailure(hr, "Failed to evaluate MSU package detect condition.");
67 }
68
69 // update detect state
70 pPackage->currentState = fDetected ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
71
72LExit:
73 return hr;
74}
75
76//
77// PlanCalculate - calculates the execute and rollback state for the requested package state.
78//
79extern "C" HRESULT MsuEnginePlanCalculatePackage(
80 __in BURN_PACKAGE* pPackage,
81 __out_opt BOOL* pfBARequestedCache
82 )
83{
84 HRESULT hr = S_OK;
85 BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE;
86 BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
87 BOOL fBARequestedCache = FALSE;
88
89 BOOL fAllowUninstall = FALSE;
90 OS_VERSION osVersion = OS_VERSION_UNKNOWN;
91 DWORD dwServicePack = 0;
92
93 // We can only uninstall MSU packages if they have a KB and we are on Win7 or newer.
94 OsGetVersion(&osVersion, &dwServicePack);
95 fAllowUninstall = (pPackage->Msu.sczKB && *pPackage->Msu.sczKB) && OS_VERSION_WIN7 <= osVersion;
96
97 // execute action
98 switch (pPackage->currentState)
99 {
100 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
101 switch (pPackage->requested)
102 {
103 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
104 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
105 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
106 break;
107
108 case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough;
109 case BOOTSTRAPPER_REQUEST_STATE_CACHE:
110 execute = fAllowUninstall && pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
111 break;
112
113 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT:
114 execute = fAllowUninstall ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
115 break;
116
117 default:
118 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
119 break;
120 }
121 break;
122
123 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
124 switch (pPackage->requested)
125 {
126 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
127 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
128 execute = BOOTSTRAPPER_ACTION_STATE_INSTALL;
129 break;
130
131 case BOOTSTRAPPER_REQUEST_STATE_CACHE:
132 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
133 fBARequestedCache = TRUE;
134
135 default:
136 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
137 break;
138 }
139 break;
140
141 default:
142 hr = E_INVALIDARG;
143 ExitOnRootFailure(hr, "Invalid package state.");
144 }
145
146 // Calculate the rollback action if there is an execute action.
147 if (BOOTSTRAPPER_ACTION_STATE_NONE != execute)
148 {
149 switch (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN != pPackage->expected ? pPackage->expected : pPackage->currentState)
150 {
151 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
152 switch (pPackage->requested)
153 {
154 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough;
155 case BOOTSTRAPPER_REQUEST_STATE_ABSENT:
156 rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL;
157 break;
158
159 default:
160 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
161 break;
162 }
163 break;
164
165 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
166 switch (pPackage->requested)
167 {
168 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
169 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
170 rollback = fAllowUninstall ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE;
171 break;
172
173 default:
174 rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
175 break;
176 }
177 break;
178
179 default:
180 hr = E_INVALIDARG;
181 ExitOnRootFailure(hr, "Invalid package expected state.");
182 }
183 }
184
185 // return values
186 pPackage->execute = execute;
187 pPackage->rollback = rollback;
188
189 if (pfBARequestedCache)
190 {
191 *pfBARequestedCache = fBARequestedCache;
192 }
193
194LExit:
195 return hr;
196}
197
198//
199// PlanAdd - adds the calculated execute and rollback actions for the package.
200//
201extern "C" HRESULT MsuEnginePlanAddPackage(
202 __in BURN_PACKAGE* pPackage,
203 __in BURN_PLAN* pPlan,
204 __in BURN_LOGGING* pLog,
205 __in BURN_VARIABLES* pVariables,
206 __in HANDLE hCacheEvent,
207 __in BOOL fPlanPackageCacheRollback
208 )
209{
210 HRESULT hr = S_OK;
211 BURN_EXECUTE_ACTION* pAction = NULL;
212
213 // add wait for cache
214 if (hCacheEvent)
215 {
216 hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent, fPlanPackageCacheRollback);
217 ExitOnFailure(hr, "Failed to plan package cache syncpoint");
218 }
219
220 hr = DependencyPlanPackage(NULL, pPackage, pPlan);
221 ExitOnFailure(hr, "Failed to plan package dependency actions.");
222
223 // add execute action
224 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute)
225 {
226 hr = PlanAppendExecuteAction(pPlan, &pAction);
227 ExitOnFailure(hr, "Failed to append execute action.");
228
229 pAction->type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE;
230 pAction->msuPackage.pPackage = pPackage;
231 pAction->msuPackage.action = pPackage->execute;
232
233 LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, &pAction->msuPackage.sczLogPath); // ignore errors.
234 }
235
236 // add rollback action
237 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)
238 {
239 hr = PlanAppendRollbackAction(pPlan, &pAction);
240 ExitOnFailure(hr, "Failed to append rollback action.");
241
242 pAction->type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE;
243 pAction->msuPackage.pPackage = pPackage;
244 pAction->msuPackage.action = pPackage->rollback;
245
246 LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, &pAction->msuPackage.sczLogPath); // ignore errors.
247 }
248
249LExit:
250 return hr;
251}
252
253extern "C" HRESULT MsuEngineExecutePackage(
254 __in BURN_EXECUTE_ACTION* pExecuteAction,
255 __in BURN_VARIABLES* pVariables,
256 __in BOOL fRollback,
257 __in BOOL fStopWusaService,
258 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
259 __in LPVOID pvContext,
260 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
261 )
262{
263 HRESULT hr = S_OK;
264 int nResult = IDNOACTION;
265 LPWSTR sczCachedDirectory = NULL;
266 LPWSTR sczMsuPath = NULL;
267 LPWSTR sczWindowsPath = NULL;
268 LPWSTR sczSystemPath = NULL;
269 LPWSTR sczWusaPath = NULL;
270 LPWSTR sczCommand = NULL;
271 SC_HANDLE schWu = NULL;
272 BOOL fWuWasDisabled = FALSE;
273 STARTUPINFOW si = { };
274 PROCESS_INFORMATION pi = { };
275 GENERIC_EXECUTE_MESSAGE message = { };
276 DWORD dwExitCode = 0;
277 BOOL fUseSysNativePath = FALSE;
278
279#if !defined(_WIN64)
280 hr = ProcWow64(::GetCurrentProcess(), &fUseSysNativePath);
281 ExitOnFailure(hr, "Failed to determine WOW64 status.");
282#endif
283
284 *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
285
286 // get wusa.exe path
287 if (fUseSysNativePath)
288 {
289 hr = PathGetKnownFolder(CSIDL_WINDOWS, &sczWindowsPath);
290 ExitOnFailure(hr, "Failed to find Windows directory.");
291
292 hr = PathConcat(sczWindowsPath, L"SysNative\\", &sczSystemPath);
293 ExitOnFailure(hr, "Failed to append SysNative directory.");
294 }
295 else
296 {
297 hr = PathGetKnownFolder(CSIDL_SYSTEM, &sczSystemPath);
298 ExitOnFailure(hr, "Failed to find System32 directory.");
299 }
300
301 hr = PathConcat(sczSystemPath, L"wusa.exe", &sczWusaPath);
302 ExitOnFailure(hr, "Failed to allocate WUSA.exe path.");
303
304 // build command
305 switch (pExecuteAction->msuPackage.action)
306 {
307 case BOOTSTRAPPER_ACTION_STATE_INSTALL:
308 // get cached MSU path
309 hr = CacheGetCompletedPath(TRUE, pExecuteAction->msuPackage.pPackage->sczCacheId, &sczCachedDirectory);
310 ExitOnFailure(hr, "Failed to get cached path for package: %ls", pExecuteAction->msuPackage.pPackage->sczId);
311
312 // Best effort to set the execute package cache folder variable.
313 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE);
314
315 hr = PathConcat(sczCachedDirectory, pExecuteAction->msuPackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczMsuPath);
316 ExitOnFailure(hr, "Failed to build MSU path.");
317
318 // format command
319 hr = StrAllocFormatted(&sczCommand, L"\"%ls\" \"%ls\" /quiet /norestart", sczWusaPath, sczMsuPath);
320 ExitOnFailure(hr, "Failed to format MSU install command.");
321 break;
322
323 case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
324 // format command
325 hr = StrAllocFormatted(&sczCommand, L"\"%ls\" /uninstall /kb:%ls /quiet /norestart", sczWusaPath, pExecuteAction->msuPackage.pPackage->Msu.sczKB);
326 ExitOnFailure(hr, "Failed to format MSU uninstall command.");
327 break;
328
329 default:
330 hr = E_UNEXPECTED;
331 ExitOnFailure(hr, "Failed to get action arguments for MSU package.");
332 }
333
334 if (pExecuteAction->msuPackage.sczLogPath && *pExecuteAction->msuPackage.sczLogPath)
335 {
336 hr = StrAllocConcat(&sczCommand, L" /log:", 0);
337 ExitOnFailure(hr, "Failed to append log switch to MSU command-line.");
338
339 hr = StrAllocConcat(&sczCommand, pExecuteAction->msuPackage.sczLogPath, 0);
340 ExitOnFailure(hr, "Failed to append log path to MSU command-line.");
341 }
342
343 LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->msuPackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->msuPackage.action), sczMsuPath ? sczMsuPath : pExecuteAction->msuPackage.pPackage->Msu.sczKB, sczCommand);
344
345 hr = EnsureWUServiceEnabled(fStopWusaService, &schWu, &fWuWasDisabled);
346 ExitOnFailure(hr, "Failed to ensure WU service was enabled to install MSU package.");
347
348 // create process
349 si.cb = sizeof(si);
350 if (!::CreateProcessW(sczWusaPath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
351 {
352 ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", sczWusaPath);
353 }
354
355 do
356 {
357 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
358 message.dwAllowedResults = MB_OKCANCEL;
359 message.progress.dwPercentage = 50;
360 nResult = pfnGenericMessageHandler(&message, pvContext);
361 hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE);
362 ExitOnRootFailure(hr, "Bootstrapper application aborted during MSU progress.");
363
364 // wait for process to terminate
365 hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode);
366 if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr)
367 {
368 ExitOnFailure(hr, "Failed to wait for executable to complete: %ls", sczWusaPath);
369 }
370 } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr);
371
372 // get process exit code
373 if (!::GetExitCodeProcess(pi.hProcess, &dwExitCode))
374 {
375 ExitWithLastError(hr, "Failed to get process exit code.");
376 }
377
378 // We'll normalize the restart required error code from wusa.exe just in case. Most likely
379 // that on reboot we'll actually get WU_S_REBOOT_REQUIRED.
380 if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == static_cast<HRESULT>(dwExitCode))
381 {
382 dwExitCode = ERROR_SUCCESS_REBOOT_REQUIRED;
383 }
384
385 // handle exit code
386 switch (dwExitCode)
387 {
388 case S_OK: __fallthrough;
389 case S_FALSE: __fallthrough;
390 case WU_S_ALREADY_INSTALLED:
391 hr = S_OK;
392 break;
393
394 case ERROR_SUCCESS_REBOOT_REQUIRED: __fallthrough;
395 case WU_S_REBOOT_REQUIRED:
396 *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
397 hr = S_OK;
398 break;
399
400 default:
401 hr = static_cast<HRESULT>(dwExitCode);
402 break;
403 }
404
405LExit:
406 ReleaseStr(sczCachedDirectory);
407 ReleaseStr(sczMsuPath);
408 ReleaseStr(sczSystemPath);
409 ReleaseStr(sczWindowsPath);
410 ReleaseStr(sczWusaPath);
411 ReleaseStr(sczCommand);
412
413 ReleaseHandle(pi.hProcess);
414 ReleaseHandle(pi.hThread);
415
416 if (fWuWasDisabled)
417 {
418 SetServiceStartType(schWu, SERVICE_DISABLED);
419 }
420
421 // Best effort to clear the execute package cache folder variable.
422 VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE);
423
424 return hr;
425}
426
427static HRESULT EnsureWUServiceEnabled(
428 __in BOOL fStopWusaService,
429 __out SC_HANDLE* pschWu,
430 __out BOOL* pfPreviouslyDisabled
431 )
432{
433 HRESULT hr = S_OK;
434 SC_HANDLE schSCM = NULL;
435 SC_HANDLE schWu = NULL;
436 SERVICE_STATUS serviceStatus = { };
437 QUERY_SERVICE_CONFIGW* pConfig = NULL;
438
439 schSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
440 ExitOnNullWithLastError(schSCM, hr, "Failed to open service control manager.");
441
442 schWu = ::OpenServiceW(schSCM, L"wuauserv", SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS | SERVICE_STOP );
443 ExitOnNullWithLastError(schWu, hr, "Failed to open WU service.");
444
445 if (!::QueryServiceStatus(schWu, &serviceStatus) )
446 {
447 ExitWithLastError(hr, "Failed to query status of WU service.");
448 }
449
450 // Stop service if requested to.
451 if (SERVICE_STOPPED != serviceStatus.dwCurrentState && fStopWusaService)
452 {
453 hr = StopWUService(schWu);
454 }
455
456 // If the service is not running then it might be disabled so let's check.
457 if (SERVICE_RUNNING != serviceStatus.dwCurrentState)
458 {
459 hr = SvcQueryConfig(schWu, &pConfig);
460 ExitOnFailure(hr, "Failed to read configuration for WU service.");
461
462 // If WU is disabled, change it to a demand start service (but touch nothing else).
463 if (SERVICE_DISABLED == pConfig->dwStartType)
464 {
465 hr = SetServiceStartType(schWu, SERVICE_DEMAND_START);
466 ExitOnFailure(hr, "Failed to mark WU service to start on demand.");
467
468 *pfPreviouslyDisabled = TRUE;
469 }
470 }
471
472 *pschWu = schWu;
473 schWu = NULL;
474
475LExit:
476 ReleaseMem(pConfig);
477 ReleaseServiceHandle(schWu);
478 ReleaseServiceHandle(schSCM);
479
480 return hr;
481}
482
483static HRESULT SetServiceStartType(
484 __in SC_HANDLE sch,
485 __in DWORD startType
486 )
487{
488 HRESULT hr = S_OK;
489
490 if (!::ChangeServiceConfigW(sch, SERVICE_NO_CHANGE, startType, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
491 {
492 ExitWithLastError(hr, "Failed to set service start type.");
493 }
494
495LExit:
496 return hr;
497}
498
499static HRESULT StopWUService(
500 __in SC_HANDLE schWu
501 )
502{
503 HRESULT hr = S_OK;
504 SERVICE_STATUS serviceStatus = { };
505
506 if(!::ControlService(schWu, SERVICE_CONTROL_STOP, &serviceStatus))
507 {
508 ExitWithLastError(hr, "Failed to stop wusa service.");
509 }
510
511LExit:
512 return hr;
513}
diff --git a/src/engine/msuengine.h b/src/engine/msuengine.h
new file mode 100644
index 00000000..d0323b06
--- /dev/null
+++ b/src/engine/msuengine.h
@@ -0,0 +1,48 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// function declarations
11
12HRESULT MsuEngineParsePackageFromXml(
13 __in IXMLDOMNode* pixnMsiPackage,
14 __in BURN_PACKAGE* pPackage
15 );
16void MsuEnginePackageUninitialize(
17 __in BURN_PACKAGE* pPackage
18 );
19HRESULT MsuEngineDetectPackage(
20 __in BURN_PACKAGE* pPackage,
21 __in BURN_VARIABLES* pVariables
22 );
23HRESULT MsuEnginePlanCalculatePackage(
24 __in BURN_PACKAGE* pPackage,
25 __out_opt BOOL* pfBARequestedCache
26 );
27HRESULT MsuEnginePlanAddPackage(
28 __in BURN_PACKAGE* pPackage,
29 __in BURN_PLAN* pPlan,
30 __in BURN_LOGGING* pLog,
31 __in BURN_VARIABLES* pVariables,
32 __in HANDLE hCacheEvent,
33 __in BOOL fPlanPackageCacheRollback
34 );
35HRESULT MsuEngineExecutePackage(
36 __in BURN_EXECUTE_ACTION* pExecuteAction,
37 __in BURN_VARIABLES* pVariables,
38 __in BOOL fRollback,
39 __in BOOL fStopWusaService,
40 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
41 __in LPVOID pvContext,
42 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
43 );
44
45
46#if defined(__cplusplus)
47}
48#endif
diff --git a/src/engine/netfxchainer.cpp b/src/engine/netfxchainer.cpp
new file mode 100644
index 00000000..4e7a7720
--- /dev/null
+++ b/src/engine/netfxchainer.cpp
@@ -0,0 +1,418 @@
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#include "precomp.h"
4
5static VOID DestroyNetFxChainer(
6 __in NetFxChainer* pChainer
7 )
8{
9 if (pChainer)
10 {
11 ReleaseHandle(pChainer->hSection);
12 ReleaseHandle(pChainer->hEventChaineeSend);
13 ReleaseHandle(pChainer->hEventChainerSend);
14 ReleaseHandle(pChainer->hMutex);
15
16 if (pChainer->pData)
17 {
18 ::UnmapViewOfFile(pChainer->pData);
19 }
20
21 MemFree(pChainer);
22 }
23}
24
25static HRESULT CreateNetFxChainer(
26 __in LPCWSTR wzSectionName,
27 __in LPCWSTR wzEventName,
28 __out NetFxChainer** ppChainer
29 )
30{
31 HRESULT hr = S_OK;
32 LPWSTR sczName = NULL;
33 NetFxChainer* pChainer = NULL;
34
35 pChainer = (NetFxChainer*)MemAlloc(sizeof(NetFxChainer), TRUE);
36 ExitOnNull(pChainer, hr, E_OUTOFMEMORY, "Failed to allocate memory for NetFxChainer struct.");
37
38 pChainer->hEventChaineeSend = ::CreateEvent(NULL, FALSE, FALSE, wzEventName);
39 ExitOnNullWithLastError(pChainer->hEventChaineeSend, hr, "Failed to create event: %ls", wzEventName);
40
41 hr = StrAllocFormatted(&sczName, L"%ls_send", wzEventName);
42 ExitOnFailure(hr, "failed to allocate memory for event name");
43
44 pChainer->hEventChainerSend = ::CreateEvent(NULL, FALSE, FALSE, sczName);
45 ExitOnNullWithLastError(pChainer->hEventChainerSend, hr, "Failed to create event: %ls", sczName);
46
47 hr = StrAllocFormatted(&sczName, L"%ls_mutex", wzEventName);
48 ExitOnFailure(hr, "failed to allocate memory for mutex name");
49
50 // Create the mutex, we initially own
51 pChainer->hMutex = ::CreateMutex(NULL, TRUE, sczName);
52 ExitOnNullWithLastError(pChainer->hMutex, hr, "Failed to create mutex: %ls", sczName);
53
54 pChainer->hSection = ::CreateFileMapping(INVALID_HANDLE_VALUE,
55 NULL, // security attributes
56 PAGE_READWRITE,
57 0, // high-order DWORD of maximum size
58 NETFXDATA_SIZE, // low-order DWORD of maximum size
59 wzSectionName);
60 ExitOnNullWithLastError(pChainer->hSection, hr, "Failed to memory map cabinet file: %ls", wzSectionName);
61
62 pChainer->pData = reinterpret_cast<NetFxDataStructure*>(::MapViewOfFile(pChainer->hSection,
63 FILE_MAP_WRITE,
64 0, 0, // offsets
65 0 // map entire file
66 ));
67 ExitOnNullWithLastError(pChainer->pData, hr, "Failed to MapViewOfFile for %ls.", wzSectionName);
68
69 // Initialize the shared memory
70 hr = ::StringCchCopyW(pChainer->pData->szEventName, countof(pChainer->pData->szEventName), wzEventName);
71 ExitOnFailure(hr, "failed to copy event name to shared memory structure.");
72 pChainer->pData->downloadFinished = false;
73 pChainer->pData->downloadSoFar = 0;
74 pChainer->pData->hrDownloadFinished = E_PENDING;
75 pChainer->pData->downloadAbort = false;
76 pChainer->pData->installFinished = false;
77 pChainer->pData->installSoFar = 0;
78 pChainer->pData->hrInstallFinished = E_PENDING;
79 pChainer->pData->installAbort = false;
80 pChainer->pData->hrInternalError = S_OK;
81 pChainer->pData->version = NETFXDATA_VERSION;
82 pChainer->pData->messageCode = 0;
83 pChainer->pData->messageResponse = 0;
84 pChainer->pData->messageDataLength = 0;
85
86 // Done with initialization, allow others to access.
87 ::ReleaseMutex(pChainer->hMutex);
88
89 *ppChainer = pChainer;
90 pChainer = NULL;
91
92LExit:
93 ReleaseStr(sczName);
94
95 if (pChainer)
96 {
97 // Something failed, release the mutex and destroy the object
98 if (pChainer->hMutex)
99 {
100 ::ReleaseMutex(pChainer->hMutex);
101 }
102
103 DestroyNetFxChainer(pChainer);
104 }
105
106 return hr;
107}
108
109
110static VOID NetFxAbort(
111 __in NetFxChainer* pChainer
112 )
113{
114 ::WaitForSingleObject(pChainer->hMutex, INFINITE);
115
116 pChainer->pData->downloadAbort = true;
117 pChainer->pData->installAbort = true;
118
119 ::ReleaseMutex(pChainer->hMutex);
120
121 ::SetEvent(pChainer->hEventChainerSend);
122}
123
124static BYTE NetFxGetProgress(
125 __in NetFxChainer* pChainer
126 )
127{
128 BYTE bProgress = 0;
129 ::WaitForSingleObject(pChainer->hMutex, INFINITE);
130
131 bProgress = (pChainer->pData->installSoFar + pChainer->pData->downloadSoFar) / 2;
132
133 ::ReleaseMutex(pChainer->hMutex);
134
135 return bProgress;
136}
137
138static HRESULT NetFxGetMessage(
139 __in NetFxChainer* pChainer,
140 __out DWORD* pdwMessage,
141 __out LPVOID* ppBuffer,
142 __out DWORD* pdwBufferSize
143 )
144{
145 HRESULT hr = S_OK;
146 ::WaitForSingleObject(pChainer->hMutex, INFINITE);
147
148 *pdwMessage = pChainer->pData->messageCode;
149 *ppBuffer = NULL;
150 *pdwBufferSize = 0;
151
152 if (NETFX_NO_MESSAGE != *pdwMessage)
153 {
154 *ppBuffer = MemAlloc(pChainer->pData->messageDataLength, TRUE);
155 ExitOnNull(*ppBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for message data");
156
157 memcpy(*ppBuffer, pChainer->pData->messageData, pChainer->pData->messageDataLength);
158 *pdwBufferSize = pChainer->pData->messageDataLength;
159 }
160
161LExit:
162 ::ReleaseMutex(pChainer->hMutex);
163
164 return hr;
165}
166
167static void NetFxRespond(
168 __in NetFxChainer* pChainer,
169 __in DWORD dwResponse
170 )
171{
172 ::WaitForSingleObject(pChainer->hMutex, INFINITE);
173
174 pChainer->pData->messageCode = NETFX_NO_MESSAGE;
175 pChainer->pData->messageResponse = dwResponse;
176 if (IDCANCEL == dwResponse)
177 {
178 pChainer->pData->downloadAbort = true;
179 pChainer->pData->installAbort = true;
180 }
181
182 ::ReleaseMutex(pChainer->hMutex);
183
184 ::SetEvent(pChainer->hEventChainerSend);
185}
186
187static HRESULT NetFxGetResult(
188 __in NetFxChainer* pChainer,
189 __out HRESULT* phrInternalError
190 )
191{
192 HRESULT hr = S_OK;
193 ::WaitForSingleObject(pChainer->hMutex, INFINITE);
194
195 hr = pChainer->pData->hrInstallFinished;
196
197 if (FAILED(pChainer->pData->hrDownloadFinished) && // Download failed
198 (S_OK == hr || E_ABORT == hr)) // Install succeeded or was aborted
199 {
200 hr = pChainer->pData->hrDownloadFinished;
201 }
202
203 if (phrInternalError)
204 {
205 *phrInternalError = pChainer->pData->hrInternalError;
206 }
207
208 ::ReleaseMutex(pChainer->hMutex);
209
210 return hr;
211}
212
213static HRESULT OnNetFxFilesInUse(
214 __in NetFxChainer* pNetfxChainer,
215 __in NetFxCloseApplications* pCloseApps,
216 __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler,
217 __in LPVOID pvContext
218 )
219{
220 HRESULT hr = S_OK;
221 DWORD cFiles = 0;
222 LPWSTR* rgwzFiles = NULL;
223 GENERIC_EXECUTE_MESSAGE message = { };
224 DWORD dwResponse = 0;
225
226 cFiles = pCloseApps->dwApplicationsSize;
227 rgwzFiles = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cFiles, TRUE);
228 ExitOnNull(rgwzFiles, hr, E_OUTOFMEMORY, "Failed to allocate buffer.");
229
230 for (DWORD i = 0; i < pCloseApps->dwApplicationsSize; ++i)
231 {
232 rgwzFiles[i] = pCloseApps->applications[i].szName;
233 }
234
235 // send message
236 message.type = GENERIC_EXECUTE_MESSAGE_FILES_IN_USE;
237 message.dwAllowedResults = MB_ABORTRETRYIGNORE;
238 message.filesInUse.cFiles = cFiles;
239 message.filesInUse.rgwzFiles = (LPCWSTR*)rgwzFiles;
240 dwResponse = (DWORD)pfnMessageHandler(&message, pvContext);
241
242 NetFxRespond(pNetfxChainer, dwResponse);
243
244LExit:
245 ReleaseMem(rgwzFiles);
246
247 return hr;
248}
249
250static HRESULT OnNetFxProgress(
251 __in NetFxChainer* pNetfxChainer,
252 __in BYTE bProgress,
253 __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler,
254 __in LPVOID pvContext
255 )
256{
257 GENERIC_EXECUTE_MESSAGE message = { };
258 DWORD dwResponse = 0;
259
260 // send message
261 message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
262 message.dwAllowedResults = MB_OKCANCEL;
263 message.progress.dwPercentage = 100 * (DWORD)bProgress / BYTE_MAX;
264 dwResponse = (DWORD)pfnMessageHandler(&message, pvContext);
265
266 if (IDCANCEL == dwResponse)
267 {
268 NetFxAbort(pNetfxChainer);
269 }
270
271 return S_OK;
272}
273
274static HRESULT OnNetFxError(
275 __in NetFxChainer* /*pNetfxChainer*/,
276 __in HRESULT hrError,
277 __in PFN_GENERICMESSAGEHANDLER pfnMessageHandler,
278 __in LPVOID pvContext
279 )
280{
281 GENERIC_EXECUTE_MESSAGE message = { };
282 DWORD dwResponse = 0;
283
284 // send message
285 message.type = GENERIC_EXECUTE_MESSAGE_ERROR;
286 message.dwAllowedResults = MB_OK;
287 message.error.dwErrorCode = hrError;
288 message.error.wzMessage = NULL;
289 dwResponse = (DWORD)pfnMessageHandler(&message, pvContext);
290
291 return S_OK;
292}
293
294static HRESULT ProcessNetFxMessage(
295 __in NetFxChainer* pNetfxChainer,
296 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
297 __in LPVOID pvContext
298 )
299{
300 HRESULT hr = S_OK;
301 DWORD dwMessage = NETFX_NO_MESSAGE;
302 DWORD dwBufferSize = 0;
303 LPVOID pBuffer = NULL;
304
305 // send progress
306 hr = OnNetFxProgress(pNetfxChainer, NetFxGetProgress(pNetfxChainer), pfnGenericMessageHandler, pvContext);
307 ExitOnFailure(hr, "Failed to send progress from netfx chainer.");
308
309 // Check for message
310 hr = NetFxGetMessage(pNetfxChainer, &dwMessage, &pBuffer, &dwBufferSize);
311 ExitOnFailure(hr, "Failed to get message from netfx chainer.");
312
313 switch(dwMessage)
314 {
315 case NETFX_CLOSE_APPS:
316 hr = OnNetFxFilesInUse(pNetfxChainer, (NetFxCloseApplications*)pBuffer, pfnGenericMessageHandler, pvContext);
317 ExitOnFailure(hr, "Failed to send files in use message from netfx chainer.");
318 break;
319
320 default:
321 // No message we understand.
322 break;
323 }
324
325LExit:
326 ReleaseMem(pBuffer);
327
328 return hr;
329}
330
331extern "C" HRESULT NetFxRunChainer(
332 __in LPCWSTR wzExecutablePath,
333 __in LPCWSTR wzArguments,
334 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
335 __in LPVOID pvContext,
336 __out DWORD* pdwExitCode
337 )
338{
339 HRESULT hr = S_OK;
340 DWORD er = 0;
341 WCHAR wzGuid[GUID_STRING_LENGTH];
342 LPWSTR sczEventName = NULL;
343 LPWSTR sczSectionName = NULL;
344 LPWSTR sczCommand = NULL;
345 NetFxChainer* pNetfxChainer = NULL;
346 STARTUPINFOW si = { };
347 PROCESS_INFORMATION pi = { };
348 HRESULT hrInternalError = 0;
349
350 // Create the unique name suffix.
351 hr = GuidFixedCreate(wzGuid);
352 ExitOnRootFailure(hr, "Failed to create netfx chainer guid.");
353
354 hr = StrAllocFormatted(&sczSectionName, L"NetFxSection.%ls", wzGuid);
355 ExitOnFailure(hr, "Failed to allocate section name.");
356
357 hr = StrAllocFormatted(&sczEventName, L"NetFxEvent.%ls", wzGuid);
358 ExitOnFailure(hr, "Failed to allocate event name.");
359
360 hr = CreateNetFxChainer(sczSectionName, sczEventName, &pNetfxChainer);
361 ExitOnFailure(hr, "Failed to create netfx chainer.");
362
363 hr = StrAllocFormattedSecure(&sczCommand, L"%ls /pipe %ls", wzArguments, sczSectionName);
364 ExitOnFailure(hr, "Failed to allocate netfx chainer arguments.");
365
366 si.cb = sizeof(si);
367 if (!::CreateProcessW(wzExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
368 {
369 ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", wzExecutablePath);
370 }
371
372 HANDLE handles[2] = { pi.hProcess, pNetfxChainer->hEventChaineeSend };
373
374 for (;;)
375 {
376 er = ::WaitForMultipleObjects(2, handles, FALSE, 100);
377 if (WAIT_OBJECT_0 == er)
378 {
379 // Process has exited
380 *pdwExitCode = NetFxGetResult(pNetfxChainer, &hrInternalError);
381 if (E_PENDING == *pdwExitCode)
382 {
383 if (!::GetExitCodeProcess(pi.hProcess, pdwExitCode))
384 {
385 ExitWithLastError(hr, "Failed to get netfx return code.");
386 }
387 }
388 else if (FAILED(hrInternalError))
389 {
390 // push internal error message
391 OnNetFxError(pNetfxChainer, hrInternalError, pfnGenericMessageHandler, pvContext);
392 ExitOnFailure(hr, "Failed to send internal error message from netfx chainer.");
393 }
394
395 break;
396 }
397 else if (WAIT_OBJECT_0 + 1 == er)
398 {
399 // Chainee has notified us of a change.
400 hr = ProcessNetFxMessage(pNetfxChainer, pfnGenericMessageHandler, pvContext);
401 ExitOnFailure(hr, "Failed to process netfx chainer message.");
402 }
403 else if (WAIT_FAILED == er)
404 {
405 ExitWithLastError(hr, "Failed to wait for netfx chainer process to complete");
406 }
407 }
408
409LExit:
410 ReleaseStr(sczSectionName);
411 ReleaseStr(sczEventName);
412 StrSecureZeroFreeString(sczCommand);
413 DestroyNetFxChainer(pNetfxChainer);
414 ReleaseHandle(pi.hThread);
415 ReleaseHandle(pi.hProcess);
416
417 return hr;
418}
diff --git a/src/engine/netfxchainer.h b/src/engine/netfxchainer.h
new file mode 100644
index 00000000..7d3aff1c
--- /dev/null
+++ b/src/engine/netfxchainer.h
@@ -0,0 +1,98 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9struct NetFxDataStructure
10{
11 bool downloadFinished; // download done yet?
12 bool installFinished; // install done yet?
13 bool downloadAbort; // set downloader to abort
14 bool installAbort; // set installer to abort
15 HRESULT hrDownloadFinished; // resultant HRESULT for download
16 HRESULT hrInstallFinished; // resultant HRESULT for install
17 HRESULT hrInternalError;
18 WCHAR szCurrentItemStep[MAX_PATH];
19 BYTE downloadSoFar; // download progress 0 - 255 (0 to 100% done)
20 BYTE installSoFar; // install progress 0 - 255 (0 to 100% done)
21 WCHAR szEventName[MAX_PATH]; // event that chainer 'creates' and chainee 'opens'to sync communications
22
23 BYTE version; // version of the data structure, set by chainer.
24
25 DWORD messageCode; // current message being sent by the chainee, 0 if no message is active
26 DWORD messageResponse; // chainer's response to current message, 0 if not yet handled
27 DWORD messageDataLength; // length of the m_messageData field in bytes
28 BYTE messageData[1]; // variable length buffer, content depends on m_messageCode
29};
30
31struct NetFxChainer
32{
33 HANDLE hSection;
34
35 HANDLE hEventChaineeSend;
36 HANDLE hEventChainerSend;
37 HANDLE hMutex;
38
39 NetFxDataStructure* pData;
40 DWORD dwDataSize;
41};
42
43#define NETFXDATA_SIZE 65536
44
45#define NETFXDATA_VERSION 1
46
47#define NETFX_MESSAGE(version, defaultResponse, messageCode) \
48 ((((DWORD)version & 0xFF) << 24) | (((DWORD)defaultResponse & 0xFF) << 16) | ((DWORD)messageCode & 0xFFFF))
49#define NETFX_MESSAGE_CODE(messageId) \
50 (messageId & 0xFFFF)
51#define NETFX_MESSAGE_DEFAULT_RESPONSE(messageId) \
52 ((messageId >> 16) & 0xFF)
53#define NETFX_MESSAGE_VERSION(messageId) \
54 ((messageId >>24) & 0xFF)
55
56#define NETFX_NO_MESSAGE 0
57
58
59//------------------------------------------------------------------------------
60// NETFX_CLOSE_APPS
61//
62// Sent by the chainee when it detects that applications are holding files in
63// use. Respond to this message in order to tell the chainee to close the
64// applications to prevent a reboot.
65//
66// pData : NetFxCloseApplications : The list of applications
67// Acceptable responses:
68// IDYES : Indicates that the chainee should attempt to shutdown the apps.
69// If all apps do not successfully close the message may be sent again.
70// IDNO : Indicates that the chainee should not attempt to close apps.
71// IDRETRY : Indicates that the chainee should refresh the list of apps.
72// Another NETFX_CLOSE_APPS message will be sent asynchronously with
73// the new list of apps.
74//------------------------------------------------------------------------------
75#define NETFX_CLOSE_APPS NETFX_MESSAGE(NETFXDATA_VERSION, IDNO, 1)
76
77struct NetFxApplication
78{
79 WCHAR szName[MAX_PATH];
80 DWORD dwPid;
81};
82
83struct NetFxCloseApplications
84{
85 DWORD dwApplicationsSize;
86 NetFxApplication applications[1];
87};
88
89HRESULT NetFxRunChainer(
90 __in LPCWSTR wzExecutablePath,
91 __in LPCWSTR wzArguments,
92 __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
93 __in LPVOID pvContext,
94 __out DWORD* pdwExitCode
95 );
96#if defined(__cplusplus)
97}
98#endif
diff --git a/src/engine/package.cpp b/src/engine/package.cpp
new file mode 100644
index 00000000..02958efd
--- /dev/null
+++ b/src/engine/package.cpp
@@ -0,0 +1,678 @@
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#include "precomp.h"
4
5
6// internal function declarations
7
8static HRESULT ParsePayloadRefsFromXml(
9 __in BURN_PACKAGE* pPackage,
10 __in BURN_PAYLOADS* pPayloads,
11 __in IXMLDOMNode* pixnPackage
12 );
13static HRESULT ParsePatchTargetCode(
14 __in BURN_PACKAGES* pPackages,
15 __in IXMLDOMNode* pixnBundle
16 );
17static HRESULT FindRollbackBoundaryById(
18 __in BURN_PACKAGES* pPackages,
19 __in_z LPCWSTR wzId,
20 __out BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary
21 );
22
23
24// function definitions
25
26extern "C" HRESULT PackagesParseFromXml(
27 __in BURN_PACKAGES* pPackages,
28 __in BURN_PAYLOADS* pPayloads,
29 __in IXMLDOMNode* pixnBundle
30 )
31{
32 HRESULT hr = S_OK;
33 IXMLDOMNodeList* pixnNodes = NULL;
34 IXMLDOMNode* pixnNode = NULL;
35 DWORD cNodes = 0;
36 BSTR bstrNodeName = NULL;
37 DWORD cMspPackages = 0;
38 LPWSTR scz = NULL;
39
40 // select rollback boundary nodes
41 hr = XmlSelectNodes(pixnBundle, L"RollbackBoundary", &pixnNodes);
42 ExitOnFailure(hr, "Failed to select rollback boundary nodes.");
43
44 // get rollback boundary node count
45 hr = pixnNodes->get_length((long*)&cNodes);
46 ExitOnFailure(hr, "Failed to get rollback bundary node count.");
47
48 if (cNodes)
49 {
50 // allocate memory for rollback boundaries
51 pPackages->rgRollbackBoundaries = (BURN_ROLLBACK_BOUNDARY*)MemAlloc(sizeof(BURN_ROLLBACK_BOUNDARY) * cNodes, TRUE);
52 ExitOnNull(pPackages->rgRollbackBoundaries, hr, E_OUTOFMEMORY, "Failed to allocate memory for rollback boundary structs.");
53
54 pPackages->cRollbackBoundaries = cNodes;
55
56 // parse rollback boundary elements
57 for (DWORD i = 0; i < cNodes; ++i)
58 {
59 BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = &pPackages->rgRollbackBoundaries[i];
60
61 hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName);
62 ExitOnFailure(hr, "Failed to get next node.");
63
64 // @Id
65 hr = XmlGetAttributeEx(pixnNode, L"Id", &pRollbackBoundary->sczId);
66 ExitOnFailure(hr, "Failed to get @Id.");
67
68 // @Vital
69 hr = XmlGetYesNoAttribute(pixnNode, L"Vital", &pRollbackBoundary->fVital);
70 ExitOnFailure(hr, "Failed to get @Vital.");
71
72 // @Transaction
73 hr = XmlGetYesNoAttribute(pixnNode, L"Transaction", &pRollbackBoundary->fTransaction);
74 ExitOnFailure(hr, "Failed to get @Transaction.");
75
76 // prepare next iteration
77 ReleaseNullObject(pixnNode);
78 ReleaseNullBSTR(bstrNodeName);
79 }
80 }
81
82 ReleaseNullObject(pixnNodes); // done with the RollbackBoundary elements.
83
84 // select package nodes
85 hr = XmlSelectNodes(pixnBundle, L"Chain/ExePackage|Chain/MsiPackage|Chain/MspPackage|Chain/MsuPackage", &pixnNodes);
86 ExitOnFailure(hr, "Failed to select package nodes.");
87
88 // get package node count
89 hr = pixnNodes->get_length((long*)&cNodes);
90 ExitOnFailure(hr, "Failed to get package node count.");
91
92 if (!cNodes)
93 {
94 ExitFunction1(hr = S_OK);
95 }
96
97 // allocate memory for packages
98 pPackages->rgPackages = (BURN_PACKAGE*)MemAlloc(sizeof(BURN_PACKAGE) * cNodes, TRUE);
99 ExitOnNull(pPackages->rgPackages, hr, E_OUTOFMEMORY, "Failed to allocate memory for package structs.");
100
101 pPackages->cPackages = cNodes;
102
103 // parse package elements
104 for (DWORD i = 0; i < cNodes; ++i)
105 {
106 BURN_PACKAGE* pPackage = &pPackages->rgPackages[i];
107
108 hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName);
109 ExitOnFailure(hr, "Failed to get next node.");
110
111 // @Id
112 hr = XmlGetAttributeEx(pixnNode, L"Id", &pPackage->sczId);
113 ExitOnFailure(hr, "Failed to get @Id.");
114
115 // @Cache
116 hr = XmlGetAttributeEx(pixnNode, L"Cache", &scz);
117 if (SUCCEEDED(hr))
118 {
119 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"no", -1))
120 {
121 pPackage->cacheType = BURN_CACHE_TYPE_NO;
122 }
123 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"yes", -1))
124 {
125 pPackage->cacheType = BURN_CACHE_TYPE_YES;
126 }
127 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"always", -1))
128 {
129 pPackage->cacheType = BURN_CACHE_TYPE_ALWAYS;
130 }
131 else
132 {
133 hr = E_UNEXPECTED;
134 ExitOnFailure(hr, "Invalid cache type: %ls", scz);
135 }
136 }
137 ExitOnFailure(hr, "Failed to get @Cache.");
138
139 // @CacheId
140 hr = XmlGetAttributeEx(pixnNode, L"CacheId", &pPackage->sczCacheId);
141 ExitOnFailure(hr, "Failed to get @CacheId.");
142
143 // @Size
144 hr = XmlGetAttributeLargeNumber(pixnNode, L"Size", &pPackage->qwSize);
145 ExitOnFailure(hr, "Failed to get @Size.");
146
147 // @InstallSize
148 hr = XmlGetAttributeLargeNumber(pixnNode, L"InstallSize", &pPackage->qwInstallSize);
149 ExitOnFailure(hr, "Failed to get @InstallSize.");
150
151 // @PerMachine
152 hr = XmlGetYesNoAttribute(pixnNode, L"PerMachine", &pPackage->fPerMachine);
153 ExitOnFailure(hr, "Failed to get @PerMachine.");
154
155 // @Permanent
156 hr = XmlGetYesNoAttribute(pixnNode, L"Permanent", &pPackage->fUninstallable);
157 ExitOnFailure(hr, "Failed to get @Permanent.");
158 pPackage->fUninstallable = !pPackage->fUninstallable; // TODO: change "Uninstallable" variable name to permanent, until then Uninstallable is the opposite of Permanent so fix the variable.
159
160 // @Vital
161 hr = XmlGetYesNoAttribute(pixnNode, L"Vital", &pPackage->fVital);
162 ExitOnFailure(hr, "Failed to get @Vital.");
163
164 // @LogPathVariable
165 hr = XmlGetAttributeEx(pixnNode, L"LogPathVariable", &pPackage->sczLogPathVariable);
166 if (E_NOTFOUND != hr)
167 {
168 ExitOnFailure(hr, "Failed to get @LogPathVariable.");
169 }
170
171 // @RollbackLogPathVariable
172 hr = XmlGetAttributeEx(pixnNode, L"RollbackLogPathVariable", &pPackage->sczRollbackLogPathVariable);
173 if (E_NOTFOUND != hr)
174 {
175 ExitOnFailure(hr, "Failed to get @RollbackLogPathVariable.");
176 }
177
178 // @InstallCondition
179 hr = XmlGetAttributeEx(pixnNode, L"InstallCondition", &pPackage->sczInstallCondition);
180 if (E_NOTFOUND != hr)
181 {
182 ExitOnFailure(hr, "Failed to get @InstallCondition.");
183 }
184
185 // @RollbackBoundaryForward
186 hr = XmlGetAttributeEx(pixnNode, L"RollbackBoundaryForward", &scz);
187 if (E_NOTFOUND != hr)
188 {
189 ExitOnFailure(hr, "Failed to get @RollbackBoundaryForward.");
190
191 hr = FindRollbackBoundaryById(pPackages, scz, &pPackage->pRollbackBoundaryForward);
192 ExitOnFailure(hr, "Failed to find forward transaction boundary: %ls", scz);
193 }
194
195 // @RollbackBoundaryBackward
196 hr = XmlGetAttributeEx(pixnNode, L"RollbackBoundaryBackward", &scz);
197 if (E_NOTFOUND != hr)
198 {
199 ExitOnFailure(hr, "Failed to get @RollbackBoundaryBackward.");
200
201 hr = FindRollbackBoundaryById(pPackages, scz, &pPackage->pRollbackBoundaryBackward);
202 ExitOnFailure(hr, "Failed to find backward transaction boundary: %ls", scz);
203 }
204
205 // read type specific attributes
206 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"ExePackage", -1))
207 {
208 pPackage->type = BURN_PACKAGE_TYPE_EXE;
209
210 hr = ExeEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization
211 ExitOnFailure(hr, "Failed to parse EXE package.");
212 }
213 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiPackage", -1))
214 {
215 pPackage->type = BURN_PACKAGE_TYPE_MSI;
216
217 hr = MsiEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization
218 ExitOnFailure(hr, "Failed to parse MSI package.");
219 }
220 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MspPackage", -1))
221 {
222 pPackage->type = BURN_PACKAGE_TYPE_MSP;
223
224 hr = MspEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization
225 ExitOnFailure(hr, "Failed to parse MSP package.");
226
227 ++cMspPackages;
228 }
229 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsuPackage", -1))
230 {
231 pPackage->type = BURN_PACKAGE_TYPE_MSU;
232
233 hr = MsuEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization
234 ExitOnFailure(hr, "Failed to parse MSU package.");
235 }
236 else
237 {
238 // ignore other package types for now
239 }
240
241 // parse payload references
242 hr = ParsePayloadRefsFromXml(pPackage, pPayloads, pixnNode);
243 ExitOnFailure(hr, "Failed to parse payload references.");
244
245 // parse dependency providers
246 hr = DependencyParseProvidersFromXml(pPackage, pixnNode);
247 ExitOnFailure(hr, "Failed to parse dependency providers.");
248
249 // prepare next iteration
250 ReleaseNullObject(pixnNode);
251 ReleaseNullBSTR(bstrNodeName);
252 }
253
254 if (cMspPackages)
255 {
256 pPackages->rgPatchInfo = static_cast<MSIPATCHSEQUENCEINFOW*>(MemAlloc(sizeof(MSIPATCHSEQUENCEINFOW) * cMspPackages, TRUE));
257 ExitOnNull(pPackages->rgPatchInfo, hr, E_OUTOFMEMORY, "Failed to allocate memory for MSP patch sequence information.");
258
259 pPackages->rgPatchInfoToPackage = static_cast<BURN_PACKAGE**>(MemAlloc(sizeof(BURN_PACKAGE*) * cMspPackages, TRUE));
260 ExitOnNull(pPackages->rgPatchInfoToPackage, hr, E_OUTOFMEMORY, "Failed to allocate memory for patch sequence information to package lookup.");
261
262 for (DWORD i = 0; i < pPackages->cPackages; ++i)
263 {
264 BURN_PACKAGE* pPackage = &pPackages->rgPackages[i];
265
266 if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
267 {
268 pPackages->rgPatchInfo[pPackages->cPatchInfo].szPatchData = pPackage->Msp.sczApplicabilityXml;
269 pPackages->rgPatchInfo[pPackages->cPatchInfo].ePatchDataType = MSIPATCH_DATATYPE_XMLBLOB;
270 pPackages->rgPatchInfoToPackage[pPackages->cPatchInfo] = pPackage;
271 ++pPackages->cPatchInfo;
272
273 // Loop through all MSI packages seeing if any of them slipstream this MSP.
274 for (DWORD j = 0; j < pPackages->cPackages; ++j)
275 {
276 BURN_PACKAGE* pMsiPackage = &pPackages->rgPackages[j];
277
278 if (BURN_PACKAGE_TYPE_MSI == pMsiPackage->type)
279 {
280 for (DWORD k = 0; k < pMsiPackage->Msi.cSlipstreamMspPackages; ++k)
281 {
282 if (pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k] && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k], -1))
283 {
284 pMsiPackage->Msi.rgpSlipstreamMspPackages[k] = pPackage;
285
286 ReleaseNullStr(pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k]); // we don't need the slipstream package id any longer so free it.
287 }
288 }
289 }
290 }
291 }
292 }
293 }
294
295 AssertSz(pPackages->cPatchInfo == cMspPackages, "Count of packages patch info should be equal to the number of MSP packages.");
296
297 hr = ParsePatchTargetCode(pPackages, pixnBundle);
298 ExitOnFailure(hr, "Failed to parse target product codes.");
299
300 hr = S_OK;
301
302LExit:
303 ReleaseObject(pixnNodes);
304 ReleaseObject(pixnNode);
305 ReleaseBSTR(bstrNodeName);
306 ReleaseStr(scz);
307
308 return hr;
309}
310
311extern "C" void PackageUninitialize(
312 __in BURN_PACKAGE* pPackage
313 )
314{
315 ReleaseStr(pPackage->sczId);
316 ReleaseStr(pPackage->sczLogPathVariable);
317 ReleaseStr(pPackage->sczRollbackLogPathVariable);
318 ReleaseStr(pPackage->sczInstallCondition);
319 ReleaseStr(pPackage->sczRollbackInstallCondition);
320 ReleaseStr(pPackage->sczCacheId);
321
322 if (pPackage->rgDependencyProviders)
323 {
324 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
325 {
326 DependencyUninitialize(pPackage->rgDependencyProviders + i);
327 }
328 MemFree(pPackage->rgDependencyProviders);
329 }
330
331 ReleaseMem(pPackage->rgPayloads);
332
333 switch (pPackage->type)
334 {
335 case BURN_PACKAGE_TYPE_EXE:
336 ExeEnginePackageUninitialize(pPackage); // TODO: Modularization
337 break;
338 case BURN_PACKAGE_TYPE_MSI:
339 MsiEnginePackageUninitialize(pPackage); // TODO: Modularization
340 break;
341 case BURN_PACKAGE_TYPE_MSP:
342 MspEnginePackageUninitialize(pPackage); // TODO: Modularization
343 break;
344 case BURN_PACKAGE_TYPE_MSU:
345 MsuEnginePackageUninitialize(pPackage); // TODO: Modularization
346 break;
347 }
348}
349
350extern "C" void PackagesUninitialize(
351 __in BURN_PACKAGES* pPackages
352 )
353{
354 if (pPackages->rgRollbackBoundaries)
355 {
356 for (DWORD i = 0; i < pPackages->cRollbackBoundaries; ++i)
357 {
358 ReleaseStr(pPackages->rgRollbackBoundaries[i].sczId);
359 }
360 MemFree(pPackages->rgRollbackBoundaries);
361 }
362
363 if (pPackages->rgPackages)
364 {
365 for (DWORD i = 0; i < pPackages->cPackages; ++i)
366 {
367 PackageUninitialize(pPackages->rgPackages + i);
368 }
369 MemFree(pPackages->rgPackages);
370 }
371
372 if (pPackages->rgCompatiblePackages)
373 {
374 for (DWORD i = 0; i < pPackages->cCompatiblePackages; ++i)
375 {
376 PackageUninitialize(pPackages->rgCompatiblePackages + i);
377 }
378 MemFree(pPackages->rgCompatiblePackages);
379 }
380
381 if (pPackages->rgPatchTargetCodes)
382 {
383 for (DWORD i = 0; i < pPackages->cPatchTargetCodes; ++i)
384 {
385 ReleaseStr(pPackages->rgPatchTargetCodes[i].sczTargetCode);
386 }
387 MemFree(pPackages->rgPatchTargetCodes);
388 }
389
390 ReleaseMem(pPackages->rgPatchInfo);
391 ReleaseMem(pPackages->rgPatchInfoToPackage);
392
393 // clear struct
394 memset(pPackages, 0, sizeof(BURN_PACKAGES));
395}
396
397extern "C" HRESULT PackageFindById(
398 __in BURN_PACKAGES* pPackages,
399 __in_z LPCWSTR wzId,
400 __out BURN_PACKAGE** ppPackage
401 )
402{
403 HRESULT hr = S_OK;
404 BURN_PACKAGE* pPackage = NULL;
405
406 for (DWORD i = 0; i < pPackages->cPackages; ++i)
407 {
408 pPackage = &pPackages->rgPackages[i];
409
410 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, wzId, -1))
411 {
412 *ppPackage = pPackage;
413 ExitFunction1(hr = S_OK);
414 }
415 }
416
417 for (DWORD i = 0; i < pPackages->cCompatiblePackages; ++i)
418 {
419 pPackage = &pPackages->rgCompatiblePackages[i];
420
421 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, wzId, -1))
422 {
423 *ppPackage = pPackage;
424 ExitFunction1(hr = S_OK);
425 }
426 }
427
428 hr = E_NOTFOUND;
429
430LExit:
431 return hr;
432}
433
434
435extern "C" HRESULT PackageFindRelatedById(
436 __in BURN_RELATED_BUNDLES* pRelatedBundles,
437 __in_z LPCWSTR wzId,
438 __out BURN_PACKAGE** ppPackage
439 )
440{
441 HRESULT hr = S_OK;
442 BURN_PACKAGE* pPackage = NULL;
443
444 for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i)
445 {
446 pPackage = &pRelatedBundles->rgRelatedBundles[i].package;
447
448 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, wzId, -1))
449 {
450 *ppPackage = pPackage;
451 ExitFunction1(hr = S_OK);
452 }
453 }
454
455 hr = E_NOTFOUND;
456
457LExit:
458 return hr;
459}
460
461/********************************************************************
462 PackageGetProperty - Determines if the property is defined
463 and optionally copies the property value.
464
465 Note: The caller must free psczValue if requested.
466
467 Note: Returns E_NOTFOUND if the property was not defined or if the
468 package does not support properties.
469
470*********************************************************************/
471extern "C" HRESULT PackageGetProperty(
472 __in const BURN_PACKAGE* pPackage,
473 __in_z LPCWSTR wzProperty,
474 __out_z_opt LPWSTR* psczValue
475 )
476{
477 HRESULT hr = E_NOTFOUND;
478 BURN_MSIPROPERTY* rgProperties = NULL;
479 DWORD cProperties = 0;
480
481 // For MSIs and MSPs, enumerate the properties looking for wzProperty.
482 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
483 {
484 rgProperties = pPackage->Msi.rgProperties;
485 cProperties = pPackage->Msi.cProperties;
486 }
487 else if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
488 {
489 rgProperties = pPackage->Msp.rgProperties;
490 cProperties = pPackage->Msp.cProperties;
491 }
492
493 for (DWORD i = 0; i < cProperties; ++i)
494 {
495 const BURN_MSIPROPERTY* pProperty = &rgProperties[i];
496
497 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pProperty->sczId, -1, wzProperty, -1))
498 {
499 if (psczValue)
500 {
501 hr = StrAllocString(psczValue, pProperty->sczValue, 0);
502 ExitOnFailure(hr, "Failed to copy the property value.");
503 }
504
505 ExitFunction1(hr = S_OK);
506 }
507 }
508
509LExit:
510 return hr;
511}
512
513HRESULT PackageEnsureCompatiblePackagesArray(
514 __in BURN_PACKAGES* pPackages
515 )
516{
517 HRESULT hr = S_OK;
518
519 if (!pPackages->rgCompatiblePackages)
520 {
521 pPackages->rgCompatiblePackages = (BURN_PACKAGE*)MemAlloc(sizeof(BURN_PACKAGE) * pPackages->cPackages, TRUE);
522 ExitOnNull(pPackages->rgCompatiblePackages, hr, E_OUTOFMEMORY, "Failed to allocate memory for compatible packages.");
523 }
524
525LExit:
526 return hr;
527}
528
529
530// internal function declarations
531
532static HRESULT ParsePayloadRefsFromXml(
533 __in BURN_PACKAGE* pPackage,
534 __in BURN_PAYLOADS* pPayloads,
535 __in IXMLDOMNode* pixnPackage
536 )
537{
538 HRESULT hr = S_OK;
539 IXMLDOMNodeList* pixnNodes = NULL;
540 IXMLDOMNode* pixnNode = NULL;
541 DWORD cNodes = 0;
542 LPWSTR sczId = NULL;
543
544 // select package nodes
545 hr = XmlSelectNodes(pixnPackage, L"PayloadRef", &pixnNodes);
546 ExitOnFailure(hr, "Failed to select package nodes.");
547
548 // get package node count
549 hr = pixnNodes->get_length((long*)&cNodes);
550 ExitOnFailure(hr, "Failed to get package node count.");
551
552 if (!cNodes)
553 {
554 ExitFunction1(hr = S_OK);
555 }
556
557 // allocate memory for payload pointers
558 pPackage->rgPayloads = (BURN_PACKAGE_PAYLOAD*)MemAlloc(sizeof(BURN_PACKAGE_PAYLOAD) * cNodes, TRUE);
559 ExitOnNull(pPackage->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate memory for package payloads.");
560
561 pPackage->cPayloads = cNodes;
562
563 // parse package elements
564 for (DWORD i = 0; i < cNodes; ++i)
565 {
566 BURN_PACKAGE_PAYLOAD* pPackagePayload = &pPackage->rgPayloads[i];
567
568 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
569 ExitOnFailure(hr, "Failed to get next node.");
570
571 // @Id
572 hr = XmlGetAttributeEx(pixnNode, L"Id", &sczId);
573 ExitOnFailure(hr, "Failed to get Id attribute.");
574
575 // find payload
576 hr = PayloadFindById(pPayloads, sczId, &pPackagePayload->pPayload);
577 ExitOnFailure(hr, "Failed to find payload.");
578
579 // prepare next iteration
580 ReleaseNullObject(pixnNode);
581 }
582
583 hr = S_OK;
584
585LExit:
586 ReleaseObject(pixnNodes);
587 ReleaseObject(pixnNode);
588 ReleaseStr(sczId);
589
590 return hr;
591}
592
593static HRESULT ParsePatchTargetCode(
594 __in BURN_PACKAGES* pPackages,
595 __in IXMLDOMNode* pixnBundle
596 )
597{
598 HRESULT hr = S_OK;
599 IXMLDOMNodeList* pixnNodes = NULL;
600 IXMLDOMNode* pixnNode = NULL;
601 DWORD cNodes = 0;
602 BSTR bstrNodeText = NULL;
603 BOOL fProduct;
604
605 hr = XmlSelectNodes(pixnBundle, L"PatchTargetCode", &pixnNodes);
606 ExitOnFailure(hr, "Failed to select PatchTargetCode nodes.");
607
608 hr = pixnNodes->get_length((long*)&cNodes);
609 ExitOnFailure(hr, "Failed to get PatchTargetCode node count.");
610
611 if (!cNodes)
612 {
613 ExitFunction1(hr = S_OK);
614 }
615
616 pPackages->rgPatchTargetCodes = (BURN_PATCH_TARGETCODE*)MemAlloc(sizeof(BURN_PATCH_TARGETCODE) * cNodes, TRUE);
617 ExitOnNull(pPackages->rgPatchTargetCodes, hr, E_OUTOFMEMORY, "Failed to allocate memory for patch targetcodes.");
618
619 pPackages->cPatchTargetCodes = cNodes;
620
621 for (DWORD i = 0; i < cNodes; ++i)
622 {
623 BURN_PATCH_TARGETCODE* pTargetCode = pPackages->rgPatchTargetCodes + i;
624
625 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
626 ExitOnFailure(hr, "Failed to get next node.");
627
628 hr = XmlGetAttributeEx(pixnNode, L"TargetCode", &pTargetCode->sczTargetCode);
629 ExitOnFailure(hr, "Failed to get @TargetCode attribute.");
630
631 hr = XmlGetYesNoAttribute(pixnNode, L"Product", &fProduct);
632 if (E_NOTFOUND == hr)
633 {
634 fProduct = FALSE;
635 hr = S_OK;
636 }
637 ExitOnFailure(hr, "Failed to get @Product.");
638
639 pTargetCode->type = fProduct ? BURN_PATCH_TARGETCODE_TYPE_PRODUCT : BURN_PATCH_TARGETCODE_TYPE_UPGRADE;
640
641 // prepare next iteration
642 ReleaseNullBSTR(bstrNodeText);
643 ReleaseNullObject(pixnNode);
644 }
645
646LExit:
647 ReleaseBSTR(bstrNodeText);
648 ReleaseObject(pixnNode);
649 ReleaseObject(pixnNodes);
650
651 return hr;
652}
653
654static HRESULT FindRollbackBoundaryById(
655 __in BURN_PACKAGES* pPackages,
656 __in_z LPCWSTR wzId,
657 __out BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary
658 )
659{
660 HRESULT hr = S_OK;
661 BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL;
662
663 for (DWORD i = 0; i < pPackages->cRollbackBoundaries; ++i)
664 {
665 pRollbackBoundary = &pPackages->rgRollbackBoundaries[i];
666
667 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pRollbackBoundary->sczId, -1, wzId, -1))
668 {
669 *ppRollbackBoundary = pRollbackBoundary;
670 ExitFunction1(hr = S_OK);
671 }
672 }
673
674 hr = E_NOTFOUND;
675
676LExit:
677 return hr;
678}
diff --git a/src/engine/package.h b/src/engine/package.h
new file mode 100644
index 00000000..c295378e
--- /dev/null
+++ b/src/engine/package.h
@@ -0,0 +1,336 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9struct _BURN_RELATED_BUNDLES;
10typedef _BURN_RELATED_BUNDLES BURN_RELATED_BUNDLES;
11
12struct _BURN_PACKAGE;
13typedef _BURN_PACKAGE BURN_PACKAGE;
14
15// constants
16
17enum BURN_EXE_EXIT_CODE_TYPE
18{
19 BURN_EXE_EXIT_CODE_TYPE_NONE,
20 BURN_EXE_EXIT_CODE_TYPE_SUCCESS,
21 BURN_EXE_EXIT_CODE_TYPE_ERROR,
22 BURN_EXE_EXIT_CODE_TYPE_SCHEDULE_REBOOT,
23 BURN_EXE_EXIT_CODE_TYPE_FORCE_REBOOT,
24};
25
26enum BURN_EXE_PROTOCOL_TYPE
27{
28 BURN_EXE_PROTOCOL_TYPE_NONE,
29 BURN_EXE_PROTOCOL_TYPE_BURN,
30 BURN_EXE_PROTOCOL_TYPE_NETFX4,
31};
32
33enum BURN_PACKAGE_TYPE
34{
35 BURN_PACKAGE_TYPE_NONE,
36 BURN_PACKAGE_TYPE_EXE,
37 BURN_PACKAGE_TYPE_MSI,
38 BURN_PACKAGE_TYPE_MSP,
39 BURN_PACKAGE_TYPE_MSU,
40};
41
42enum BURN_CACHE_STATE
43{
44 BURN_CACHE_STATE_NONE,
45 BURN_CACHE_STATE_PARTIAL,
46 BURN_CACHE_STATE_COMPLETE,
47};
48
49enum BURN_CACHE_TYPE
50{
51 BURN_CACHE_TYPE_NO,
52 BURN_CACHE_TYPE_YES,
53 BURN_CACHE_TYPE_ALWAYS,
54};
55
56enum BURN_DEPENDENCY_ACTION
57{
58 BURN_DEPENDENCY_ACTION_NONE,
59 BURN_DEPENDENCY_ACTION_REGISTER,
60 BURN_DEPENDENCY_ACTION_UNREGISTER,
61};
62
63enum BURN_PATCH_TARGETCODE_TYPE
64{
65 BURN_PATCH_TARGETCODE_TYPE_UNKNOWN,
66 BURN_PATCH_TARGETCODE_TYPE_PRODUCT,
67 BURN_PATCH_TARGETCODE_TYPE_UPGRADE,
68};
69
70// structs
71
72typedef struct _BURN_EXE_EXIT_CODE
73{
74 BURN_EXE_EXIT_CODE_TYPE type;
75 DWORD dwCode;
76 BOOL fWildcard;
77} BURN_EXE_EXIT_CODE;
78
79typedef struct _BURN_EXE_COMMAND_LINE_ARGUMENT
80{
81 LPWSTR sczInstallArgument;
82 LPWSTR sczUninstallArgument;
83 LPWSTR sczRepairArgument;
84 LPWSTR sczCondition;
85} BURN_EXE_COMMAND_LINE_ARGUMENT;
86
87typedef struct _BURN_MSPTARGETPRODUCT
88{
89 MSIINSTALLCONTEXT context;
90 DWORD dwOrder;
91 WCHAR wzTargetProductCode[39];
92 BURN_PACKAGE* pChainedTargetPackage;
93 BOOL fSlipstream;
94
95 BOOTSTRAPPER_PACKAGE_STATE patchPackageState; // only valid after Detect.
96 BOOTSTRAPPER_ACTION_STATE execute; // only valid during Plan.
97 BOOTSTRAPPER_ACTION_STATE rollback; // only valid during Plan.
98} BURN_MSPTARGETPRODUCT;
99
100typedef struct _BURN_MSIPROPERTY
101{
102 LPWSTR sczId;
103 LPWSTR sczValue; // used during forward execution
104 LPWSTR sczRollbackValue; // used during rollback
105 LPWSTR sczCondition;
106} BURN_MSIPROPERTY;
107
108typedef struct _BURN_MSIFEATURE
109{
110 LPWSTR sczId;
111 LPWSTR sczAddLocalCondition;
112 LPWSTR sczAddSourceCondition;
113 LPWSTR sczAdvertiseCondition;
114 LPWSTR sczRollbackAddLocalCondition;
115 LPWSTR sczRollbackAddSourceCondition;
116 LPWSTR sczRollbackAdvertiseCondition;
117
118 BOOTSTRAPPER_FEATURE_STATE currentState; // only valid after Detect.
119 BOOTSTRAPPER_FEATURE_ACTION execute; // only valid during Plan.
120 BOOTSTRAPPER_FEATURE_ACTION rollback; // only valid during Plan.
121} BURN_MSIFEATURE;
122
123typedef struct _BURN_RELATED_MSI
124{
125 LPWSTR sczUpgradeCode;
126 DWORD64 qwMinVersion;
127 DWORD64 qwMaxVersion;
128 BOOL fMinProvided;
129 BOOL fMaxProvided;
130 BOOL fMinInclusive;
131 BOOL fMaxInclusive;
132 BOOL fOnlyDetect;
133 BOOL fLangInclusive;
134
135 DWORD* rgdwLanguages;
136 DWORD cLanguages;
137} BURN_RELATED_MSI;
138
139typedef struct _BURN_PACKAGE_PAYLOAD
140{
141 BURN_PAYLOAD* pPayload;
142 BOOL fCached;
143} BURN_PACKAGE_PAYLOAD;
144
145typedef struct _BURN_DEPENDENCY_PROVIDER
146{
147 LPWSTR sczKey;
148 LPWSTR sczVersion;
149 LPWSTR sczDisplayName;
150 BOOL fImported;
151} BURN_DEPENDENCY_PROVIDER;
152
153typedef struct _BURN_ROLLBACK_BOUNDARY
154{
155 LPWSTR sczId;
156 BOOL fVital;
157 BOOL fTransaction;
158} BURN_ROLLBACK_BOUNDARY;
159
160typedef struct _BURN_PATCH_TARGETCODE
161{
162 LPWSTR sczTargetCode;
163 BURN_PATCH_TARGETCODE_TYPE type;
164} BURN_PATCH_TARGETCODE;
165
166typedef struct _BURN_PACKAGE
167{
168 LPWSTR sczId;
169
170 LPWSTR sczLogPathVariable; // name of the variable that will be set to the log path.
171 LPWSTR sczRollbackLogPathVariable; // name of the variable that will be set to the rollback path.
172
173 LPWSTR sczInstallCondition;
174 LPWSTR sczRollbackInstallCondition;
175 BOOL fPerMachine;
176 BOOL fUninstallable;
177 BOOL fVital;
178
179 BURN_CACHE_TYPE cacheType;
180 LPWSTR sczCacheId;
181
182 DWORD64 qwInstallSize;
183 DWORD64 qwSize;
184
185 BURN_ROLLBACK_BOUNDARY* pRollbackBoundaryForward; // used during install and repair.
186 BURN_ROLLBACK_BOUNDARY* pRollbackBoundaryBackward; // used during uninstall.
187
188 BOOTSTRAPPER_PACKAGE_STATE currentState; // only valid after Detect.
189 BURN_CACHE_STATE cache; // only valid after Detect.
190 BOOTSTRAPPER_PACKAGE_STATE expected; // only valid during Plan.
191 BOOTSTRAPPER_REQUEST_STATE defaultRequested;// only valid during Plan.
192 BOOTSTRAPPER_REQUEST_STATE requested; // only valid during Plan.
193 BOOL fAcquire; // only valid during Plan.
194 BOOL fUncache; // only valid during Plan.
195 BOOTSTRAPPER_ACTION_STATE execute; // only valid during Plan.
196 BOOTSTRAPPER_ACTION_STATE rollback; // only valid during Plan.
197 BURN_DEPENDENCY_ACTION providerExecute; // only valid during Plan.
198 BURN_DEPENDENCY_ACTION providerRollback; // only valid during Plan.
199 BURN_DEPENDENCY_ACTION dependencyExecute; // only valid during Plan.
200 BURN_DEPENDENCY_ACTION dependencyRollback; // only valid during Plan.
201 BOOL fDependencyManagerWasHere; // only valid during Plan.
202 HRESULT hrCacheResult; // only valid during Apply.
203
204 BURN_PACKAGE_PAYLOAD* rgPayloads;
205 DWORD cPayloads;
206
207 BURN_DEPENDENCY_PROVIDER* rgDependencyProviders;
208 DWORD cDependencyProviders;
209
210 BURN_PACKAGE_TYPE type;
211 union
212 {
213 struct
214 {
215 LPWSTR sczDetectCondition;
216 LPWSTR sczInstallArguments;
217 LPWSTR sczRepairArguments;
218 LPWSTR sczUninstallArguments;
219 LPWSTR sczIgnoreDependencies;
220 LPWSTR sczAncestors;
221
222 BOOL fPseudoBundle;
223
224 BOOL fRepairable;
225 BURN_EXE_PROTOCOL_TYPE protocol;
226
227 BOOL fSupportsAncestors;
228
229 BURN_EXE_EXIT_CODE* rgExitCodes;
230 DWORD cExitCodes;
231
232 BURN_EXE_COMMAND_LINE_ARGUMENT* rgCommandLineArguments;
233 DWORD cCommandLineArguments;
234 } Exe;
235 struct
236 {
237 LPWSTR sczProductCode;
238 DWORD dwLanguage;
239 DWORD64 qwVersion;
240 LPWSTR sczInstalledProductCode;
241 DWORD64 qwInstalledVersion;
242 BOOL fDisplayInternalUI;
243 LPWSTR sczUpgradeCode;
244
245 BURN_MSIPROPERTY* rgProperties;
246 DWORD cProperties;
247
248 BURN_MSIFEATURE* rgFeatures;
249 DWORD cFeatures;
250
251 BURN_RELATED_MSI* rgRelatedMsis;
252 DWORD cRelatedMsis;
253
254 _BURN_PACKAGE** rgpSlipstreamMspPackages;
255 LPWSTR* rgsczSlipstreamMspPackageIds;
256 DWORD cSlipstreamMspPackages;
257
258 BOOL fCompatibleInstalled;
259 } Msi;
260 struct
261 {
262 LPWSTR sczPatchCode;
263 LPWSTR sczApplicabilityXml;
264 BOOL fDisplayInternalUI;
265
266 BURN_MSIPROPERTY* rgProperties;
267 DWORD cProperties;
268
269 BURN_MSPTARGETPRODUCT* rgTargetProducts;
270 DWORD cTargetProductCodes;
271 } Msp;
272 struct
273 {
274 LPWSTR sczDetectCondition;
275 LPWSTR sczKB;
276 } Msu;
277 };
278} BURN_PACKAGE;
279
280typedef struct _BURN_PACKAGES
281{
282 BURN_ROLLBACK_BOUNDARY* rgRollbackBoundaries;
283 DWORD cRollbackBoundaries;
284
285 BURN_PACKAGE* rgPackages;
286 DWORD cPackages;
287
288 BURN_PACKAGE* rgCompatiblePackages;
289 DWORD cCompatiblePackages;
290
291 BURN_PATCH_TARGETCODE* rgPatchTargetCodes;
292 DWORD cPatchTargetCodes;
293
294 MSIPATCHSEQUENCEINFOW* rgPatchInfo;
295 BURN_PACKAGE** rgPatchInfoToPackage; // direct lookup from patch information to the (MSP) package it describes.
296 // Thus this array is the exact same size as rgPatchInfo.
297 DWORD cPatchInfo;
298} BURN_PACKAGES;
299
300
301// function declarations
302
303HRESULT PackagesParseFromXml(
304 __in BURN_PACKAGES* pPackages,
305 __in BURN_PAYLOADS* pPayloads,
306 __in IXMLDOMNode* pixnBundle
307 );
308void PackageUninitialize(
309 __in BURN_PACKAGE* pPackage
310 );
311void PackagesUninitialize(
312 __in BURN_PACKAGES* pPackages
313 );
314HRESULT PackageFindById(
315 __in BURN_PACKAGES* pPackages,
316 __in_z LPCWSTR wzId,
317 __out BURN_PACKAGE** ppPackage
318 );
319HRESULT PackageFindRelatedById(
320 __in BURN_RELATED_BUNDLES* pRelatedBundles,
321 __in_z LPCWSTR wzId,
322 __out BURN_PACKAGE** ppPackage
323 );
324HRESULT PackageGetProperty(
325 __in const BURN_PACKAGE* pPackage,
326 __in_z LPCWSTR wzProperty,
327 __out_z_opt LPWSTR* psczValue
328 );
329HRESULT PackageEnsureCompatiblePackagesArray(
330 __in BURN_PACKAGES* pPackages
331 );
332
333
334#if defined(__cplusplus)
335}
336#endif
diff --git a/src/engine/payload.cpp b/src/engine/payload.cpp
new file mode 100644
index 00000000..6833288f
--- /dev/null
+++ b/src/engine/payload.cpp
@@ -0,0 +1,367 @@
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#include "precomp.h"
4
5
6// internal function declarations
7
8static HRESULT FindEmbeddedBySourcePath(
9 __in BURN_PAYLOADS* pPayloads,
10 __in_opt BURN_CONTAINER* pContainer,
11 __in_z LPCWSTR wzStreamName,
12 __out BURN_PAYLOAD** ppPayload
13 );
14
15
16// function definitions
17
18extern "C" HRESULT PayloadsParseFromXml(
19 __in BURN_PAYLOADS* pPayloads,
20 __in_opt BURN_CONTAINERS* pContainers,
21 __in_opt BURN_CATALOGS* pCatalogs,
22 __in IXMLDOMNode* pixnBundle
23 )
24{
25 HRESULT hr = S_OK;
26 IXMLDOMNodeList* pixnNodes = NULL;
27 IXMLDOMNode* pixnNode = NULL;
28 DWORD cNodes = 0;
29 LPWSTR scz = NULL;
30
31 // select payload nodes
32 hr = XmlSelectNodes(pixnBundle, L"Payload", &pixnNodes);
33 ExitOnFailure(hr, "Failed to select payload nodes.");
34
35 // get payload node count
36 hr = pixnNodes->get_length((long*)&cNodes);
37 ExitOnFailure(hr, "Failed to get payload node count.");
38
39 if (!cNodes)
40 {
41 ExitFunction();
42 }
43
44 // allocate memory for payloads
45 pPayloads->rgPayloads = (BURN_PAYLOAD*)MemAlloc(sizeof(BURN_PAYLOAD) * cNodes, TRUE);
46 ExitOnNull(pPayloads->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate memory for payload structs.");
47
48 pPayloads->cPayloads = cNodes;
49
50 // parse search elements
51 for (DWORD i = 0; i < cNodes; ++i)
52 {
53 BURN_PAYLOAD* pPayload = &pPayloads->rgPayloads[i];
54
55 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
56 ExitOnFailure(hr, "Failed to get next node.");
57
58 // @Id
59 hr = XmlGetAttributeEx(pixnNode, L"Id", &pPayload->sczKey);
60 ExitOnFailure(hr, "Failed to get @Id.");
61
62 // @FilePath
63 hr = XmlGetAttributeEx(pixnNode, L"FilePath", &pPayload->sczFilePath);
64 ExitOnFailure(hr, "Failed to get @FilePath.");
65
66 // @Packaging
67 hr = XmlGetAttributeEx(pixnNode, L"Packaging", &scz);
68 ExitOnFailure(hr, "Failed to get @Packaging.");
69
70 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"download", -1))
71 {
72 pPayload->packaging = BURN_PAYLOAD_PACKAGING_DOWNLOAD;
73 }
74 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"embedded", -1))
75 {
76 pPayload->packaging = BURN_PAYLOAD_PACKAGING_EMBEDDED;
77 }
78 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"external", -1))
79 {
80 pPayload->packaging = BURN_PAYLOAD_PACKAGING_EXTERNAL;
81 }
82 else
83 {
84 hr = E_INVALIDARG;
85 ExitOnFailure(hr, "Invalid value for @Packaging: %ls", scz);
86 }
87
88 // @Container
89 if (pContainers)
90 {
91 hr = XmlGetAttributeEx(pixnNode, L"Container", &scz);
92 if (E_NOTFOUND != hr || BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging)
93 {
94 ExitOnFailure(hr, "Failed to get @Container.");
95
96 // find container
97 hr = ContainerFindById(pContainers, scz, &pPayload->pContainer);
98 ExitOnFailure(hr, "Failed to to find container: %ls", scz);
99 }
100 }
101
102 // @LayoutOnly
103 hr = XmlGetYesNoAttribute(pixnNode, L"LayoutOnly", &pPayload->fLayoutOnly);
104 if (E_NOTFOUND != hr)
105 {
106 ExitOnFailure(hr, "Failed to get @LayoutOnly.");
107 }
108
109 // @SourcePath
110 hr = XmlGetAttributeEx(pixnNode, L"SourcePath", &pPayload->sczSourcePath);
111 if (E_NOTFOUND != hr || BURN_PAYLOAD_PACKAGING_DOWNLOAD != pPayload->packaging)
112 {
113 ExitOnFailure(hr, "Failed to get @SourcePath.");
114 }
115
116 // @DownloadUrl
117 hr = XmlGetAttributeEx(pixnNode, L"DownloadUrl", &pPayload->downloadSource.sczUrl);
118 if (E_NOTFOUND != hr || BURN_PAYLOAD_PACKAGING_DOWNLOAD == pPayload->packaging)
119 {
120 ExitOnFailure(hr, "Failed to get @DownloadUrl.");
121 }
122
123 // @FileSize
124 hr = XmlGetAttributeEx(pixnNode, L"FileSize", &scz);
125 if (E_NOTFOUND != hr)
126 {
127 ExitOnFailure(hr, "Failed to get @FileSize.");
128
129 hr = StrStringToUInt64(scz, 0, &pPayload->qwFileSize);
130 ExitOnFailure(hr, "Failed to parse @FileSize.");
131 }
132
133 // @CertificateAuthorityKeyIdentifier
134 hr = XmlGetAttributeEx(pixnNode, L"CertificateRootPublicKeyIdentifier", &scz);
135 if (E_NOTFOUND != hr)
136 {
137 ExitOnFailure(hr, "Failed to get @CertificateRootPublicKeyIdentifier.");
138
139 hr = StrAllocHexDecode(scz, &pPayload->pbCertificateRootPublicKeyIdentifier, &pPayload->cbCertificateRootPublicKeyIdentifier);
140 ExitOnFailure(hr, "Failed to hex decode @CertificateRootPublicKeyIdentifier.");
141 }
142
143 // @CertificateThumbprint
144 hr = XmlGetAttributeEx(pixnNode, L"CertificateRootThumbprint", &scz);
145 if (E_NOTFOUND != hr)
146 {
147 ExitOnFailure(hr, "Failed to get @CertificateRootThumbprint.");
148
149 hr = StrAllocHexDecode(scz, &pPayload->pbCertificateRootThumbprint, &pPayload->cbCertificateRootThumbprint);
150 ExitOnFailure(hr, "Failed to hex decode @CertificateRootThumbprint.");
151 }
152
153 // @Hash
154 hr = XmlGetAttributeEx(pixnNode, L"Hash", &scz);
155 ExitOnFailure(hr, "Failed to get @Hash.");
156
157 hr = StrAllocHexDecode(scz, &pPayload->pbHash, &pPayload->cbHash);
158 ExitOnFailure(hr, "Failed to hex decode the Payload/@Hash.");
159
160 // @Catalog
161 hr = XmlGetAttributeEx(pixnNode, L"Catalog", &scz);
162 if (E_NOTFOUND != hr)
163 {
164 ExitOnFailure(hr, "Failed to get @Catalog.");
165
166 hr = CatalogFindById(pCatalogs, scz, &pPayload->pCatalog);
167 ExitOnFailure(hr, "Failed to find catalog.");
168 }
169
170 // prepare next iteration
171 ReleaseNullObject(pixnNode);
172 }
173
174 hr = S_OK;
175
176LExit:
177 ReleaseObject(pixnNodes);
178 ReleaseObject(pixnNode);
179 ReleaseStr(scz);
180
181 return hr;
182}
183
184extern "C" void PayloadsUninitialize(
185 __in BURN_PAYLOADS* pPayloads
186 )
187{
188 if (pPayloads->rgPayloads)
189 {
190 for (DWORD i = 0; i < pPayloads->cPayloads; ++i)
191 {
192 BURN_PAYLOAD* pPayload = &pPayloads->rgPayloads[i];
193
194 ReleaseStr(pPayload->sczKey);
195 ReleaseStr(pPayload->sczFilePath);
196 ReleaseMem(pPayload->pbHash);
197 ReleaseMem(pPayload->pbCertificateRootThumbprint);
198 ReleaseMem(pPayload->pbCertificateRootPublicKeyIdentifier);
199 ReleaseStr(pPayload->sczSourcePath);
200 ReleaseStr(pPayload->sczLocalFilePath);
201 ReleaseStr(pPayload->downloadSource.sczUrl);
202 ReleaseStr(pPayload->downloadSource.sczUser);
203 ReleaseStr(pPayload->downloadSource.sczPassword);
204 }
205 MemFree(pPayloads->rgPayloads);
206 }
207
208 // clear struct
209 memset(pPayloads, 0, sizeof(BURN_PAYLOADS));
210}
211
212extern "C" HRESULT PayloadExtractFromContainer(
213 __in BURN_PAYLOADS* pPayloads,
214 __in_opt BURN_CONTAINER* pContainer,
215 __in BURN_CONTAINER_CONTEXT* pContainerContext,
216 __in_z LPCWSTR wzTargetDir
217 )
218{
219 HRESULT hr = S_OK;
220 LPWSTR sczStreamName = NULL;
221 LPWSTR sczDirectory = NULL;
222 BURN_PAYLOAD* pPayload = NULL;
223
224 // extract all payloads
225 for (;;)
226 {
227 // get next stream
228 hr = ContainerNextStream(pContainerContext, &sczStreamName);
229 if (E_NOMOREITEMS == hr)
230 {
231 hr = S_OK;
232 break;
233 }
234 ExitOnFailure(hr, "Failed to get next stream.");
235
236 // find payload by stream name
237 hr = FindEmbeddedBySourcePath(pPayloads, pContainer, sczStreamName, &pPayload);
238 ExitOnFailure(hr, "Failed to find embedded payload: %ls", sczStreamName);
239
240 // make file path
241 hr = PathConcat(wzTargetDir, pPayload->sczFilePath, &pPayload->sczLocalFilePath);
242 ExitOnFailure(hr, "Failed to concat file paths.");
243
244 // extract file
245 hr = PathGetDirectory(pPayload->sczLocalFilePath, &sczDirectory);
246 ExitOnFailure(hr, "Failed to get directory portion of local file path");
247
248 hr = DirEnsureExists(sczDirectory, NULL);
249 ExitOnFailure(hr, "Failed to ensure directory exists");
250
251 hr = ContainerStreamToFile(pContainerContext, pPayload->sczLocalFilePath);
252 ExitOnFailure(hr, "Failed to extract file.");
253
254 // flag that the payload has been acquired
255 pPayload->state = BURN_PAYLOAD_STATE_ACQUIRED;
256 }
257
258 // locate any payloads that were not extracted
259 for (DWORD i = 0; i < pPayloads->cPayloads; ++i)
260 {
261 pPayload = &pPayloads->rgPayloads[i];
262
263 // if the payload is part of the container
264 if (!pContainer || pPayload->pContainer == pContainer)
265 {
266 // if the payload has not been acquired
267 if (BURN_PAYLOAD_STATE_ACQUIRED > pPayload->state)
268 {
269 hr = E_INVALIDDATA;
270 ExitOnRootFailure(hr, "Payload was not found in container: %ls", pPayload->sczKey);
271 }
272 }
273 }
274
275LExit:
276 ReleaseStr(sczStreamName);
277 ReleaseStr(sczDirectory);
278
279 return hr;
280}
281
282extern "C" HRESULT PayloadFindById(
283 __in BURN_PAYLOADS* pPayloads,
284 __in_z LPCWSTR wzId,
285 __out BURN_PAYLOAD** ppPayload
286 )
287{
288 HRESULT hr = S_OK;
289 BURN_PAYLOAD* pPayload = NULL;
290
291 for (DWORD i = 0; i < pPayloads->cPayloads; ++i)
292 {
293 pPayload = &pPayloads->rgPayloads[i];
294
295 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPayload->sczKey, -1, wzId, -1))
296 {
297 *ppPayload = pPayload;
298 ExitFunction1(hr = S_OK);
299 }
300 }
301
302 hr = E_NOTFOUND;
303
304LExit:
305 return hr;
306}
307
308extern "C" HRESULT PayloadFindEmbeddedBySourcePath(
309 __in BURN_PAYLOADS* pPayloads,
310 __in_z LPCWSTR wzStreamName,
311 __out BURN_PAYLOAD** ppPayload
312 )
313{
314 HRESULT hr = S_OK;
315 BURN_PAYLOAD* pPayload = NULL;
316
317 for (DWORD i = 0; i < pPayloads->cPayloads; ++i)
318 {
319 pPayload = &pPayloads->rgPayloads[i];
320
321 if (BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging)
322 {
323 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPayload->sczSourcePath, -1, wzStreamName, -1))
324 {
325 *ppPayload = pPayload;
326 ExitFunction1(hr = S_OK);
327 }
328 }
329 }
330
331 hr = E_NOTFOUND;
332
333LExit:
334 return hr;
335}
336
337
338// internal function definitions
339
340static HRESULT FindEmbeddedBySourcePath(
341 __in BURN_PAYLOADS* pPayloads,
342 __in_opt BURN_CONTAINER* pContainer,
343 __in_z LPCWSTR wzStreamName,
344 __out BURN_PAYLOAD** ppPayload
345 )
346{
347 HRESULT hr = S_OK;
348
349 for (DWORD i = 0; i < pPayloads->cPayloads; ++i)
350 {
351 BURN_PAYLOAD* pPayload = &pPayloads->rgPayloads[i];
352
353 if (BURN_PAYLOAD_PACKAGING_EMBEDDED == pPayload->packaging && (!pContainer || pPayload->pContainer == pContainer))
354 {
355 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPayload->sczSourcePath, -1, wzStreamName, -1))
356 {
357 *ppPayload = pPayload;
358 ExitFunction1(hr = S_OK);
359 }
360 }
361 }
362
363 hr = E_NOTFOUND;
364
365LExit:
366 return hr;
367}
diff --git a/src/engine/payload.h b/src/engine/payload.h
new file mode 100644
index 00000000..0c7b68e4
--- /dev/null
+++ b/src/engine/payload.h
@@ -0,0 +1,93 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// constants
11
12enum BURN_PAYLOAD_PACKAGING
13{
14 BURN_PAYLOAD_PACKAGING_NONE,
15 BURN_PAYLOAD_PACKAGING_DOWNLOAD,
16 BURN_PAYLOAD_PACKAGING_EMBEDDED,
17 BURN_PAYLOAD_PACKAGING_EXTERNAL,
18};
19
20enum BURN_PAYLOAD_STATE
21{
22 BURN_PAYLOAD_STATE_NONE,
23 BURN_PAYLOAD_STATE_ACQUIRED,
24 BURN_PAYLOAD_STATE_CACHED,
25};
26
27
28// structs
29
30typedef struct _BURN_PAYLOAD
31{
32 LPWSTR sczKey;
33 BURN_PAYLOAD_PACKAGING packaging;
34 BOOL fLayoutOnly;
35 DWORD64 qwFileSize;
36 LPWSTR sczFilePath; // file path relative to the execute location
37
38 BURN_CATALOG *pCatalog; // used to verify this payload
39 BYTE* pbCertificateRootPublicKeyIdentifier;
40 DWORD cbCertificateRootPublicKeyIdentifier;
41 BYTE* pbCertificateRootThumbprint;
42 DWORD cbCertificateRootThumbprint;
43 BYTE* pbHash;
44 DWORD cbHash;
45
46 LPWSTR sczSourcePath;
47 BURN_CONTAINER* pContainer;
48 DOWNLOAD_SOURCE downloadSource;
49
50 // mutable members
51 BURN_PAYLOAD_STATE state;
52 LPWSTR sczLocalFilePath; // location of extracted or downloaded copy
53} BURN_PAYLOAD;
54
55typedef struct _BURN_PAYLOADS
56{
57 BURN_PAYLOAD* rgPayloads;
58 DWORD cPayloads;
59} BURN_PAYLOADS;
60
61
62// functions
63
64HRESULT PayloadsParseFromXml(
65 __in BURN_PAYLOADS* pPayloads,
66 __in_opt BURN_CONTAINERS* pContainers,
67 __in_opt BURN_CATALOGS* pCatalogs,
68 __in IXMLDOMNode* pixnBundle
69 );
70void PayloadsUninitialize(
71 __in BURN_PAYLOADS* pPayloads
72 );
73HRESULT PayloadExtractFromContainer(
74 __in BURN_PAYLOADS* pPayloads,
75 __in_opt BURN_CONTAINER* pContainer,
76 __in BURN_CONTAINER_CONTEXT* pContainerContext,
77 __in_z LPCWSTR wzTargetDir
78 );
79HRESULT PayloadFindById(
80 __in BURN_PAYLOADS* pPayloads,
81 __in_z LPCWSTR wzId,
82 __out BURN_PAYLOAD** ppPayload
83 );
84HRESULT PayloadFindEmbeddedBySourcePath(
85 __in BURN_PAYLOADS* pPayloads,
86 __in_z LPCWSTR wzStreamName,
87 __out BURN_PAYLOAD** ppPayload
88 );
89
90
91#if defined(__cplusplus)
92}
93#endif
diff --git a/src/engine/pipe.cpp b/src/engine/pipe.cpp
new file mode 100644
index 00000000..7ecc4859
--- /dev/null
+++ b/src/engine/pipe.cpp
@@ -0,0 +1,873 @@
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#include "precomp.h"
4
5static const DWORD PIPE_64KB = 64 * 1024;
6static const DWORD PIPE_WAIT_FOR_CONNECTION = 100; // wait a 10th of a second,
7static const DWORD PIPE_RETRY_FOR_CONNECTION = 1800; // for up to 3 minutes.
8
9static const LPCWSTR PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls";
10static const LPCWSTR CACHE_PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls.Cache";
11
12static HRESULT AllocatePipeMessage(
13 __in DWORD dwMessage,
14 __in_bcount_opt(cbData) LPVOID pvData,
15 __in DWORD cbData,
16 __out_bcount(cb) LPVOID* ppvMessage,
17 __out DWORD* cbMessage
18 );
19static void FreePipeMessage(
20 __in BURN_PIPE_MESSAGE *pMsg
21 );
22static HRESULT WritePipeMessage(
23 __in HANDLE hPipe,
24 __in DWORD dwMessage,
25 __in_bcount_opt(cbData) LPVOID pvData,
26 __in DWORD cbData
27 );
28static HRESULT GetPipeMessage(
29 __in HANDLE hPipe,
30 __in BURN_PIPE_MESSAGE* pMsg
31 );
32static HRESULT ChildPipeConnected(
33 __in HANDLE hPipe,
34 __in_z LPCWSTR wzSecret,
35 __inout DWORD* pdwProcessId
36 );
37
38
39
40/*******************************************************************
41 PipeConnectionInitialize - initialize pipe connection data.
42
43*******************************************************************/
44void PipeConnectionInitialize(
45 __in BURN_PIPE_CONNECTION* pConnection
46 )
47{
48 memset(pConnection, 0, sizeof(BURN_PIPE_CONNECTION));
49 pConnection->hPipe = INVALID_HANDLE_VALUE;
50 pConnection->hCachePipe = INVALID_HANDLE_VALUE;
51}
52
53/*******************************************************************
54 PipeConnectionUninitialize - free data in a pipe connection.
55
56*******************************************************************/
57void PipeConnectionUninitialize(
58 __in BURN_PIPE_CONNECTION* pConnection
59 )
60{
61 ReleaseFileHandle(pConnection->hCachePipe);
62 ReleaseFileHandle(pConnection->hPipe);
63 ReleaseHandle(pConnection->hProcess);
64 ReleaseStr(pConnection->sczSecret);
65 ReleaseStr(pConnection->sczName);
66
67 memset(pConnection, 0, sizeof(BURN_PIPE_CONNECTION));
68 pConnection->hPipe = INVALID_HANDLE_VALUE;
69 pConnection->hCachePipe = INVALID_HANDLE_VALUE;
70}
71
72/*******************************************************************
73 PipeSendMessage -
74
75*******************************************************************/
76extern "C" HRESULT PipeSendMessage(
77 __in HANDLE hPipe,
78 __in DWORD dwMessage,
79 __in_bcount_opt(cbData) LPVOID pvData,
80 __in DWORD cbData,
81 __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback,
82 __in_opt LPVOID pvContext,
83 __out DWORD* pdwResult
84 )
85{
86 HRESULT hr = S_OK;
87 BURN_PIPE_RESULT result = { };
88
89 hr = WritePipeMessage(hPipe, dwMessage, pvData, cbData);
90 ExitOnFailure(hr, "Failed to write send message to pipe.");
91
92 hr = PipePumpMessages(hPipe, pfnCallback, pvContext, &result);
93 ExitOnFailure(hr, "Failed to pump messages during send message to pipe.");
94
95 *pdwResult = result.dwResult;
96
97LExit:
98 return hr;
99}
100
101/*******************************************************************
102 PipePumpMessages -
103
104*******************************************************************/
105extern "C" HRESULT PipePumpMessages(
106 __in HANDLE hPipe,
107 __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback,
108 __in_opt LPVOID pvContext,
109 __in BURN_PIPE_RESULT* pResult
110 )
111{
112 HRESULT hr = S_OK;
113 BURN_PIPE_MESSAGE msg = { };
114 SIZE_T iData = 0;
115 LPSTR sczMessage = NULL;
116 DWORD dwResult = 0;
117
118 // Pump messages from child process.
119 while (S_OK == (hr = GetPipeMessage(hPipe, &msg)))
120 {
121 switch (msg.dwMessage)
122 {
123 case BURN_PIPE_MESSAGE_TYPE_LOG:
124 iData = 0;
125
126 hr = BuffReadStringAnsi((BYTE*)msg.pvData, msg.cbData, &iData, &sczMessage);
127 ExitOnFailure(hr, "Failed to read log message.");
128
129 hr = LogStringWorkRaw(sczMessage);
130 ExitOnFailure(hr, "Failed to write log message:'%hs'.", sczMessage);
131
132 dwResult = static_cast<DWORD>(hr);
133 break;
134
135 case BURN_PIPE_MESSAGE_TYPE_COMPLETE:
136 if (!msg.pvData || sizeof(DWORD) != msg.cbData)
137 {
138 hr = E_INVALIDARG;
139 ExitOnRootFailure(hr, "No status returned to PipePumpMessages()");
140 }
141
142 pResult->dwResult = *static_cast<DWORD*>(msg.pvData);
143 ExitFunction1(hr = S_OK); // exit loop.
144
145 case BURN_PIPE_MESSAGE_TYPE_TERMINATE:
146 iData = 0;
147
148 hr = BuffReadNumber(static_cast<BYTE*>(msg.pvData), msg.cbData, &iData, &pResult->dwResult);
149 ExitOnFailure(hr, "Failed to read returned result to PipePumpMessages()");
150
151 if (sizeof(DWORD) * 2 == msg.cbData)
152 {
153 hr = BuffReadNumber(static_cast<BYTE*>(msg.pvData), msg.cbData, &iData, (DWORD*)&pResult->fRestart);
154 ExitOnFailure(hr, "Failed to read returned restart to PipePumpMessages()");
155 }
156
157 ExitFunction1(hr = S_OK); // exit loop.
158
159 default:
160 if (pfnCallback)
161 {
162 hr = pfnCallback(&msg, pvContext, &dwResult);
163 }
164 else
165 {
166 hr = E_INVALIDARG;
167 }
168 ExitOnFailure(hr, "Failed to process message: %u", msg.dwMessage);
169 break;
170 }
171
172 // post result
173 hr = WritePipeMessage(hPipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_COMPLETE), &dwResult, sizeof(dwResult));
174 ExitOnFailure(hr, "Failed to post result to child process.");
175
176 FreePipeMessage(&msg);
177 }
178 ExitOnFailure(hr, "Failed to get message over pipe");
179
180 if (S_FALSE == hr)
181 {
182 hr = S_OK;
183 }
184
185LExit:
186 ReleaseStr(sczMessage);
187 FreePipeMessage(&msg);
188
189 return hr;
190}
191
192/*******************************************************************
193 PipeCreateNameAndSecret -
194
195*******************************************************************/
196extern "C" HRESULT PipeCreateNameAndSecret(
197 __out_z LPWSTR *psczConnectionName,
198 __out_z LPWSTR *psczSecret
199 )
200{
201 HRESULT hr = S_OK;
202 WCHAR wzGuid[GUID_STRING_LENGTH];
203 LPWSTR sczConnectionName = NULL;
204 LPWSTR sczSecret = NULL;
205
206 // Create the unique pipe name.
207 hr = GuidFixedCreate(wzGuid);
208 ExitOnRootFailure(hr, "Failed to create pipe guid.");
209
210 hr = StrAllocFormatted(&sczConnectionName, L"BurnPipe.%s", wzGuid);
211 ExitOnFailure(hr, "Failed to allocate pipe name.");
212
213 // Create the unique client secret.
214 hr = GuidFixedCreate(wzGuid);
215 ExitOnRootFailure(hr, "Failed to create pipe secret.");
216
217 hr = StrAllocString(&sczSecret, wzGuid, 0);
218 ExitOnFailure(hr, "Failed to allocate pipe secret.");
219
220 *psczConnectionName = sczConnectionName;
221 sczConnectionName = NULL;
222 *psczSecret = sczSecret;
223 sczSecret = NULL;
224
225LExit:
226 ReleaseStr(sczSecret);
227 ReleaseStr(sczConnectionName);
228
229 return hr;
230}
231
232/*******************************************************************
233 PipeCreatePipes - create the pipes and event to signal child process.
234
235*******************************************************************/
236extern "C" HRESULT PipeCreatePipes(
237 __in BURN_PIPE_CONNECTION* pConnection,
238 __in BOOL fCreateCachePipe,
239 __out HANDLE* phEvent
240 )
241{
242 Assert(pConnection->sczName);
243 Assert(INVALID_HANDLE_VALUE == pConnection->hPipe);
244 Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe);
245
246 HRESULT hr = S_OK;
247 PSECURITY_DESCRIPTOR psd = NULL;
248 SECURITY_ATTRIBUTES sa = { };
249 LPWSTR sczFullPipeName = NULL;
250 HANDLE hPipe = INVALID_HANDLE_VALUE;
251 HANDLE hCachePipe = INVALID_HANDLE_VALUE;
252
253 // Only the grant special rights when the pipe is being used for "embedded"
254 // scenarios (aka: there is no cache pipe).
255 if (!fCreateCachePipe)
256 {
257 // Create the security descriptor that grants read/write/sync access to Everyone.
258 // TODO: consider locking down "WD" to LogonIds (logon session)
259 LPCWSTR wzSddl = L"D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW0x00100000;;;WD)";
260 if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(wzSddl, SDDL_REVISION_1, &psd, NULL))
261 {
262 ExitWithLastError(hr, "Failed to create the security descriptor for the connection event and pipe.");
263 }
264
265 sa.nLength = sizeof(sa);
266 sa.lpSecurityDescriptor = psd;
267 sa.bInheritHandle = FALSE;
268 }
269
270 // Create the pipe.
271 hr = StrAllocFormatted(&sczFullPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName);
272 ExitOnFailure(hr, "Failed to allocate full name of pipe: %ls", pConnection->sczName);
273
274 // TODO: consider using overlapped IO to do waits on the pipe and still be able to cancel and such.
275 hPipe = ::CreateNamedPipeW(sczFullPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, PIPE_64KB, PIPE_64KB, 1, psd ? &sa : NULL);
276 if (INVALID_HANDLE_VALUE == hPipe)
277 {
278 ExitWithLastError(hr, "Failed to create pipe: %ls", sczFullPipeName);
279 }
280
281 if (fCreateCachePipe)
282 {
283 // Create the cache pipe.
284 hr = StrAllocFormatted(&sczFullPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName);
285 ExitOnFailure(hr, "Failed to allocate full name of cache pipe: %ls", pConnection->sczName);
286
287 hCachePipe = ::CreateNamedPipeW(sczFullPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, PIPE_64KB, PIPE_64KB, 1, NULL);
288 if (INVALID_HANDLE_VALUE == hCachePipe)
289 {
290 ExitWithLastError(hr, "Failed to create pipe: %ls", sczFullPipeName);
291 }
292 }
293
294 pConnection->hCachePipe = hCachePipe;
295 hCachePipe = INVALID_HANDLE_VALUE;
296
297 pConnection->hPipe = hPipe;
298 hPipe = INVALID_HANDLE_VALUE;
299
300 // TODO: remove the following
301 *phEvent = NULL;
302
303LExit:
304 ReleaseFileHandle(hCachePipe);
305 ReleaseFileHandle(hPipe);
306 ReleaseStr(sczFullPipeName);
307
308 if (psd)
309 {
310 ::LocalFree(psd);
311 }
312
313 return hr;
314}
315
316/*******************************************************************
317 PipeLaunchParentProcess - Called from the per-machine process to create
318 a per-user process and set up the
319 communication pipe.
320
321*******************************************************************/
322const LPCWSTR BURN_COMMANDLINE_SWITCH_UNELEVATED = L"burn.unelevated";
323HRESULT PipeLaunchParentProcess(
324 __in_z LPCWSTR wzCommandLine,
325 __in int nCmdShow,
326 __in_z LPWSTR sczConnectionName,
327 __in_z LPWSTR sczSecret,
328 __in BOOL /*fDisableUnelevate*/
329 )
330{
331 HRESULT hr = S_OK;
332 DWORD dwProcessId = 0;
333 LPWSTR sczBurnPath = NULL;
334 LPWSTR sczParameters = NULL;
335 HANDLE hProcess = NULL;
336
337 dwProcessId = ::GetCurrentProcessId();
338
339 hr = PathForCurrentProcess(&sczBurnPath, NULL);
340 ExitOnFailure(hr, "Failed to get current process path.");
341
342 hr = StrAllocFormatted(&sczParameters, L"-%ls %ls %ls %u %ls", BURN_COMMANDLINE_SWITCH_UNELEVATED, sczConnectionName, sczSecret, dwProcessId, wzCommandLine);
343 ExitOnFailure(hr, "Failed to allocate parameters for unelevated process.");
344
345#ifdef ENABLE_UNELEVATE
346 if (fDisableUnelevate)
347 {
348 hr = ProcExec(sczBurnPath, sczParameters, nCmdShow, &hProcess);
349 ExitOnFailure(hr, "Failed to launch parent process with unelevate disabled: %ls", sczBurnPath);
350 }
351 else
352 {
353 // Try to launch unelevated and if that fails for any reason, try launch our process normally (even though that may make it elevated).
354 hr = ProcExecuteAsInteractiveUser(sczBurnPath, sczParameters, &hProcess);
355 if (FAILED(hr))
356 {
357 hr = ShelExecUnelevated(sczBurnPath, sczParameters, L"open", NULL, nCmdShow);
358 if (FAILED(hr))
359 {
360 hr = ShelExec(sczBurnPath, sczParameters, L"open", NULL, nCmdShow, NULL, NULL);
361 ExitOnFailure(hr, "Failed to launch parent process: %ls", sczBurnPath);
362 }
363 }
364 }
365#else
366 hr = ProcExec(sczBurnPath, sczParameters, nCmdShow, &hProcess);
367 ExitOnFailure(hr, "Failed to launch parent process with unelevate disabled: %ls", sczBurnPath);
368#endif
369
370LExit:
371 ReleaseHandle(hProcess);
372 ReleaseStr(sczParameters);
373 ReleaseStr(sczBurnPath);
374
375 return hr;
376}
377
378/*******************************************************************
379 PipeLaunchChildProcess - Called from the per-user process to create
380 the per-machine process and set up the
381 communication pipe.
382
383*******************************************************************/
384extern "C" HRESULT PipeLaunchChildProcess(
385 __in_z LPCWSTR wzExecutablePath,
386 __in BURN_PIPE_CONNECTION* pConnection,
387 __in BOOL fElevate,
388 __in_opt HWND hwndParent
389 )
390{
391 HRESULT hr = S_OK;
392 DWORD dwCurrentProcessId = ::GetCurrentProcessId();
393 LPWSTR sczParameters = NULL;
394 OS_VERSION osVersion = OS_VERSION_UNKNOWN;
395 DWORD dwServicePack = 0;
396 LPCWSTR wzVerb = NULL;
397 HANDLE hProcess = NULL;
398
399 hr = StrAllocFormatted(&sczParameters, L"-q -%ls %ls %ls %u", BURN_COMMANDLINE_SWITCH_ELEVATED, pConnection->sczName, pConnection->sczSecret, dwCurrentProcessId);
400 ExitOnFailure(hr, "Failed to allocate parameters for elevated process.");
401
402 OsGetVersion(&osVersion, &dwServicePack);
403 wzVerb = (OS_VERSION_VISTA > osVersion) || !fElevate ? L"open" : L"runas";
404
405 // Since ShellExecuteEx doesn't support passing inherited handles, don't bother with CoreAppendFileHandleSelfToCommandLine.
406 // We could fallback to using ::DuplicateHandle to inject the file handle later if necessary.
407 hr = ShelExec(wzExecutablePath, sczParameters, wzVerb, NULL, SW_HIDE, hwndParent, &hProcess);
408 ExitOnFailure(hr, "Failed to launch elevated child process: %ls", wzExecutablePath);
409
410 pConnection->dwProcessId = ::GetProcessId(hProcess);
411 pConnection->hProcess = hProcess;
412 hProcess = NULL;
413
414LExit:
415 ReleaseHandle(hProcess);
416 ReleaseStr(sczParameters);
417
418 return hr;
419}
420
421/*******************************************************************
422 PipeWaitForChildConnect -
423
424*******************************************************************/
425extern "C" HRESULT PipeWaitForChildConnect(
426 __in BURN_PIPE_CONNECTION* pConnection
427 )
428{
429 HRESULT hr = S_OK;
430 HANDLE hPipes[2] = { pConnection->hPipe, pConnection->hCachePipe};
431 LPCWSTR wzSecret = pConnection->sczSecret;
432 DWORD cbSecret = lstrlenW(wzSecret) * sizeof(WCHAR);
433 DWORD dwCurrentProcessId = ::GetCurrentProcessId();
434 DWORD dwAck = 0;
435 DWORD cb = 0;
436
437 for (DWORD i = 0; i < countof(hPipes) && INVALID_HANDLE_VALUE != hPipes[i]; ++i)
438 {
439 HANDLE hPipe = hPipes[i];
440 DWORD dwPipeState = PIPE_READMODE_BYTE | PIPE_NOWAIT;
441
442 // Temporarily make the pipe non-blocking so we will not get stuck in ::ConnectNamedPipe() forever
443 // if the child decides not to show up.
444 if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL))
445 {
446 ExitWithLastError(hr, "Failed to set pipe to non-blocking.");
447 }
448
449 // Loop for a while waiting for a connection from child process.
450 DWORD cRetry = 0;
451 do
452 {
453 if (!::ConnectNamedPipe(hPipe, NULL))
454 {
455 DWORD er = ::GetLastError();
456 if (ERROR_PIPE_CONNECTED == er)
457 {
458 hr = S_OK;
459 break;
460 }
461 else if (ERROR_PIPE_LISTENING == er)
462 {
463 if (cRetry < PIPE_RETRY_FOR_CONNECTION)
464 {
465 hr = HRESULT_FROM_WIN32(er);
466 }
467 else
468 {
469 hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT);
470 break;
471 }
472
473 ++cRetry;
474 ::Sleep(PIPE_WAIT_FOR_CONNECTION);
475 }
476 else
477 {
478 hr = HRESULT_FROM_WIN32(er);
479 break;
480 }
481 }
482 } while (HRESULT_FROM_WIN32(ERROR_PIPE_LISTENING) == hr);
483 ExitOnRootFailure(hr, "Failed to wait for child to connect to pipe.");
484
485 // Put the pipe back in blocking mode.
486 dwPipeState = PIPE_READMODE_BYTE | PIPE_WAIT;
487 if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL))
488 {
489 ExitWithLastError(hr, "Failed to reset pipe to blocking.");
490 }
491
492 // Prove we are the one that created the elevated process by passing the secret.
493 if (!::WriteFile(hPipe, &cbSecret, sizeof(cbSecret), &cb, NULL))
494 {
495 ExitWithLastError(hr, "Failed to write secret length to pipe.");
496 }
497
498 if (!::WriteFile(hPipe, wzSecret, cbSecret, &cb, NULL))
499 {
500 ExitWithLastError(hr, "Failed to write secret to pipe.");
501 }
502
503 if (!::WriteFile(hPipe, &dwCurrentProcessId, sizeof(dwCurrentProcessId), &cb, NULL))
504 {
505 ExitWithLastError(hr, "Failed to write our process id to pipe.");
506 }
507
508 // Wait until the elevated process responds that it is ready to go.
509 if (!::ReadFile(hPipe, &dwAck, sizeof(dwAck), &cb, NULL))
510 {
511 ExitWithLastError(hr, "Failed to read ACK from pipe.");
512 }
513
514 // The ACK should match out expected child process id.
515 //if (pConnection->dwProcessId != dwAck)
516 //{
517 // hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
518 // ExitOnRootFailure(hr, "Incorrect ACK from elevated pipe: %u", dwAck);
519 //}
520 }
521
522LExit:
523 return hr;
524}
525
526/*******************************************************************
527 PipeTerminateChildProcess -
528
529*******************************************************************/
530extern "C" HRESULT PipeTerminateChildProcess(
531 __in BURN_PIPE_CONNECTION* pConnection,
532 __in DWORD dwParentExitCode,
533 __in BOOL fRestart
534 )
535{
536 HRESULT hr = S_OK;
537 BYTE* pbData = NULL;
538 SIZE_T cbData = 0;
539
540 // Prepare the exit message.
541 hr = BuffWriteNumber(&pbData, &cbData, dwParentExitCode);
542 ExitOnFailure(hr, "Failed to write exit code to message buffer.");
543
544 hr = BuffWriteNumber(&pbData, &cbData, fRestart);
545 ExitOnFailure(hr, "Failed to write restart to message buffer.");
546
547 // Send the messages.
548 if (INVALID_HANDLE_VALUE != pConnection->hCachePipe)
549 {
550 hr = WritePipeMessage(pConnection->hCachePipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_TERMINATE), pbData, cbData);
551 ExitOnFailure(hr, "Failed to post terminate message to child process cache thread.");
552 }
553
554 hr = WritePipeMessage(pConnection->hPipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_TERMINATE), pbData, cbData);
555 ExitOnFailure(hr, "Failed to post terminate message to child process.");
556
557 // If we were able to get a handle to the other process, wait for it to exit.
558 if (pConnection->hProcess)
559 {
560 if (WAIT_FAILED == ::WaitForSingleObject(pConnection->hProcess, PIPE_WAIT_FOR_CONNECTION * PIPE_RETRY_FOR_CONNECTION))
561 {
562 ExitWithLastError(hr, "Failed to wait for child process exit.");
563 }
564
565#ifdef DEBUG
566 DWORD dwChildExitCode = 0;
567 DWORD dwErrorCode = ERROR_SUCCESS;
568 BOOL fReturnedExitCode = ::GetExitCodeProcess(pConnection->hProcess, &dwChildExitCode);
569 if (!fReturnedExitCode)
570 {
571 dwErrorCode = ::GetLastError(); // if the other process is elevated and we are not, then we'll get ERROR_ACCESS_DENIED.
572
573 // The unit test use a thread instead of a process so try to get the exit code from
574 // the thread because we failed to get it from the process.
575 if (ERROR_INVALID_HANDLE == dwErrorCode)
576 {
577 fReturnedExitCode = ::GetExitCodeThread(pConnection->hProcess, &dwChildExitCode);
578 }
579 }
580 AssertSz((fReturnedExitCode && dwChildExitCode == dwParentExitCode) ||
581 (!fReturnedExitCode && ERROR_ACCESS_DENIED == dwErrorCode),
582 "Child elevated process did not return matching exit code to parent process.");
583#endif
584 }
585
586LExit:
587 return hr;
588}
589
590/*******************************************************************
591 PipeChildConnect - Called from the child process to connect back
592 to the pipe provided by the parent process.
593
594*******************************************************************/
595extern "C" HRESULT PipeChildConnect(
596 __in BURN_PIPE_CONNECTION* pConnection,
597 __in BOOL fConnectCachePipe
598 )
599{
600 Assert(pConnection->sczName);
601 Assert(pConnection->sczSecret);
602 Assert(!pConnection->hProcess);
603 Assert(INVALID_HANDLE_VALUE == pConnection->hPipe);
604 Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe);
605
606 HRESULT hr = S_OK;
607 LPWSTR sczPipeName = NULL;
608
609 // Try to connect to the parent.
610 hr = StrAllocFormatted(&sczPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName);
611 ExitOnFailure(hr, "Failed to allocate name of parent pipe.");
612
613 hr = E_UNEXPECTED;
614 for (DWORD cRetry = 0; FAILED(hr) && cRetry < PIPE_RETRY_FOR_CONNECTION; ++cRetry)
615 {
616 pConnection->hPipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
617 if (INVALID_HANDLE_VALUE == pConnection->hPipe)
618 {
619 hr = HRESULT_FROM_WIN32(::GetLastError());
620 if (E_FILENOTFOUND == hr) // if the pipe isn't created, call it a timeout waiting on the parent.
621 {
622 hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT);
623 }
624
625 ::Sleep(PIPE_WAIT_FOR_CONNECTION);
626 }
627 else // we have a connection, go with it.
628 {
629 hr = S_OK;
630 }
631 }
632 ExitOnRootFailure(hr, "Failed to open parent pipe: %ls", sczPipeName)
633
634 // Verify the parent and notify it that the child connected.
635 hr = ChildPipeConnected(pConnection->hPipe, pConnection->sczSecret, &pConnection->dwProcessId);
636 ExitOnFailure(hr, "Failed to verify parent pipe: %ls", sczPipeName);
637
638 if (fConnectCachePipe)
639 {
640 // Connect to the parent for the cache pipe.
641 hr = StrAllocFormatted(&sczPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName);
642 ExitOnFailure(hr, "Failed to allocate name of parent cache pipe.");
643
644 pConnection->hCachePipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
645 if (INVALID_HANDLE_VALUE == pConnection->hCachePipe)
646 {
647 ExitWithLastError(hr, "Failed to open parent pipe: %ls", sczPipeName)
648 }
649
650 // Verify the parent and notify it that the child connected.
651 hr = ChildPipeConnected(pConnection->hCachePipe, pConnection->sczSecret, &pConnection->dwProcessId);
652 ExitOnFailure(hr, "Failed to verify parent pipe: %ls", sczPipeName);
653 }
654
655 pConnection->hProcess = ::OpenProcess(SYNCHRONIZE, FALSE, pConnection->dwProcessId);
656 ExitOnNullWithLastError(pConnection->hProcess, hr, "Failed to open companion process with PID: %u", pConnection->dwProcessId);
657
658LExit:
659 ReleaseStr(sczPipeName);
660
661 return hr;
662}
663
664
665static HRESULT AllocatePipeMessage(
666 __in DWORD dwMessage,
667 __in_bcount_opt(cbData) LPVOID pvData,
668 __in DWORD cbData,
669 __out_bcount(cb) LPVOID* ppvMessage,
670 __out DWORD* cbMessage
671 )
672{
673 HRESULT hr = S_OK;
674 LPVOID pv = NULL;
675 DWORD cb = 0;
676
677 // If no data was provided, ensure the count of bytes is zero.
678 if (!pvData)
679 {
680 cbData = 0;
681 }
682
683 // Allocate the message.
684 cb = sizeof(dwMessage) + sizeof(cbData) + cbData;
685 pv = MemAlloc(cb, FALSE);
686 ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for message.");
687
688 memcpy_s(pv, cb, &dwMessage, sizeof(dwMessage));
689 memcpy_s(static_cast<BYTE*>(pv) + sizeof(dwMessage), cb - sizeof(dwMessage), &cbData, sizeof(cbData));
690 if (cbData)
691 {
692 memcpy_s(static_cast<BYTE*>(pv) + sizeof(dwMessage) + sizeof(cbData), cb - sizeof(dwMessage) - sizeof(cbData), pvData, cbData);
693 }
694
695 *cbMessage = cb;
696 *ppvMessage = pv;
697 pv = NULL;
698
699LExit:
700 ReleaseMem(pv);
701 return hr;
702}
703
704static void FreePipeMessage(
705 __in BURN_PIPE_MESSAGE *pMsg
706 )
707{
708 if (pMsg->fAllocatedData)
709 {
710 ReleaseNullMem(pMsg->pvData);
711 pMsg->fAllocatedData = FALSE;
712 }
713}
714
715static HRESULT WritePipeMessage(
716 __in HANDLE hPipe,
717 __in DWORD dwMessage,
718 __in_bcount_opt(cbData) LPVOID pvData,
719 __in DWORD cbData
720 )
721{
722 HRESULT hr = S_OK;
723 LPVOID pv = NULL;
724 DWORD cb = 0;
725
726 hr = AllocatePipeMessage(dwMessage, pvData, cbData, &pv, &cb);
727 ExitOnFailure(hr, "Failed to allocate message to write.");
728
729 // Write the message.
730 DWORD cbWrote = 0;
731 DWORD cbTotalWritten = 0;
732 while (cbTotalWritten < cb)
733 {
734 if (!::WriteFile(hPipe, pv, cb - cbTotalWritten, &cbWrote, NULL))
735 {
736 ExitWithLastError(hr, "Failed to write message type to pipe.");
737 }
738
739 cbTotalWritten += cbWrote;
740 }
741
742LExit:
743 ReleaseMem(pv);
744 return hr;
745}
746
747static HRESULT GetPipeMessage(
748 __in HANDLE hPipe,
749 __in BURN_PIPE_MESSAGE* pMsg
750 )
751{
752 HRESULT hr = S_OK;
753 DWORD rgdwMessageAndByteCount[2] = { };
754 DWORD cb = 0;
755 DWORD cbRead = 0;
756
757 while (cbRead < sizeof(rgdwMessageAndByteCount))
758 {
759 if (!::ReadFile(hPipe, reinterpret_cast<BYTE*>(rgdwMessageAndByteCount) + cbRead, sizeof(rgdwMessageAndByteCount) - cbRead, &cb, NULL))
760 {
761 DWORD er = ::GetLastError();
762 if (ERROR_MORE_DATA == er)
763 {
764 hr = S_OK;
765 }
766 else if (ERROR_BROKEN_PIPE == er) // parent process shut down, time to exit.
767 {
768 memset(rgdwMessageAndByteCount, 0, sizeof(rgdwMessageAndByteCount));
769 hr = S_FALSE;
770 break;
771 }
772 else
773 {
774 hr = HRESULT_FROM_WIN32(er);
775 }
776 ExitOnRootFailure(hr, "Failed to read message from pipe.");
777 }
778
779 cbRead += cb;
780 }
781
782 pMsg->dwMessage = rgdwMessageAndByteCount[0];
783 pMsg->cbData = rgdwMessageAndByteCount[1];
784 if (pMsg->cbData)
785 {
786 pMsg->pvData = MemAlloc(pMsg->cbData, FALSE);
787 ExitOnNull(pMsg->pvData, hr, E_OUTOFMEMORY, "Failed to allocate data for message.");
788
789 if (!::ReadFile(hPipe, pMsg->pvData, pMsg->cbData, &cb, NULL))
790 {
791 ExitWithLastError(hr, "Failed to read data for message.");
792 }
793
794 pMsg->fAllocatedData = TRUE;
795 }
796
797LExit:
798 if (!pMsg->fAllocatedData && pMsg->pvData)
799 {
800 MemFree(pMsg->pvData);
801 }
802
803 return hr;
804}
805
806static HRESULT ChildPipeConnected(
807 __in HANDLE hPipe,
808 __in_z LPCWSTR wzSecret,
809 __inout DWORD* pdwProcessId
810 )
811{
812 HRESULT hr = S_OK;
813 LPWSTR sczVerificationSecret = NULL;
814 DWORD cbVerificationSecret = 0;
815 DWORD dwVerificationProcessId = 0;
816 DWORD dwRead = 0;
817 DWORD dwAck = ::GetCurrentProcessId(); // send our process id as the ACK.
818 DWORD cb = 0;
819
820 // Read the verification secret.
821 if (!::ReadFile(hPipe, &cbVerificationSecret, sizeof(cbVerificationSecret), &dwRead, NULL))
822 {
823 ExitWithLastError(hr, "Failed to read size of verification secret from parent pipe.");
824 }
825
826 if (255 < cbVerificationSecret / sizeof(WCHAR))
827 {
828 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
829 ExitOnRootFailure(hr, "Verification secret from parent is too big.");
830 }
831
832 hr = StrAlloc(&sczVerificationSecret, cbVerificationSecret / sizeof(WCHAR) + 1);
833 ExitOnFailure(hr, "Failed to allocate buffer for verification secret.");
834
835 if (!::ReadFile(hPipe, sczVerificationSecret, cbVerificationSecret, &dwRead, NULL))
836 {
837 ExitWithLastError(hr, "Failed to read verification secret from parent pipe.");
838 }
839
840 // Verify the secrets match.
841 if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, 0, sczVerificationSecret, -1, wzSecret, -1))
842 {
843 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
844 ExitOnRootFailure(hr, "Verification secret from parent does not match.");
845 }
846
847 // Read the verification process id.
848 if (!::ReadFile(hPipe, &dwVerificationProcessId, sizeof(dwVerificationProcessId), &dwRead, NULL))
849 {
850 ExitWithLastError(hr, "Failed to read verification process id from parent pipe.");
851 }
852
853 // If a process id was not provided, we'll trust the process id from the parent.
854 if (*pdwProcessId == 0)
855 {
856 *pdwProcessId = dwVerificationProcessId;
857 }
858 else if (*pdwProcessId != dwVerificationProcessId) // verify the ids match.
859 {
860 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
861 ExitOnRootFailure(hr, "Verification process id from parent does not match.");
862 }
863
864 // All is well, tell the parent process.
865 if (!::WriteFile(hPipe, &dwAck, sizeof(dwAck), &cb, NULL))
866 {
867 ExitWithLastError(hr, "Failed to inform parent process that child is running.");
868 }
869
870LExit:
871 ReleaseStr(sczVerificationSecret);
872 return hr;
873}
diff --git a/src/engine/pipe.h b/src/engine/pipe.h
new file mode 100644
index 00000000..b6a7742a
--- /dev/null
+++ b/src/engine/pipe.h
@@ -0,0 +1,113 @@
1#pragma once
2// 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.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9typedef struct _BURN_PIPE_CONNECTION
10{
11 LPWSTR sczName;
12 LPWSTR sczSecret;
13 DWORD dwProcessId;
14
15 HANDLE hProcess;
16 HANDLE hPipe;
17 HANDLE hCachePipe;
18} BURN_PIPE_CONNECTION;
19
20typedef enum _BURN_PIPE_MESSAGE_TYPE
21{
22 BURN_PIPE_MESSAGE_TYPE_LOG = 0xF0000001,
23 BURN_PIPE_MESSAGE_TYPE_COMPLETE = 0xF0000002,
24 BURN_PIPE_MESSAGE_TYPE_TERMINATE = 0xF0000003,
25} BURN_PIPE_MESSAGE_TYPE;
26
27typedef struct _BURN_PIPE_MESSAGE
28{
29 DWORD dwMessage;
30 DWORD cbData;
31
32 BOOL fAllocatedData;
33 LPVOID pvData;
34} BURN_PIPE_MESSAGE;
35
36typedef struct _BURN_PIPE_RESULT
37{
38 DWORD dwResult;
39 BOOL fRestart;
40} BURN_PIPE_RESULT;
41
42
43typedef HRESULT (*PFN_PIPE_MESSAGE_CALLBACK)(
44 __in BURN_PIPE_MESSAGE* pMsg,
45 __in_opt LPVOID pvContext,
46 __out DWORD* pdwResult
47 );
48
49
50// Common functions.
51void PipeConnectionInitialize(
52 __in BURN_PIPE_CONNECTION* pConnection
53 );
54void PipeConnectionUninitialize(
55 __in BURN_PIPE_CONNECTION* pConnection
56 );
57HRESULT PipeSendMessage(
58 __in HANDLE hPipe,
59 __in DWORD dwMessage,
60 __in_bcount_opt(cbData) LPVOID pvData,
61 __in DWORD cbData,
62 __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback,
63 __in_opt LPVOID pvContext,
64 __out DWORD* pdwResult
65 );
66HRESULT PipePumpMessages(
67 __in HANDLE hPipe,
68 __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback,
69 __in_opt LPVOID pvContext,
70 __in BURN_PIPE_RESULT* pResult
71 );
72
73// Parent functions.
74HRESULT PipeCreateNameAndSecret(
75 __out_z LPWSTR *psczConnectionName,
76 __out_z LPWSTR *psczSecret
77 );
78HRESULT PipeCreatePipes(
79 __in BURN_PIPE_CONNECTION* pConnection,
80 __in BOOL fCreateCachePipe,
81 __out HANDLE* phEvent
82 );
83HRESULT PipeLaunchParentProcess(
84 __in LPCWSTR wzCommandLine,
85 __in int nCmdShow,
86 __in_z LPWSTR sczConnectionName,
87 __in_z LPWSTR sczSecret,
88 __in BOOL fDisableUnelevate
89 );
90HRESULT PipeLaunchChildProcess(
91 __in_z LPCWSTR wzExecutablePath,
92 __in BURN_PIPE_CONNECTION* pConnection,
93 __in BOOL fElevate,
94 __in_opt HWND hwndParent
95 );
96HRESULT PipeWaitForChildConnect(
97 __in BURN_PIPE_CONNECTION* pConnection
98 );
99HRESULT PipeTerminateChildProcess(
100 __in BURN_PIPE_CONNECTION* pConnection,
101 __in DWORD dwParentExitCode,
102 __in BOOL fRestart
103 );
104
105// Child functions.
106HRESULT PipeChildConnect(
107 __in BURN_PIPE_CONNECTION* pConnection,
108 __in BOOL fConnectCachePipe
109 );
110
111#ifdef __cplusplus
112}
113#endif
diff --git a/src/engine/plan.cpp b/src/engine/plan.cpp
new file mode 100644
index 00000000..01c7a31d
--- /dev/null
+++ b/src/engine/plan.cpp
@@ -0,0 +1,3169 @@
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#include "precomp.h"
4
5// internal struct definitions
6
7struct PLAN_NONPERMANENT_PACKAGE_INDICES
8{
9 DWORD iAfterExecuteFirstNonPermanentPackage;
10 DWORD iBeforeRollbackFirstNonPermanentPackage;
11 DWORD iAfterExecuteLastNonPermanentPackage;
12 DWORD iAfterRollbackLastNonPermanentPackage;
13};
14
15// internal function definitions
16
17static void UninitializeRegistrationAction(
18 __in BURN_DEPENDENT_REGISTRATION_ACTION* pAction
19 );
20static void UninitializeCacheAction(
21 __in BURN_CACHE_ACTION* pCacheAction
22 );
23static void ResetPlannedPackageState(
24 __in BURN_PACKAGE* pPackage
25 );
26static HRESULT ProcessPackage(
27 __in BOOL fBundlePerMachine,
28 __in BURN_PACKAGE* pCompatiblePackageParent,
29 __in BURN_USER_EXPERIENCE* pUX,
30 __in BURN_PLAN* pPlan,
31 __in BURN_PACKAGE* pPackage,
32 __in BURN_LOGGING* pLog,
33 __in BURN_VARIABLES* pVariables,
34 __in BOOTSTRAPPER_DISPLAY display,
35 __in BOOTSTRAPPER_RELATION_TYPE relationType,
36 __in_z_opt LPCWSTR wzLayoutDirectory,
37 __inout HANDLE* phSyncpointEvent,
38 __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary,
39 __in_opt PLAN_NONPERMANENT_PACKAGE_INDICES* pNonpermanentPackageIndices
40 );
41static HRESULT ProcessPackageRollbackBoundary(
42 __in BURN_PLAN* pPlan,
43 __in_opt BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary,
44 __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary
45 );
46static HRESULT GetActionDefaultRequestState(
47 __in BOOTSTRAPPER_ACTION action,
48 __in BOOL fPermanent,
49 __in BOOTSTRAPPER_PACKAGE_STATE currentState,
50 __out BOOTSTRAPPER_REQUEST_STATE* pRequestState
51 );
52static HRESULT AddRegistrationAction(
53 __in BURN_PLAN* pPlan,
54 __in BURN_DEPENDENT_REGISTRATION_ACTION_TYPE type,
55 __in_z LPCWSTR wzDependentProviderKey,
56 __in_z LPCWSTR wzOwnerBundleId
57 );
58static HRESULT AddCachePackage(
59 __in BURN_PLAN* pPlan,
60 __in BURN_PACKAGE* pPackage,
61 __out HANDLE* phSyncpointEvent
62 );
63static HRESULT AddCachePackageHelper(
64 __in BURN_PLAN* pPlan,
65 __in BURN_PACKAGE* pPackage,
66 __out HANDLE* phSyncpointEvent
67 );
68static HRESULT AddCacheSlipstreamMsps(
69 __in BURN_PLAN* pPlan,
70 __in BURN_PACKAGE* pPackage
71 );
72static BOOL AlreadyPlannedCachePackage(
73 __in BURN_PLAN* pPlan,
74 __in_z LPCWSTR wzPackageId,
75 __out HANDLE* phSyncpointEvent
76 );
77static DWORD GetNextCheckpointId();
78static HRESULT AppendCacheAction(
79 __in BURN_PLAN* pPlan,
80 __out BURN_CACHE_ACTION** ppCacheAction
81 );
82static HRESULT AppendRollbackCacheAction(
83 __in BURN_PLAN* pPlan,
84 __out BURN_CACHE_ACTION** ppCacheAction
85 );
86static HRESULT AppendLayoutContainerAction(
87 __in BURN_PLAN* pPlan,
88 __in_opt BURN_PACKAGE* pPackage,
89 __in DWORD iPackageStartAction,
90 __in BURN_CONTAINER* pContainer,
91 __in BOOL fContainerCached,
92 __in_z LPCWSTR wzLayoutDirectory
93 );
94static HRESULT AppendCacheOrLayoutPayloadAction(
95 __in BURN_PLAN* pPlan,
96 __in_opt BURN_PACKAGE* pPackage,
97 __in DWORD iPackageStartAction,
98 __in BURN_PAYLOAD* pPayload,
99 __in BOOL fPayloadCached,
100 __in_z_opt LPCWSTR wzLayoutDirectory
101 );
102static BOOL FindContainerCacheAction(
103 __in BURN_CACHE_ACTION_TYPE type,
104 __in BURN_PLAN* pPlan,
105 __in BURN_CONTAINER* pContainer,
106 __in DWORD iSearchStart,
107 __in DWORD iSearchEnd,
108 __out_opt BURN_CACHE_ACTION** ppCacheAction,
109 __out_opt DWORD* piCacheAction
110 );
111static HRESULT CreateContainerAcquireAndExtractAction(
112 __in BURN_PLAN* pPlan,
113 __in BURN_CONTAINER* pContainer,
114 __in DWORD iPackageStartAction,
115 __in BOOL fPayloadCached,
116 __out BURN_CACHE_ACTION** ppContainerExtractAction,
117 __out DWORD* piContainerTryAgainAction
118 );
119static HRESULT AddAcquireContainer(
120 __in BURN_PLAN* pPlan,
121 __in BURN_CONTAINER* pContainer,
122 __out_opt BURN_CACHE_ACTION** ppCacheAction,
123 __out_opt DWORD* piCacheAction
124 );
125static HRESULT AddExtractPayload(
126 __in BURN_CACHE_ACTION* pCacheAction,
127 __in_opt BURN_PACKAGE* pPackage,
128 __in BURN_PAYLOAD* pPayload,
129 __in_z LPCWSTR wzPayloadWorkingPath
130 );
131static BURN_CACHE_ACTION* ProcessSharedPayload(
132 __in BURN_PLAN* pPlan,
133 __in BURN_PAYLOAD* pPayload
134 );
135static HRESULT RemoveUnnecessaryActions(
136 __in BOOL fExecute,
137 __in BURN_EXECUTE_ACTION* rgActions,
138 __in DWORD cActions
139 );
140static HRESULT FinalizeSlipstreamPatchActions(
141 __in BOOL fExecute,
142 __in BURN_EXECUTE_ACTION* rgActions,
143 __in DWORD cActions
144 );
145static HRESULT PlanDependencyActions(
146 __in BOOL fBundlePerMachine,
147 __in BURN_PLAN* pPlan,
148 __in BURN_PACKAGE* pPackage
149 );
150static HRESULT CalculateExecuteActions(
151 __in BURN_USER_EXPERIENCE* pUserExperience,
152 __in BURN_PACKAGE* pPackage,
153 __in BURN_VARIABLES* pVariables,
154 __out_opt BOOL* pfBARequestedCache
155 );
156static BOOL NeedsCache(
157 __in BURN_PLAN* pPlan,
158 __in BURN_PACKAGE* pPackage
159 );
160static HRESULT CreateContainerProgress(
161 __in BURN_PLAN* pPlan,
162 __in BURN_CONTAINER* pContainer,
163 __out BURN_CACHE_CONTAINER_PROGRESS** ppContainerProgress
164 );
165static HRESULT CreatePayloadProgress(
166 __in BURN_PLAN* pPlan,
167 __in BURN_PAYLOAD* pPayload,
168 __out BURN_CACHE_PAYLOAD_PROGRESS** ppPayloadProgress
169 );
170
171// function definitions
172
173extern "C" void PlanReset(
174 __in BURN_PLAN* pPlan,
175 __in BURN_PACKAGES* pPackages
176 )
177{
178 if (pPlan->rgRegistrationActions)
179 {
180 for (DWORD i = 0; i < pPlan->cRegistrationActions; ++i)
181 {
182 UninitializeRegistrationAction(&pPlan->rgRegistrationActions[i]);
183 }
184 MemFree(pPlan->rgRegistrationActions);
185 }
186
187 if (pPlan->rgRollbackRegistrationActions)
188 {
189 for (DWORD i = 0; i < pPlan->cRollbackRegistrationActions; ++i)
190 {
191 UninitializeRegistrationAction(&pPlan->rgRollbackRegistrationActions[i]);
192 }
193 MemFree(pPlan->rgRollbackRegistrationActions);
194 }
195
196 if (pPlan->rgCacheActions)
197 {
198 for (DWORD i = 0; i < pPlan->cCacheActions; ++i)
199 {
200 UninitializeCacheAction(&pPlan->rgCacheActions[i]);
201 }
202 MemFree(pPlan->rgCacheActions);
203 }
204
205 if (pPlan->rgExecuteActions)
206 {
207 for (DWORD i = 0; i < pPlan->cExecuteActions; ++i)
208 {
209 PlanUninitializeExecuteAction(&pPlan->rgExecuteActions[i]);
210 }
211 MemFree(pPlan->rgExecuteActions);
212 }
213
214 if (pPlan->rgRollbackActions)
215 {
216 for (DWORD i = 0; i < pPlan->cRollbackActions; ++i)
217 {
218 PlanUninitializeExecuteAction(&pPlan->rgRollbackActions[i]);
219 }
220 MemFree(pPlan->rgRollbackActions);
221 }
222
223 if (pPlan->rgCleanActions)
224 {
225 // Nothing needs to be freed inside clean actions today.
226 MemFree(pPlan->rgCleanActions);
227 }
228
229 if (pPlan->rgPlannedProviders)
230 {
231 ReleaseDependencyArray(pPlan->rgPlannedProviders, pPlan->cPlannedProviders);
232 }
233
234 if (pPlan->rgContainerProgress)
235 {
236 MemFree(pPlan->rgContainerProgress);
237 }
238
239 if (pPlan->shContainerProgress)
240 {
241 ReleaseDict(pPlan->shContainerProgress);
242 }
243
244 if (pPlan->rgPayloadProgress)
245 {
246 MemFree(pPlan->rgPayloadProgress);
247 }
248
249 if (pPlan->shPayloadProgress)
250 {
251 ReleaseDict(pPlan->shPayloadProgress);
252 }
253
254 memset(pPlan, 0, sizeof(BURN_PLAN));
255
256 // Reset the planned actions for each package.
257 if (pPackages->rgPackages)
258 {
259 for (DWORD i = 0; i < pPackages->cPackages; ++i)
260 {
261 ResetPlannedPackageState(&pPackages->rgPackages[i]);
262 }
263 }
264}
265
266extern "C" void PlanUninitializeExecuteAction(
267 __in BURN_EXECUTE_ACTION* pExecuteAction
268 )
269{
270 switch (pExecuteAction->type)
271 {
272 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
273 ReleaseStr(pExecuteAction->exePackage.sczIgnoreDependencies);
274 ReleaseStr(pExecuteAction->exePackage.sczAncestors);
275 break;
276
277 case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE:
278 ReleaseStr(pExecuteAction->msiPackage.sczLogPath);
279 ReleaseMem(pExecuteAction->msiPackage.rgFeatures);
280 ReleaseMem(pExecuteAction->msiPackage.rgSlipstreamPatches);
281 ReleaseMem(pExecuteAction->msiPackage.rgOrderedPatches);
282 break;
283
284 case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET:
285 ReleaseStr(pExecuteAction->mspTarget.sczTargetProductCode);
286 ReleaseStr(pExecuteAction->mspTarget.sczLogPath);
287 ReleaseMem(pExecuteAction->mspTarget.rgOrderedPatches);
288 break;
289
290 case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE:
291 ReleaseStr(pExecuteAction->msuPackage.sczLogPath);
292 break;
293
294 case BURN_EXECUTE_ACTION_TYPE_SERVICE_STOP: __fallthrough;
295 case BURN_EXECUTE_ACTION_TYPE_SERVICE_START:
296 ReleaseStr(pExecuteAction->service.sczServiceName);
297 break;
298
299 case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY:
300 ReleaseStr(pExecuteAction->packageDependency.sczBundleProviderKey);
301 break;
302
303 case BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE:
304 ReleaseStr(pExecuteAction->compatiblePackage.sczInstalledProductCode);
305 break;
306 }
307}
308
309extern "C" HRESULT PlanSetVariables(
310 __in BOOTSTRAPPER_ACTION action,
311 __in BURN_VARIABLES* pVariables
312 )
313{
314 HRESULT hr = S_OK;
315
316 hr = VariableSetNumeric(pVariables, BURN_BUNDLE_ACTION, action, TRUE);
317 ExitOnFailure(hr, "Failed to set the bundle action built-in variable.");
318
319LExit:
320 return hr;
321}
322
323extern "C" HRESULT PlanDefaultPackageRequestState(
324 __in BURN_PACKAGE_TYPE packageType,
325 __in BOOTSTRAPPER_PACKAGE_STATE currentState,
326 __in BOOL fPermanent,
327 __in BOOTSTRAPPER_ACTION action,
328 __in BURN_VARIABLES* pVariables,
329 __in_z_opt LPCWSTR wzInstallCondition,
330 __in BOOTSTRAPPER_RELATION_TYPE relationType,
331 __out BOOTSTRAPPER_REQUEST_STATE* pRequestState
332 )
333{
334 HRESULT hr = S_OK;
335 BOOTSTRAPPER_REQUEST_STATE defaultRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
336 BOOL fCondition = FALSE;
337
338 // If doing layout, then always default to requesting the file be cached.
339 if (BOOTSTRAPPER_ACTION_LAYOUT == action)
340 {
341 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE;
342 }
343 else if (BOOTSTRAPPER_RELATION_PATCH == relationType && BURN_PACKAGE_TYPE_MSP == packageType)
344 {
345 // For patch related bundles, only install a patch if currently absent during install, modify, or repair.
346 if (BOOTSTRAPPER_PACKAGE_STATE_ABSENT == currentState && BOOTSTRAPPER_ACTION_INSTALL <= action)
347 {
348 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
349 }
350 else
351 {
352 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
353 }
354 }
355 else if (BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED == currentState && BOOTSTRAPPER_ACTION_UNINSTALL != action)
356 {
357 // Superseded means the package is on the machine but not active, so only uninstall operations are allowed.
358 // All other operations do nothing.
359 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
360 }
361 else if (BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE == currentState && !(BOOTSTRAPPER_ACTION_UNINSTALL == action && BURN_PACKAGE_TYPE_MSP == packageType))
362 {
363 // Obsolete means the package is not on the machine and should not be installed, *except* patches can be obsolete
364 // and present so allow them to be removed during uninstall. Everyone else, gets nothing.
365 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
366 }
367 else // pick the best option for the action state and install condition.
368 {
369 hr = GetActionDefaultRequestState(action, fPermanent, currentState, &defaultRequestState);
370 ExitOnFailure(hr, "Failed to get default request state for action.");
371
372 // If there is an install condition (and we're doing an install) evaluate the condition
373 // to determine whether to use the default request state or make the package absent.
374 if (BOOTSTRAPPER_ACTION_UNINSTALL != action && wzInstallCondition && *wzInstallCondition)
375 {
376 hr = ConditionEvaluate(pVariables, wzInstallCondition, &fCondition);
377 ExitOnFailure(hr, "Failed to evaluate install condition.");
378
379 *pRequestState = fCondition ? defaultRequestState : BOOTSTRAPPER_REQUEST_STATE_ABSENT;
380 }
381 else // just set the package to the default request state.
382 {
383 *pRequestState = defaultRequestState;
384 }
385 }
386
387LExit:
388 return hr;
389}
390
391extern "C" HRESULT PlanLayoutBundle(
392 __in BURN_PLAN* pPlan,
393 __in_z LPCWSTR wzExecutableName,
394 __in DWORD64 qwBundleSize,
395 __in BURN_VARIABLES* pVariables,
396 __in BURN_PAYLOADS* pPayloads,
397 __out_z LPWSTR* psczLayoutDirectory
398 )
399{
400 HRESULT hr = S_OK;
401 BURN_CACHE_ACTION* pCacheAction = NULL;
402 LPWSTR sczExecutablePath = NULL;
403 LPWSTR sczLayoutDirectory = NULL;
404
405 // Get the layout directory.
406 hr = VariableGetString(pVariables, BURN_BUNDLE_LAYOUT_DIRECTORY, &sczLayoutDirectory);
407 if (E_NOTFOUND == hr) // if not set, use the current directory as the layout directory.
408 {
409 hr = VariableGetString(pVariables, BURN_BUNDLE_SOURCE_PROCESS_FOLDER, &sczLayoutDirectory);
410 if (E_NOTFOUND == hr) // if not set, use the current directory as the layout directory.
411 {
412 hr = PathForCurrentProcess(&sczExecutablePath, NULL);
413 ExitOnFailure(hr, "Failed to get path for current executing process as layout directory.");
414
415 hr = PathGetDirectory(sczExecutablePath, &sczLayoutDirectory);
416 ExitOnFailure(hr, "Failed to get executing process as layout directory.");
417 }
418 }
419 ExitOnFailure(hr, "Failed to get bundle layout directory property.");
420
421 hr = PathBackslashTerminate(&sczLayoutDirectory);
422 ExitOnFailure(hr, "Failed to ensure layout directory is backslash terminated.");
423
424 // Plan the layout of the bundle engine itself.
425 hr = AppendCacheAction(pPlan, &pCacheAction);
426 ExitOnFailure(hr, "Failed to append bundle start action.");
427
428 pCacheAction->type = BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE;
429
430 hr = StrAllocString(&pCacheAction->bundleLayout.sczExecutableName, wzExecutableName, 0);
431 ExitOnFailure(hr, "Failed to to copy executable name for bundle.");
432
433 hr = StrAllocString(&pCacheAction->bundleLayout.sczLayoutDirectory, sczLayoutDirectory, 0);
434 ExitOnFailure(hr, "Failed to to copy layout directory for bundle.");
435
436 hr = CacheCalculateBundleLayoutWorkingPath(pPlan->wzBundleId, &pCacheAction->bundleLayout.sczUnverifiedPath);
437 ExitOnFailure(hr, "Failed to calculate bundle layout working path.");
438
439 pCacheAction->bundleLayout.qwBundleSize = qwBundleSize;
440
441 pPlan->qwCacheSizeTotal += qwBundleSize;
442
443 ++pPlan->cOverallProgressTicksTotal;
444
445 // Plan the layout of layout-only payloads.
446 for (DWORD i = 0; i < pPayloads->cPayloads; ++i)
447 {
448 BURN_PAYLOAD* pPayload = pPayloads->rgPayloads + i;
449 if (pPayload->fLayoutOnly)
450 {
451 // TODO: determine if a payload already exists in the layout and pass appropriate value fPayloadCached
452 // (instead of always FALSE).
453 hr = AppendCacheOrLayoutPayloadAction(pPlan, NULL, BURN_PLAN_INVALID_ACTION_INDEX, pPayload, FALSE, sczLayoutDirectory);
454 ExitOnFailure(hr, "Failed to plan layout payload.");
455 }
456 }
457
458 *psczLayoutDirectory = sczLayoutDirectory;
459 sczLayoutDirectory = NULL;
460
461LExit:
462 ReleaseStr(sczLayoutDirectory);
463 ReleaseStr(sczExecutablePath);
464
465 return hr;
466}
467
468extern "C" HRESULT PlanPackages(
469 __in BURN_REGISTRATION* pRegistration,
470 __in BURN_USER_EXPERIENCE* pUX,
471 __in BURN_PACKAGES* pPackages,
472 __in BURN_PLAN* pPlan,
473 __in BURN_LOGGING* pLog,
474 __in BURN_VARIABLES* pVariables,
475 __in BOOL fBundleInstalled,
476 __in BOOTSTRAPPER_DISPLAY display,
477 __in BOOTSTRAPPER_RELATION_TYPE relationType,
478 __in_z_opt LPCWSTR wzLayoutDirectory,
479 __inout HANDLE* phSyncpointEvent
480 )
481{
482 HRESULT hr = S_OK;
483 BOOL fBundlePerMachine = pPlan->fPerMachine; // bundle is per-machine if plan starts per-machine.
484 BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL;
485
486 PLAN_NONPERMANENT_PACKAGE_INDICES nonpermanentPackageIndices;
487 nonpermanentPackageIndices.iAfterExecuteFirstNonPermanentPackage = BURN_PLAN_INVALID_ACTION_INDEX;
488 nonpermanentPackageIndices.iBeforeRollbackFirstNonPermanentPackage = BURN_PLAN_INVALID_ACTION_INDEX;
489 nonpermanentPackageIndices.iAfterExecuteLastNonPermanentPackage = BURN_PLAN_INVALID_ACTION_INDEX;
490 nonpermanentPackageIndices.iAfterRollbackLastNonPermanentPackage = BURN_PLAN_INVALID_ACTION_INDEX;
491
492 // Plan the packages.
493 for (DWORD i = 0; i < pPackages->cPackages; ++i)
494 {
495 DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackages->cPackages - 1 - i : i;
496 BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage;
497
498 // Support passing Ancestors to embedded burn bundles
499 if (BURN_PACKAGE_TYPE_EXE == pPackage->type && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol)
500 {
501 // Pass along any ancestors and ourself to prevent infinite loops.
502 if (pRegistration->sczAncestors && *pRegistration->sczAncestors)
503 {
504 hr = StrAllocFormatted(&pPackage->Exe.sczAncestors, L"%ls;%ls", pRegistration->sczAncestors, pRegistration->sczId);
505 ExitOnFailure(hr, "Failed to copy ancestors and self to related bundle ancestors.");
506 }
507 else
508 {
509 hr = StrAllocString(&pPackage->Exe.sczAncestors, pRegistration->sczId, 0);
510 ExitOnFailure(hr, "Failed to copy self to related bundle ancestors.");
511 }
512 }
513
514 hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, wzLayoutDirectory, phSyncpointEvent, &pRollbackBoundary, &nonpermanentPackageIndices);
515 ExitOnFailure(hr, "Failed to process package.");
516
517 // Attempt to remove orphaned packages during uninstall. Currently only MSI packages are supported and should not require source.
518 if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action && BURN_PACKAGE_TYPE_MSI == pPackage->type && pPackage->Msi.fCompatibleInstalled)
519 {
520 BURN_PACKAGE* pCompatiblePackage = NULL;
521 BURN_EXECUTE_ACTION* pAction = NULL;
522
523 // Add the compatible package to the list.
524 hr = MsiEngineAddCompatiblePackage(pPackages, pPackage, &pCompatiblePackage);
525 ExitOnFailure(hr, "Failed to add compatible package for package: %ls", pPackage->sczId);
526
527 // Plan to load the compatible package into the elevated engine before its needed.
528 hr = PlanAppendExecuteAction(pPlan, &pAction);
529 ExitOnFailure(hr, "Failed to append execute action.");
530
531 pAction->type = BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE;
532 pAction->compatiblePackage.pReferencePackage = pPackage;
533 pAction->compatiblePackage.qwInstalledVersion = pCompatiblePackage->Msi.qwVersion;
534
535 hr = StrAllocString(&pAction->compatiblePackage.sczInstalledProductCode, pCompatiblePackage->Msi.sczProductCode, 0);
536 ExitOnFailure(hr, "Failed to copy installed ProductCode");
537
538 // Process the compatible MSI package like any other.
539 hr = ProcessPackage(fBundlePerMachine, pPackage, pUX, pPlan, pCompatiblePackage, pLog, pVariables, display, relationType, wzLayoutDirectory, phSyncpointEvent, &pRollbackBoundary, &nonpermanentPackageIndices);
540 ExitOnFailure(hr, "Failed to process compatible package.");
541
542 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pCompatiblePackage->execute)
543 {
544 LogId(REPORT_STANDARD, MSG_PLANNED_ORPHAN_PACKAGE_FROM_PROVIDER, pPackage->sczId, pCompatiblePackage->Msi.sczProductCode, pPackage->Msi.sczProductCode);
545 }
546 }
547 }
548
549 // Insert the "keep registration" and "remove registration" actions in the plan when installing the first time and anytime we are uninstalling respectively.
550 if (!fBundleInstalled && (BOOTSTRAPPER_ACTION_INSTALL == pPlan->action || BOOTSTRAPPER_ACTION_MODIFY == pPlan->action || BOOTSTRAPPER_ACTION_REPAIR == pPlan->action))
551 {
552 if (BURN_PLAN_INVALID_ACTION_INDEX == nonpermanentPackageIndices.iAfterExecuteFirstNonPermanentPackage)
553 {
554 nonpermanentPackageIndices.iAfterExecuteFirstNonPermanentPackage = pPlan->cExecuteActions;
555 nonpermanentPackageIndices.iBeforeRollbackFirstNonPermanentPackage = pPlan->cRollbackActions;
556 }
557
558 hr = PlanKeepRegistration(pPlan, nonpermanentPackageIndices.iAfterExecuteFirstNonPermanentPackage, nonpermanentPackageIndices.iBeforeRollbackFirstNonPermanentPackage);
559 ExitOnFailure(hr, "Failed to plan install keep registration.");
560 }
561 else if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
562 {
563 if (BURN_PLAN_INVALID_ACTION_INDEX == nonpermanentPackageIndices.iAfterExecuteLastNonPermanentPackage)
564 {
565 nonpermanentPackageIndices.iAfterExecuteLastNonPermanentPackage = pPlan->cExecuteActions;
566 nonpermanentPackageIndices.iAfterRollbackLastNonPermanentPackage = pPlan->cRollbackActions;
567 }
568
569 hr = PlanRemoveRegistration(pPlan, nonpermanentPackageIndices.iAfterExecuteLastNonPermanentPackage, nonpermanentPackageIndices.iAfterRollbackLastNonPermanentPackage);
570 ExitOnFailure(hr, "Failed to plan uninstall remove registration.");
571 }
572
573 // If we still have an open rollback boundary, complete it.
574 if (pRollbackBoundary)
575 {
576 hr = PlanRollbackBoundaryComplete(pPlan);
577 ExitOnFailure(hr, "Failed to plan rollback boundary begin.");
578
579 pRollbackBoundary = NULL;
580 }
581
582 // Plan clean up of packages.
583 for (DWORD i = 0; i < pPackages->cPackages; ++i)
584 {
585 DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackages->cPackages - 1 - i : i;
586 BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage;
587
588 hr = PlanCleanPackage(pPlan, pPackage);
589 ExitOnFailure(hr, "Failed to plan clean package.");
590 }
591
592 // Plan best-effort clean up of compatible packages.
593 for (DWORD i = 0; i < pPackages->cCompatiblePackages; ++i)
594 {
595 DWORD iPackage = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackages->cCompatiblePackages - 1 - i : i;
596 BURN_PACKAGE* pCompatiblePackage = pPackages->rgCompatiblePackages + iPackage;
597
598 PlanCleanPackage(pPlan, pCompatiblePackage);
599 }
600
601LExit:
602 return hr;
603}
604
605extern "C" HRESULT PlanRegistration(
606 __in BURN_PLAN* pPlan,
607 __in BURN_REGISTRATION* pRegistration,
608 __in BOOTSTRAPPER_RESUME_TYPE resumeType,
609 __in BOOTSTRAPPER_RELATION_TYPE relationType,
610 __in_z_opt LPCWSTR wzIgnoreDependencies,
611 __out BOOL* pfContinuePlanning
612 )
613{
614 HRESULT hr = S_OK;
615 LPCWSTR wzSelfDependent = NULL;
616 STRINGDICT_HANDLE sdIgnoreDependents = NULL;
617 DEPENDENCY* rgDependencies = NULL;
618 UINT cDependencies = 0;
619
620 pPlan->fRegister = TRUE; // register the bundle since we're modifying machine state.
621
622 // Keep the registration if the bundle was already installed or we are planning after a restart.
623 pPlan->fKeepRegistrationDefault = (pRegistration->fInstalled || BOOTSTRAPPER_RESUME_TYPE_REBOOT == resumeType);
624
625 pPlan->fDisallowRemoval = FALSE; // by default the bundle can be planned to be removed
626
627 // If no parent was specified at all, use the bundle id as the self dependent.
628 if (!pRegistration->sczActiveParent)
629 {
630 wzSelfDependent = pRegistration->sczId;
631 }
632 else if (*pRegistration->sczActiveParent) // if parent was specified use that as the self dependent.
633 {
634 wzSelfDependent = pRegistration->sczActiveParent;
635 }
636 // else parent:none was used which means we should not register a dependency on ourself.
637
638 if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
639 {
640 // If our provider key was detected and it points to our current bundle then we can
641 // unregister the bundle dependency.
642 if (pRegistration->sczDetectedProviderKeyBundleId &&
643 CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczId, -1, pRegistration->sczDetectedProviderKeyBundleId, -1))
644 {
645 pPlan->dependencyRegistrationAction = BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER;
646 }
647 else // log that another bundle already owned our registration, hopefully this only happens when a newer version
648 { // of a bundle installed and is in the process of upgrading us.
649 LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_PROVIDER_KEY_REMOVAL, pRegistration->sczProviderKey, pRegistration->sczDetectedProviderKeyBundleId);
650 }
651
652 // Create the dictionary of dependents that should be ignored.
653 hr = DictCreateStringList(&sdIgnoreDependents, 5, DICT_FLAG_CASEINSENSITIVE);
654 ExitOnFailure(hr, "Failed to create the string dictionary.");
655
656 // If the self-dependent dependent exists, plan its removal. If we did not do this, we
657 // would prevent self-removal.
658 if (wzSelfDependent && DependencyDependentExists(pRegistration, wzSelfDependent))
659 {
660 hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER, wzSelfDependent, pRegistration->sczId);
661 ExitOnFailure(hr, "Failed to allocate registration action.");
662
663 hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, wzSelfDependent);
664 ExitOnFailure(hr, "Failed to add self-dependent to ignore dependents.");
665 }
666
667 // If we are not doing an upgrade, we check to see if there are still dependents on us and if so we skip planning.
668 // However, when being upgraded, we always execute our uninstall because a newer version of us is probably
669 // already on the machine and we need to clean up the stuff specific to this bundle.
670 if (BOOTSTRAPPER_RELATION_UPGRADE != relationType)
671 {
672 // If there were other dependencies to ignore, add them.
673 if (wzIgnoreDependencies && *wzIgnoreDependencies)
674 {
675 hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, wzIgnoreDependencies);
676 ExitOnFailure(hr, "Failed to add dependents ignored from command-line.");
677 }
678
679 // For addon or patch bundles, dependent related bundles should be ignored. This allows
680 // that addon or patch to be removed even though bundles it targets still are registered.
681 for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
682 {
683 const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i;
684
685 if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType)
686 {
687 for (DWORD j = 0; j < pRelatedBundle->package.cDependencyProviders; ++j)
688 {
689 const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders + j;
690
691 hr = DependencyAddIgnoreDependencies(sdIgnoreDependents, pProvider->sczKey);
692 ExitOnFailure(hr, "Failed to add dependent bundle provider key to ignore dependents.");
693 }
694 }
695 }
696
697 // If there are any (non-ignored and not-planned-to-be-removed) dependents left, uninstall.
698 hr = DepCheckDependents(pRegistration->hkRoot, pRegistration->sczProviderKey, 0, sdIgnoreDependents, &rgDependencies, &cDependencies);
699 if (E_FILENOTFOUND == hr)
700 {
701 hr = S_OK;
702 }
703 else if (SUCCEEDED(hr) && cDependencies)
704 {
705 // TODO: callback to the BA and let it have the option to ignore any of these dependents?
706
707 pPlan->fDisallowRemoval = TRUE; // ensure the registration stays
708 *pfContinuePlanning = FALSE; // skip the rest of planning.
709
710 LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS, cDependencies);
711 }
712 ExitOnFailure(hr, "Failed to check for remaining dependents during planning.");
713 }
714 }
715 else
716 {
717 BOOL fAddonOrPatchBundle = (pRegistration->cAddonCodes || pRegistration->cPatchCodes);
718
719 // If the bundle is not cached or will not be cached after restart, ensure the bundle is cached.
720 if (!FileExistsAfterRestart(pRegistration->sczCacheExecutablePath, NULL))
721 {
722 pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE;
723 pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION;
724 }
725 else if (BOOTSTRAPPER_ACTION_REPAIR == pPlan->action && !CacheBundleRunningFromCache()) // repairing but not not running from the cache.
726 {
727 pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE;
728 pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION;
729 }
730 else if (BOOTSTRAPPER_ACTION_REPAIR == pPlan->action) // just repair, make sure the registration is "fixed up".
731 {
732 pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION;
733 }
734
735 // Always update our estimated size registration when installing/modify/repair since things
736 // may have been added or removed or it just needs to be "fixed up".
737 pPlan->dwRegistrationOperations |= BURN_REGISTRATION_ACTION_OPERATIONS_UPDATE_SIZE;
738
739 // Always plan to write our provider key registration when installing/modify/repair to "fix it"
740 // if broken.
741 pPlan->dependencyRegistrationAction = BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER;
742
743 // Register each dependent related bundle. The ensures that addons and patches are reference
744 // counted and stick around until the last targeted bundle is removed.
745 for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
746 {
747 const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i;
748
749 if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType)
750 {
751 for (DWORD j = 0; j < pRelatedBundle->package.cDependencyProviders; ++j)
752 {
753 const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders + j;
754
755 if (!DependencyDependentExists(pRegistration, pProvider->sczKey))
756 {
757 hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, pProvider->sczKey, pRelatedBundle->package.sczId);
758 ExitOnFailure(hr, "Failed to add registration action for dependent related bundle.");
759 }
760 }
761 }
762 }
763
764 // Only do the following if we decided there was a dependent self to register. If so and and an explicit parent was
765 // provided, register dependent self. Otherwise, if this bundle is not an addon or patch bundle then self-regisiter
766 // as our own dependent.
767 if (wzSelfDependent && (pRegistration->sczActiveParent || !fAddonOrPatchBundle))
768 {
769 if (!DependencyDependentExists(pRegistration, wzSelfDependent))
770 {
771 hr = AddRegistrationAction(pPlan, BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER, wzSelfDependent, pRegistration->sczId);
772 ExitOnFailure(hr, "Failed to add registration action for self dependent.");
773 }
774 }
775 }
776
777LExit:
778 ReleaseDict(sdIgnoreDependents);
779 ReleaseDependencyArray(rgDependencies, cDependencies);
780
781 return hr;
782}
783
784extern "C" HRESULT PlanPassThroughBundle(
785 __in BURN_USER_EXPERIENCE* pUX,
786 __in BURN_PACKAGE* pPackage,
787 __in BURN_PLAN* pPlan,
788 __in BURN_LOGGING* pLog,
789 __in BURN_VARIABLES* pVariables,
790 __in BOOTSTRAPPER_DISPLAY display,
791 __in BOOTSTRAPPER_RELATION_TYPE relationType,
792 __inout HANDLE* phSyncpointEvent
793 )
794{
795 HRESULT hr = S_OK;
796 BOOL fBundlePerMachine = pPlan->fPerMachine; // bundle is per-machine if plan starts per-machine.
797 BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL;
798
799 // Plan passthrough package.
800 hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, NULL, phSyncpointEvent, &pRollbackBoundary, NULL);
801 ExitOnFailure(hr, "Failed to process passthrough package.");
802
803 // If we still have an open rollback boundary, complete it.
804 if (pRollbackBoundary)
805 {
806 hr = PlanRollbackBoundaryComplete(pPlan);
807 ExitOnFailure(hr, "Failed to plan rollback boundary for passthrough package.");
808 }
809
810 // Notice that the PlanCleanPackage() function is purposefully missing here. Passthrough packages
811 // are never cleaned up by the calling bundle (they delete themselves when appropriate) so we don't
812 // need to plan clean up.
813
814LExit:
815 return hr;
816}
817
818extern "C" HRESULT PlanUpdateBundle(
819 __in BURN_USER_EXPERIENCE* pUX,
820 __in BURN_PACKAGE* pPackage,
821 __in BURN_PLAN* pPlan,
822 __in BURN_LOGGING* pLog,
823 __in BURN_VARIABLES* pVariables,
824 __in BOOTSTRAPPER_DISPLAY display,
825 __in BOOTSTRAPPER_RELATION_TYPE relationType,
826 __inout HANDLE* phSyncpointEvent
827 )
828{
829 HRESULT hr = S_OK;
830 BOOL fBundlePerMachine = pPlan->fPerMachine; // bundle is per-machine if plan starts per-machine.
831 BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL;
832
833 // Plan update package.
834 hr = ProcessPackage(fBundlePerMachine, NULL, pUX, pPlan, pPackage, pLog, pVariables, display, relationType, NULL, phSyncpointEvent, &pRollbackBoundary, NULL);
835 ExitOnFailure(hr, "Failed to process update package.");
836
837 // If we still have an open rollback boundary, complete it.
838 if (pRollbackBoundary)
839 {
840 hr = PlanRollbackBoundaryComplete(pPlan);
841 ExitOnFailure(hr, "Failed to plan rollback boundary for update package.");
842 }
843
844 // Plan clean up of update package.
845 hr = PlanCleanPackage(pPlan, pPackage);
846 ExitOnFailure(hr, "Failed to plan clean of update package.");
847
848LExit:
849 return hr;
850}
851
852static HRESULT ProcessPackage(
853 __in BOOL fBundlePerMachine,
854 __in BURN_PACKAGE* pCompatiblePackageParent,
855 __in BURN_USER_EXPERIENCE* pUX,
856 __in BURN_PLAN* pPlan,
857 __in BURN_PACKAGE* pPackage,
858 __in BURN_LOGGING* pLog,
859 __in BURN_VARIABLES* pVariables,
860 __in BOOTSTRAPPER_DISPLAY display,
861 __in BOOTSTRAPPER_RELATION_TYPE relationType,
862 __in_z_opt LPCWSTR wzLayoutDirectory,
863 __inout HANDLE* phSyncpointEvent,
864 __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary,
865 __in_opt PLAN_NONPERMANENT_PACKAGE_INDICES* pNonpermanentPackageIndices
866 )
867{
868 HRESULT hr = S_OK;
869 BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary = NULL;
870 BOOL fPlanPackageBegan = FALSE;
871
872 // Remember the default requested state so the engine doesn't get blamed for planning the wrong thing if the BA changes it.
873 hr = PlanDefaultPackageRequestState(pPackage->type, pPackage->currentState, !pPackage->fUninstallable, pPlan->action, pVariables, pPackage->sczInstallCondition, relationType, &pPackage->defaultRequested);
874 ExitOnFailure(hr, "Failed to set default package state.");
875
876 pPackage->requested = pPackage->defaultRequested;
877 fPlanPackageBegan = TRUE;
878
879 if (pCompatiblePackageParent)
880 {
881 AssertSz(BURN_PACKAGE_TYPE_MSI == pPackage->type, "Currently only MSI packages have compatible packages.");
882
883 hr = UserExperienceOnPlanCompatibleMsiPackageBegin(pUX, pCompatiblePackageParent->sczId, pPackage->sczId, pPackage->Msi.qwVersion, &pPackage->requested);
884 ExitOnRootFailure(hr, "BA aborted plan compatible MSI package begin.");
885 }
886 else
887 {
888 hr = UserExperienceOnPlanPackageBegin(pUX, pPackage->sczId, &pPackage->requested);
889 ExitOnRootFailure(hr, "BA aborted plan package begin.");
890 }
891
892 pEffectiveRollbackBoundary = (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action) ? pPackage->pRollbackBoundaryBackward : pPackage->pRollbackBoundaryForward;
893 hr = ProcessPackageRollbackBoundary(pPlan, pEffectiveRollbackBoundary, ppRollbackBoundary);
894 ExitOnFailure(hr, "Failed to process package rollback boundary.");
895
896 // If the package is in a requested state, plan it.
897 if (BOOTSTRAPPER_REQUEST_STATE_NONE != pPackage->requested)
898 {
899 if (BOOTSTRAPPER_ACTION_LAYOUT == pPlan->action)
900 {
901 hr = PlanLayoutPackage(pPlan, pPackage, wzLayoutDirectory);
902 ExitOnFailure(hr, "Failed to plan layout package.");
903 }
904 else
905 {
906 if (pPackage->fUninstallable && pNonpermanentPackageIndices)
907 {
908 if (BURN_PLAN_INVALID_ACTION_INDEX == pNonpermanentPackageIndices->iBeforeRollbackFirstNonPermanentPackage)
909 {
910 pNonpermanentPackageIndices->iBeforeRollbackFirstNonPermanentPackage = pPlan->cRollbackActions;
911 }
912 }
913
914 hr = PlanExecutePackage(fBundlePerMachine, display, pUX, pPlan, pPackage, pLog, pVariables, phSyncpointEvent);
915 ExitOnFailure(hr, "Failed to plan execute package.");
916
917 if (pPackage->fUninstallable && pNonpermanentPackageIndices)
918 {
919 if (BURN_PLAN_INVALID_ACTION_INDEX == pNonpermanentPackageIndices->iAfterExecuteFirstNonPermanentPackage)
920 {
921 pNonpermanentPackageIndices->iAfterExecuteFirstNonPermanentPackage = pPlan->cExecuteActions - 1;
922 }
923
924 pNonpermanentPackageIndices->iAfterExecuteLastNonPermanentPackage = pPlan->cExecuteActions;
925 pNonpermanentPackageIndices->iAfterRollbackLastNonPermanentPackage = pPlan->cRollbackActions;
926 }
927 }
928 }
929 else if (BOOTSTRAPPER_ACTION_LAYOUT != pPlan->action)
930 {
931 // All packages that have cacheType set to always should be cached if the bundle is going to be present.
932 if (BURN_CACHE_TYPE_ALWAYS == pPackage->cacheType && BOOTSTRAPPER_ACTION_INSTALL <= pPlan->action)
933 {
934 hr = PlanCachePackage(fBundlePerMachine, pUX, pPlan, pPackage, pVariables, phSyncpointEvent);
935 ExitOnFailure(hr, "Failed to plan cache package.");
936 }
937 else
938 {
939 // Make sure the package is properly ref-counted even if no plan is requested.
940 hr = PlanDependencyActions(fBundlePerMachine, pPlan, pPackage);
941 ExitOnFailure(hr, "Failed to plan dependency actions for package: %ls", pPackage->sczId);
942 }
943 }
944
945 // Add the checkpoint after each package and dependency registration action.
946 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute || BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback || BURN_DEPENDENCY_ACTION_NONE != pPackage->dependencyExecute)
947 {
948 hr = PlanExecuteCheckpoint(pPlan);
949 ExitOnFailure(hr, "Failed to append execute checkpoint.");
950 }
951
952LExit:
953 if (fPlanPackageBegan)
954 {
955 if (pCompatiblePackageParent)
956 {
957 UserExperienceOnPlanCompatibleMsiPackageComplete(pUX, pCompatiblePackageParent->sczId, pPackage->sczId, hr, pPackage->currentState, pPackage->requested, pPackage->execute, pPackage->rollback);
958 }
959 else
960 {
961 UserExperienceOnPlanPackageComplete(pUX, pPackage->sczId, hr, pPackage->currentState, pPackage->requested, pPackage->execute, pPackage->rollback);
962 }
963 }
964
965 return hr;
966}
967
968static HRESULT ProcessPackageRollbackBoundary(
969 __in BURN_PLAN* pPlan,
970 __in_opt BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary,
971 __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary
972 )
973{
974 HRESULT hr = S_OK;
975
976 // If the package marks the start of a rollback boundary, start a new one.
977 if (pEffectiveRollbackBoundary)
978 {
979 // Complete previous rollback boundary.
980 if (*ppRollbackBoundary)
981 {
982 hr = PlanRollbackBoundaryComplete(pPlan);
983 ExitOnFailure(hr, "Failed to plan rollback boundary complete.");
984 }
985
986 // Start new rollback boundary.
987 hr = PlanRollbackBoundaryBegin(pPlan, pEffectiveRollbackBoundary);
988 ExitOnFailure(hr, "Failed to plan rollback boundary begin.");
989
990 *ppRollbackBoundary = pEffectiveRollbackBoundary;
991 }
992
993LExit:
994 return hr;
995}
996
997extern "C" HRESULT PlanLayoutPackage(
998 __in BURN_PLAN* pPlan,
999 __in BURN_PACKAGE* pPackage,
1000 __in_z_opt LPCWSTR wzLayoutDirectory
1001 )
1002{
1003 HRESULT hr = S_OK;
1004 BURN_CACHE_ACTION* pCacheAction = NULL;
1005 DWORD iPackageStartAction = 0;
1006
1007 hr = AppendCacheAction(pPlan, &pCacheAction);
1008 ExitOnFailure(hr, "Failed to append package start action.");
1009
1010 pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE_START;
1011 pCacheAction->packageStart.pPackage = pPackage;
1012
1013 // Remember the index for the package start action (which is now the last in the cache
1014 // actions array) because the array may be resized later and move around in memory.
1015 iPackageStartAction = pPlan->cCacheActions - 1;
1016
1017 // If any of the package payloads are not cached, add them to the plan.
1018 for (DWORD i = 0; i < pPackage->cPayloads; ++i)
1019 {
1020 BURN_PACKAGE_PAYLOAD* pPackagePayload = &pPackage->rgPayloads[i];
1021
1022 // If doing layout and the package is in a container.
1023 if (wzLayoutDirectory && pPackagePayload->pPayload->pContainer)
1024 {
1025 // TODO: determine if a container already exists in the layout and pass appropriate value fPayloadCached (instead of always FALSE).
1026 hr = AppendLayoutContainerAction(pPlan, pPackage, iPackageStartAction, pPackagePayload->pPayload->pContainer, FALSE, wzLayoutDirectory);
1027 ExitOnFailure(hr, "Failed to append layout container action.");
1028 }
1029 else
1030 {
1031 // TODO: determine if a payload already exists in the layout and pass appropriate value fPayloadCached (instead of always FALSE).
1032 hr = AppendCacheOrLayoutPayloadAction(pPlan, pPackage, iPackageStartAction, pPackagePayload->pPayload, FALSE, wzLayoutDirectory);
1033 ExitOnFailure(hr, "Failed to append cache/layout payload action.");
1034 }
1035
1036 Assert(BURN_CACHE_ACTION_TYPE_PACKAGE_START == pPlan->rgCacheActions[iPackageStartAction].type);
1037 ++pPlan->rgCacheActions[iPackageStartAction].packageStart.cCachePayloads;
1038 pPlan->rgCacheActions[iPackageStartAction].packageStart.qwCachePayloadSizeTotal += pPackagePayload->pPayload->qwFileSize;
1039 }
1040
1041 // Create package stop action.
1042 hr = AppendCacheAction(pPlan, &pCacheAction);
1043 ExitOnFailure(hr, "Failed to append cache action.");
1044
1045 pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE_STOP;
1046 pCacheAction->packageStop.pPackage = pPackage;
1047
1048 // Update the start action with the location of the complete action.
1049 pPlan->rgCacheActions[iPackageStartAction].packageStart.iPackageCompleteAction = pPlan->cCacheActions - 1;
1050
1051 ++pPlan->cOverallProgressTicksTotal;
1052
1053LExit:
1054 return hr;
1055}
1056
1057extern "C" HRESULT PlanCachePackage(
1058 __in BOOL fPerMachine,
1059 __in BURN_USER_EXPERIENCE* pUserExperience,
1060 __in BURN_PLAN* pPlan,
1061 __in BURN_PACKAGE* pPackage,
1062 __in BURN_VARIABLES* pVariables,
1063 __out HANDLE* phSyncpointEvent
1064 )
1065{
1066 HRESULT hr = S_OK;
1067 BOOL fBARequestedCache = FALSE;
1068
1069 // Calculate the execute actions because we need them to decide whether the package should be cached.
1070 hr = CalculateExecuteActions(pUserExperience, pPackage, pVariables, &fBARequestedCache);
1071 ExitOnFailure(hr, "Failed to calculate execute actions for package: %ls", pPackage->sczId);
1072
1073 if (fBARequestedCache || NeedsCache(pPlan, pPackage))
1074 {
1075 hr = AddCachePackage(pPlan, pPackage, phSyncpointEvent);
1076 ExitOnFailure(hr, "Failed to plan cache package.");
1077
1078 if (pPackage->fPerMachine)
1079 {
1080 pPlan->fPerMachine = TRUE;
1081 }
1082 }
1083
1084 // Make sure the package is properly ref-counted.
1085 hr = PlanDependencyActions(fPerMachine, pPlan, pPackage);
1086 ExitOnFailure(hr, "Failed to plan dependency actions for package: %ls", pPackage->sczId);
1087
1088LExit:
1089 return hr;
1090}
1091
1092extern "C" HRESULT PlanExecutePackage(
1093 __in BOOL fPerMachine,
1094 __in BOOTSTRAPPER_DISPLAY display,
1095 __in BURN_USER_EXPERIENCE* pUserExperience,
1096 __in BURN_PLAN* pPlan,
1097 __in BURN_PACKAGE* pPackage,
1098 __in BURN_LOGGING* pLog,
1099 __in BURN_VARIABLES* pVariables,
1100 __inout HANDLE* phSyncpointEvent
1101 )
1102{
1103 HRESULT hr = S_OK;
1104 BOOL fBARequestedCache = FALSE;
1105
1106 hr = CalculateExecuteActions(pUserExperience, pPackage, pVariables, &fBARequestedCache);
1107 ExitOnFailure(hr, "Failed to calculate plan actions for package: %ls", pPackage->sczId);
1108
1109 // Calculate package states based on reference count and plan certain dependency actions prior to planning the package execute action.
1110 hr = DependencyPlanPackageBegin(fPerMachine, pPackage, pPlan);
1111 ExitOnFailure(hr, "Failed to begin plan dependency actions for package: %ls", pPackage->sczId);
1112
1113 if (fBARequestedCache || NeedsCache(pPlan, pPackage))
1114 {
1115 hr = AddCachePackage(pPlan, pPackage, phSyncpointEvent);
1116 ExitOnFailure(hr, "Failed to plan cache package.");
1117 }
1118 else if (BURN_CACHE_STATE_COMPLETE != pPackage->cache && // if the package is not in the cache, disable any rollback that would require the package from the cache.
1119 (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pPackage->rollback || (BURN_PACKAGE_TYPE_EXE == pPackage->type && BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback))
1120 )
1121 {
1122 LogId(REPORT_STANDARD, MSG_PLAN_DISABLING_ROLLBACK_NO_CACHE, pPackage->sczId, LoggingCacheStateToString(pPackage->cache), LoggingActionStateToString(pPackage->rollback));
1123 pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
1124 }
1125
1126 // Add the cache and install size to estimated size if it will be on the machine at the end of the install
1127 if (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested ||
1128 (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_ABSENT < pPackage->requested) ||
1129 BURN_CACHE_TYPE_ALWAYS == pPackage->cacheType
1130 )
1131 {
1132 // If the package will remain in the cache, add the package size to the estimated size
1133 if (BURN_CACHE_TYPE_YES <= pPackage->cacheType)
1134 {
1135 pPlan->qwEstimatedSize += pPackage->qwSize;
1136 }
1137
1138 // If the package will end up installed on the machine, add the install size to the estimated size.
1139 if (BOOTSTRAPPER_REQUEST_STATE_CACHE < pPackage->requested)
1140 {
1141 // MSP packages get cached automatically by windows installer with any embedded cabs, so include that in the size as well
1142 if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
1143 {
1144 pPlan->qwEstimatedSize += pPackage->qwSize;
1145 }
1146
1147 pPlan->qwEstimatedSize += pPackage->qwInstallSize;
1148 }
1149 }
1150
1151 // Add execute actions.
1152 switch (pPackage->type)
1153 {
1154 case BURN_PACKAGE_TYPE_EXE:
1155 hr = ExeEnginePlanAddPackage(NULL, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire);
1156 break;
1157
1158 case BURN_PACKAGE_TYPE_MSI:
1159 hr = MsiEnginePlanAddPackage(display, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire);
1160 break;
1161
1162 case BURN_PACKAGE_TYPE_MSP:
1163 hr = MspEnginePlanAddPackage(display, pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire);
1164 break;
1165
1166 case BURN_PACKAGE_TYPE_MSU:
1167 hr = MsuEnginePlanAddPackage(pPackage, pPlan, pLog, pVariables, *phSyncpointEvent, pPackage->fAcquire);
1168 break;
1169
1170 default:
1171 hr = E_UNEXPECTED;
1172 ExitOnFailure(hr, "Invalid package type.");
1173 }
1174 ExitOnFailure(hr, "Failed to add plan actions for package: %ls", pPackage->sczId);
1175
1176 // Plan certain dependency actions after planning the package execute action.
1177 hr = DependencyPlanPackageComplete(pPackage, pPlan);
1178 ExitOnFailure(hr, "Failed to complete plan dependency actions for package: %ls", pPackage->sczId);
1179
1180 // If we are going to take any action on this package, add progress for it.
1181 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute || BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback)
1182 {
1183 LoggingIncrementPackageSequence();
1184
1185 ++pPlan->cExecutePackagesTotal;
1186 ++pPlan->cOverallProgressTicksTotal;
1187
1188 // If package is per-machine and is being executed, flag the plan to be per-machine as well.
1189 if (pPackage->fPerMachine)
1190 {
1191 pPlan->fPerMachine = TRUE;
1192 }
1193 }
1194
1195LExit:
1196 return hr;
1197}
1198
1199extern "C" HRESULT PlanRelatedBundlesBegin(
1200 __in BURN_USER_EXPERIENCE* pUserExperience,
1201 __in BURN_REGISTRATION* pRegistration,
1202 __in BOOTSTRAPPER_RELATION_TYPE relationType,
1203 __in BURN_PLAN* pPlan
1204 )
1205{
1206 HRESULT hr = S_OK;
1207 LPWSTR* rgsczAncestors = NULL;
1208 UINT cAncestors = 0;
1209 STRINGDICT_HANDLE sdAncestors = NULL;
1210
1211 if (pRegistration->sczAncestors)
1212 {
1213 hr = StrSplitAllocArray(&rgsczAncestors, &cAncestors, pRegistration->sczAncestors, L";");
1214 ExitOnFailure(hr, "Failed to create string array from ancestors.");
1215
1216 hr = DictCreateStringListFromArray(&sdAncestors, rgsczAncestors, cAncestors, DICT_FLAG_CASEINSENSITIVE);
1217 ExitOnFailure(hr, "Failed to create dictionary from ancestors array.");
1218 }
1219
1220 for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
1221 {
1222 BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i;
1223 pRelatedBundle->package.defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1224 pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1225
1226 // Do not execute the same bundle twice.
1227 if (sdAncestors)
1228 {
1229 hr = DictKeyExists(sdAncestors, pRelatedBundle->package.sczId);
1230 if (SUCCEEDED(hr))
1231 {
1232 LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_RELATED_BUNDLE_SCHEDULED, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType));
1233 continue;
1234 }
1235 else if (E_NOTFOUND != hr)
1236 {
1237 ExitOnFailure(hr, "Failed to lookup the bundle ID in the ancestors dictionary.");
1238 }
1239 }
1240 else if (BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_RELATION_NONE != relationType)
1241 {
1242 // Avoid repair loops for older bundles that do not handle ancestors.
1243 LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_RELATED_BUNDLE_DEPENDENT, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingRelationTypeToString(relationType));
1244 continue;
1245 }
1246
1247 // Pass along any ancestors and ourself to prevent infinite loops.
1248 if (pRegistration->sczAncestors && *pRegistration->sczAncestors)
1249 {
1250 hr = StrAllocFormatted(&pRelatedBundle->package.Exe.sczAncestors, L"%ls;%ls", pRegistration->sczAncestors, pRegistration->sczId);
1251 ExitOnFailure(hr, "Failed to copy ancestors and self to related bundle ancestors.");
1252 }
1253 else
1254 {
1255 hr = StrAllocString(&pRelatedBundle->package.Exe.sczAncestors, pRegistration->sczId, 0);
1256 ExitOnFailure(hr, "Failed to copy self to related bundle ancestors.");
1257 }
1258
1259 switch (pRelatedBundle->relationType)
1260 {
1261 case BOOTSTRAPPER_RELATION_UPGRADE:
1262 if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL < pPlan->action)
1263 {
1264 pRelatedBundle->package.requested = (pRegistration->qwVersion > pRelatedBundle->qwVersion) ? BOOTSTRAPPER_REQUEST_STATE_ABSENT : BOOTSTRAPPER_REQUEST_STATE_NONE;
1265 }
1266 break;
1267 case BOOTSTRAPPER_RELATION_PATCH: __fallthrough;
1268 case BOOTSTRAPPER_RELATION_ADDON:
1269 if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
1270 {
1271 pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_ABSENT;
1272 }
1273 else if (BOOTSTRAPPER_ACTION_INSTALL == pPlan->action || BOOTSTRAPPER_ACTION_MODIFY == pPlan->action)
1274 {
1275 pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
1276 }
1277 else if (BOOTSTRAPPER_ACTION_REPAIR == pPlan->action)
1278 {
1279 pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_REPAIR;
1280 }
1281 break;
1282 case BOOTSTRAPPER_RELATION_DEPENDENT:
1283 // Automatically repair dependent bundles to restore missing
1284 // packages after uninstall unless we're being upgraded with the
1285 // assumption that upgrades are cumulative (as intended).
1286 if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
1287 {
1288 pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_REPAIR;
1289 }
1290 break;
1291 case BOOTSTRAPPER_RELATION_DETECT:
1292 break;
1293 default:
1294 hr = E_UNEXPECTED;
1295 ExitOnFailure(hr, "Unexpected relation type encountered during plan: %d", pRelatedBundle->relationType);
1296 break;
1297 }
1298
1299 pRelatedBundle->package.defaultRequested = pRelatedBundle->package.requested;
1300
1301 hr = UserExperienceOnPlanRelatedBundle(pUserExperience, pRelatedBundle->package.sczId, &pRelatedBundle->package.requested);
1302 ExitOnRootFailure(hr, "BA aborted plan related bundle.");
1303
1304 // Log when the BA changed the bundle state so the engine doesn't get blamed for planning the wrong thing.
1305 if (pRelatedBundle->package.requested != pRelatedBundle->package.defaultRequested)
1306 {
1307 LogId(REPORT_STANDARD, MSG_PLANNED_BUNDLE_UX_CHANGED_REQUEST, pRelatedBundle->package.sczId, LoggingRequestStateToString(pRelatedBundle->package.requested), LoggingRequestStateToString(pRelatedBundle->package.defaultRequested));
1308 }
1309
1310 // If uninstalling and the dependent related bundle may be executed, ignore its provider key to allow for downgrades with ref-counting.
1311 if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action && BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_REQUEST_STATE_NONE != pRelatedBundle->package.requested)
1312 {
1313 if (0 < pRelatedBundle->package.cDependencyProviders)
1314 {
1315 // Bundles only support a single provider key.
1316 const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders;
1317
1318 hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pProvider->sczKey, pProvider->sczDisplayName);
1319 ExitOnFailure(hr, "Failed to add the package provider key \"%ls\" to the planned list.", pProvider->sczKey);
1320 }
1321 }
1322 }
1323
1324LExit:
1325 ReleaseDict(sdAncestors);
1326 ReleaseStrArray(rgsczAncestors, cAncestors);
1327
1328 return hr;
1329}
1330
1331extern "C" HRESULT PlanRelatedBundlesComplete(
1332 __in BURN_REGISTRATION* pRegistration,
1333 __in BURN_PLAN* pPlan,
1334 __in BURN_LOGGING* pLog,
1335 __in BURN_VARIABLES* pVariables,
1336 __inout HANDLE* phSyncpointEvent,
1337 __in DWORD dwExecuteActionEarlyIndex
1338 )
1339{
1340 HRESULT hr = S_OK;
1341 LPWSTR sczIgnoreDependencies = NULL;
1342 STRINGDICT_HANDLE sdProviderKeys = NULL;
1343
1344 // Get the list of dependencies to ignore to pass to related bundles.
1345 hr = DependencyAllocIgnoreDependencies(pPlan, &sczIgnoreDependencies);
1346 ExitOnFailure(hr, "Failed to get the list of dependencies to ignore.");
1347
1348 hr = DictCreateStringList(&sdProviderKeys, pPlan->cExecuteActions, DICT_FLAG_CASEINSENSITIVE);
1349 ExitOnFailure(hr, "Failed to create dictionary for planned packages.");
1350
1351 BOOL fExecutingAnyPackage = FALSE;
1352
1353 for (DWORD i = 0; i < pPlan->cExecuteActions; ++i)
1354 {
1355 if (BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE == pPlan->rgExecuteActions[i].type && BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].exePackage.action)
1356 {
1357 fExecutingAnyPackage = TRUE;
1358
1359 BURN_PACKAGE* pPackage = pPlan->rgExecuteActions[i].packageProvider.pPackage;
1360 if (BURN_PACKAGE_TYPE_EXE == pPackage->type && BURN_EXE_PROTOCOL_TYPE_BURN == pPackage->Exe.protocol)
1361 {
1362 if (0 < pPackage->cDependencyProviders)
1363 {
1364 // Bundles only support a single provider key.
1365 const BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders;
1366 DictAddKey(sdProviderKeys, pProvider->sczKey);
1367 }
1368 }
1369 }
1370 else
1371 {
1372 switch (pPlan->rgExecuteActions[i].type)
1373 {
1374 case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE:
1375 fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].msiPackage.action);
1376 break;
1377
1378 case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET:
1379 fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].mspTarget.action);
1380 break;
1381
1382 case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE:
1383 fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].msuPackage.action);
1384 break;
1385 }
1386 }
1387 }
1388
1389 for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
1390 {
1391 DWORD *pdwInsertIndex = NULL;
1392 BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + i;
1393
1394 // Do not execute if a major upgrade to the related bundle is an embedded bundle (Provider keys are the same)
1395 if (0 < pRelatedBundle->package.cDependencyProviders)
1396 {
1397 // Bundles only support a single provider key.
1398 const BURN_DEPENDENCY_PROVIDER* pProvider = pRelatedBundle->package.rgDependencyProviders;
1399 hr = DictKeyExists(sdProviderKeys, pProvider->sczKey);
1400 if (E_NOTFOUND != hr)
1401 {
1402 ExitOnFailure(hr, "Failed to check the dictionary for a related bundle provider key: \"%ls\".", pProvider->sczKey);
1403 // Key found, so there is an embedded bundle with the same provider key that will be executed. So this related bundle should not be added to the plan
1404 LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_RELATED_BUNDLE_EMBEDDED_BUNDLE_NEWER, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), pProvider->sczKey);
1405 continue;
1406 }
1407 else
1408 {
1409 hr = S_OK;
1410 }
1411 }
1412
1413 // For an uninstall, there is no need to repair dependent bundles if no packages are executing.
1414 if (!fExecutingAnyPackage && BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_REQUEST_STATE_REPAIR == pRelatedBundle->package.requested && BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
1415 {
1416 pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1417 LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DEPENDENT_BUNDLE_REPAIR, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType));
1418 }
1419
1420 if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType)
1421 {
1422 // Addon and patch bundles will be passed a list of dependencies to ignore for planning.
1423 hr = StrAllocString(&pRelatedBundle->package.Exe.sczIgnoreDependencies, sczIgnoreDependencies, 0);
1424 ExitOnFailure(hr, "Failed to copy the list of dependencies to ignore.");
1425
1426 // Uninstall addons and patches early in the chain, before other packages are uninstalled.
1427 if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
1428 {
1429 pdwInsertIndex = &dwExecuteActionEarlyIndex;
1430 }
1431 }
1432
1433 if (BOOTSTRAPPER_REQUEST_STATE_NONE != pRelatedBundle->package.requested)
1434 {
1435 hr = ExeEnginePlanCalculatePackage(&pRelatedBundle->package, NULL);
1436 ExitOnFailure(hr, "Failed to calcuate plan for related bundle: %ls", pRelatedBundle->package.sczId);
1437
1438 // Calculate package states based on reference count for addon and patch related bundles.
1439 if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType)
1440 {
1441 hr = DependencyPlanPackageBegin(pRegistration->fPerMachine, &pRelatedBundle->package, pPlan);
1442 ExitOnFailure(hr, "Failed to begin plan dependency actions to package: %ls", pRelatedBundle->package.sczId);
1443
1444 // If uninstalling a related bundle, make sure the bundle is uninstalled after removing registration.
1445 if (pdwInsertIndex && BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action)
1446 {
1447 ++(*pdwInsertIndex);
1448 }
1449 }
1450
1451 hr = ExeEnginePlanAddPackage(pdwInsertIndex, &pRelatedBundle->package, pPlan, pLog, pVariables, *phSyncpointEvent, FALSE);
1452 ExitOnFailure(hr, "Failed to add to plan related bundle: %ls", pRelatedBundle->package.sczId);
1453
1454 // Calculate package states based on reference count for addon and patch related bundles.
1455 if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType)
1456 {
1457 hr = DependencyPlanPackageComplete(&pRelatedBundle->package, pPlan);
1458 ExitOnFailure(hr, "Failed to complete plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId);
1459 }
1460
1461 // If we are going to take any action on this package, add progress for it.
1462 if (BOOTSTRAPPER_ACTION_STATE_NONE != pRelatedBundle->package.execute || BOOTSTRAPPER_ACTION_STATE_NONE != pRelatedBundle->package.rollback)
1463 {
1464 LoggingIncrementPackageSequence();
1465
1466 ++pPlan->cExecutePackagesTotal;
1467 ++pPlan->cOverallProgressTicksTotal;
1468 }
1469
1470 // If package is per-machine and is being executed, flag the plan to be per-machine as well.
1471 if (pRelatedBundle->package.fPerMachine)
1472 {
1473 pPlan->fPerMachine = TRUE;
1474 }
1475 }
1476 else if (BOOTSTRAPPER_RELATION_ADDON == pRelatedBundle->relationType || BOOTSTRAPPER_RELATION_PATCH == pRelatedBundle->relationType)
1477 {
1478 // Make sure the package is properly ref-counted even if no plan is requested.
1479 hr = DependencyPlanPackageBegin(pRegistration->fPerMachine, &pRelatedBundle->package, pPlan);
1480 ExitOnFailure(hr, "Failed to begin plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId);
1481
1482 hr = DependencyPlanPackage(pdwInsertIndex, &pRelatedBundle->package, pPlan);
1483 ExitOnFailure(hr, "Failed to plan related bundle package provider actions.");
1484
1485 hr = DependencyPlanPackageComplete(&pRelatedBundle->package, pPlan);
1486 ExitOnFailure(hr, "Failed to complete plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId);
1487 }
1488 }
1489
1490LExit:
1491 ReleaseDict(sdProviderKeys);
1492 ReleaseStr(sczIgnoreDependencies);
1493
1494 return hr;
1495}
1496
1497extern "C" HRESULT PlanFinalizeActions(
1498 __in BURN_PLAN* pPlan
1499 )
1500{
1501 HRESULT hr = S_OK;
1502
1503 hr = RemoveUnnecessaryActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions);
1504 ExitOnFailure(hr, "Failed to remove unnecessary execute actions.");
1505
1506 hr = RemoveUnnecessaryActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions);
1507 ExitOnFailure(hr, "Failed to remove unnecessary execute actions.");
1508
1509 hr = FinalizeSlipstreamPatchActions(TRUE, pPlan->rgExecuteActions, pPlan->cExecuteActions);
1510 ExitOnFailure(hr, "Failed to finalize slipstream execute actions.");
1511
1512 hr = FinalizeSlipstreamPatchActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions);
1513 ExitOnFailure(hr, "Failed to finalize slipstream rollback actions.");
1514
1515LExit:
1516 return hr;
1517}
1518
1519extern "C" HRESULT PlanCleanPackage(
1520 __in BURN_PLAN* pPlan,
1521 __in BURN_PACKAGE* pPackage
1522 )
1523{
1524 HRESULT hr = S_OK;
1525 BOOL fPlanCleanPackage = FALSE;
1526 BURN_CLEAN_ACTION* pCleanAction = NULL;
1527
1528 // The following is a complex set of logic that determines when a package should be cleaned
1529 // from the cache. Start by noting that we only clean if the package is being acquired or
1530 // already cached and the package is not supposed to always be cached.
1531 if ((pPackage->fAcquire || BURN_CACHE_STATE_PARTIAL == pPackage->cache || BURN_CACHE_STATE_COMPLETE == pPackage->cache) &&
1532 (BURN_CACHE_TYPE_ALWAYS > pPackage->cacheType || BOOTSTRAPPER_ACTION_INSTALL > pPlan->action))
1533 {
1534 // The following are all different reasons why the package should be cleaned from the cache.
1535 // The else-ifs are used to make the conditions easier to see (rather than have them combined
1536 // in one huge condition).
1537 if (BURN_CACHE_TYPE_YES > pPackage->cacheType) // easy, package is not supposed to stay cached.
1538 {
1539 fPlanCleanPackage = TRUE;
1540 }
1541 else if ((BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested ||
1542 BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested) && // requested to be removed and
1543 BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) // actually being removed.
1544 {
1545 fPlanCleanPackage = TRUE;
1546 }
1547 else if ((BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested ||
1548 BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested) && // requested to be removed but
1549 BOOTSTRAPPER_ACTION_STATE_NONE == pPackage->execute && // execute is do nothing and
1550 !pPackage->fDependencyManagerWasHere && // dependency manager didn't change execute and
1551 BOOTSTRAPPER_PACKAGE_STATE_PRESENT > pPackage->currentState) // currently not installed.
1552 {
1553 fPlanCleanPackage = TRUE;
1554 }
1555 else if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action && // uninstalling and
1556 BOOTSTRAPPER_REQUEST_STATE_NONE == pPackage->requested && // requested do nothing (aka: default) and
1557 BOOTSTRAPPER_ACTION_STATE_NONE == pPackage->execute && // execute is still do nothing and
1558 !pPackage->fDependencyManagerWasHere && // dependency manager didn't change execute and
1559 BOOTSTRAPPER_PACKAGE_STATE_PRESENT > pPackage->currentState) // currently not installed.
1560 {
1561 fPlanCleanPackage = TRUE;
1562 }
1563 }
1564
1565 if (fPlanCleanPackage)
1566 {
1567 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgCleanActions), pPlan->cCleanActions + 1, sizeof(BURN_CLEAN_ACTION), 5);
1568 ExitOnFailure(hr, "Failed to grow plan's array of clean actions.");
1569
1570 pCleanAction = pPlan->rgCleanActions + pPlan->cCleanActions;
1571 ++pPlan->cCleanActions;
1572
1573 pCleanAction->pPackage = pPackage;
1574
1575 pPackage->fUncache = TRUE;
1576 }
1577
1578LExit:
1579 return hr;
1580}
1581
1582extern "C" HRESULT PlanExecuteCacheSyncAndRollback(
1583 __in BURN_PLAN* pPlan,
1584 __in BURN_PACKAGE* pPackage,
1585 __in HANDLE hCacheEvent,
1586 __in BOOL fPlanPackageCacheRollback
1587 )
1588{
1589 HRESULT hr = S_OK;
1590 BURN_EXECUTE_ACTION* pAction = NULL;
1591
1592 hr = PlanAppendExecuteAction(pPlan, &pAction);
1593 ExitOnFailure(hr, "Failed to append wait action for caching.");
1594
1595 pAction->type = BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT;
1596 pAction->syncpoint.hEvent = hCacheEvent;
1597
1598 if (fPlanPackageCacheRollback)
1599 {
1600 hr = PlanAppendRollbackAction(pPlan, &pAction);
1601 ExitOnFailure(hr, "Failed to append rollback action.");
1602
1603 pAction->type = BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE;
1604 pAction->uncachePackage.pPackage = pPackage;
1605
1606 hr = PlanExecuteCheckpoint(pPlan);
1607 ExitOnFailure(hr, "Failed to append execute checkpoint for cache rollback.");
1608 }
1609
1610LExit:
1611 return hr;
1612}
1613
1614extern "C" HRESULT PlanExecuteCheckpoint(
1615 __in BURN_PLAN* pPlan
1616 )
1617{
1618 HRESULT hr = S_OK;
1619 BURN_EXECUTE_ACTION* pAction = NULL;
1620 DWORD dwCheckpointId = GetNextCheckpointId();
1621
1622 // execute checkpoint
1623 hr = PlanAppendExecuteAction(pPlan, &pAction);
1624 ExitOnFailure(hr, "Failed to append execute action.");
1625
1626 pAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT;
1627 pAction->checkpoint.dwId = dwCheckpointId;
1628
1629 // rollback checkpoint
1630 hr = PlanAppendRollbackAction(pPlan, &pAction);
1631 ExitOnFailure(hr, "Failed to append rollback action.");
1632
1633 pAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT;
1634 pAction->checkpoint.dwId = dwCheckpointId;
1635
1636LExit:
1637 return hr;
1638}
1639
1640extern "C" HRESULT PlanInsertExecuteAction(
1641 __in DWORD dwIndex,
1642 __in BURN_PLAN* pPlan,
1643 __out BURN_EXECUTE_ACTION** ppExecuteAction
1644 )
1645{
1646 HRESULT hr = S_OK;
1647
1648 hr = MemInsertIntoArray((void**)&pPlan->rgExecuteActions, dwIndex, 1, pPlan->cExecuteActions + 1, sizeof(BURN_EXECUTE_ACTION), 5);
1649 ExitOnFailure(hr, "Failed to grow plan's array of execute actions.");
1650
1651 *ppExecuteAction = pPlan->rgExecuteActions + dwIndex;
1652 ++pPlan->cExecuteActions;
1653
1654LExit:
1655 return hr;
1656}
1657
1658extern "C" HRESULT PlanInsertRollbackAction(
1659 __in DWORD dwIndex,
1660 __in BURN_PLAN* pPlan,
1661 __out BURN_EXECUTE_ACTION** ppRollbackAction
1662 )
1663{
1664 HRESULT hr = S_OK;
1665
1666 hr = MemInsertIntoArray((void**)&pPlan->rgRollbackActions, dwIndex, 1, pPlan->cRollbackActions + 1, sizeof(BURN_EXECUTE_ACTION), 5);
1667 ExitOnFailure(hr, "Failed to grow plan's array of rollback actions.");
1668
1669 *ppRollbackAction = pPlan->rgRollbackActions + dwIndex;
1670 ++pPlan->cRollbackActions;
1671
1672LExit:
1673 return hr;
1674}
1675
1676extern "C" HRESULT PlanAppendExecuteAction(
1677 __in BURN_PLAN* pPlan,
1678 __out BURN_EXECUTE_ACTION** ppExecuteAction
1679 )
1680{
1681 HRESULT hr = S_OK;
1682
1683 hr = MemEnsureArraySize((void**)&pPlan->rgExecuteActions, pPlan->cExecuteActions + 1, sizeof(BURN_EXECUTE_ACTION), 5);
1684 ExitOnFailure(hr, "Failed to grow plan's array of execute actions.");
1685
1686 *ppExecuteAction = pPlan->rgExecuteActions + pPlan->cExecuteActions;
1687 ++pPlan->cExecuteActions;
1688
1689LExit:
1690 return hr;
1691}
1692
1693extern "C" HRESULT PlanAppendRollbackAction(
1694 __in BURN_PLAN* pPlan,
1695 __out BURN_EXECUTE_ACTION** ppRollbackAction
1696 )
1697{
1698 HRESULT hr = S_OK;
1699
1700 hr = MemEnsureArraySize((void**)&pPlan->rgRollbackActions, pPlan->cRollbackActions + 1, sizeof(BURN_EXECUTE_ACTION), 5);
1701 ExitOnFailure(hr, "Failed to grow plan's array of rollback actions.");
1702
1703 *ppRollbackAction = pPlan->rgRollbackActions + pPlan->cRollbackActions;
1704 ++pPlan->cRollbackActions;
1705
1706LExit:
1707 return hr;
1708}
1709
1710extern "C" HRESULT PlanKeepRegistration(
1711 __in BURN_PLAN* pPlan,
1712 __in DWORD iAfterExecutePackageAction,
1713 __in DWORD iBeforeRollbackPackageAction
1714 )
1715{
1716 HRESULT hr = S_OK;
1717 BURN_EXECUTE_ACTION* pAction = NULL;
1718
1719 if (BURN_PLAN_INVALID_ACTION_INDEX != iAfterExecutePackageAction)
1720 {
1721 hr = PlanInsertExecuteAction(iAfterExecutePackageAction, pPlan, &pAction);
1722 ExitOnFailure(hr, "Failed to insert keep registration execute action.");
1723
1724 pAction->type = BURN_EXECUTE_ACTION_TYPE_REGISTRATION;
1725 pAction->registration.fKeep = TRUE;
1726 }
1727
1728 if (BURN_PLAN_INVALID_ACTION_INDEX != iBeforeRollbackPackageAction)
1729 {
1730 hr = PlanInsertRollbackAction(iBeforeRollbackPackageAction, pPlan, &pAction);
1731 ExitOnFailure(hr, "Failed to insert keep registration rollback action.");
1732
1733 pAction->type = BURN_EXECUTE_ACTION_TYPE_REGISTRATION;
1734 pAction->registration.fKeep = FALSE;
1735 }
1736
1737LExit:
1738 return hr;
1739}
1740
1741extern "C" HRESULT PlanRemoveRegistration(
1742 __in BURN_PLAN* pPlan,
1743 __in DWORD iAfterExecutePackageAction,
1744 __in DWORD iAfterRollbackPackageAction
1745 )
1746{
1747 HRESULT hr = S_OK;
1748 BURN_EXECUTE_ACTION* pAction = NULL;
1749
1750 if (BURN_PLAN_INVALID_ACTION_INDEX != iAfterExecutePackageAction)
1751 {
1752 hr = PlanInsertExecuteAction(iAfterExecutePackageAction, pPlan, &pAction);
1753 ExitOnFailure(hr, "Failed to insert remove registration execute action.");
1754
1755 pAction->type = BURN_EXECUTE_ACTION_TYPE_REGISTRATION;
1756 pAction->registration.fKeep = FALSE;
1757 }
1758
1759 if (BURN_PLAN_INVALID_ACTION_INDEX != iAfterRollbackPackageAction)
1760 {
1761 hr = PlanInsertRollbackAction(iAfterRollbackPackageAction, pPlan, &pAction);
1762 ExitOnFailure(hr, "Failed to insert remove registration rollback action.");
1763
1764 pAction->type = BURN_EXECUTE_ACTION_TYPE_REGISTRATION;
1765 pAction->registration.fKeep = TRUE;
1766 }
1767
1768LExit:
1769 return hr;
1770}
1771
1772extern "C" HRESULT PlanRollbackBoundaryBegin(
1773 __in BURN_PLAN* pPlan,
1774 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
1775 )
1776{
1777 HRESULT hr = S_OK;
1778 BURN_EXECUTE_ACTION* pExecuteAction = NULL;
1779
1780 // Add begin rollback boundary to execute plan.
1781 hr = PlanAppendExecuteAction(pPlan, &pExecuteAction);
1782 ExitOnFailure(hr, "Failed to append rollback boundary begin action.");
1783
1784 pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY;
1785 pExecuteAction->rollbackBoundary.pRollbackBoundary = pRollbackBoundary;
1786
1787 // Add begin rollback boundary to rollback plan.
1788 hr = PlanAppendRollbackAction(pPlan, &pExecuteAction);
1789 ExitOnFailure(hr, "Failed to append rollback boundary begin action.");
1790
1791 pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY;
1792 pExecuteAction->rollbackBoundary.pRollbackBoundary = pRollbackBoundary;
1793
1794LExit:
1795 return hr;
1796}
1797
1798extern "C" HRESULT PlanRollbackBoundaryComplete(
1799 __in BURN_PLAN* pPlan
1800 )
1801{
1802 HRESULT hr = S_OK;
1803 BURN_EXECUTE_ACTION* pExecuteAction = NULL;
1804 DWORD dwCheckpointId = 0;
1805
1806 // Add checkpoints.
1807 dwCheckpointId = GetNextCheckpointId();
1808
1809 hr = PlanAppendExecuteAction(pPlan, &pExecuteAction);
1810 ExitOnFailure(hr, "Failed to append execute action.");
1811
1812 pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT;
1813 pExecuteAction->checkpoint.dwId = dwCheckpointId;
1814
1815 hr = PlanAppendRollbackAction(pPlan, &pExecuteAction);
1816 ExitOnFailure(hr, "Failed to append rollback action.");
1817
1818 pExecuteAction->type = BURN_EXECUTE_ACTION_TYPE_CHECKPOINT;
1819 pExecuteAction->checkpoint.dwId = dwCheckpointId;
1820
1821LExit:
1822 return hr;
1823}
1824
1825/*******************************************************************
1826 PlanSetResumeCommand - Initializes resume command string
1827
1828*******************************************************************/
1829extern "C" HRESULT PlanSetResumeCommand(
1830 __in BURN_REGISTRATION* pRegistration,
1831 __in BOOTSTRAPPER_ACTION action,
1832 __in BOOTSTRAPPER_COMMAND* pCommand,
1833 __in BURN_LOGGING* pLog
1834 )
1835{
1836 HRESULT hr = S_OK;
1837
1838 // build the resume command-line.
1839 hr = CoreRecreateCommandLine(&pRegistration->sczResumeCommandLine, action, pCommand->display, pCommand->restart, pCommand->relationType, pCommand->fPassthrough, pRegistration->sczActiveParent, pRegistration->sczAncestors, pLog->sczPath, pCommand->wzCommandLine);
1840 ExitOnFailure(hr, "Failed to recreate resume command-line.");
1841
1842LExit:
1843 return hr;
1844}
1845
1846
1847// internal function definitions
1848
1849static void UninitializeRegistrationAction(
1850 __in BURN_DEPENDENT_REGISTRATION_ACTION* pAction
1851 )
1852{
1853 ReleaseStr(pAction->sczDependentProviderKey);
1854 ReleaseStr(pAction->sczBundleId);
1855 memset(pAction, 0, sizeof(BURN_DEPENDENT_REGISTRATION_ACTION));
1856}
1857
1858static void UninitializeCacheAction(
1859 __in BURN_CACHE_ACTION* pCacheAction
1860 )
1861{
1862 switch (pCacheAction->type)
1863 {
1864 case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT:
1865 ReleaseHandle(pCacheAction->syncpoint.hEvent);
1866 break;
1867
1868 case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE:
1869 ReleaseStr(pCacheAction->bundleLayout.sczExecutableName);
1870 ReleaseStr(pCacheAction->bundleLayout.sczLayoutDirectory);
1871 ReleaseStr(pCacheAction->bundleLayout.sczUnverifiedPath);
1872 break;
1873
1874 case BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER:
1875 ReleaseStr(pCacheAction->resolveContainer.sczUnverifiedPath);
1876 break;
1877
1878 case BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER:
1879 ReleaseStr(pCacheAction->extractContainer.sczContainerUnverifiedPath);
1880 ReleaseMem(pCacheAction->extractContainer.rgPayloads);
1881 break;
1882
1883 case BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD:
1884 ReleaseStr(pCacheAction->resolvePayload.sczUnverifiedPath);
1885 break;
1886
1887 case BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD:
1888 ReleaseStr(pCacheAction->cachePayload.sczUnverifiedPath);
1889 break;
1890 }
1891}
1892
1893static void ResetPlannedPackageState(
1894 __in BURN_PACKAGE* pPackage
1895 )
1896{
1897 // Reset package state that is a result of planning.
1898 pPackage->expected = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN;
1899 pPackage->defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1900 pPackage->requested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1901 pPackage->fAcquire = FALSE;
1902 pPackage->fUncache = FALSE;
1903 pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE;
1904 pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
1905 pPackage->providerExecute = BURN_DEPENDENCY_ACTION_NONE;
1906 pPackage->providerRollback = BURN_DEPENDENCY_ACTION_NONE;
1907 pPackage->dependencyExecute = BURN_DEPENDENCY_ACTION_NONE;
1908 pPackage->dependencyRollback = BURN_DEPENDENCY_ACTION_NONE;
1909 pPackage->fDependencyManagerWasHere = FALSE;
1910
1911 if (BURN_PACKAGE_TYPE_MSI == pPackage->type && pPackage->Msi.rgFeatures)
1912 {
1913 for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
1914 {
1915 BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
1916
1917 pFeature->execute = BOOTSTRAPPER_FEATURE_ACTION_NONE;
1918 pFeature->rollback = BOOTSTRAPPER_FEATURE_ACTION_NONE;
1919 }
1920 }
1921 else if (BURN_PACKAGE_TYPE_MSP == pPackage->type && pPackage->Msp.rgTargetProducts)
1922 {
1923 for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i)
1924 {
1925 BURN_MSPTARGETPRODUCT* pTargetProduct = &pPackage->Msp.rgTargetProducts[i];
1926
1927 pTargetProduct->execute = BOOTSTRAPPER_ACTION_STATE_NONE;
1928 pTargetProduct->rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
1929 }
1930 }
1931}
1932
1933static HRESULT GetActionDefaultRequestState(
1934 __in BOOTSTRAPPER_ACTION action,
1935 __in BOOL fPermanent,
1936 __in BOOTSTRAPPER_PACKAGE_STATE currentState,
1937 __out BOOTSTRAPPER_REQUEST_STATE* pRequestState
1938 )
1939{
1940 HRESULT hr = S_OK;
1941
1942 switch (action)
1943 {
1944 case BOOTSTRAPPER_ACTION_CACHE:
1945 switch (currentState)
1946 {
1947 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
1948 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
1949 break;
1950
1951 case BOOTSTRAPPER_PACKAGE_STATE_CACHED:
1952 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
1953 break;
1954
1955 default:
1956 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE;
1957 break;
1958 }
1959 break;
1960
1961 case BOOTSTRAPPER_ACTION_INSTALL: __fallthrough;
1962 case BOOTSTRAPPER_ACTION_UPDATE_REPLACE: __fallthrough;
1963 case BOOTSTRAPPER_ACTION_UPDATE_REPLACE_EMBEDDED:
1964 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
1965 break;
1966
1967 case BOOTSTRAPPER_ACTION_REPAIR:
1968 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_REPAIR;
1969 break;
1970
1971 case BOOTSTRAPPER_ACTION_UNINSTALL:
1972 *pRequestState = fPermanent ? BOOTSTRAPPER_REQUEST_STATE_NONE : BOOTSTRAPPER_REQUEST_STATE_ABSENT;
1973 break;
1974
1975 case BOOTSTRAPPER_ACTION_MODIFY:
1976 switch (currentState)
1977 {
1978 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
1979 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_ABSENT;
1980 break;
1981
1982 case BOOTSTRAPPER_PACKAGE_STATE_CACHED:
1983 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_CACHE;
1984 break;
1985
1986 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
1987 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
1988 break;
1989
1990 default:
1991 *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
1992 break;
1993 }
1994 break;
1995
1996 default:
1997 hr = E_INVALIDARG;
1998 ExitOnRootFailure(hr, "Invalid action state.");
1999 }
2000
2001LExit:
2002 return hr;
2003}
2004
2005static HRESULT AddRegistrationAction(
2006 __in BURN_PLAN* pPlan,
2007 __in BURN_DEPENDENT_REGISTRATION_ACTION_TYPE type,
2008 __in_z LPCWSTR wzDependentProviderKey,
2009 __in_z LPCWSTR wzOwnerBundleId
2010 )
2011{
2012 HRESULT hr = S_OK;
2013 BURN_DEPENDENT_REGISTRATION_ACTION_TYPE rollbackType = (BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER == type) ? BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER : BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER;
2014 BURN_DEPENDENT_REGISTRATION_ACTION* pAction = NULL;
2015
2016 // Create forward registration action.
2017 hr = MemEnsureArraySize((void**)&pPlan->rgRegistrationActions, pPlan->cRegistrationActions + 1, sizeof(BURN_DEPENDENT_REGISTRATION_ACTION), 5);
2018 ExitOnFailure(hr, "Failed to grow plan's array of registration actions.");
2019
2020 pAction = pPlan->rgRegistrationActions + pPlan->cRegistrationActions;
2021 ++pPlan->cRegistrationActions;
2022
2023 pAction->type = type;
2024
2025 hr = StrAllocString(&pAction->sczBundleId, wzOwnerBundleId, 0);
2026 ExitOnFailure(hr, "Failed to copy owner bundle to registration action.");
2027
2028 hr = StrAllocString(&pAction->sczDependentProviderKey, wzDependentProviderKey, 0);
2029 ExitOnFailure(hr, "Failed to copy dependent provider key to registration action.");
2030
2031 // Create rollback registration action.
2032 hr = MemEnsureArraySize((void**)&pPlan->rgRollbackRegistrationActions, pPlan->cRollbackRegistrationActions + 1, sizeof(BURN_DEPENDENT_REGISTRATION_ACTION), 5);
2033 ExitOnFailure(hr, "Failed to grow plan's array of rollback registration actions.");
2034
2035 pAction = pPlan->rgRollbackRegistrationActions + pPlan->cRollbackRegistrationActions;
2036 ++pPlan->cRollbackRegistrationActions;
2037
2038 pAction->type = rollbackType;
2039
2040 hr = StrAllocString(&pAction->sczBundleId, wzOwnerBundleId, 0);
2041 ExitOnFailure(hr, "Failed to copy owner bundle to registration action.");
2042
2043 hr = StrAllocString(&pAction->sczDependentProviderKey, wzDependentProviderKey, 0);
2044 ExitOnFailure(hr, "Failed to copy dependent provider key to rollback registration action.");
2045
2046LExit:
2047 return hr;
2048}
2049
2050static HRESULT AddCachePackage(
2051 __in BURN_PLAN* pPlan,
2052 __in BURN_PACKAGE* pPackage,
2053 __out HANDLE* phSyncpointEvent
2054 )
2055{
2056 HRESULT hr = S_OK;
2057
2058 // If this is an MSI package with slipstream MSPs, ensure the MSPs are cached first.
2059 if (BURN_PACKAGE_TYPE_MSI == pPackage->type && 0 < pPackage->Msi.cSlipstreamMspPackages)
2060 {
2061 hr = AddCacheSlipstreamMsps(pPlan, pPackage);
2062 ExitOnFailure(hr, "Failed to plan slipstream patches for package.");
2063 }
2064
2065 hr = AddCachePackageHelper(pPlan, pPackage, phSyncpointEvent);
2066 ExitOnFailure(hr, "Failed to plan cache package.");
2067
2068LExit:
2069 return hr;
2070}
2071
2072static HRESULT AddCachePackageHelper(
2073 __in BURN_PLAN* pPlan,
2074 __in BURN_PACKAGE* pPackage,
2075 __out HANDLE* phSyncpointEvent
2076 )
2077{
2078 AssertSz(pPackage->sczCacheId && *pPackage->sczCacheId, "AddCachePackageHelper() expects the package to have a cache id.");
2079
2080 HRESULT hr = S_OK;
2081 BURN_CACHE_ACTION* pCacheAction = NULL;
2082 DWORD dwCheckpoint = 0;
2083 DWORD iPackageStartAction = 0;
2084
2085 BOOL fPlanned = AlreadyPlannedCachePackage(pPlan, pPackage->sczId, phSyncpointEvent);
2086 if (fPlanned)
2087 {
2088 ExitFunction();
2089 }
2090
2091 // Cache checkpoints happen before the package is cached because downloading packages'
2092 // payloads will not roll themselves back the way installation packages rollback on
2093 // failure automatically.
2094 dwCheckpoint = GetNextCheckpointId();
2095
2096 hr = AppendCacheAction(pPlan, &pCacheAction);
2097 ExitOnFailure(hr, "Failed to append package start action.");
2098
2099 pCacheAction->type = BURN_CACHE_ACTION_TYPE_CHECKPOINT;
2100 pCacheAction->checkpoint.dwId = dwCheckpoint;
2101
2102 // Only plan the cache rollback if the package is also going to be uninstalled;
2103 // otherwise, future operations like repair will not be able to locate the cached package.
2104 BOOL fPlanCacheRollback = (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->rollback);
2105
2106 if (fPlanCacheRollback)
2107 {
2108 hr = AppendRollbackCacheAction(pPlan, &pCacheAction);
2109 ExitOnFailure(hr, "Failed to append rollback cache action.");
2110
2111 pCacheAction->type = BURN_CACHE_ACTION_TYPE_CHECKPOINT;
2112 pCacheAction->checkpoint.dwId = dwCheckpoint;
2113 }
2114
2115 // Plan the package start.
2116 hr = AppendCacheAction(pPlan, &pCacheAction);
2117 ExitOnFailure(hr, "Failed to append package start action.");
2118
2119 pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE_START;
2120 pCacheAction->packageStart.pPackage = pPackage;
2121
2122 // Remember the index for the package start action (which is now the last in the cache
2123 // actions array) because we have to update this action after processing all the payloads
2124 // and the array may be resized later which would move a pointer around in memory.
2125 iPackageStartAction = pPlan->cCacheActions - 1;
2126
2127 if (fPlanCacheRollback)
2128 {
2129 // Create a package cache rollback action.
2130 hr = AppendRollbackCacheAction(pPlan, &pCacheAction);
2131 ExitOnFailure(hr, "Failed to append rollback cache action.");
2132
2133 pCacheAction->type = BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE;
2134 pCacheAction->rollbackPackage.pPackage = pPackage;
2135 }
2136
2137 // Add all the payload cache operations to the plan for this package.
2138 for (DWORD i = 0; i < pPackage->cPayloads; ++i)
2139 {
2140 BURN_PACKAGE_PAYLOAD* pPackagePayload = &pPackage->rgPayloads[i];
2141
2142 hr = AppendCacheOrLayoutPayloadAction(pPlan, pPackage, iPackageStartAction, pPackagePayload->pPayload, pPackagePayload->fCached, NULL);
2143 ExitOnFailure(hr, "Failed to append payload cache action.");
2144
2145 Assert(BURN_CACHE_ACTION_TYPE_PACKAGE_START == pPlan->rgCacheActions[iPackageStartAction].type);
2146 ++pPlan->rgCacheActions[iPackageStartAction].packageStart.cCachePayloads;
2147 pPlan->rgCacheActions[iPackageStartAction].packageStart.qwCachePayloadSizeTotal += pPackagePayload->pPayload->qwFileSize;
2148 }
2149
2150 // Create package stop action.
2151 hr = AppendCacheAction(pPlan, &pCacheAction);
2152 ExitOnFailure(hr, "Failed to append cache action.");
2153
2154 pCacheAction->type = BURN_CACHE_ACTION_TYPE_PACKAGE_STOP;
2155 pCacheAction->packageStop.pPackage = pPackage;
2156
2157 // Update the start action with the location of the complete action.
2158 pPlan->rgCacheActions[iPackageStartAction].packageStart.iPackageCompleteAction = pPlan->cCacheActions - 1;
2159
2160 // Create syncpoint action.
2161 hr = AppendCacheAction(pPlan, &pCacheAction);
2162 ExitOnFailure(hr, "Failed to append cache action.");
2163
2164 pCacheAction->type = BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT;
2165 pCacheAction->syncpoint.hEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL);
2166 ExitOnNullWithLastError(pCacheAction->syncpoint.hEvent, hr, "Failed to create syncpoint event.");
2167
2168 *phSyncpointEvent = pCacheAction->syncpoint.hEvent;
2169
2170 ++pPlan->cOverallProgressTicksTotal;
2171
2172 // If the package was not already fully cached then note that we planned the cache here. Otherwise, we only
2173 // did cache operations to verify the cache is valid so we did not plan the acquisition of the package.
2174 pPackage->fAcquire = (BURN_CACHE_STATE_COMPLETE != pPackage->cache);
2175
2176LExit:
2177 return hr;
2178}
2179
2180static HRESULT AddCacheSlipstreamMsps(
2181 __in BURN_PLAN* pPlan,
2182 __in BURN_PACKAGE* pPackage
2183 )
2184{
2185 HRESULT hr = S_OK;
2186 HANDLE hIgnored = NULL;
2187
2188 AssertSz(BURN_PACKAGE_TYPE_MSI == pPackage->type, "Only MSI packages can have slipstream patches.");
2189
2190 for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i)
2191 {
2192 BURN_PACKAGE* pMspPackage = pPackage->Msi.rgpSlipstreamMspPackages[i];
2193 AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches.");
2194
2195 hr = AddCachePackageHelper(pPlan, pMspPackage, &hIgnored);
2196 ExitOnFailure(hr, "Failed to plan slipstream MSP: %ls", pMspPackage->sczId);
2197 }
2198
2199LExit:
2200 return hr;
2201}
2202
2203static BOOL AlreadyPlannedCachePackage(
2204 __in BURN_PLAN* pPlan,
2205 __in_z LPCWSTR wzPackageId,
2206 __out HANDLE* phSyncpointEvent
2207 )
2208{
2209 BOOL fPlanned = FALSE;
2210
2211 for (DWORD iCacheAction = 0; iCacheAction < pPlan->cCacheActions; ++iCacheAction)
2212 {
2213 BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + iCacheAction;
2214
2215 if (BURN_CACHE_ACTION_TYPE_PACKAGE_STOP == pCacheAction->type)
2216 {
2217 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pCacheAction->packageStop.pPackage->sczId, -1, wzPackageId, -1))
2218 {
2219 if (iCacheAction + 1 < pPlan->cCacheActions && BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT == pPlan->rgCacheActions[iCacheAction + 1].type)
2220 {
2221 *phSyncpointEvent = pPlan->rgCacheActions[iCacheAction + 1].syncpoint.hEvent;
2222 }
2223
2224 fPlanned = TRUE;
2225 break;
2226 }
2227 }
2228 }
2229
2230 return fPlanned;
2231}
2232
2233static DWORD GetNextCheckpointId()
2234{
2235 static DWORD dwCounter = 0;
2236 return ++dwCounter;
2237}
2238
2239static HRESULT AppendCacheAction(
2240 __in BURN_PLAN* pPlan,
2241 __out BURN_CACHE_ACTION** ppCacheAction
2242 )
2243{
2244 HRESULT hr = S_OK;
2245
2246 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgCacheActions), pPlan->cCacheActions + 1, sizeof(BURN_CACHE_ACTION), 5);
2247 ExitOnFailure(hr, "Failed to grow plan's array of cache actions.");
2248
2249 *ppCacheAction = pPlan->rgCacheActions + pPlan->cCacheActions;
2250 ++pPlan->cCacheActions;
2251
2252LExit:
2253 return hr;
2254}
2255
2256static HRESULT AppendRollbackCacheAction(
2257 __in BURN_PLAN* pPlan,
2258 __out BURN_CACHE_ACTION** ppCacheAction
2259 )
2260{
2261 HRESULT hr = S_OK;
2262
2263 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgRollbackCacheActions), pPlan->cRollbackCacheActions + 1, sizeof(BURN_CACHE_ACTION), 5);
2264 ExitOnFailure(hr, "Failed to grow plan's array of rollback cache actions.");
2265
2266 *ppCacheAction = pPlan->rgRollbackCacheActions + pPlan->cRollbackCacheActions;
2267 ++pPlan->cRollbackCacheActions;
2268
2269LExit:
2270 return hr;
2271}
2272
2273static HRESULT AppendLayoutContainerAction(
2274 __in BURN_PLAN* pPlan,
2275 __in_opt BURN_PACKAGE* pPackage,
2276 __in DWORD iPackageStartAction,
2277 __in BURN_CONTAINER* pContainer,
2278 __in BOOL fContainerCached,
2279 __in_z LPCWSTR wzLayoutDirectory
2280 )
2281{
2282 HRESULT hr = S_OK;
2283 BURN_CACHE_ACTION* pAcquireAction = NULL;
2284 DWORD iAcquireAction = BURN_PLAN_INVALID_ACTION_INDEX;
2285 LPWSTR sczContainerWorkingPath = NULL;
2286 BURN_CACHE_ACTION* pCacheAction = NULL;
2287 BURN_CACHE_CONTAINER_PROGRESS* pContainerProgress = NULL;
2288
2289 // No need to do anything if the container is already cached or is attached to the bundle (since the
2290 // bundle itself will already have a layout action).
2291 if (fContainerCached || pContainer->fAttached)
2292 {
2293 ExitFunction();
2294 }
2295
2296 // Ensure the container is being acquired. If it is, then some earlier package already planned the layout of this container so
2297 // don't do it again. Otherwise, plan away!
2298 if (!FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER, pPlan, pContainer, 0, iPackageStartAction, NULL, NULL))
2299 {
2300 hr = AddAcquireContainer(pPlan, pContainer, &pAcquireAction, &iAcquireAction);
2301 ExitOnFailure(hr, "Failed to append acquire container action for layout to plan.");
2302
2303 Assert(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pAcquireAction->type);
2304
2305 // Create the layout container action.
2306 hr = StrAllocString(&sczContainerWorkingPath, pAcquireAction->resolveContainer.sczUnverifiedPath, 0);
2307 ExitOnFailure(hr, "Failed to copy container working path for layout.");
2308
2309 hr = AppendCacheAction(pPlan, &pCacheAction);
2310 ExitOnFailure(hr, "Failed to append cache action to cache payload.");
2311
2312 hr = CreateContainerProgress(pPlan, pContainer, &pContainerProgress);
2313 ExitOnFailure(hr, "Failed to create container progress.");
2314
2315 hr = StrAllocString(&pCacheAction->layoutContainer.sczLayoutDirectory, wzLayoutDirectory, 0);
2316 ExitOnFailure(hr, "Failed to copy layout directory into plan.");
2317
2318 pCacheAction->type = BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER;
2319 pCacheAction->layoutContainer.pPackage = pPackage;
2320 pCacheAction->layoutContainer.pContainer = pContainer;
2321 pCacheAction->layoutContainer.iProgress = pContainerProgress->iIndex;
2322 pCacheAction->layoutContainer.fMove = TRUE;
2323 pCacheAction->layoutContainer.iTryAgainAction = iAcquireAction;
2324 pCacheAction->layoutContainer.sczUnverifiedPath = sczContainerWorkingPath;
2325 sczContainerWorkingPath = NULL;
2326 }
2327
2328LExit:
2329 ReleaseNullStr(sczContainerWorkingPath);
2330
2331 return hr;
2332}
2333
2334static HRESULT AppendCacheOrLayoutPayloadAction(
2335 __in BURN_PLAN* pPlan,
2336 __in_opt BURN_PACKAGE* pPackage,
2337 __in DWORD iPackageStartAction,
2338 __in BURN_PAYLOAD* pPayload,
2339 __in BOOL fPayloadCached,
2340 __in_z_opt LPCWSTR wzLayoutDirectory
2341 )
2342{
2343 HRESULT hr = S_OK;
2344 LPWSTR sczPayloadWorkingPath = NULL;
2345 BURN_CACHE_ACTION* pCacheAction = NULL;
2346 DWORD iTryAgainAction = BURN_PLAN_INVALID_ACTION_INDEX;
2347 BURN_CACHE_PAYLOAD_PROGRESS* pPayloadProgress = NULL;
2348
2349 hr = CacheCalculatePayloadWorkingPath(pPlan->wzBundleId, pPayload, &sczPayloadWorkingPath);
2350 ExitOnFailure(hr, "Failed to calculate unverified path for payload.");
2351
2352 // If the payload is in a container, ensure the container is being acquired
2353 // then add this payload to the list of payloads to extract already in the plan.
2354 if (pPayload->pContainer)
2355 {
2356 BURN_CACHE_ACTION* pPreviousPackageExtractAction = NULL;
2357 BURN_CACHE_ACTION* pThisPackageExtractAction = NULL;
2358
2359 // If the payload is not already cached, then add it to the first extract container action in the plan. Extracting
2360 // all the needed payloads from the container in a single pass is the most efficient way to extract files from
2361 // containers. If there is not an extract container action before our package, that is okay because we'll create
2362 // an extract container action for our package in a second anyway.
2363 if (!fPayloadCached)
2364 {
2365 if (FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER, pPlan, pPayload->pContainer, 0, iPackageStartAction, &pPreviousPackageExtractAction, NULL))
2366 {
2367 hr = AddExtractPayload(pPreviousPackageExtractAction, pPackage, pPayload, sczPayloadWorkingPath);
2368 ExitOnFailure(hr, "Failed to add extract payload action to previous package.");
2369 }
2370 }
2371
2372 // If there is already an extract container action after our package start action then try to find an acquire action
2373 // that is matched with it. If there is an acquire action then that is our "try again" action, otherwise we'll use the existing
2374 // extract action as the "try again" action.
2375 if (FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER, pPlan, pPayload->pContainer, iPackageStartAction, BURN_PLAN_INVALID_ACTION_INDEX, &pThisPackageExtractAction, &iTryAgainAction))
2376 {
2377 DWORD iAcquireAction = BURN_PLAN_INVALID_ACTION_INDEX;
2378 if (FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER, pPlan, pPayload->pContainer, iPackageStartAction, iTryAgainAction, NULL, &iAcquireAction))
2379 {
2380 iTryAgainAction = iAcquireAction;
2381 }
2382 }
2383 else // did not find an extract container action for our package.
2384 {
2385 // Ensure there is an extract action (and maybe an acquire action) for every package that has payloads. The
2386 // acquire and extract action will be skipped if the payload is already cached or was added to a previous
2387 // package's extract action above.
2388 //
2389 // These actions always exist (even when they are likely to be skipped) so that "try again" will not
2390 // jump so far back in the plan that you end up extracting payloads for other packages. With these actions
2391 // "try again" will only retry the extraction for payloads in this package.
2392 hr = CreateContainerAcquireAndExtractAction(pPlan, pPayload->pContainer, iPackageStartAction, pPreviousPackageExtractAction ? TRUE : fPayloadCached, &pThisPackageExtractAction, &iTryAgainAction);
2393 ExitOnFailure(hr, "Failed to create container extract action.");
2394 }
2395 ExitOnFailure(hr, "Failed while searching for package's container extract action.");
2396
2397 // We *always* add the payload to this package's extract action even though the extract action
2398 // is probably being skipped until retry if there was a previous package extract action.
2399 hr = AddExtractPayload(pThisPackageExtractAction, pPackage, pPayload, sczPayloadWorkingPath);
2400 ExitOnFailure(hr, "Failed to add extract payload to current package.");
2401 }
2402 else // add a payload acquire action to the plan.
2403 {
2404 // Try to find an existing acquire action for this payload. If one is not found,
2405 // we'll create it. At the same time we will change any cache/layout payload actions
2406 // that would "MOVE" the file to "COPY" so that our new cache/layout action below
2407 // can do the move.
2408 pCacheAction = ProcessSharedPayload(pPlan, pPayload);
2409 if (!pCacheAction)
2410 {
2411 hr = AppendCacheAction(pPlan, &pCacheAction);
2412 ExitOnFailure(hr, "Failed to append cache action to acquire payload.");
2413
2414 pCacheAction->type = BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD;
2415 pCacheAction->fSkipUntilRetried = fPayloadCached;
2416 pCacheAction->resolvePayload.pPackage = pPackage;
2417 pCacheAction->resolvePayload.pPayload = pPayload;
2418 hr = StrAllocString(&pCacheAction->resolvePayload.sczUnverifiedPath, sczPayloadWorkingPath, 0);
2419 ExitOnFailure(hr, "Failed to copy unverified path for payload to acquire.");
2420 }
2421
2422 iTryAgainAction = static_cast<DWORD>(pCacheAction - pPlan->rgCacheActions);
2423 pCacheAction = NULL;
2424 }
2425
2426 Assert(BURN_PLAN_INVALID_ACTION_INDEX != iTryAgainAction);
2427 Assert(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pPlan->rgCacheActions[iTryAgainAction].type ||
2428 BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pPlan->rgCacheActions[iTryAgainAction].type ||
2429 BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD == pPlan->rgCacheActions[iTryAgainAction].type);
2430
2431 hr = AppendCacheAction(pPlan, &pCacheAction);
2432 ExitOnFailure(hr, "Failed to append cache action to cache payload.");
2433
2434 hr = CreatePayloadProgress(pPlan, pPayload, &pPayloadProgress);
2435 ExitOnFailure(hr, "Failed to create payload progress.");
2436
2437 if (!wzLayoutDirectory)
2438 {
2439 pCacheAction->type = BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD;
2440 pCacheAction->cachePayload.pPackage = pPackage;
2441 pCacheAction->cachePayload.pPayload = pPayload;
2442 pCacheAction->cachePayload.iProgress = pPayloadProgress->iIndex;
2443 pCacheAction->cachePayload.fMove = TRUE;
2444 pCacheAction->cachePayload.iTryAgainAction = iTryAgainAction;
2445 pCacheAction->cachePayload.sczUnverifiedPath = sczPayloadWorkingPath;
2446 sczPayloadWorkingPath = NULL;
2447 }
2448 else
2449 {
2450 hr = StrAllocString(&pCacheAction->layoutPayload.sczLayoutDirectory, wzLayoutDirectory, 0);
2451 ExitOnFailure(hr, "Failed to copy layout directory into plan.");
2452
2453 pCacheAction->type = BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD;
2454 pCacheAction->layoutPayload.pPackage = pPackage;
2455 pCacheAction->layoutPayload.pPayload = pPayload;
2456 pCacheAction->layoutPayload.iProgress = pPayloadProgress->iIndex;
2457 pCacheAction->layoutPayload.fMove = TRUE;
2458 pCacheAction->layoutPayload.iTryAgainAction = iTryAgainAction;
2459 pCacheAction->layoutPayload.sczUnverifiedPath = sczPayloadWorkingPath;
2460 sczPayloadWorkingPath = NULL;
2461 }
2462
2463 pCacheAction = NULL;
2464
2465LExit:
2466 ReleaseStr(sczPayloadWorkingPath);
2467
2468 return hr;
2469}
2470
2471static BOOL FindContainerCacheAction(
2472 __in BURN_CACHE_ACTION_TYPE type,
2473 __in BURN_PLAN* pPlan,
2474 __in BURN_CONTAINER* pContainer,
2475 __in DWORD iSearchStart,
2476 __in DWORD iSearchEnd,
2477 __out_opt BURN_CACHE_ACTION** ppCacheAction,
2478 __out_opt DWORD* piCacheAction
2479 )
2480{
2481 BOOL fFound = FALSE; // assume we won't find what we are looking for.
2482
2483 Assert(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == type || BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == type);
2484
2485 iSearchStart = (BURN_PLAN_INVALID_ACTION_INDEX == iSearchStart) ? 0 : iSearchStart;
2486 iSearchEnd = (BURN_PLAN_INVALID_ACTION_INDEX == iSearchEnd) ? pPlan->cCacheActions : iSearchEnd;
2487
2488 for (DWORD iSearch = iSearchStart; iSearch < iSearchEnd; ++iSearch)
2489 {
2490 BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + iSearch;
2491 if (pCacheAction->type == type &&
2492 ((BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pCacheAction->type && pCacheAction->resolveContainer.pContainer == pContainer) ||
2493 (BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pCacheAction->type && pCacheAction->extractContainer.pContainer == pContainer)))
2494 {
2495 if (ppCacheAction)
2496 {
2497 *ppCacheAction = pCacheAction;
2498 }
2499
2500 if (piCacheAction)
2501 {
2502 *piCacheAction = iSearch;
2503 }
2504
2505 fFound = TRUE;
2506 break;
2507 }
2508 }
2509
2510 return fFound;
2511}
2512
2513static HRESULT CreateContainerAcquireAndExtractAction(
2514 __in BURN_PLAN* pPlan,
2515 __in BURN_CONTAINER* pContainer,
2516 __in DWORD iPackageStartAction,
2517 __in BOOL fPayloadCached,
2518 __out BURN_CACHE_ACTION** ppContainerExtractAction,
2519 __out DWORD* piContainerTryAgainAction
2520 )
2521{
2522 HRESULT hr = S_OK;
2523 DWORD iAcquireAction = BURN_PLAN_INVALID_ACTION_INDEX;
2524 BURN_CACHE_ACTION* pContainerExtractAction = NULL;
2525 DWORD iExtractAction = BURN_PLAN_INVALID_ACTION_INDEX;
2526 DWORD iTryAgainAction = BURN_PLAN_INVALID_ACTION_INDEX;
2527 LPWSTR sczContainerWorkingPath = NULL;
2528
2529 // If the container is actually attached to the executable then we will not need an acquire
2530 // container action.
2531 if (!pContainer->fActuallyAttached)
2532 {
2533 BURN_CACHE_ACTION* pAcquireContainerAction = NULL;
2534
2535 // If there is no plan to acquire the container then add acquire action since we
2536 // can't extract stuff out of a container until we acquire the container.
2537 if (!FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER, pPlan, pContainer, iPackageStartAction, BURN_PLAN_INVALID_ACTION_INDEX, &pAcquireContainerAction, &iAcquireAction))
2538 {
2539 hr = AddAcquireContainer(pPlan, pContainer, &pAcquireContainerAction, &iAcquireAction);
2540 ExitOnFailure(hr, "Failed to append acquire container action to plan.");
2541
2542 pAcquireContainerAction->fSkipUntilRetried = TRUE; // we'll start by assuming the acquire is not necessary and the fPayloadCached below will set us straight if wrong.
2543 }
2544
2545 Assert(BURN_PLAN_INVALID_ACTION_INDEX != iAcquireAction);
2546 Assert(BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pAcquireContainerAction->type);
2547 Assert(pContainer == pAcquireContainerAction->resolveContainer.pContainer);
2548 }
2549
2550 Assert((pContainer->fActuallyAttached && BURN_PLAN_INVALID_ACTION_INDEX == iAcquireAction) ||
2551 (!pContainer->fActuallyAttached && BURN_PLAN_INVALID_ACTION_INDEX != iAcquireAction));
2552
2553 // If we do not find an action for extracting payloads from this container, create it now.
2554 if (!FindContainerCacheAction(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER, pPlan, pContainer, (BURN_PLAN_INVALID_ACTION_INDEX == iAcquireAction) ? iPackageStartAction : iAcquireAction, BURN_PLAN_INVALID_ACTION_INDEX, &pContainerExtractAction, &iExtractAction))
2555 {
2556 // Attached containers that are actually attached use the executable path for their working path.
2557 if (pContainer->fActuallyAttached)
2558 {
2559 Assert(BURN_PLAN_INVALID_ACTION_INDEX == iAcquireAction);
2560
2561 hr = PathForCurrentProcess(&sczContainerWorkingPath, NULL);
2562 ExitOnFailure(hr, "Failed to get path for executing module as attached container working path.");
2563 }
2564 else // use the acquired working path as the location of the container.
2565 {
2566 Assert(BURN_PLAN_INVALID_ACTION_INDEX != iAcquireAction);
2567
2568 hr = StrAllocString(&sczContainerWorkingPath, pPlan->rgCacheActions[iAcquireAction].resolveContainer.sczUnverifiedPath, 0);
2569 ExitOnFailure(hr, "Failed to copy container unverified path for cache action to extract container.");
2570 }
2571
2572 hr = AppendCacheAction(pPlan, &pContainerExtractAction);
2573 ExitOnFailure(hr, "Failed to append cache action to extract payloads from container.");
2574
2575 iExtractAction = pPlan->cCacheActions - 1;
2576
2577 pContainerExtractAction->type = BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER;
2578 pContainerExtractAction->fSkipUntilRetried = pContainer->fActuallyAttached; // assume we can skip the extract engine when the container is already attached and the fPayloadCached below will set us straight if wrong.
2579 pContainerExtractAction->extractContainer.pContainer = pContainer;
2580 pContainerExtractAction->extractContainer.iSkipUntilAcquiredByAction = iAcquireAction;
2581 pContainerExtractAction->extractContainer.sczContainerUnverifiedPath = sczContainerWorkingPath;
2582 sczContainerWorkingPath = NULL;
2583 }
2584
2585 Assert(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pContainerExtractAction->type);
2586 Assert(BURN_PLAN_INVALID_ACTION_INDEX != iExtractAction);
2587
2588 // If there is an acquire action, that is our try again action. Otherwise, we'll use the extract action.
2589 iTryAgainAction = (BURN_PLAN_INVALID_ACTION_INDEX != iAcquireAction) ? iAcquireAction : iExtractAction;
2590
2591 // If the try again action thinks it can be skipped but the payload is not cached,
2592 // ensure the action will not be skipped.
2593 BURN_CACHE_ACTION* pTryAgainAction = pPlan->rgCacheActions + iTryAgainAction;
2594 Assert((BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER == pTryAgainAction->type && pContainer == pTryAgainAction->resolveContainer.pContainer) ||
2595 (BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pTryAgainAction->type && pContainer == pTryAgainAction->extractContainer.pContainer));
2596 if (pTryAgainAction->fSkipUntilRetried && !fPayloadCached)
2597 {
2598 pTryAgainAction->fSkipUntilRetried = FALSE;
2599 }
2600
2601 *ppContainerExtractAction = pContainerExtractAction;
2602 *piContainerTryAgainAction = iTryAgainAction;
2603
2604LExit:
2605 ReleaseStr(sczContainerWorkingPath);
2606
2607 return hr;
2608}
2609
2610static HRESULT AddAcquireContainer(
2611 __in BURN_PLAN* pPlan,
2612 __in BURN_CONTAINER* pContainer,
2613 __out_opt BURN_CACHE_ACTION** ppCacheAction,
2614 __out_opt DWORD* piCacheAction
2615 )
2616{
2617 HRESULT hr = S_OK;
2618 LPWSTR sczContainerWorkingPath = NULL;
2619 BURN_CACHE_ACTION* pAcquireContainerAction = NULL;
2620 BURN_CACHE_CONTAINER_PROGRESS* pContainerProgress = NULL;
2621
2622 hr = CacheCalculateContainerWorkingPath(pPlan->wzBundleId, pContainer, &sczContainerWorkingPath);
2623 ExitOnFailure(hr, "Failed to calculate unverified path for container.");
2624
2625 hr = AppendCacheAction(pPlan, &pAcquireContainerAction);
2626 ExitOnFailure(hr, "Failed to append acquire container action to plan.");
2627
2628 hr = CreateContainerProgress(pPlan, pContainer, &pContainerProgress);
2629 ExitOnFailure(hr, "Failed to create container progress.");
2630
2631 pAcquireContainerAction->type = BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER;
2632 pAcquireContainerAction->resolveContainer.pContainer = pContainer;
2633 pAcquireContainerAction->resolveContainer.iProgress = pContainerProgress->iIndex;
2634 pAcquireContainerAction->resolveContainer.sczUnverifiedPath = sczContainerWorkingPath;
2635 sczContainerWorkingPath = NULL;
2636
2637 if (ppCacheAction)
2638 {
2639 *ppCacheAction = pAcquireContainerAction;
2640 }
2641
2642 if (piCacheAction)
2643 {
2644 *piCacheAction = pPlan->cCacheActions - 1;
2645 }
2646
2647LExit:
2648 ReleaseStr(sczContainerWorkingPath);
2649
2650 return hr;
2651}
2652
2653static HRESULT AddExtractPayload(
2654 __in BURN_CACHE_ACTION* pCacheAction,
2655 __in_opt BURN_PACKAGE* pPackage,
2656 __in BURN_PAYLOAD* pPayload,
2657 __in_z LPCWSTR wzPayloadWorkingPath
2658 )
2659{
2660 HRESULT hr = S_OK;
2661
2662 Assert(BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER == pCacheAction->type);
2663
2664 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pCacheAction->extractContainer.rgPayloads), pCacheAction->extractContainer.cPayloads + 1, sizeof(BURN_EXTRACT_PAYLOAD), 5);
2665 ExitOnFailure(hr, "Failed to grow list of payloads to extract from container.");
2666
2667 BURN_EXTRACT_PAYLOAD* pExtractPayload = pCacheAction->extractContainer.rgPayloads + pCacheAction->extractContainer.cPayloads;
2668 pExtractPayload->pPackage = pPackage;
2669 pExtractPayload->pPayload = pPayload;
2670 hr = StrAllocString(&pExtractPayload->sczUnverifiedPath, wzPayloadWorkingPath, 0);
2671 ExitOnFailure(hr, "Failed to copy unverified path for payload to extract.");
2672 ++pCacheAction->extractContainer.cPayloads;
2673
2674LExit:
2675 return hr;
2676}
2677
2678static BURN_CACHE_ACTION* ProcessSharedPayload(
2679 __in BURN_PLAN* pPlan,
2680 __in BURN_PAYLOAD* pPayload
2681 )
2682{
2683 BURN_CACHE_ACTION* pAcquireAction = NULL;
2684#ifdef DEBUG
2685 DWORD cMove = 0;
2686#endif
2687
2688 for (DWORD i = 0; i < pPlan->cCacheActions; ++i)
2689 {
2690 BURN_CACHE_ACTION* pCacheAction = pPlan->rgCacheActions + i;
2691
2692 if (BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD == pCacheAction->type &&
2693 pCacheAction->resolvePayload.pPayload == pPayload)
2694 {
2695 AssertSz(!pAcquireAction, "There should be at most one acquire cache action per payload.");
2696 pAcquireAction = pCacheAction;
2697 }
2698 else if (BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD == pCacheAction->type &&
2699 pCacheAction->cachePayload.pPayload == pPayload &&
2700 pCacheAction->cachePayload.fMove)
2701 {
2702 // Since we found a shared payload, change its operation from MOVE to COPY.
2703 pCacheAction->cachePayload.fMove = FALSE;
2704
2705 AssertSz(1 == ++cMove, "Shared payload should be moved once and only once.");
2706#ifndef DEBUG
2707 break;
2708#endif
2709 }
2710 else if (BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD == pCacheAction->type &&
2711 pCacheAction->layoutPayload.pPayload == pPayload &&
2712 pCacheAction->layoutPayload.fMove)
2713 {
2714 // Since we found a shared payload, change its operation from MOVE to COPY if necessary
2715 pCacheAction->layoutPayload.fMove = FALSE;
2716
2717 AssertSz(1 == ++cMove, "Shared payload should be moved once and only once.");
2718#ifndef DEBUG
2719 break;
2720#endif
2721 }
2722 }
2723
2724 return pAcquireAction;
2725}
2726
2727static HRESULT RemoveUnnecessaryActions(
2728 __in BOOL fExecute,
2729 __in BURN_EXECUTE_ACTION* rgActions,
2730 __in DWORD cActions
2731 )
2732{
2733 HRESULT hr = S_OK;
2734 LPCSTR szExecuteOrRollback = fExecute ? "execute" : "rollback";
2735
2736 for (DWORD i = 0; i < cActions; ++i)
2737 {
2738 BURN_EXECUTE_ACTION* pAction = rgActions + i;
2739
2740 // If this MSP targets a package in the chain, check the target's execute state
2741 // to see if this patch should be skipped.
2742 if (BURN_EXECUTE_ACTION_TYPE_MSP_TARGET == pAction->type && pAction->mspTarget.pChainedTargetPackage)
2743 {
2744 BOOTSTRAPPER_ACTION_STATE chainedTargetPackageAction = fExecute ? pAction->mspTarget.pChainedTargetPackage->execute : pAction->mspTarget.pChainedTargetPackage->rollback;
2745 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == chainedTargetPackageAction)
2746 {
2747 LogId(REPORT_STANDARD, MSG_PLAN_SKIP_PATCH_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback);
2748 pAction->fDeleted = TRUE;
2749 }
2750 else if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < chainedTargetPackageAction && pAction->mspTarget.fSlipstream && BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pAction->mspTarget.action)
2751 {
2752 // If the slipstream target is being installed or upgraded (not uninstalled or repaired) then we will slipstream so skip
2753 // this action to install the patch standalone. Also, if the slipstream target is being repaired and the patch is being
2754 // repaired, skip this operation since it will be redundant.
2755 //
2756 // The primary goal here is to ensure that a slipstream patch that is yet not installed is installed even if the MSI
2757 // is already on the machine. The slipstream must be installed standalone if the MSI is being repaired.
2758 if (BOOTSTRAPPER_ACTION_STATE_REPAIR != chainedTargetPackageAction || BOOTSTRAPPER_ACTION_STATE_REPAIR == pAction->mspTarget.action)
2759 {
2760 LogId(REPORT_STANDARD, MSG_PLAN_SKIP_SLIPSTREAM_ACTION, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.pChainedTargetPackage->sczId, LoggingActionStateToString(chainedTargetPackageAction), szExecuteOrRollback);
2761 pAction->fDeleted = TRUE;
2762 }
2763 }
2764 }
2765 }
2766
2767 return hr;
2768}
2769
2770static HRESULT FinalizeSlipstreamPatchActions(
2771 __in BOOL fExecute,
2772 __in BURN_EXECUTE_ACTION* rgActions,
2773 __in DWORD cActions
2774 )
2775{
2776 HRESULT hr = S_OK;
2777
2778 for (DWORD i = 0; i < cActions; ++i)
2779 {
2780 BURN_EXECUTE_ACTION* pAction = rgActions + i;
2781
2782 // If this MSI package contains slipstream patches store the slipstream actions.
2783 if (BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE == pAction->type && pAction->msiPackage.pPackage->Msi.cSlipstreamMspPackages)
2784 {
2785 BURN_PACKAGE* pPackage = pAction->msiPackage.pPackage;
2786
2787 // By default all slipstream actions will be initialized to "no action" (aka: 0).
2788 pAction->msiPackage.rgSlipstreamPatches = (BOOTSTRAPPER_ACTION_STATE*)MemAlloc(sizeof(BOOTSTRAPPER_ACTION_STATE) * pPackage->Msi.cSlipstreamMspPackages, TRUE);
2789 ExitOnNull(pAction->msiPackage.rgSlipstreamPatches, hr, E_OUTOFMEMORY, "Failed to allocate memory for patch actions.");
2790
2791 // If we are uninstalling or repairing the MSI, we must ignore all the slipstream patches because they cannot
2792 // be applied right now.
2793 if (BOOTSTRAPPER_ACTION_STATE_REPAIR != pAction->msiPackage.action && BOOTSTRAPPER_ACTION_STATE_UNINSTALL != pAction->msiPackage.action)
2794 {
2795 for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j)
2796 {
2797 BURN_PACKAGE* pMspPackage = pPackage->Msi.rgpSlipstreamMspPackages[j];
2798 AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Only MSP packages can be slipstream patches.");
2799
2800 pAction->msiPackage.rgSlipstreamPatches[j] = fExecute ? pMspPackage->execute : pMspPackage->rollback;
2801 for (DWORD k = 0; k < pMspPackage->Msp.cTargetProductCodes; ++k)
2802 {
2803 BURN_MSPTARGETPRODUCT* pTargetProduct = pMspPackage->Msp.rgTargetProducts + k;
2804 if (pPackage == pTargetProduct->pChainedTargetPackage)
2805 {
2806 pAction->msiPackage.rgSlipstreamPatches[j] = fExecute ? pTargetProduct->execute : pTargetProduct->rollback;
2807 break;
2808 }
2809 }
2810 }
2811 }
2812 }
2813 }
2814
2815LExit:
2816 return hr;
2817}
2818
2819static HRESULT PlanDependencyActions(
2820 __in BOOL fBundlePerMachine,
2821 __in BURN_PLAN* pPlan,
2822 __in BURN_PACKAGE* pPackage
2823 )
2824{
2825 HRESULT hr = S_OK;
2826
2827 hr = DependencyPlanPackageBegin(fBundlePerMachine, pPackage, pPlan);
2828 ExitOnFailure(hr, "Failed to begin plan dependency actions for package: %ls", pPackage->sczId);
2829
2830 hr = DependencyPlanPackage(NULL, pPackage, pPlan);
2831 ExitOnFailure(hr, "Failed to plan package dependency actions.");
2832
2833 hr = DependencyPlanPackageComplete(pPackage, pPlan);
2834 ExitOnFailure(hr, "Failed to complete plan dependency actions for package: %ls", pPackage->sczId);
2835
2836LExit:
2837 return hr;
2838}
2839
2840static HRESULT CalculateExecuteActions(
2841 __in BURN_USER_EXPERIENCE* pUserExperience,
2842 __in BURN_PACKAGE* pPackage,
2843 __in BURN_VARIABLES* pVariables,
2844 __out_opt BOOL* pfBARequestedCache
2845 )
2846{
2847 HRESULT hr = S_OK;
2848
2849 // Calculate execute actions.
2850 switch (pPackage->type)
2851 {
2852 case BURN_PACKAGE_TYPE_EXE:
2853 hr = ExeEnginePlanCalculatePackage(pPackage, pfBARequestedCache);
2854 break;
2855
2856 case BURN_PACKAGE_TYPE_MSI:
2857 hr = MsiEnginePlanCalculatePackage(pPackage, pVariables, pUserExperience, pfBARequestedCache);
2858 break;
2859
2860 case BURN_PACKAGE_TYPE_MSP:
2861 hr = MspEnginePlanCalculatePackage(pPackage, pUserExperience, pfBARequestedCache);
2862 break;
2863
2864 case BURN_PACKAGE_TYPE_MSU:
2865 hr = MsuEnginePlanCalculatePackage(pPackage, pfBARequestedCache);
2866 break;
2867
2868 default:
2869 hr = E_UNEXPECTED;
2870 ExitOnFailure(hr, "Invalid package type.");
2871 }
2872
2873LExit:
2874 return hr;
2875}
2876
2877static BOOL NeedsCache(
2878 __in BURN_PLAN* pPlan,
2879 __in BURN_PACKAGE* pPackage
2880 )
2881{
2882 // All packages that have cacheType set to always should be cached if the bundle is going to be present.
2883 if (BURN_CACHE_TYPE_ALWAYS == pPackage->cacheType && BOOTSTRAPPER_ACTION_INSTALL <= pPlan->action)
2884 {
2885 return TRUE;
2886 }
2887 else if (BURN_PACKAGE_TYPE_EXE == pPackage->type) // Exe packages require the package for all operations (even uninstall).
2888 {
2889 return BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute;
2890 }
2891 else // The other package types can uninstall without the original package.
2892 {
2893 return BOOTSTRAPPER_ACTION_STATE_UNINSTALL < pPackage->execute;
2894 }
2895}
2896
2897static HRESULT CreateContainerProgress(
2898 __in BURN_PLAN* pPlan,
2899 __in BURN_CONTAINER* pContainer,
2900 __out BURN_CACHE_CONTAINER_PROGRESS** ppContainerProgress
2901 )
2902{
2903 HRESULT hr = S_OK;
2904 BURN_CACHE_CONTAINER_PROGRESS* pContainerProgress = NULL;
2905
2906 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgContainerProgress), pPlan->cContainerProgress + 1, sizeof(BURN_CACHE_CONTAINER_PROGRESS), 5);
2907 ExitOnFailure(hr, "Failed to grow container progress list.");
2908
2909 if (!pPlan->shContainerProgress)
2910 {
2911 hr = DictCreateWithEmbeddedKey(&pPlan->shContainerProgress, 5, reinterpret_cast<void **>(&pPlan->rgContainerProgress), offsetof(BURN_CACHE_CONTAINER_PROGRESS, wzId), DICT_FLAG_NONE);
2912 ExitOnFailure(hr, "Failed to create container progress dictionary.");
2913 }
2914
2915 hr = DictGetValue(pPlan->shContainerProgress, pContainer->sczId, reinterpret_cast<void **>(&pContainerProgress));
2916 if (E_NOTFOUND == hr)
2917 {
2918 pContainerProgress = &pPlan->rgContainerProgress[pPlan->cContainerProgress];
2919 pContainerProgress->iIndex = pPlan->cContainerProgress;
2920 pContainerProgress->pContainer = pContainer;
2921 pContainerProgress->wzId = pContainer->sczId;
2922
2923 hr = DictAddValue(pPlan->shContainerProgress, pContainerProgress);
2924 ExitOnFailure(hr, "Failed to add \"%ls\" to the container progress dictionary.", pContainerProgress->wzId);
2925
2926 ++pPlan->cContainerProgress;
2927 pPlan->qwCacheSizeTotal += pContainer->qwFileSize;
2928 }
2929 ExitOnFailure(hr, "Failed to retrieve \"%ls\" from the container progress dictionary.", pContainer->sczId);
2930
2931 *ppContainerProgress = pContainerProgress;
2932
2933LExit:
2934 return hr;
2935}
2936
2937static HRESULT CreatePayloadProgress(
2938 __in BURN_PLAN* pPlan,
2939 __in BURN_PAYLOAD* pPayload,
2940 __out BURN_CACHE_PAYLOAD_PROGRESS** ppPayloadProgress
2941 )
2942{
2943 HRESULT hr = S_OK;
2944 BURN_CACHE_PAYLOAD_PROGRESS* pPayloadProgress = NULL;
2945
2946 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgPayloadProgress), pPlan->cPayloadProgress + 1, sizeof(BURN_CACHE_PAYLOAD_PROGRESS), 5);
2947 ExitOnFailure(hr, "Failed to grow payload progress list.");
2948
2949 if (!pPlan->shPayloadProgress)
2950 {
2951 hr = DictCreateWithEmbeddedKey(&pPlan->shPayloadProgress, 5, reinterpret_cast<void **>(&pPlan->rgPayloadProgress), offsetof(BURN_CACHE_PAYLOAD_PROGRESS, wzId), DICT_FLAG_NONE);
2952 ExitOnFailure(hr, "Failed to create payload progress dictionary.");
2953 }
2954
2955 hr = DictGetValue(pPlan->shPayloadProgress, pPayload->sczKey, reinterpret_cast<void **>(&pPayloadProgress));
2956 if (E_NOTFOUND == hr)
2957 {
2958 pPayloadProgress = &pPlan->rgPayloadProgress[pPlan->cPayloadProgress];
2959 pPayloadProgress->iIndex = pPlan->cPayloadProgress;
2960 pPayloadProgress->pPayload = pPayload;
2961 pPayloadProgress->wzId = pPayload->sczKey;
2962
2963 hr = DictAddValue(pPlan->shPayloadProgress, pPayloadProgress);
2964 ExitOnFailure(hr, "Failed to add \"%ls\" to the payload progress dictionary.", pPayloadProgress->wzId);
2965
2966 ++pPlan->cPayloadProgress;
2967 pPlan->qwCacheSizeTotal += pPayload->qwFileSize;
2968 }
2969 ExitOnFailure(hr, "Failed to retrieve \"%ls\" from the payload progress dictionary.", pPayload->sczKey);
2970
2971 *ppPayloadProgress = pPayloadProgress;
2972
2973LExit:
2974 return hr;
2975}
2976
2977
2978#ifdef DEBUG
2979
2980static void CacheActionLog(
2981 __in DWORD iAction,
2982 __in BURN_CACHE_ACTION* pAction,
2983 __in BOOL fRollback
2984 )
2985{
2986 LPCWSTR wzBase = fRollback ? L" Rollback cache" : L" Cache";
2987 switch (pAction->type)
2988 {
2989 case BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER:
2990 LogStringLine(REPORT_STANDARD, "%ls action[%u]: ACQUIRE_CONTAINER id: %ls, source path: %ls, working path: %ls, skip until retried: %hs", wzBase, iAction, pAction->resolveContainer.pContainer->sczId, pAction->resolveContainer.pContainer->sczSourcePath, pAction->resolveContainer.sczUnverifiedPath, LoggingBoolToString(pAction->fSkipUntilRetried));
2991 break;
2992
2993 case BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD:
2994 LogStringLine(REPORT_STANDARD, "%ls action[%u]: ACQUIRE_PAYLOAD package id: %ls, payload id: %ls, source path: %ls, working path: %ls, skip until retried: %hs", wzBase, iAction, pAction->resolvePayload.pPackage ? pAction->resolvePayload.pPackage->sczId : L"", pAction->resolvePayload.pPayload->sczKey, pAction->resolvePayload.pPayload->sczSourcePath, pAction->resolvePayload.sczUnverifiedPath, LoggingBoolToString(pAction->fSkipUntilRetried));
2995 break;
2996
2997 case BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD:
2998 LogStringLine(REPORT_STANDARD, "%ls action[%u]: CACHE_PAYLOAD package id: %ls, payload id: %ls, working path: %ls, operation: %ls, skip until retried: %hs, retry action: %u", wzBase, iAction, pAction->cachePayload.pPackage->sczId, pAction->cachePayload.pPayload->sczKey, pAction->cachePayload.sczUnverifiedPath, pAction->cachePayload.fMove ? L"move" : L"copy", LoggingBoolToString(pAction->fSkipUntilRetried), pAction->cachePayload.iTryAgainAction);
2999 break;
3000
3001 case BURN_CACHE_ACTION_TYPE_CHECKPOINT:
3002 LogStringLine(REPORT_STANDARD, "%ls action[%u]: CHECKPOINT id: %u", wzBase, iAction, pAction->checkpoint.dwId);
3003 break;
3004
3005 case BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER:
3006 LogStringLine(REPORT_STANDARD, "%ls action[%u]: EXTRACT_CONTAINER id: %ls, working path: %ls, skip until retried: %hs, skip until acquired by action: %u", wzBase, iAction, pAction->extractContainer.pContainer->sczId, pAction->extractContainer.sczContainerUnverifiedPath, LoggingBoolToString(pAction->fSkipUntilRetried), pAction->extractContainer.iSkipUntilAcquiredByAction);
3007 for (DWORD j = 0; j < pAction->extractContainer.cPayloads; j++)
3008 {
3009 LogStringLine(REPORT_STANDARD, " extract package id: %ls, payload id: %ls, working path: %ls", pAction->extractContainer.rgPayloads[j].pPackage->sczId, pAction->extractContainer.rgPayloads[j].pPayload->sczKey, pAction->extractContainer.rgPayloads[j].sczUnverifiedPath);
3010 }
3011 break;
3012
3013 case BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE:
3014 LogStringLine(REPORT_STANDARD, "%ls action[%u]: LAYOUT_BUNDLE working path: %ls, layout directory: %ls, exe name: %ls, skip until retried: %hs", wzBase, iAction, pAction->bundleLayout.sczUnverifiedPath, pAction->bundleLayout.sczLayoutDirectory, pAction->bundleLayout.sczExecutableName, LoggingBoolToString(pAction->fSkipUntilRetried));
3015 break;
3016
3017 case BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER:
3018 LogStringLine(REPORT_STANDARD, "%ls action[%u]: LAYOUT_CONTAINER package id: %ls, container id: %ls, working path: %ls, layout directory: %ls, operation: %ls, skip until retried: %hs, retry action: %u", wzBase, iAction, pAction->layoutContainer.pPackage ? pAction->layoutContainer.pPackage->sczId : L"", pAction->layoutContainer.pContainer->sczId, pAction->layoutContainer.sczUnverifiedPath, pAction->layoutContainer.sczLayoutDirectory, pAction->layoutContainer.fMove ? L"move" : L"copy", LoggingBoolToString(pAction->fSkipUntilRetried), pAction->layoutContainer.iTryAgainAction);
3019 break;
3020
3021 case BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD:
3022 LogStringLine(REPORT_STANDARD, "%ls action[%u]: LAYOUT_PAYLOAD package id: %ls, payload id: %ls, working path: %ls, layout directory: %ls, operation: %ls, skip until retried: %hs, retry action: %u", wzBase, iAction, pAction->layoutPayload.pPackage ? pAction->layoutPayload.pPackage->sczId : L"", pAction->layoutPayload.pPayload->sczKey, pAction->layoutPayload.sczUnverifiedPath, pAction->layoutPayload.sczLayoutDirectory, pAction->layoutPayload.fMove ? L"move" : L"copy", LoggingBoolToString(pAction->fSkipUntilRetried), pAction->layoutPayload.iTryAgainAction);
3023 break;
3024
3025 case BURN_CACHE_ACTION_TYPE_PACKAGE_START:
3026 LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_START id: %ls, plan index for skip: %u, payloads to cache: %u, bytes to cache: %llu, skip until retried: %hs", wzBase, iAction, pAction->packageStart.pPackage->sczId, pAction->packageStart.iPackageCompleteAction, pAction->packageStart.cCachePayloads, pAction->packageStart.qwCachePayloadSizeTotal, LoggingBoolToString(pAction->fSkipUntilRetried));
3027 break;
3028
3029 case BURN_CACHE_ACTION_TYPE_PACKAGE_STOP:
3030 LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_STOP id: %ls, skip until retried: %hs", wzBase, iAction, pAction->packageStop.pPackage->sczId, LoggingBoolToString(pAction->fSkipUntilRetried));
3031 break;
3032
3033 case BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE:
3034 LogStringLine(REPORT_STANDARD, "%ls action[%u]: ROLLBACK_PACKAGE id: %ls, skip until retried: %hs", wzBase, iAction, pAction->rollbackPackage.pPackage->sczId, LoggingBoolToString(pAction->fSkipUntilRetried));
3035 break;
3036
3037 case BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT:
3038 LogStringLine(REPORT_STANDARD, "%ls action[%u]: SIGNAL_SYNCPOINT event handle: 0x%x, skip until retried: %hs", wzBase, iAction, pAction->syncpoint.hEvent, LoggingBoolToString(pAction->fSkipUntilRetried));
3039 break;
3040
3041 case BURN_CACHE_ACTION_TYPE_TRANSACTION_BOUNDARY:
3042 LogStringLine(REPORT_STANDARD, "%ls action[%u]: TRANSACTION_BOUNDARY id: %ls, event handle: 0x%x, vital: %ls, transaction: %ls", wzBase, iAction, pAction->rollbackBoundary.pRollbackBoundary->sczId, pAction->rollbackBoundary.hEvent, pAction->rollbackBoundary.pRollbackBoundary->fVital ? L"yes" : L"no", pAction->rollbackBoundary.pRollbackBoundary->fTransaction ? L"yes" : L"no");
3043 break;
3044
3045 default:
3046 AssertSz(FALSE, "Unknown cache action type.");
3047 break;
3048 }
3049}
3050
3051static void ExecuteActionLog(
3052 __in DWORD iAction,
3053 __in BURN_EXECUTE_ACTION* pAction,
3054 __in BOOL fRollback
3055 )
3056{
3057 LPCWSTR wzBase = fRollback ? L" Rollback" : L" Execute";
3058 switch (pAction->type)
3059 {
3060 case BURN_EXECUTE_ACTION_TYPE_CHECKPOINT:
3061 LogStringLine(REPORT_STANDARD, "%ls action[%u]: CHECKPOINT id: %u", wzBase, iAction, pAction->checkpoint.dwId);
3062 break;
3063
3064 case BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER:
3065 LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_PROVIDER package id: %ls, action: %u", wzBase, iAction, pAction->packageProvider.pPackage->sczId, pAction->packageProvider.action);
3066 break;
3067
3068 case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY:
3069 LogStringLine(REPORT_STANDARD, "%ls action[%u]: PACKAGE_DEPENDENCY package id: %ls, bundle provider key: %ls, action: %u", wzBase, iAction, pAction->packageDependency.pPackage->sczId, pAction->packageDependency.sczBundleProviderKey, pAction->packageDependency.action);
3070 break;
3071
3072 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
3073 LogStringLine(REPORT_STANDARD, "%ls action[%u]: EXE_PACKAGE package id: %ls, action: %hs, ignore dependencies: %ls", wzBase, iAction, pAction->exePackage.pPackage->sczId, LoggingActionStateToString(pAction->exePackage.action), pAction->exePackage.sczIgnoreDependencies);
3074 break;
3075
3076 case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE:
3077 LogStringLine(REPORT_STANDARD, "%ls action[%u]: MSI_PACKAGE package id: %ls, action: %hs, ui level: %u, log path: %ls, logging attrib: %u", wzBase, iAction, pAction->msiPackage.pPackage->sczId, LoggingActionStateToString(pAction->msiPackage.action), pAction->msiPackage.uiLevel, pAction->msiPackage.sczLogPath, pAction->msiPackage.dwLoggingAttributes);
3078 for (DWORD j = 0; j < pAction->msiPackage.cPatches; ++j)
3079 {
3080 LogStringLine(REPORT_STANDARD, " Patch[%u]: order: %u, msp package id: %ls", j, pAction->msiPackage.rgOrderedPatches->dwOrder, pAction->msiPackage.rgOrderedPatches[j].dwOrder, pAction->msiPackage.rgOrderedPatches[j].pPackage->sczId);
3081 }
3082 break;
3083
3084 case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET:
3085 LogStringLine(REPORT_STANDARD, "%ls action[%u]: MSP_TARGET package id: %ls, action: %hs, target product code: %ls, target per-machine: %ls, ui level: %u, log path: %ls", wzBase, iAction, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.sczTargetProductCode, pAction->mspTarget.fPerMachineTarget ? L"yes" : L"no", pAction->mspTarget.uiLevel, pAction->mspTarget.sczLogPath);
3086 for (DWORD j = 0; j < pAction->mspTarget.cOrderedPatches; ++j)
3087 {
3088 LogStringLine(REPORT_STANDARD, " Patch[%u]: order: %u, msp package id: %ls", j, pAction->mspTarget.rgOrderedPatches[j].dwOrder, pAction->mspTarget.rgOrderedPatches[j].pPackage->sczId);
3089 }
3090 break;
3091
3092 case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE:
3093 LogStringLine(REPORT_STANDARD, "%ls action[%u]: MSU_PACKAGE package id: %ls, action: %hs, log path: %ls", wzBase, iAction, pAction->msuPackage.pPackage->sczId, LoggingActionStateToString(pAction->msuPackage.action), pAction->msuPackage.sczLogPath);
3094 break;
3095
3096 case BURN_EXECUTE_ACTION_TYPE_REGISTRATION:
3097 LogStringLine(REPORT_STANDARD, "%ls action[%u]: REGISTRATION keep: %ls", wzBase, iAction, pAction->registration.fKeep ? L"yes" : L"no");
3098 break;
3099
3100 case BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY:
3101 LogStringLine(REPORT_STANDARD, "%ls action[%u]: ROLLBACK_BOUNDARY id: %ls, vital: %ls", wzBase, iAction, pAction->rollbackBoundary.pRollbackBoundary->sczId, pAction->rollbackBoundary.pRollbackBoundary->fVital ? L"yes" : L"no");
3102 break;
3103
3104 case BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT:
3105 LogStringLine(REPORT_STANDARD, "%ls action[%u]: WAIT_SYNCPOINT event handle: 0x%x", wzBase, iAction, pAction->syncpoint.hEvent);
3106 break;
3107
3108 case BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE:
3109 LogStringLine(REPORT_STANDARD, "%ls action[%u]: UNCACHE_PACKAGE id: %ls", wzBase, iAction, pAction->uncachePackage.pPackage->sczId);
3110 break;
3111
3112 case BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE:
3113 LogStringLine(REPORT_STANDARD, "%ls action[%u]: COMPATIBLE_PACKAGE reference id: %ls, installed ProductCode: %ls", wzBase, iAction, pAction->compatiblePackage.pReferencePackage->sczId, pAction->compatiblePackage.sczInstalledProductCode);
3114 break;
3115
3116 default:
3117 AssertSz(FALSE, "Unknown execute action type.");
3118 break;
3119 }
3120}
3121
3122extern "C" void PlanDump(
3123 __in BURN_PLAN* pPlan
3124 )
3125{
3126 LogStringLine(REPORT_STANDARD, "--- Begin plan dump ---");
3127
3128 LogStringLine(REPORT_STANDARD, "Plan action: %hs", LoggingBurnActionToString(pPlan->action));
3129 LogStringLine(REPORT_STANDARD, " per-machine: %hs", LoggingTrueFalseToString(pPlan->fPerMachine));
3130 LogStringLine(REPORT_STANDARD, " keep registration by default: %hs", LoggingTrueFalseToString(pPlan->fKeepRegistrationDefault));
3131 LogStringLine(REPORT_STANDARD, " estimated size: %llu", pPlan->qwEstimatedSize);
3132
3133 LogStringLine(REPORT_STANDARD, "Plan cache size: %llu", pPlan->qwCacheSizeTotal);
3134 for (DWORD i = 0; i < pPlan->cCacheActions; ++i)
3135 {
3136 CacheActionLog(i, pPlan->rgCacheActions + i, FALSE);
3137 }
3138
3139 for (DWORD i = 0; i < pPlan->cRollbackCacheActions; ++i)
3140 {
3141 CacheActionLog(i, pPlan->rgRollbackCacheActions + i, TRUE);
3142 }
3143
3144 LogStringLine(REPORT_STANDARD, "Plan execute package count: %u", pPlan->cExecutePackagesTotal);
3145 LogStringLine(REPORT_STANDARD, " overall progress ticks: %u", pPlan->cOverallProgressTicksTotal);
3146 for (DWORD i = 0; i < pPlan->cExecuteActions; ++i)
3147 {
3148 ExecuteActionLog(i, pPlan->rgExecuteActions + i, FALSE);
3149 }
3150
3151 for (DWORD i = 0; i < pPlan->cRollbackActions; ++i)
3152 {
3153 ExecuteActionLog(i, pPlan->rgRollbackActions + i, TRUE);
3154 }
3155
3156 for (DWORD i = 0; i < pPlan->cCleanActions; ++i)
3157 {
3158 LogStringLine(REPORT_STANDARD, " Clean action[%u]: CLEAN_PACKAGE package id: %ls", i, pPlan->rgCleanActions[i].pPackage->sczId);
3159 }
3160
3161 for (DWORD i = 0; i < pPlan->cPlannedProviders; ++i)
3162 {
3163 LogStringLine(REPORT_STANDARD, " Dependency action[%u]: PLANNED_PROVIDER key: %ls, name: %ls", i, pPlan->rgPlannedProviders[i].sczKey, pPlan->rgPlannedProviders[i].sczName);
3164 }
3165
3166 LogStringLine(REPORT_STANDARD, "--- End plan dump ---");
3167}
3168
3169#endif
diff --git a/src/engine/plan.h b/src/engine/plan.h
new file mode 100644
index 00000000..54cfe59d
--- /dev/null
+++ b/src/engine/plan.h
@@ -0,0 +1,543 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// constants
11
12const DWORD BURN_PLAN_INVALID_ACTION_INDEX = 0x80000000;
13
14enum BURN_REGISTRATION_ACTION_OPERATIONS
15{
16 BURN_REGISTRATION_ACTION_OPERATIONS_NONE = 0x0,
17 BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE = 0x1,
18 BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION = 0x2,
19 BURN_REGISTRATION_ACTION_OPERATIONS_UPDATE_SIZE = 0x4,
20};
21
22enum BURN_DEPENDENCY_REGISTRATION_ACTION
23{
24 BURN_DEPENDENCY_REGISTRATION_ACTION_NONE,
25 BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER,
26 BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER,
27};
28
29enum BURN_DEPENDENT_REGISTRATION_ACTION_TYPE
30{
31 BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_NONE,
32 BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER,
33 BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER,
34};
35
36enum BURN_CACHE_ACTION_TYPE
37{
38 BURN_CACHE_ACTION_TYPE_NONE,
39 BURN_CACHE_ACTION_TYPE_CHECKPOINT,
40 BURN_CACHE_ACTION_TYPE_LAYOUT_BUNDLE,
41 BURN_CACHE_ACTION_TYPE_PACKAGE_START,
42 BURN_CACHE_ACTION_TYPE_PACKAGE_STOP,
43 BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE,
44 BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT,
45 BURN_CACHE_ACTION_TYPE_ACQUIRE_CONTAINER,
46 BURN_CACHE_ACTION_TYPE_EXTRACT_CONTAINER,
47 BURN_CACHE_ACTION_TYPE_LAYOUT_CONTAINER,
48 BURN_CACHE_ACTION_TYPE_ACQUIRE_PAYLOAD,
49 BURN_CACHE_ACTION_TYPE_CACHE_PAYLOAD,
50 BURN_CACHE_ACTION_TYPE_LAYOUT_PAYLOAD,
51 BURN_CACHE_ACTION_TYPE_TRANSACTION_BOUNDARY,
52};
53
54enum BURN_EXECUTE_ACTION_TYPE
55{
56 BURN_EXECUTE_ACTION_TYPE_NONE,
57 BURN_EXECUTE_ACTION_TYPE_CHECKPOINT,
58 BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT,
59 BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE,
60 BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE,
61 BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE,
62 BURN_EXECUTE_ACTION_TYPE_MSP_TARGET,
63 BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE,
64 BURN_EXECUTE_ACTION_TYPE_SERVICE_STOP,
65 BURN_EXECUTE_ACTION_TYPE_SERVICE_START,
66 BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER,
67 BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY,
68 BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY,
69 BURN_EXECUTE_ACTION_TYPE_REGISTRATION,
70 BURN_EXECUTE_ACTION_TYPE_COMPATIBLE_PACKAGE,
71};
72
73enum BURN_CLEAN_ACTION_TYPE
74{
75 BURN_CLEAN_ACTION_TYPE_NONE,
76 BURN_CLEAN_ACTION_TYPE_BUNDLE,
77 BURN_CLEAN_ACTION_TYPE_PACKAGE,
78};
79
80
81// structs
82
83typedef struct _BURN_EXTRACT_PAYLOAD
84{
85 BURN_PACKAGE* pPackage;
86 BURN_PAYLOAD* pPayload;
87 LPWSTR sczUnverifiedPath;
88} BURN_EXTRACT_PAYLOAD;
89
90typedef struct _BURN_DEPENDENT_REGISTRATION_ACTION
91{
92 BURN_DEPENDENT_REGISTRATION_ACTION_TYPE type;
93 LPWSTR sczBundleId;
94 LPWSTR sczDependentProviderKey;
95} BURN_DEPENDENT_REGISTRATION_ACTION;
96
97typedef struct _BURN_CACHE_CONTAINER_PROGRESS
98{
99 LPWSTR wzId;
100 DWORD iIndex;
101 BOOL fCachedDuringApply;
102 BURN_CONTAINER* pContainer;
103} BURN_CACHE_CONTAINER_PROGRESS;
104
105typedef struct _BURN_CACHE_PAYLOAD_PROGRESS
106{
107 LPWSTR wzId;
108 DWORD iIndex;
109 BOOL fCachedDuringApply;
110 BURN_PAYLOAD* pPayload;
111} BURN_CACHE_PAYLOAD_PROGRESS;
112
113typedef struct _BURN_CACHE_ACTION
114{
115 BURN_CACHE_ACTION_TYPE type;
116 BOOL fSkipUntilRetried;
117 union
118 {
119 struct
120 {
121 DWORD dwId;
122 } checkpoint;
123 struct
124 {
125 LPWSTR sczExecutableName;
126 LPWSTR sczLayoutDirectory;
127 LPWSTR sczUnverifiedPath;
128 DWORD64 qwBundleSize;
129 } bundleLayout;
130 struct
131 {
132 BURN_PACKAGE* pPackage;
133 DWORD cCachePayloads;
134 DWORD64 qwCachePayloadSizeTotal;
135 DWORD iPackageCompleteAction;
136 } packageStart;
137 struct
138 {
139 BURN_PACKAGE* pPackage;
140 } packageStop;
141 struct
142 {
143 BURN_PACKAGE* pPackage;
144 } rollbackPackage;
145 struct
146 {
147 HANDLE hEvent;
148 } syncpoint;
149 struct
150 {
151 BURN_CONTAINER* pContainer;
152 DWORD iProgress;
153 LPWSTR sczUnverifiedPath;
154 } resolveContainer;
155 struct
156 {
157 BURN_CONTAINER* pContainer;
158 DWORD iSkipUntilAcquiredByAction;
159 LPWSTR sczContainerUnverifiedPath;
160
161 BURN_EXTRACT_PAYLOAD* rgPayloads;
162 DWORD cPayloads;
163 } extractContainer;
164 struct
165 {
166 BURN_PACKAGE* pPackage;
167 BURN_CONTAINER* pContainer;
168 DWORD iProgress;
169 DWORD iTryAgainAction;
170 DWORD cTryAgainAttempts;
171 LPWSTR sczLayoutDirectory;
172 LPWSTR sczUnverifiedPath;
173 BOOL fMove;
174 } layoutContainer;
175 struct
176 {
177 BURN_PACKAGE* pPackage;
178 BURN_PAYLOAD* pPayload;
179 DWORD iProgress;
180 LPWSTR sczUnverifiedPath;
181 } resolvePayload;
182 struct
183 {
184 BURN_PACKAGE* pPackage;
185 BURN_PAYLOAD* pPayload;
186 DWORD iProgress;
187 DWORD iTryAgainAction;
188 DWORD cTryAgainAttempts;
189 LPWSTR sczUnverifiedPath;
190 BOOL fMove;
191 } cachePayload;
192 struct
193 {
194 BURN_PACKAGE* pPackage;
195 BURN_PAYLOAD* pPayload;
196 DWORD iProgress;
197 DWORD iTryAgainAction;
198 DWORD cTryAgainAttempts;
199 LPWSTR sczLayoutDirectory;
200 LPWSTR sczUnverifiedPath;
201 BOOL fMove;
202 } layoutPayload;
203 struct
204 {
205 BURN_ROLLBACK_BOUNDARY* pRollbackBoundary;
206 HANDLE hEvent;
207 } rollbackBoundary;
208 };
209} BURN_CACHE_ACTION;
210
211typedef struct _BURN_ORDERED_PATCHES
212{
213 DWORD dwOrder;
214 BURN_PACKAGE* pPackage;
215} BURN_ORDERED_PATCHES;
216
217typedef struct _BURN_EXECUTE_ACTION
218{
219 BURN_EXECUTE_ACTION_TYPE type;
220 BOOL fDeleted; // used to skip an action after it was planned since deleting actions out of the plan is too hard.
221 union
222 {
223 struct
224 {
225 DWORD dwId;
226 } checkpoint;
227 struct
228 {
229 HANDLE hEvent;
230 } syncpoint;
231 struct
232 {
233 BURN_PACKAGE* pPackage;
234 } uncachePackage;
235 struct
236 {
237 BURN_PACKAGE* pPackage;
238 BOOL fFireAndForget;
239 BOOTSTRAPPER_ACTION_STATE action;
240 LPWSTR sczIgnoreDependencies;
241 LPWSTR sczAncestors;
242 } exePackage;
243 struct
244 {
245 BURN_PACKAGE* pPackage;
246 LPWSTR sczLogPath;
247 DWORD dwLoggingAttributes;
248 INSTALLUILEVEL uiLevel;
249 BOOTSTRAPPER_ACTION_STATE action;
250
251 BOOTSTRAPPER_FEATURE_ACTION* rgFeatures;
252 BOOTSTRAPPER_ACTION_STATE* rgSlipstreamPatches;
253
254 BURN_ORDERED_PATCHES* rgOrderedPatches;
255 DWORD cPatches;
256 } msiPackage;
257 struct
258 {
259 BURN_PACKAGE* pPackage;
260 LPWSTR sczTargetProductCode;
261 BURN_PACKAGE* pChainedTargetPackage;
262 BOOL fSlipstream;
263 BOOL fPerMachineTarget;
264 LPWSTR sczLogPath;
265 INSTALLUILEVEL uiLevel;
266 BOOTSTRAPPER_ACTION_STATE action;
267
268 BURN_ORDERED_PATCHES* rgOrderedPatches;
269 DWORD cOrderedPatches;
270 } mspTarget;
271 struct
272 {
273 BURN_PACKAGE* pPackage;
274 LPWSTR sczLogPath;
275 BOOTSTRAPPER_ACTION_STATE action;
276 } msuPackage;
277 struct
278 {
279 LPWSTR sczServiceName;
280 } service;
281 struct
282 {
283 BOOL fKeep;
284 } registration;
285 struct
286 {
287 BURN_ROLLBACK_BOUNDARY* pRollbackBoundary;
288 } rollbackBoundary;
289 struct
290 {
291 BURN_PACKAGE* pPackage;
292 BURN_DEPENDENCY_ACTION action;
293 } packageProvider;
294 struct
295 {
296 BURN_PACKAGE* pPackage;
297 LPWSTR sczBundleProviderKey;
298 BURN_DEPENDENCY_ACTION action;
299 } packageDependency;
300 struct
301 {
302 BURN_PACKAGE* pReferencePackage;
303 LPWSTR sczInstalledProductCode;
304 DWORD64 qwInstalledVersion;
305 } compatiblePackage;
306 };
307} BURN_EXECUTE_ACTION;
308
309typedef struct _BURN_CLEAN_ACTION
310{
311 BURN_PACKAGE* pPackage;
312} BURN_CLEAN_ACTION;
313
314typedef struct _BURN_PLAN
315{
316 BOOTSTRAPPER_ACTION action;
317 LPWSTR wzBundleId; // points directly into parent the ENGINE_STATE.
318 LPWSTR wzBundleProviderKey; // points directly into parent the ENGINE_STATE.
319 BOOL fPerMachine;
320 BOOL fRegister;
321 DWORD dwRegistrationOperations;
322 BOOL fKeepRegistrationDefault;
323 BOOL fDisallowRemoval;
324
325 DWORD64 qwCacheSizeTotal;
326
327 DWORD64 qwEstimatedSize;
328
329 DWORD cExecutePackagesTotal;
330 DWORD cOverallProgressTicksTotal;
331
332 BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction;
333
334 BURN_DEPENDENT_REGISTRATION_ACTION* rgRegistrationActions;
335 DWORD cRegistrationActions;
336
337 BURN_DEPENDENT_REGISTRATION_ACTION* rgRollbackRegistrationActions;
338 DWORD cRollbackRegistrationActions;
339
340 BURN_CACHE_ACTION* rgCacheActions;
341 DWORD cCacheActions;
342
343 BURN_CACHE_ACTION* rgRollbackCacheActions;
344 DWORD cRollbackCacheActions;
345
346 BURN_EXECUTE_ACTION* rgExecuteActions;
347 DWORD cExecuteActions;
348
349 BURN_EXECUTE_ACTION* rgRollbackActions;
350 DWORD cRollbackActions;
351
352 BURN_CLEAN_ACTION* rgCleanActions;
353 DWORD cCleanActions;
354
355 DEPENDENCY* rgPlannedProviders;
356 UINT cPlannedProviders;
357
358 BURN_CACHE_CONTAINER_PROGRESS* rgContainerProgress;
359 DWORD cContainerProgress;
360 STRINGDICT_HANDLE shContainerProgress;
361
362 BURN_CACHE_PAYLOAD_PROGRESS* rgPayloadProgress;
363 DWORD cPayloadProgress;
364 STRINGDICT_HANDLE shPayloadProgress;
365} BURN_PLAN;
366
367
368// functions
369
370void PlanReset(
371 __in BURN_PLAN* pPlan,
372 __in BURN_PACKAGES* pPackages
373 );
374void PlanUninitializeExecuteAction(
375 __in BURN_EXECUTE_ACTION* pExecuteAction
376 );
377HRESULT PlanSetVariables(
378 __in BOOTSTRAPPER_ACTION action,
379 __in BURN_VARIABLES* pVariables
380 );
381HRESULT PlanDefaultPackageRequestState(
382 __in BURN_PACKAGE_TYPE packageType,
383 __in BOOTSTRAPPER_PACKAGE_STATE currentState,
384 __in BOOL fPermanent,
385 __in BOOTSTRAPPER_ACTION action,
386 __in BURN_VARIABLES* pVariables,
387 __in_z_opt LPCWSTR wzInstallCondition,
388 __in BOOTSTRAPPER_RELATION_TYPE relationType,
389 __out BOOTSTRAPPER_REQUEST_STATE* pRequestState
390 );
391HRESULT PlanLayoutBundle(
392 __in BURN_PLAN* pPlan,
393 __in_z LPCWSTR wzExecutableName,
394 __in DWORD64 qwBundleSize,
395 __in BURN_VARIABLES* pVariables,
396 __in BURN_PAYLOADS* pPayloads,
397 __out_z LPWSTR* psczLayoutDirectory
398 );
399HRESULT PlanPackages(
400 __in BURN_REGISTRATION* pRegistration,
401 __in BURN_USER_EXPERIENCE* pUX,
402 __in BURN_PACKAGES* pPackages,
403 __in BURN_PLAN* pPlan,
404 __in BURN_LOGGING* pLog,
405 __in BURN_VARIABLES* pVariables,
406 __in BOOL fBundleInstalled,
407 __in BOOTSTRAPPER_DISPLAY display,
408 __in BOOTSTRAPPER_RELATION_TYPE relationType,
409 __in_z_opt LPCWSTR wzLayoutDirectory,
410 __inout HANDLE* phSyncpointEvent
411 );
412HRESULT PlanRegistration(
413 __in BURN_PLAN* pPlan,
414 __in BURN_REGISTRATION* pRegistration,
415 __in BOOTSTRAPPER_RESUME_TYPE resumeType,
416 __in BOOTSTRAPPER_RELATION_TYPE relationType,
417 __in_z_opt LPCWSTR wzIgnoreDependencies,
418 __out BOOL* pfContinuePlanning
419 );
420HRESULT PlanPassThroughBundle(
421 __in BURN_USER_EXPERIENCE* pUX,
422 __in BURN_PACKAGE* pPackage,
423 __in BURN_PLAN* pPlan,
424 __in BURN_LOGGING* pLog,
425 __in BURN_VARIABLES* pVariables,
426 __in BOOTSTRAPPER_DISPLAY display,
427 __in BOOTSTRAPPER_RELATION_TYPE relationType,
428 __inout HANDLE* phSyncpointEvent
429 );
430HRESULT PlanUpdateBundle(
431 __in BURN_USER_EXPERIENCE* pUX,
432 __in BURN_PACKAGE* pPackage,
433 __in BURN_PLAN* pPlan,
434 __in BURN_LOGGING* pLog,
435 __in BURN_VARIABLES* pVariables,
436 __in BOOTSTRAPPER_DISPLAY display,
437 __in BOOTSTRAPPER_RELATION_TYPE relationType,
438 __inout HANDLE* phSyncpointEvent
439 );
440HRESULT PlanLayoutPackage(
441 __in BURN_PLAN* pPlan,
442 __in BURN_PACKAGE* pPackage,
443 __in_z_opt LPCWSTR wzLayoutDirectory
444 );
445HRESULT PlanCachePackage(
446 __in BOOL fPerMachine,
447 __in BURN_USER_EXPERIENCE* pUserExperience,
448 __in BURN_PLAN* pPlan,
449 __in BURN_PACKAGE* pPackage,
450 __in BURN_VARIABLES* pVariables,
451 __out HANDLE* phSyncpointEvent
452 );
453HRESULT PlanExecutePackage(
454 __in BOOL fPerMachine,
455 __in BOOTSTRAPPER_DISPLAY display,
456 __in BURN_USER_EXPERIENCE* pUserExperience,
457 __in BURN_PLAN* pPlan,
458 __in BURN_PACKAGE* pPackage,
459 __in BURN_LOGGING* pLog,
460 __in BURN_VARIABLES* pVariables,
461 __inout HANDLE* phSyncpointEvent
462 );
463HRESULT PlanRelatedBundlesBegin(
464 __in BURN_USER_EXPERIENCE* pUserExperience,
465 __in BURN_REGISTRATION* pRegistration,
466 __in BOOTSTRAPPER_RELATION_TYPE relationType,
467 __in BURN_PLAN* pPlan
468 );
469HRESULT PlanRelatedBundlesComplete(
470 __in BURN_REGISTRATION* pRegistration,
471 __in BURN_PLAN* pPlan,
472 __in BURN_LOGGING* pLog,
473 __in BURN_VARIABLES* pVariables,
474 __inout HANDLE* phSyncpointEvent,
475 __in DWORD dwExecuteActionEarlyIndex
476 );
477HRESULT PlanFinalizeActions(
478 __in BURN_PLAN* pPlan
479 );
480HRESULT PlanCleanPackage(
481 __in BURN_PLAN* pPlan,
482 __in BURN_PACKAGE* pPackage
483 );
484HRESULT PlanExecuteCacheSyncAndRollback(
485 __in BURN_PLAN* pPlan,
486 __in BURN_PACKAGE* pPackage,
487 __in HANDLE hCacheEvent,
488 __in BOOL fPlanPackageCacheRollback
489 );
490HRESULT PlanExecuteCheckpoint(
491 __in BURN_PLAN* pPlan
492 );
493HRESULT PlanInsertExecuteAction(
494 __in DWORD dwIndex,
495 __in BURN_PLAN* pPlan,
496 __out BURN_EXECUTE_ACTION** ppExecuteAction
497 );
498HRESULT PlanInsertRollbackAction(
499 __in DWORD dwIndex,
500 __in BURN_PLAN* pPlan,
501 __out BURN_EXECUTE_ACTION** ppRollbackAction
502 );
503HRESULT PlanAppendExecuteAction(
504 __in BURN_PLAN* pPlan,
505 __out BURN_EXECUTE_ACTION** ppExecuteAction
506 );
507HRESULT PlanAppendRollbackAction(
508 __in BURN_PLAN* pPlan,
509 __out BURN_EXECUTE_ACTION** ppExecuteAction
510 );
511HRESULT PlanKeepRegistration(
512 __in BURN_PLAN* pPlan,
513 __in DWORD iAfterExecutePackageAction,
514 __in DWORD iBeforeRollbackPackageAction
515 );
516HRESULT PlanRemoveRegistration(
517 __in BURN_PLAN* pPlan,
518 __in DWORD iAfterExecutePackageAction,
519 __in DWORD iAfterRollbackPackageAction
520 );
521HRESULT PlanRollbackBoundaryBegin(
522 __in BURN_PLAN* pPlan,
523 __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary
524 );
525HRESULT PlanRollbackBoundaryComplete(
526 __in BURN_PLAN* pPlan
527 );
528HRESULT PlanSetResumeCommand(
529 __in BURN_REGISTRATION* pRegistration,
530 __in BOOTSTRAPPER_ACTION action,
531 __in BOOTSTRAPPER_COMMAND* pCommand,
532 __in BURN_LOGGING* pLog
533 );
534
535#ifdef DEBUG
536void PlanDump(
537 __in BURN_PLAN* pPlan
538 );
539#endif
540
541#if defined(__cplusplus)
542}
543#endif
diff --git a/src/engine/platform.cpp b/src/engine/platform.cpp
new file mode 100644
index 00000000..9469ff49
--- /dev/null
+++ b/src/engine/platform.cpp
@@ -0,0 +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#include "precomp.h"
4
5
6// variables
7
8PFN_INITIATESYSTEMSHUTDOWNEXW vpfnInitiateSystemShutdownExW;
9
10
11// function definitions
12
13extern "C" void PlatformInitialize()
14{
15 vpfnInitiateSystemShutdownExW = ::InitiateSystemShutdownExW;
16}
diff --git a/src/engine/platform.h b/src/engine/platform.h
new file mode 100644
index 00000000..3681f248
--- /dev/null
+++ b/src/engine/platform.h
@@ -0,0 +1,34 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// typedefs
11
12typedef BOOL (WINAPI *PFN_INITIATESYSTEMSHUTDOWNEXW)(
13 __in_opt LPWSTR lpMachineName,
14 __in_opt LPWSTR lpMessage,
15 __in DWORD dwTimeout,
16 __in BOOL bForceAppsClosed,
17 __in BOOL bRebootAfterShutdown,
18 __in DWORD dwReason
19 );
20
21
22// variable declarations
23
24extern PFN_INITIATESYSTEMSHUTDOWNEXW vpfnInitiateSystemShutdownExW;
25
26
27// function declarations
28
29void PlatformInitialize();
30
31
32#if defined(__cplusplus)
33}
34#endif
diff --git a/src/engine/precomp.h b/src/engine/precomp.h
new file mode 100644
index 00000000..d3ebe354
--- /dev/null
+++ b/src/engine/precomp.h
@@ -0,0 +1,100 @@
1#pragma once
2// 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.
3
4
5#define ExitTrace LogErrorString
6
7#include <wixver.h>
8
9#include <windows.h>
10#include <aclapi.h>
11#include <Bits.h>
12#include <gdiplus.h>
13#include <math.h>
14#include <msiquery.h>
15#include <sddl.h>
16#include <shlobj.h>
17#include <shlwapi.h>
18#include <softpub.h>
19#include <strsafe.h>
20#include <intsafe.h>
21#include <mscat.h>
22#include <lmcons.h>
23#include <wininet.h>
24#include <stddef.h>
25
26#include <dutil.h>
27#include <aclutil.h>
28#include <apputil.h>
29#include <buffutil.h>
30#include <cabutil.h>
31#include <certutil.h>
32#include <cryputil.h>
33#include <dirutil.h>
34#include <fileutil.h>
35#include <gdiputil.h>
36#include <guidutil.h>
37#include <logutil.h>
38#include <memutil.h>
39#include <osutil.h>
40#include <pathutil.h>
41#include <polcutil.h>
42#include <procutil.h>
43#include <regutil.h>
44#include <resrutil.h>
45#include <shelutil.h>
46#include <srputil.h>
47#include <strutil.h>
48#include <svcutil.h>
49#include <userutil.h>
50#include <wiutil.h>
51#include <wuautil.h>
52#include <xmlutil.h>
53#include <dictutil.h>
54#include <deputil.h>
55#include <dlutil.h>
56#include <atomutil.h>
57#include <apuputil.h>
58
59#include "BootstrapperEngine.h"
60#include "BootstrapperApplication.h"
61
62#include "platform.h"
63#include "variant.h"
64#include "variable.h"
65#include "condition.h"
66#include "search.h"
67#include "section.h"
68#include "approvedexe.h"
69#include "container.h"
70#include "catalog.h"
71#include "payload.h"
72#include "cabextract.h"
73#include "userexperience.h"
74#include "package.h"
75#include "update.h"
76#include "pseudobundle.h"
77#include "registration.h"
78#include "relatedbundle.h"
79#include "detect.h"
80#include "plan.h"
81#include "logging.h"
82#include "pipe.h"
83#include "core.h"
84#include "cache.h"
85#include "apply.h"
86#include "exeengine.h"
87#include "msiengine.h"
88#include "mspengine.h"
89#include "msuengine.h"
90#include "dependency.h"
91#include "elevation.h"
92#include "embedded.h"
93#include "manifest.h"
94#include "splashscreen.h"
95#include "uithread.h"
96#include "bitsengine.h"
97#include "netfxchainer.h"
98
99#include "EngineForApplication.h"
100#include "engine.messages.h"
diff --git a/src/engine/pseudobundle.cpp b/src/engine/pseudobundle.cpp
new file mode 100644
index 00000000..ebdc040a
--- /dev/null
+++ b/src/engine/pseudobundle.cpp
@@ -0,0 +1,271 @@
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#include "precomp.h"
4
5
6extern "C" HRESULT PseudoBundleInitialize(
7 __in DWORD64 qwEngineVersion,
8 __in BURN_PACKAGE* pPackage,
9 __in BOOL fPerMachine,
10 __in_z LPCWSTR wzId,
11 __in BOOTSTRAPPER_RELATION_TYPE relationType,
12 __in BOOTSTRAPPER_PACKAGE_STATE state,
13 __in_z LPCWSTR wzFilePath,
14 __in_z LPCWSTR wzLocalSource,
15 __in_z_opt LPCWSTR wzDownloadSource,
16 __in DWORD64 qwSize,
17 __in BOOL fVital,
18 __in_z_opt LPCWSTR wzInstallArguments,
19 __in_z_opt LPCWSTR wzRepairArguments,
20 __in_z_opt LPCWSTR wzUninstallArguments,
21 __in_opt BURN_DEPENDENCY_PROVIDER* pDependencyProvider,
22 __in_opt BYTE* pbHash,
23 __in DWORD cbHash
24 )
25{
26 HRESULT hr = S_OK;
27 LPWSTR sczRelationTypeCommandLineSwitch = NULL;
28
29 LPCWSTR wzRelationTypeCommandLine = CoreRelationTypeToCommandLineString(relationType);
30 if (wzRelationTypeCommandLine)
31 {
32 hr = StrAllocFormatted(&sczRelationTypeCommandLineSwitch, L" -%ls", wzRelationTypeCommandLine);
33 }
34
35 // Initialize the single payload, and fill out all the necessary fields
36 pPackage->rgPayloads = (BURN_PACKAGE_PAYLOAD *)MemAlloc(sizeof(BURN_PACKAGE_PAYLOAD), TRUE);
37 ExitOnNull(pPackage->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate space for burn package payload inside of related bundle struct");
38 pPackage->cPayloads = 1;
39
40 pPackage->rgPayloads->pPayload = (BURN_PAYLOAD *)MemAlloc(sizeof(BURN_PAYLOAD), TRUE);
41 ExitOnNull(pPackage->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate space for burn payload inside of related bundle struct");
42 pPackage->rgPayloads->pPayload->packaging = BURN_PAYLOAD_PACKAGING_EXTERNAL;
43 pPackage->rgPayloads->pPayload->qwFileSize = qwSize;
44
45 hr = StrAllocString(&pPackage->rgPayloads->pPayload->sczKey, wzId, 0);
46 ExitOnFailure(hr, "Failed to copy key for pseudo bundle payload.");
47
48 hr = StrAllocString(&pPackage->rgPayloads->pPayload->sczFilePath, wzFilePath, 0);
49 ExitOnFailure(hr, "Failed to copy filename for pseudo bundle.");
50
51 hr = StrAllocString(&pPackage->rgPayloads->pPayload->sczSourcePath, wzLocalSource, 0);
52 ExitOnFailure(hr, "Failed to copy local source path for pseudo bundle.");
53
54 if (wzDownloadSource && *wzDownloadSource)
55 {
56 hr = StrAllocString(&pPackage->rgPayloads->pPayload->downloadSource.sczUrl, wzDownloadSource, 0);
57 ExitOnFailure(hr, "Failed to copy download source for pseudo bundle.");
58 }
59
60 if (pbHash)
61 {
62 pPackage->rgPayloads->pPayload->pbHash = static_cast<BYTE*>(MemAlloc(cbHash, FALSE));
63 ExitOnNull(pPackage->rgPayloads->pPayload->pbHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for pseudo bundle payload hash.");
64
65 pPackage->rgPayloads->pPayload->cbHash = cbHash;
66 memcpy_s(pPackage->rgPayloads->pPayload->pbHash, pPackage->rgPayloads->pPayload->cbHash, pbHash, cbHash);
67 }
68
69 pPackage->rgPayloads->fCached = (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == state || BOOTSTRAPPER_PACKAGE_STATE_CACHED == state);
70
71 pPackage->Exe.fPseudoBundle = TRUE;
72
73 pPackage->type = BURN_PACKAGE_TYPE_EXE;
74 pPackage->fPerMachine = fPerMachine;
75 pPackage->currentState = state;
76 pPackage->cache = (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == state || BOOTSTRAPPER_PACKAGE_STATE_CACHED == state) ? BURN_CACHE_STATE_COMPLETE : BURN_CACHE_STATE_NONE;
77 pPackage->qwInstallSize = qwSize;
78 pPackage->qwSize = qwSize;
79 pPackage->fVital = fVital;
80
81 hr = StrAllocString(&pPackage->sczId, wzId, 0);
82 ExitOnFailure(hr, "Failed to copy key for pseudo bundle.");
83
84 hr = StrAllocString(&pPackage->sczCacheId, wzId, 0);
85 ExitOnFailure(hr, "Failed to copy cache id for pseudo bundle.");
86
87 // If we are a self updating bundle, we don't have to have Install arguments.
88 if (wzInstallArguments)
89 {
90 hr = StrAllocString(&pPackage->Exe.sczInstallArguments, wzInstallArguments, 0);
91 ExitOnFailure(hr, "Failed to copy install arguments for related bundle package");
92 }
93
94 if (sczRelationTypeCommandLineSwitch)
95 {
96 hr = StrAllocConcat(&pPackage->Exe.sczInstallArguments, sczRelationTypeCommandLineSwitch, 0);
97 ExitOnFailure(hr, "Failed to append relation type to install arguments for related bundle package");
98 }
99
100 if (wzRepairArguments)
101 {
102 hr = StrAllocString(&pPackage->Exe.sczRepairArguments, wzRepairArguments, 0);
103 ExitOnFailure(hr, "Failed to copy repair arguments for related bundle package");
104
105 if (sczRelationTypeCommandLineSwitch)
106 {
107 hr = StrAllocConcat(&pPackage->Exe.sczRepairArguments, sczRelationTypeCommandLineSwitch, 0);
108 ExitOnFailure(hr, "Failed to append relation type to repair arguments for related bundle package");
109 }
110
111 pPackage->Exe.fRepairable = TRUE;
112 }
113
114 if (wzUninstallArguments)
115 {
116 hr = StrAllocString(&pPackage->Exe.sczUninstallArguments, wzUninstallArguments, 0);
117 ExitOnFailure(hr, "Failed to copy uninstall arguments for related bundle package");
118
119 if (sczRelationTypeCommandLineSwitch)
120 {
121 hr = StrAllocConcat(&pPackage->Exe.sczUninstallArguments, sczRelationTypeCommandLineSwitch, 0);
122 ExitOnFailure(hr, "Failed to append relation type to uninstall arguments for related bundle package");
123 }
124
125 pPackage->fUninstallable = TRUE;
126 }
127
128 // Only support progress from engines that are compatible (aka: version greater than or equal to last protocol breaking change *and* versions that are older or the same as this engine).
129 pPackage->Exe.protocol = (FILEMAKEVERSION(3, 6, 2221, 0) <= qwEngineVersion && qwEngineVersion <= FILEMAKEVERSION(rmj, rmm, rup, 0)) ? BURN_EXE_PROTOCOL_TYPE_BURN : BURN_EXE_PROTOCOL_TYPE_NONE;
130
131 // All versions of Burn past v3.9 RTM support suppressing ancestors.
132 pPackage->Exe.fSupportsAncestors = FILEMAKEVERSION(3, 9, 1006, 0) <= qwEngineVersion;
133
134 if (pDependencyProvider)
135 {
136 pPackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER), TRUE);
137 ExitOnNull(pPackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate memory for dependency providers.");
138 pPackage->cDependencyProviders = 1;
139
140 pPackage->rgDependencyProviders[0].fImported = pDependencyProvider->fImported;
141
142 hr = StrAllocString(&pPackage->rgDependencyProviders[0].sczKey, pDependencyProvider->sczKey, 0);
143 ExitOnFailure(hr, "Failed to copy key for pseudo bundle.");
144
145 hr = StrAllocString(&pPackage->rgDependencyProviders[0].sczVersion, pDependencyProvider->sczVersion, 0);
146 ExitOnFailure(hr, "Failed to copy version for pseudo bundle.");
147
148 hr = StrAllocString(&pPackage->rgDependencyProviders[0].sczDisplayName, pDependencyProvider->sczDisplayName, 0);
149 ExitOnFailure(hr, "Failed to copy display name for pseudo bundle.");
150 }
151
152LExit:
153 ReleaseStr(sczRelationTypeCommandLineSwitch);
154
155 return hr;
156}
157
158extern "C" HRESULT PseudoBundleInitializePassthrough(
159 __in BURN_PACKAGE* pPassthroughPackage,
160 __in BOOTSTRAPPER_COMMAND* pCommand,
161 __in_z_opt LPCWSTR wzAppendLogPath,
162 __in_z_opt LPWSTR wzActiveParent,
163 __in_z_opt LPWSTR wzAncestors,
164 __in BURN_PACKAGE* pPackage
165 )
166{
167 Assert(BURN_PACKAGE_TYPE_EXE == pPackage->type);
168
169 HRESULT hr = S_OK;
170 LPWSTR sczArguments = NULL;
171
172 // Initialize the payloads, and copy the necessary fields.
173 pPassthroughPackage->rgPayloads = (BURN_PACKAGE_PAYLOAD *)MemAlloc(sizeof(BURN_PACKAGE_PAYLOAD) * pPackage->cPayloads, TRUE);
174 ExitOnNull(pPassthroughPackage->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate space for burn package payload inside of passthrough bundle.");
175 pPassthroughPackage->cPayloads = pPackage->cPayloads;
176
177 for (DWORD iPayload = 0; iPayload < pPackage->cPayloads; ++iPayload)
178 {
179 BURN_PACKAGE_PAYLOAD* pPayload = pPackage->rgPayloads + iPayload;
180
181 pPassthroughPackage->rgPayloads[iPayload].pPayload = (BURN_PAYLOAD *)MemAlloc(sizeof(BURN_PAYLOAD), TRUE);
182 ExitOnNull(pPassthroughPackage->rgPayloads[iPayload].pPayload, hr, E_OUTOFMEMORY, "Failed to allocate space for burn payload inside of related bundle struct");
183 pPassthroughPackage->rgPayloads[iPayload].pPayload->packaging = pPayload->pPayload->packaging;
184 pPassthroughPackage->rgPayloads[iPayload].pPayload->qwFileSize = pPayload->pPayload->qwFileSize;
185
186 hr = StrAllocString(&pPassthroughPackage->rgPayloads[iPayload].pPayload->sczKey, pPayload->pPayload->sczKey, 0);
187 ExitOnFailure(hr, "Failed to copy key for passthrough pseudo bundle payload.");
188
189 hr = StrAllocString(&pPassthroughPackage->rgPayloads[iPayload].pPayload->sczFilePath, pPayload->pPayload->sczFilePath, 0);
190 ExitOnFailure(hr, "Failed to copy filename for passthrough pseudo bundle.");
191
192 hr = StrAllocString(&pPassthroughPackage->rgPayloads[iPayload].pPayload->sczSourcePath, pPayload->pPayload->sczSourcePath, 0);
193 ExitOnFailure(hr, "Failed to copy local source path for passthrough pseudo bundle.");
194
195 if (pPayload->pPayload->downloadSource.sczUrl)
196 {
197 hr = StrAllocString(&pPassthroughPackage->rgPayloads[iPayload].pPayload->downloadSource.sczUrl, pPayload->pPayload->downloadSource.sczUrl, 0);
198 ExitOnFailure(hr, "Failed to copy download source for passthrough pseudo bundle.");
199 }
200
201 if (pPayload->pPayload->pbHash)
202 {
203 pPassthroughPackage->rgPayloads[iPayload].pPayload->pbHash = static_cast<BYTE*>(MemAlloc(pPayload->pPayload->cbHash, FALSE));
204 ExitOnNull(pPassthroughPackage->rgPayloads[iPayload].pPayload->pbHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for pseudo bundle payload hash.");
205
206 pPassthroughPackage->rgPayloads[iPayload].pPayload->cbHash = pPayload->pPayload->cbHash;
207 memcpy_s(pPassthroughPackage->rgPayloads[iPayload].pPayload->pbHash, pPassthroughPackage->rgPayloads[iPayload].pPayload->cbHash, pPayload->pPayload->pbHash, pPayload->pPayload->cbHash);
208 }
209
210 pPassthroughPackage->rgPayloads[iPayload].fCached = pPayload->fCached;
211 }
212
213 pPassthroughPackage->Exe.fPseudoBundle = TRUE;
214
215 pPassthroughPackage->fPerMachine = FALSE; // passthrough bundles are always launched per-user.
216 pPassthroughPackage->type = pPackage->type;
217 pPassthroughPackage->currentState = pPackage->currentState;
218 pPassthroughPackage->cache = pPackage->cache;
219 pPassthroughPackage->qwInstallSize = pPackage->qwInstallSize;
220 pPassthroughPackage->qwSize = pPackage->qwSize;
221 pPassthroughPackage->fVital = pPackage->fVital;
222
223 hr = StrAllocString(&pPassthroughPackage->sczId, pPackage->sczId, 0);
224 ExitOnFailure(hr, "Failed to copy key for passthrough pseudo bundle.");
225
226 hr = StrAllocString(&pPassthroughPackage->sczCacheId, pPackage->sczCacheId, 0);
227 ExitOnFailure(hr, "Failed to copy cache id for passthrough pseudo bundle.");
228
229 pPassthroughPackage->Exe.protocol = pPackage->Exe.protocol;
230
231 // No matter the operation, we're passing the same command-line. That's what makes
232 // this a passthrough bundle.
233 hr = CoreRecreateCommandLine(&sczArguments, pCommand->action, pCommand->display, pCommand->restart, pCommand->relationType, TRUE, wzActiveParent, wzAncestors, wzAppendLogPath, pCommand->wzCommandLine);
234 ExitOnFailure(hr, "Failed to recreate command-line arguments.");
235
236 hr = StrAllocString(&pPassthroughPackage->Exe.sczInstallArguments, sczArguments, 0);
237 ExitOnFailure(hr, "Failed to copy install arguments for passthrough bundle package");
238
239 hr = StrAllocString(&pPassthroughPackage->Exe.sczRepairArguments, sczArguments, 0);
240 ExitOnFailure(hr, "Failed to copy related arguments for passthrough bundle package");
241
242 pPassthroughPackage->Exe.fRepairable = TRUE;
243
244 hr = StrAllocString(&pPassthroughPackage->Exe.sczUninstallArguments, sczArguments, 0);
245 ExitOnFailure(hr, "Failed to copy uninstall arguments for passthrough bundle package");
246
247 pPassthroughPackage->fUninstallable = TRUE;
248
249 // TODO: consider bringing this back in the near future.
250 //if (pDependencyProvider)
251 //{
252 // pPassthroughPackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER), TRUE);
253 // ExitOnNull(pPassthroughPackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate memory for dependency providers.");
254 // pPassthroughPackage->cDependencyProviders = 1;
255
256 // pPassthroughPackage->rgDependencyProviders[0].fImported = pDependencyProvider->fImported;
257
258 // hr = StrAllocString(&pPassthroughPackage->rgDependencyProviders[0].sczKey, pDependencyProvider->sczKey, 0);
259 // ExitOnFailure(hr, "Failed to copy key for pseudo bundle.");
260
261 // hr = StrAllocString(&pPassthroughPackage->rgDependencyProviders[0].sczVersion, pDependencyProvider->sczVersion, 0);
262 // ExitOnFailure(hr, "Failed to copy version for pseudo bundle.");
263
264 // hr = StrAllocString(&pPassthroughPackage->rgDependencyProviders[0].sczDisplayName, pDependencyProvider->sczDisplayName, 0);
265 // ExitOnFailure(hr, "Failed to copy display name for pseudo bundle.");
266 //}
267
268LExit:
269 ReleaseStr(sczArguments);
270 return hr;
271}
diff --git a/src/engine/pseudobundle.h b/src/engine/pseudobundle.h
new file mode 100644
index 00000000..144f6880
--- /dev/null
+++ b/src/engine/pseudobundle.h
@@ -0,0 +1,39 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9HRESULT PseudoBundleInitialize(
10 __in DWORD64 qwEngineVersion,
11 __in BURN_PACKAGE* pPackage,
12 __in BOOL fPerMachine,
13 __in_z LPCWSTR wzId,
14 __in BOOTSTRAPPER_RELATION_TYPE relationType,
15 __in BOOTSTRAPPER_PACKAGE_STATE state,
16 __in_z LPCWSTR wzFilePath,
17 __in_z LPCWSTR wzLocalSource,
18 __in_z_opt LPCWSTR wzDownloadSource,
19 __in DWORD64 qwSize,
20 __in BOOL fVital,
21 __in_z_opt LPCWSTR wzInstallArguments,
22 __in_z_opt LPCWSTR wzRepairArguments,
23 __in_z_opt LPCWSTR wzUninstallArguments,
24 __in_opt BURN_DEPENDENCY_PROVIDER* pDependencyProvider,
25 __in_opt BYTE* pbHash,
26 __in DWORD cbHash
27 );
28HRESULT PseudoBundleInitializePassthrough(
29 __in BURN_PACKAGE* pPassthroughPackage,
30 __in BOOTSTRAPPER_COMMAND* pCommand,
31 __in_z_opt LPCWSTR wzAppendLogPath,
32 __in_z_opt LPWSTR wzActiveParent,
33 __in_z_opt LPWSTR wzAncestors,
34 __in BURN_PACKAGE* pPackage
35 );
36
37#if defined(__cplusplus)
38}
39#endif
diff --git a/src/engine/registration.cpp b/src/engine/registration.cpp
new file mode 100644
index 00000000..93c990f5
--- /dev/null
+++ b/src/engine/registration.cpp
@@ -0,0 +1,1599 @@
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#include "precomp.h"
4
5
6// constants
7
8const LPCWSTR REGISTRY_RUN_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run";
9const LPCWSTR REGISTRY_RUN_ONCE_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce";
10const LPCWSTR REGISTRY_REBOOT_PENDING_FORMAT = L"%ls.RebootRequired";
11const LPCWSTR REGISTRY_BUNDLE_INSTALLED = L"Installed";
12const LPCWSTR REGISTRY_BUNDLE_DISPLAY_ICON = L"DisplayIcon";
13const LPCWSTR REGISTRY_BUNDLE_DISPLAY_VERSION = L"DisplayVersion";
14const LPCWSTR REGISTRY_BUNDLE_ESTIMATED_SIZE = L"EstimatedSize";
15const LPCWSTR REGISTRY_BUNDLE_PUBLISHER = L"Publisher";
16const LPCWSTR REGISTRY_BUNDLE_HELP_LINK = L"HelpLink";
17const LPCWSTR REGISTRY_BUNDLE_HELP_TELEPHONE = L"HelpTelephone";
18const LPCWSTR REGISTRY_BUNDLE_URL_INFO_ABOUT = L"URLInfoAbout";
19const LPCWSTR REGISTRY_BUNDLE_URL_UPDATE_INFO = L"URLUpdateInfo";
20const LPCWSTR REGISTRY_BUNDLE_PARENT_DISPLAY_NAME = L"ParentDisplayName";
21const LPCWSTR REGISTRY_BUNDLE_PARENT_KEY_NAME = L"ParentKeyName";
22const LPCWSTR REGISTRY_BUNDLE_COMMENTS = L"Comments";
23const LPCWSTR REGISTRY_BUNDLE_CONTACT = L"Contact";
24const LPCWSTR REGISTRY_BUNDLE_NO_MODIFY = L"NoModify";
25const LPCWSTR REGISTRY_BUNDLE_MODIFY_PATH = L"ModifyPath";
26const LPCWSTR REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY = L"NoElevateOnModify";
27const LPCWSTR REGISTRY_BUNDLE_NO_REMOVE = L"NoRemove";
28const LPCWSTR REGISTRY_BUNDLE_SYSTEM_COMPONENT = L"SystemComponent";
29const LPCWSTR REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING = L"QuietUninstallString";
30const LPCWSTR REGISTRY_BUNDLE_UNINSTALL_STRING = L"UninstallString";
31const LPCWSTR REGISTRY_BUNDLE_RESUME_COMMAND_LINE = L"BundleResumeCommandLine";
32const LPCWSTR REGISTRY_BUNDLE_VERSION_MAJOR = L"VersionMajor";
33const LPCWSTR REGISTRY_BUNDLE_VERSION_MINOR = L"VersionMinor";
34
35// internal function declarations
36
37static HRESULT ParseSoftwareTagsFromXml(
38 __in IXMLDOMNode* pixnRegistrationNode,
39 __out BURN_SOFTWARE_TAG** prgSoftwareTags,
40 __out DWORD* pcSoftwareTags
41 );
42static HRESULT SetPaths(
43 __in BURN_REGISTRATION* pRegistration
44 );
45static HRESULT GetBundleManufacturer(
46 __in BURN_REGISTRATION* pRegistration,
47 __in BURN_VARIABLES* pVariables,
48 __out LPWSTR* psczBundleManufacturer
49 );
50static HRESULT GetBundleName(
51 __in BURN_REGISTRATION* pRegistration,
52 __in BURN_VARIABLES* pVariables,
53 __out LPWSTR* psczBundleName
54 );
55static HRESULT UpdateResumeMode(
56 __in BURN_REGISTRATION* pRegistration,
57 __in HKEY hkRegistration,
58 __in BURN_RESUME_MODE resumeMode,
59 __in BOOL fRestartInitiated
60 );
61static HRESULT ParseRelatedCodes(
62 __in BURN_REGISTRATION* pRegistration,
63 __in IXMLDOMNode* pixnBundle
64 );
65static HRESULT FormatUpdateRegistrationKey(
66 __in BURN_REGISTRATION* pRegistration,
67 __out_z LPWSTR* psczKey
68 );
69static HRESULT WriteSoftwareTags(
70 __in BOOL fPerMachine,
71 __in BURN_SOFTWARE_TAGS* pSoftwareTags
72 );
73static HRESULT RemoveSoftwareTags(
74 __in BOOL fPerMachine,
75 __in BURN_SOFTWARE_TAGS* pSoftwareTags
76 );
77static HRESULT WriteUpdateRegistration(
78 __in BURN_REGISTRATION* pRegistration,
79 __in BURN_VARIABLES* pVariables
80 );
81static HRESULT RemoveUpdateRegistration(
82 __in BURN_REGISTRATION* pRegistration
83 );
84static HRESULT RegWriteStringVariable(
85 __in HKEY hkKey,
86 __in BURN_VARIABLES* pVariables,
87 __in LPCWSTR wzVariable,
88 __in LPCWSTR wzName
89 );
90static HRESULT UpdateBundleNameRegistration(
91 __in BURN_REGISTRATION* pRegistration,
92 __in BURN_VARIABLES* pVariables,
93 __in HKEY hkRegistration
94 );
95
96// function definitions
97
98/*******************************************************************
99 RegistrationParseFromXml - Parses registration information from manifest.
100
101*******************************************************************/
102extern "C" HRESULT RegistrationParseFromXml(
103 __in BURN_REGISTRATION* pRegistration,
104 __in IXMLDOMNode* pixnBundle
105 )
106{
107 HRESULT hr = S_OK;
108 IXMLDOMNode* pixnRegistrationNode = NULL;
109 IXMLDOMNode* pixnArpNode = NULL;
110 IXMLDOMNode* pixnUpdateNode = NULL;
111 LPWSTR scz = NULL;
112
113 // select registration node
114 hr = XmlSelectSingleNode(pixnBundle, L"Registration", &pixnRegistrationNode);
115 if (S_FALSE == hr)
116 {
117 hr = E_NOTFOUND;
118 }
119 ExitOnFailure(hr, "Failed to select registration node.");
120
121 // @Id
122 hr = XmlGetAttributeEx(pixnRegistrationNode, L"Id", &pRegistration->sczId);
123 ExitOnFailure(hr, "Failed to get @Id.");
124
125 // @Tag
126 hr = XmlGetAttributeEx(pixnRegistrationNode, L"Tag", &pRegistration->sczTag);
127 ExitOnFailure(hr, "Failed to get @Tag.");
128
129 hr = ParseRelatedCodes(pRegistration, pixnBundle);
130 ExitOnFailure(hr, "Failed to parse related bundles");
131
132 // @Version
133 hr = XmlGetAttributeEx(pixnRegistrationNode, L"Version", &scz);
134 ExitOnFailure(hr, "Failed to get @Version.");
135
136 hr = FileVersionFromStringEx(scz, 0, &pRegistration->qwVersion);
137 ExitOnFailure(hr, "Failed to parse @Version: %ls", scz);
138
139 // @ProviderKey
140 hr = XmlGetAttributeEx(pixnRegistrationNode, L"ProviderKey", &pRegistration->sczProviderKey);
141 ExitOnFailure(hr, "Failed to get @ProviderKey.");
142
143 // @ExecutableName
144 hr = XmlGetAttributeEx(pixnRegistrationNode, L"ExecutableName", &pRegistration->sczExecutableName);
145 ExitOnFailure(hr, "Failed to get @ExecutableName.");
146
147 // @PerMachine
148 hr = XmlGetYesNoAttribute(pixnRegistrationNode, L"PerMachine", &pRegistration->fPerMachine);
149 ExitOnFailure(hr, "Failed to get @PerMachine.");
150
151 // select ARP node
152 hr = XmlSelectSingleNode(pixnRegistrationNode, L"Arp", &pixnArpNode);
153 if (S_FALSE != hr)
154 {
155 ExitOnFailure(hr, "Failed to select ARP node.");
156
157 // @Register
158 hr = XmlGetYesNoAttribute(pixnArpNode, L"Register", &pRegistration->fRegisterArp);
159 ExitOnFailure(hr, "Failed to get @Register.");
160
161 // @DisplayName
162 hr = XmlGetAttributeEx(pixnArpNode, L"DisplayName", &pRegistration->sczDisplayName);
163 if (E_NOTFOUND != hr)
164 {
165 ExitOnFailure(hr, "Failed to get @DisplayName.");
166 }
167
168 // @DisplayVersion
169 hr = XmlGetAttributeEx(pixnArpNode, L"DisplayVersion", &pRegistration->sczDisplayVersion);
170 if (E_NOTFOUND != hr)
171 {
172 ExitOnFailure(hr, "Failed to get @DisplayVersion.");
173 }
174
175 // @Publisher
176 hr = XmlGetAttributeEx(pixnArpNode, L"Publisher", &pRegistration->sczPublisher);
177 if (E_NOTFOUND != hr)
178 {
179 ExitOnFailure(hr, "Failed to get @Publisher.");
180 }
181
182 // @HelpLink
183 hr = XmlGetAttributeEx(pixnArpNode, L"HelpLink", &pRegistration->sczHelpLink);
184 if (E_NOTFOUND != hr)
185 {
186 ExitOnFailure(hr, "Failed to get @HelpLink.");
187 }
188
189 // @HelpTelephone
190 hr = XmlGetAttributeEx(pixnArpNode, L"HelpTelephone", &pRegistration->sczHelpTelephone);
191 if (E_NOTFOUND != hr)
192 {
193 ExitOnFailure(hr, "Failed to get @HelpTelephone.");
194 }
195
196 // @AboutUrl
197 hr = XmlGetAttributeEx(pixnArpNode, L"AboutUrl", &pRegistration->sczAboutUrl);
198 if (E_NOTFOUND != hr)
199 {
200 ExitOnFailure(hr, "Failed to get @AboutUrl.");
201 }
202
203 // @UpdateUrl
204 hr = XmlGetAttributeEx(pixnArpNode, L"UpdateUrl", &pRegistration->sczUpdateUrl);
205 if (E_NOTFOUND != hr)
206 {
207 ExitOnFailure(hr, "Failed to get @UpdateUrl.");
208 }
209
210 // @ParentDisplayName
211 hr = XmlGetAttributeEx(pixnArpNode, L"ParentDisplayName", &pRegistration->sczParentDisplayName);
212 if (E_NOTFOUND != hr)
213 {
214 ExitOnFailure(hr, "Failed to get @ParentDisplayName.");
215 }
216
217 // @Comments
218 hr = XmlGetAttributeEx(pixnArpNode, L"Comments", &pRegistration->sczComments);
219 if (E_NOTFOUND != hr)
220 {
221 ExitOnFailure(hr, "Failed to get @Comments.");
222 }
223
224 // @Contact
225 hr = XmlGetAttributeEx(pixnArpNode, L"Contact", &pRegistration->sczContact);
226 if (E_NOTFOUND != hr)
227 {
228 ExitOnFailure(hr, "Failed to get @Contact.");
229 }
230
231 // @DisableModify
232 hr = XmlGetAttributeEx(pixnArpNode, L"DisableModify", &scz);
233 if (SUCCEEDED(hr))
234 {
235 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"button", -1))
236 {
237 pRegistration->modify = BURN_REGISTRATION_MODIFY_DISABLE_BUTTON;
238 }
239 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"yes", -1))
240 {
241 pRegistration->modify = BURN_REGISTRATION_MODIFY_DISABLE;
242 }
243 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"no", -1))
244 {
245 pRegistration->modify = BURN_REGISTRATION_MODIFY_ENABLED;
246 }
247 else
248 {
249 hr = E_UNEXPECTED;
250 ExitOnRootFailure(hr, "Invalid modify disabled type: %ls", scz);
251 }
252 }
253 else if (E_NOTFOUND == hr)
254 {
255 pRegistration->modify = BURN_REGISTRATION_MODIFY_ENABLED;
256 hr = S_OK;
257 }
258 ExitOnFailure(hr, "Failed to get @DisableModify.");
259
260 // @DisableRemove
261 hr = XmlGetYesNoAttribute(pixnArpNode, L"DisableRemove", &pRegistration->fNoRemove);
262 if (E_NOTFOUND != hr)
263 {
264 ExitOnFailure(hr, "Failed to get @DisableRemove.");
265 pRegistration->fNoRemoveDefined = TRUE;
266 }
267 }
268
269 hr = ParseSoftwareTagsFromXml(pixnRegistrationNode, &pRegistration->softwareTags.rgSoftwareTags, &pRegistration->softwareTags.cSoftwareTags);
270 ExitOnFailure(hr, "Failed to parse software tag.");
271
272 // select Update node
273 hr = XmlSelectSingleNode(pixnRegistrationNode, L"Update", &pixnUpdateNode);
274 if (S_FALSE != hr)
275 {
276 ExitOnFailure(hr, "Failed to select Update node.");
277
278 pRegistration->update.fRegisterUpdate = TRUE;
279
280 // @Manufacturer
281 hr = XmlGetAttributeEx(pixnUpdateNode, L"Manufacturer", &pRegistration->update.sczManufacturer);
282 ExitOnFailure(hr, "Failed to get @Manufacturer.");
283
284 // @Department
285 hr = XmlGetAttributeEx(pixnUpdateNode, L"Department", &pRegistration->update.sczDepartment);
286 if (E_NOTFOUND != hr)
287 {
288 ExitOnFailure(hr, "Failed to get @Department.");
289 }
290
291 // @ProductFamily
292 hr = XmlGetAttributeEx(pixnUpdateNode, L"ProductFamily", &pRegistration->update.sczProductFamily);
293 if (E_NOTFOUND != hr)
294 {
295 ExitOnFailure(hr, "Failed to get @ProductFamily.");
296 }
297
298 // @Name
299 hr = XmlGetAttributeEx(pixnUpdateNode, L"Name", &pRegistration->update.sczName);
300 ExitOnFailure(hr, "Failed to get @Name.");
301
302 // @Classification
303 hr = XmlGetAttributeEx(pixnUpdateNode, L"Classification", &pRegistration->update.sczClassification);
304 ExitOnFailure(hr, "Failed to get @Classification.");
305 }
306
307 hr = SetPaths(pRegistration);
308 ExitOnFailure(hr, "Failed to set registration paths.");
309
310LExit:
311 ReleaseObject(pixnRegistrationNode);
312 ReleaseObject(pixnArpNode);
313 ReleaseObject(pixnUpdateNode);
314 ReleaseStr(scz);
315
316 return hr;
317}
318
319/*******************************************************************
320 RegistrationUninitialize -
321
322*******************************************************************/
323extern "C" void RegistrationUninitialize(
324 __in BURN_REGISTRATION* pRegistration
325 )
326{
327 ReleaseStr(pRegistration->sczId);
328 ReleaseStr(pRegistration->sczTag);
329
330 for (DWORD i = 0; i < pRegistration->cDetectCodes; ++i)
331 {
332 ReleaseStr(pRegistration->rgsczDetectCodes[i]);
333 }
334 ReleaseMem(pRegistration->rgsczDetectCodes);
335
336 for (DWORD i = 0; i < pRegistration->cUpgradeCodes; ++i)
337 {
338 ReleaseStr(pRegistration->rgsczUpgradeCodes[i]);
339 }
340 ReleaseMem(pRegistration->rgsczUpgradeCodes);
341
342 for (DWORD i = 0; i < pRegistration->cAddonCodes; ++i)
343 {
344 ReleaseStr(pRegistration->rgsczAddonCodes[i]);
345 }
346 ReleaseMem(pRegistration->rgsczAddonCodes);
347
348 for (DWORD i = 0; i < pRegistration->cPatchCodes; ++i)
349 {
350 ReleaseStr(pRegistration->rgsczPatchCodes[i]);
351 }
352 ReleaseMem(pRegistration->rgsczPatchCodes);
353
354 ReleaseStr(pRegistration->sczProviderKey);
355 ReleaseStr(pRegistration->sczActiveParent);
356 ReleaseStr(pRegistration->sczExecutableName);
357
358 ReleaseStr(pRegistration->sczRegistrationKey);
359 ReleaseStr(pRegistration->sczCacheExecutablePath);
360 ReleaseStr(pRegistration->sczResumeCommandLine);
361 ReleaseStr(pRegistration->sczStateFile);
362
363 ReleaseStr(pRegistration->sczDisplayName);
364 ReleaseStr(pRegistration->sczDisplayVersion);
365 ReleaseStr(pRegistration->sczPublisher);
366 ReleaseStr(pRegistration->sczHelpLink);
367 ReleaseStr(pRegistration->sczHelpTelephone);
368 ReleaseStr(pRegistration->sczAboutUrl);
369 ReleaseStr(pRegistration->sczUpdateUrl);
370 ReleaseStr(pRegistration->sczParentDisplayName);
371 ReleaseStr(pRegistration->sczComments);
372 ReleaseStr(pRegistration->sczContact);
373
374 ReleaseStr(pRegistration->update.sczManufacturer);
375 ReleaseStr(pRegistration->update.sczDepartment);
376 ReleaseStr(pRegistration->update.sczProductFamily);
377 ReleaseStr(pRegistration->update.sczName);
378 ReleaseStr(pRegistration->update.sczClassification);
379
380 if (pRegistration->softwareTags.rgSoftwareTags)
381 {
382 for (DWORD i = 0; i < pRegistration->softwareTags.cSoftwareTags; ++i)
383 {
384 ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczFilename);
385 ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczRegid);
386 ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczTag);
387 }
388
389 MemFree(pRegistration->softwareTags.rgSoftwareTags);
390 }
391
392 ReleaseStr(pRegistration->sczDetectedProviderKeyBundleId);
393 ReleaseStr(pRegistration->sczAncestors);
394 RelatedBundlesUninitialize(&pRegistration->relatedBundles);
395
396 // clear struct
397 memset(pRegistration, 0, sizeof(BURN_REGISTRATION));
398}
399
400/*******************************************************************
401 RegistrationSetVariables - Initializes bundle variables that map to
402 registration entities.
403
404*******************************************************************/
405extern "C" HRESULT RegistrationSetVariables(
406 __in BURN_REGISTRATION* pRegistration,
407 __in BURN_VARIABLES* pVariables
408 )
409{
410 HRESULT hr = S_OK;
411 LPWSTR sczBundleManufacturer = NULL;
412 LPWSTR sczBundleName = NULL;
413
414 if (pRegistration->fInstalled)
415 {
416 hr = VariableSetNumeric(pVariables, BURN_BUNDLE_INSTALLED, 1, TRUE);
417 ExitOnFailure(hr, "Failed to set the bundle installed built-in variable.");
418 }
419
420 // Ensure the registration bundle name is updated.
421 hr = GetBundleName(pRegistration, pVariables, &sczBundleName);
422 ExitOnFailure(hr, "Failed to initialize bundle name.");
423
424 hr = GetBundleManufacturer(pRegistration, pVariables, &sczBundleName);
425 ExitOnFailure(hr, "Failed to initialize bundle manufacturer.");
426
427 if (pRegistration->sczActiveParent && *pRegistration->sczActiveParent)
428 {
429 hr = VariableSetString(pVariables, BURN_BUNDLE_ACTIVE_PARENT, pRegistration->sczActiveParent, TRUE);
430 ExitOnFailure(hr, "Failed to overwrite the bundle active parent built-in variable.");
431 }
432
433 hr = VariableSetString(pVariables, BURN_BUNDLE_PROVIDER_KEY, pRegistration->sczProviderKey, TRUE);
434 ExitOnFailure(hr, "Failed to overwrite the bundle provider key built-in variable.");
435
436 hr = VariableSetString(pVariables, BURN_BUNDLE_TAG, pRegistration->sczTag, TRUE);
437 ExitOnFailure(hr, "Failed to overwrite the bundle tag built-in variable.");
438
439 hr = VariableSetVersion(pVariables, BURN_BUNDLE_VERSION, pRegistration->qwVersion, TRUE);
440 ExitOnFailure(hr, "Failed to overwrite the bundle tag built-in variable.");
441
442LExit:
443 ReleaseStr(sczBundleManufacturer);
444 ReleaseStr(sczBundleName);
445
446 return hr;
447}
448
449extern "C" HRESULT RegistrationDetectInstalled(
450 __in BURN_REGISTRATION* pRegistration,
451 __out BOOL* pfInstalled
452 )
453{
454 HRESULT hr = S_OK;
455 HKEY hkRegistration = NULL;
456 DWORD dwInstalled = 0;
457
458 // open registration key
459 hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration);
460 if (SUCCEEDED(hr))
461 {
462 hr = RegReadNumber(hkRegistration, REGISTRY_BUNDLE_INSTALLED, &dwInstalled);
463 }
464
465 // Not finding the key or value is okay.
466 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
467 {
468 hr = S_OK;
469 }
470
471 *pfInstalled = (1 == dwInstalled);
472
473 ReleaseRegKey(hkRegistration);
474 return hr;
475}
476
477/*******************************************************************
478 RegistrationDetectResumeMode - Detects registration information on the system
479 to determine if a resume is taking place.
480
481*******************************************************************/
482extern "C" HRESULT RegistrationDetectResumeType(
483 __in BURN_REGISTRATION* pRegistration,
484 __out BOOTSTRAPPER_RESUME_TYPE* pResumeType
485 )
486{
487 HRESULT hr = S_OK;
488 LPWSTR sczRebootRequiredKey = NULL;
489 HKEY hkRebootRequired = NULL;
490 HKEY hkRegistration = NULL;
491 DWORD dwResume = 0;
492
493 // Check to see if a restart is pending for this bundle.
494 hr = StrAllocFormatted(&sczRebootRequiredKey, REGISTRY_REBOOT_PENDING_FORMAT, pRegistration->sczRegistrationKey);
495 ExitOnFailure(hr, "Failed to format pending restart registry key to read.");
496
497 hr = RegOpen(pRegistration->hkRoot, sczRebootRequiredKey, KEY_QUERY_VALUE, &hkRebootRequired);
498 if (SUCCEEDED(hr))
499 {
500 *pResumeType = BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING;
501 ExitFunction1(hr = S_OK);
502 }
503
504 // open registration key
505 hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration);
506 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
507 {
508 *pResumeType = BOOTSTRAPPER_RESUME_TYPE_NONE;
509 ExitFunction1(hr = S_OK);
510 }
511 ExitOnFailure(hr, "Failed to open registration key.");
512
513 // read Resume value
514 hr = RegReadNumber(hkRegistration, L"Resume", &dwResume);
515 if (E_FILENOTFOUND == hr)
516 {
517 *pResumeType = BOOTSTRAPPER_RESUME_TYPE_INVALID;
518 ExitFunction1(hr = S_OK);
519 }
520 ExitOnFailure(hr, "Failed to read Resume value.");
521
522 switch (dwResume)
523 {
524 case BURN_RESUME_MODE_ACTIVE:
525 // a previous run was interrupted
526 *pResumeType = BOOTSTRAPPER_RESUME_TYPE_INTERRUPTED;
527 break;
528
529 case BURN_RESUME_MODE_SUSPEND:
530 *pResumeType = BOOTSTRAPPER_RESUME_TYPE_SUSPEND;
531 break;
532
533 case BURN_RESUME_MODE_ARP:
534 *pResumeType = BOOTSTRAPPER_RESUME_TYPE_ARP;
535 break;
536
537 case BURN_RESUME_MODE_REBOOT_PENDING:
538 // The volatile pending registry doesn't exist (checked above) which means
539 // the system was successfully restarted.
540 *pResumeType = BOOTSTRAPPER_RESUME_TYPE_REBOOT;
541 break;
542
543 default:
544 // the value stored in the registry is not valid
545 *pResumeType = BOOTSTRAPPER_RESUME_TYPE_INVALID;
546 break;
547 }
548
549LExit:
550 ReleaseRegKey(hkRegistration);
551 ReleaseRegKey(hkRebootRequired);
552 ReleaseStr(sczRebootRequiredKey);
553
554 return hr;
555}
556
557/*******************************************************************
558 RegistrationDetectRelatedBundles - finds the bundles with same
559 upgrade/detect/addon/patch codes.
560
561*******************************************************************/
562extern "C" HRESULT RegistrationDetectRelatedBundles(
563 __in BURN_REGISTRATION* pRegistration
564 )
565{
566 HRESULT hr = S_OK;
567
568 hr = RelatedBundlesInitializeForScope(TRUE, pRegistration, &pRegistration->relatedBundles);
569 ExitOnFailure(hr, "Failed to initialize per-machine related bundles.");
570
571 hr = RelatedBundlesInitializeForScope(FALSE, pRegistration, &pRegistration->relatedBundles);
572 ExitOnFailure(hr, "Failed to initialize per-user related bundles.");
573
574LExit:
575 return hr;
576}
577
578/*******************************************************************
579 RegistrationSessionBegin - Registers a run session on the system.
580
581*******************************************************************/
582extern "C" HRESULT RegistrationSessionBegin(
583 __in_z LPCWSTR wzEngineWorkingPath,
584 __in BURN_REGISTRATION* pRegistration,
585 __in BURN_VARIABLES* pVariables,
586 __in BURN_USER_EXPERIENCE* pUserExperience,
587 __in DWORD dwRegistrationOptions,
588 __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction,
589 __in DWORD64 qwEstimatedSize
590 )
591{
592 HRESULT hr = S_OK;
593 DWORD dwSize = 0;
594 HKEY hkRegistration = NULL;
595 LPWSTR sczPublisher = NULL;
596
597 LogId(REPORT_VERBOSE, MSG_SESSION_BEGIN, pRegistration->sczRegistrationKey, dwRegistrationOptions, LoggingBoolToString(pRegistration->fDisableResume));
598
599 // Cache bundle executable.
600 if (dwRegistrationOptions & BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE)
601 {
602 hr = CacheCompleteBundle(pRegistration->fPerMachine, pRegistration->sczExecutableName, pRegistration->sczId, &pUserExperience->payloads, wzEngineWorkingPath
603#ifdef DEBUG
604 , pRegistration->sczCacheExecutablePath
605#endif
606 );
607 ExitOnFailure(hr, "Failed to cache bundle from path: %ls", wzEngineWorkingPath);
608 }
609
610 // create registration key
611 hr = RegCreate(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_WRITE, &hkRegistration);
612 ExitOnFailure(hr, "Failed to create registration key.");
613
614 // Write any ARP values and software tags.
615 if (dwRegistrationOptions & BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION)
616 {
617 // Upgrade information
618 hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH, pRegistration->sczCacheExecutablePath);
619 ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH);
620
621 hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, pRegistration->rgsczUpgradeCodes, pRegistration->cUpgradeCodes);
622 ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE);
623
624 hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE, pRegistration->rgsczAddonCodes, pRegistration->cAddonCodes);
625 ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE);
626
627 hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE, pRegistration->rgsczDetectCodes, pRegistration->cDetectCodes);
628 ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE);
629
630 hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE, pRegistration->rgsczPatchCodes, pRegistration->cPatchCodes);
631 ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE);
632
633 hr = RegWriteStringFormatted(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, L"%hu.%hu.%hu.%hu",
634 static_cast<WORD>(pRegistration->qwVersion >> 48), static_cast<WORD>(pRegistration->qwVersion >> 32),
635 static_cast<WORD>(pRegistration->qwVersion >> 16), static_cast<WORD>(pRegistration->qwVersion));
636 ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION);
637
638 hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_VERSION_MAJOR, static_cast<WORD>(pRegistration->qwVersion >> 48));
639 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_VERSION_MAJOR);
640
641 hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_VERSION_MINOR, static_cast<WORD>(pRegistration->qwVersion >> 32));
642 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_VERSION_MINOR);
643
644 if (pRegistration->sczProviderKey)
645 {
646 hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY, pRegistration->sczProviderKey);
647 ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY);
648 }
649
650 if (pRegistration->sczTag)
651 {
652 hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_TAG, pRegistration->sczTag);
653 ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_TAG);
654 }
655
656 hr = RegWriteStringFormatted(hkRegistration, BURN_REGISTRATION_REGISTRY_ENGINE_VERSION, L"%hs", szVerMajorMinorBuild);
657 ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_ENGINE_VERSION);
658
659 // DisplayIcon: [path to exe] and ",0" to refer to the first icon in the executable.
660 hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_DISPLAY_ICON, L"%s,0", pRegistration->sczCacheExecutablePath);
661 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_DISPLAY_ICON);
662
663 // update display name
664 hr = UpdateBundleNameRegistration(pRegistration, pVariables, hkRegistration);
665 ExitOnFailure(hr, "Failed to update name and publisher.");
666
667 // DisplayVersion: provided by UI
668 if (pRegistration->sczDisplayVersion)
669 {
670 hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_DISPLAY_VERSION, pRegistration->sczDisplayVersion);
671 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_DISPLAY_VERSION);
672 }
673
674 // Publisher: provided by UI
675 hr = GetBundleManufacturer(pRegistration, pVariables, &sczPublisher);
676 hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_PUBLISHER, SUCCEEDED(hr) ? sczPublisher : pRegistration->sczPublisher);
677 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_PUBLISHER);
678
679 // HelpLink: provided by UI
680 if (pRegistration->sczHelpLink)
681 {
682 hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_HELP_LINK, pRegistration->sczHelpLink);
683 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_HELP_LINK);
684 }
685
686 // HelpTelephone: provided by UI
687 if (pRegistration->sczHelpTelephone)
688 {
689 hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_HELP_TELEPHONE, pRegistration->sczHelpTelephone);
690 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_HELP_TELEPHONE);
691 }
692
693 // URLInfoAbout, provided by UI
694 if (pRegistration->sczAboutUrl)
695 {
696 hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_URL_INFO_ABOUT, pRegistration->sczAboutUrl);
697 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_URL_INFO_ABOUT);
698 }
699
700 // URLUpdateInfo, provided by UI
701 if (pRegistration->sczUpdateUrl)
702 {
703 hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_URL_UPDATE_INFO, pRegistration->sczUpdateUrl);
704 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_URL_UPDATE_INFO);
705 }
706
707 // ParentDisplayName
708 if (pRegistration->sczParentDisplayName)
709 {
710 hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_PARENT_DISPLAY_NAME, pRegistration->sczParentDisplayName);
711 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_PARENT_DISPLAY_NAME);
712
713 // Need to write the ParentKeyName but can be set to anything.
714 hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_PARENT_KEY_NAME, pRegistration->sczParentDisplayName);
715 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_PARENT_KEY_NAME);
716 }
717
718 // Comments, provided by UI
719 if (pRegistration->sczComments)
720 {
721 hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_COMMENTS, pRegistration->sczComments);
722 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_COMMENTS);
723 }
724
725 // Contact, provided by UI
726 if (pRegistration->sczContact)
727 {
728 hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_CONTACT, pRegistration->sczContact);
729 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_CONTACT);
730 }
731
732 // InstallLocation: provided by UI
733 // TODO: need to figure out what "InstallLocation" means in a chainer. <smile/>
734
735 // NoModify
736 if (BURN_REGISTRATION_MODIFY_DISABLE == pRegistration->modify)
737 {
738 hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_NO_MODIFY, 1);
739 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_NO_MODIFY);
740 }
741 else if (BURN_REGISTRATION_MODIFY_DISABLE_BUTTON != pRegistration->modify) // if support modify (aka: did not disable anything)
742 {
743 // ModifyPath: [path to exe] /modify
744 hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_MODIFY_PATH, L"\"%ls\" /modify", pRegistration->sczCacheExecutablePath);
745 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_MODIFY_PATH);
746
747 // NoElevateOnModify: 1
748 hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY, 1);
749 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY);
750 }
751
752 // NoRemove: should this be allowed?
753 if (pRegistration->fNoRemoveDefined)
754 {
755 hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_NO_REMOVE, (DWORD)pRegistration->fNoRemove);
756 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_NO_REMOVE);
757 }
758
759 // Conditionally hide the ARP entry.
760 if (!pRegistration->fRegisterArp)
761 {
762 hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_SYSTEM_COMPONENT, 1);
763 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_SYSTEM_COMPONENT);
764 }
765
766 // QuietUninstallString: [path to exe] /uninstall /quiet
767 hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING, L"\"%ls\" /uninstall /quiet", pRegistration->sczCacheExecutablePath);
768 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING);
769
770 // UninstallString, [path to exe]
771 // If the modify button is to be disabled, we'll add "/modify" to the uninstall string because the button is "Uninstall/Change". Otherwise,
772 // it's just the "Uninstall" button so we add "/uninstall" to make the program just go away.
773 LPCWSTR wzUninstallParameters = (BURN_REGISTRATION_MODIFY_DISABLE_BUTTON == pRegistration->modify) ? L"/modify" : L" /uninstall";
774 hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_UNINSTALL_STRING, L"\"%ls\" %ls", pRegistration->sczCacheExecutablePath, wzUninstallParameters);
775 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_UNINSTALL_STRING);
776
777 if (pRegistration->softwareTags.cSoftwareTags)
778 {
779 hr = WriteSoftwareTags(pRegistration->fPerMachine, &pRegistration->softwareTags);
780 ExitOnFailure(hr, "Failed to write software tags.");
781 }
782
783 // Update registration.
784 if (pRegistration->update.fRegisterUpdate)
785 {
786 hr = WriteUpdateRegistration(pRegistration, pVariables);
787 ExitOnFailure(hr, "Failed to write update registration.");
788 }
789 }
790
791 // Update estimated size.
792 if (dwRegistrationOptions & BURN_REGISTRATION_ACTION_OPERATIONS_UPDATE_SIZE)
793 {
794 qwEstimatedSize /= 1024; // Convert bytes to KB
795 if (0 < qwEstimatedSize)
796 {
797 if (DWORD_MAX < qwEstimatedSize)
798 {
799 // ARP doesn't support QWORDs here
800 dwSize = DWORD_MAX;
801 }
802 else
803 {
804 dwSize = static_cast<DWORD>(qwEstimatedSize);
805 }
806
807 hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_ESTIMATED_SIZE, dwSize);
808 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_ESTIMATED_SIZE);
809 }
810 }
811
812 // Register the bundle dependency key.
813 if (BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER == dependencyRegistrationAction)
814 {
815 hr = DependencyRegisterBundle(pRegistration);
816 ExitOnFailure(hr, "Failed to register the bundle dependency key.");
817 }
818
819 // update resume mode
820 hr = UpdateResumeMode(pRegistration, hkRegistration, BURN_RESUME_MODE_ACTIVE, FALSE);
821 ExitOnFailure(hr, "Failed to update resume mode.");
822
823LExit:
824 ReleaseStr(sczPublisher);
825 ReleaseRegKey(hkRegistration);
826
827 return hr;
828}
829
830
831/*******************************************************************
832 RegistrationSessionResume - Resumes a previous run session.
833
834*******************************************************************/
835extern "C" HRESULT RegistrationSessionResume(
836 __in BURN_REGISTRATION* pRegistration,
837 __in BURN_VARIABLES* pVariables
838 )
839{
840 HRESULT hr = S_OK;
841 HKEY hkRegistration = NULL;
842
843 // open registration key
844 hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_WRITE, &hkRegistration);
845 ExitOnFailure(hr, "Failed to open registration key.");
846
847 // update resume mode
848 hr = UpdateResumeMode(pRegistration, hkRegistration, BURN_RESUME_MODE_ACTIVE, FALSE);
849 ExitOnFailure(hr, "Failed to update resume mode.");
850
851 // update display name
852 hr = UpdateBundleNameRegistration(pRegistration, pVariables, hkRegistration);
853 ExitOnFailure(hr, "Failed to update name and publisher.");
854
855LExit:
856 ReleaseRegKey(hkRegistration);
857
858 return hr;
859}
860
861
862/*******************************************************************
863 RegistrationSessionEnd - Unregisters a run session from the system.
864
865 *******************************************************************/
866extern "C" HRESULT RegistrationSessionEnd(
867 __in BURN_REGISTRATION* pRegistration,
868 __in BURN_RESUME_MODE resumeMode,
869 __in BOOTSTRAPPER_APPLY_RESTART restart,
870 __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction
871 )
872{
873 HRESULT hr = S_OK;
874 LPWSTR sczRebootRequiredKey = NULL;
875 HKEY hkRebootRequired = NULL;
876 HKEY hkRegistration = NULL;
877
878 LogId(REPORT_STANDARD, MSG_SESSION_END, pRegistration->sczRegistrationKey, LoggingResumeModeToString(resumeMode), LoggingRestartToString(restart), LoggingBoolToString(pRegistration->fDisableResume));
879
880 // If a restart is required for any reason, write a volatile registry key to track of
881 // of that fact until the reboot has taken place.
882 if (BOOTSTRAPPER_APPLY_RESTART_NONE != restart)
883 {
884 // We'll write the volatile registry key right next to the bundle ARP registry key
885 // because that's easy. This is all best effort since the worst case just means in
886 // the rare case the user launches the same install again before taking the restart
887 // the BA won't know a restart was still required.
888 hr = StrAllocFormatted(&sczRebootRequiredKey, REGISTRY_REBOOT_PENDING_FORMAT, pRegistration->sczRegistrationKey);
889 if (SUCCEEDED(hr))
890 {
891 hr = RegCreateEx(pRegistration->hkRoot, sczRebootRequiredKey, KEY_WRITE, TRUE, NULL, &hkRebootRequired, NULL);
892 }
893
894 if (FAILED(hr))
895 {
896 ExitTrace(hr, "Failed to write volatile reboot required registry key.");
897 hr = S_OK;
898 }
899 }
900
901 // If no resume mode, then remove the bundle registration.
902 if (BURN_RESUME_MODE_NONE == resumeMode)
903 {
904 // If we just registered the bundle dependency but something went wrong and caused us to not
905 // keep the bundle registration (like rollback) or we are supposed to unregister the bundle
906 // dependency when unregistering the bundle, do so.
907 if (BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER == dependencyRegistrationAction ||
908 BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER == dependencyRegistrationAction)
909 {
910 // Remove the bundle dependency key.
911 DependencyUnregisterBundle(pRegistration);
912 }
913
914 // Delete update registration key.
915 if (pRegistration->update.fRegisterUpdate)
916 {
917 RemoveUpdateRegistration(pRegistration);
918 }
919
920 RemoveSoftwareTags(pRegistration->fPerMachine, &pRegistration->softwareTags);
921
922 // Delete registration key.
923 hr = RegDelete(pRegistration->hkRoot, pRegistration->sczRegistrationKey, REG_KEY_DEFAULT, FALSE);
924 if (E_FILENOTFOUND != hr)
925 {
926 ExitOnFailure(hr, "Failed to delete registration key: %ls", pRegistration->sczRegistrationKey);
927 }
928
929 CacheRemoveBundle(pRegistration->fPerMachine, pRegistration->sczId);
930 }
931 else // the mode needs to be updated so open the registration key.
932 {
933 // Open registration key.
934 hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_WRITE, &hkRegistration);
935 ExitOnFailure(hr, "Failed to open registration key.");
936 }
937
938 // Update resume mode.
939 hr = UpdateResumeMode(pRegistration, hkRegistration, resumeMode, BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart);
940 ExitOnFailure(hr, "Failed to update resume mode.");
941
942LExit:
943 ReleaseRegKey(hkRegistration);
944 ReleaseRegKey(hkRebootRequired);
945 ReleaseStr(sczRebootRequiredKey);
946
947 return hr;
948}
949
950/*******************************************************************
951 RegistrationSaveState - Saves an engine state BLOB for retreval after a resume.
952
953*******************************************************************/
954extern "C" HRESULT RegistrationSaveState(
955 __in BURN_REGISTRATION* pRegistration,
956 __in_bcount(cbBuffer) BYTE* pbBuffer,
957 __in SIZE_T cbBuffer
958 )
959{
960 HRESULT hr = S_OK;
961
962 // write data to file
963 hr = FileWrite(pRegistration->sczStateFile, FILE_ATTRIBUTE_NORMAL, pbBuffer, cbBuffer, NULL);
964 if (E_PATHNOTFOUND == hr)
965 {
966 // TODO: should we log that the bundle's cache folder was not present so the state file wasn't created either?
967 hr = S_OK;
968 }
969 ExitOnFailure(hr, "Failed to write state to file: %ls", pRegistration->sczStateFile);
970
971LExit:
972 return hr;
973}
974
975/*******************************************************************
976 RegistrationLoadState - Loads a previously stored engine state BLOB.
977
978*******************************************************************/
979extern "C" HRESULT RegistrationLoadState(
980 __in BURN_REGISTRATION* pRegistration,
981 __out_bcount(*pcbBuffer) BYTE** ppbBuffer,
982 __out DWORD* pcbBuffer
983 )
984{
985 // read data from file
986 HRESULT hr = FileRead(ppbBuffer, pcbBuffer, pRegistration->sczStateFile);
987 return hr;
988}
989
990/*******************************************************************
991RegistrationGetResumeCommandLine - Gets the resume command line from the registry
992
993*******************************************************************/
994extern "C" HRESULT RegistrationGetResumeCommandLine(
995 __in const BURN_REGISTRATION* pRegistration,
996 __deref_out_z LPWSTR* psczResumeCommandLine
997 )
998{
999 HRESULT hr = S_OK;
1000 HKEY hkRegistration = NULL;
1001
1002 // open registration key
1003 hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration);
1004 if (SUCCEEDED(hr))
1005 {
1006 hr = RegReadString(hkRegistration, REGISTRY_BUNDLE_RESUME_COMMAND_LINE, psczResumeCommandLine);
1007 }
1008
1009 // Not finding the key or value is okay.
1010 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
1011 {
1012 hr = S_OK;
1013 }
1014
1015 ReleaseRegKey(hkRegistration);
1016
1017 return hr;
1018}
1019
1020
1021// internal helper functions
1022
1023static HRESULT ParseSoftwareTagsFromXml(
1024 __in IXMLDOMNode* pixnRegistrationNode,
1025 __out BURN_SOFTWARE_TAG** prgSoftwareTags,
1026 __out DWORD* pcSoftwareTags
1027 )
1028{
1029 HRESULT hr = S_OK;
1030 IXMLDOMNodeList* pixnNodes = NULL;
1031 IXMLDOMNode* pixnNode = NULL;
1032 DWORD cNodes = 0;
1033
1034 BURN_SOFTWARE_TAG* pSoftwareTags = NULL;
1035 BSTR bstrTagXml = NULL;
1036
1037 // select tag nodes
1038 hr = XmlSelectNodes(pixnRegistrationNode, L"SoftwareTag", &pixnNodes);
1039 ExitOnFailure(hr, "Failed to select software tag nodes.");
1040
1041 // get tag node count
1042 hr = pixnNodes->get_length((long*)&cNodes);
1043 ExitOnFailure(hr, "Failed to get software tag count.");
1044
1045 if (cNodes)
1046 {
1047 pSoftwareTags = (BURN_SOFTWARE_TAG*)MemAlloc(sizeof(BURN_SOFTWARE_TAG) * cNodes, TRUE);
1048 ExitOnNull(pSoftwareTags, hr, E_OUTOFMEMORY, "Failed to allocate memory for software tag structs.");
1049
1050 for (DWORD i = 0; i < cNodes; ++i)
1051 {
1052 BURN_SOFTWARE_TAG* pSoftwareTag = &pSoftwareTags[i];
1053
1054 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
1055 ExitOnFailure(hr, "Failed to get next node.");
1056
1057 hr = XmlGetAttributeEx(pixnNode, L"Filename", &pSoftwareTag->sczFilename);
1058 ExitOnFailure(hr, "Failed to get @Filename.");
1059
1060 hr = XmlGetAttributeEx(pixnNode, L"Regid", &pSoftwareTag->sczRegid);
1061 ExitOnFailure(hr, "Failed to get @Regid.");
1062
1063 hr = XmlGetText(pixnNode, &bstrTagXml);
1064 ExitOnFailure(hr, "Failed to get SoftwareTag text.");
1065
1066 hr = StrAnsiAllocString(&pSoftwareTag->sczTag, bstrTagXml, 0, CP_UTF8);
1067 ExitOnFailure(hr, "Failed to convert SoftwareTag text to UTF-8");
1068
1069 // prepare next iteration
1070 ReleaseNullBSTR(bstrTagXml);
1071 ReleaseNullObject(pixnNode);
1072 }
1073 }
1074
1075 *pcSoftwareTags = cNodes;
1076 *prgSoftwareTags = pSoftwareTags;
1077 pSoftwareTags = NULL;
1078
1079 hr = S_OK;
1080
1081LExit:
1082 ReleaseBSTR(bstrTagXml);
1083 ReleaseObject(pixnNode);
1084 ReleaseObject(pixnNodes);
1085 ReleaseMem(pSoftwareTags);
1086
1087 return hr;
1088}
1089
1090static HRESULT SetPaths(
1091 __in BURN_REGISTRATION* pRegistration
1092 )
1093{
1094 HRESULT hr = S_OK;
1095 LPWSTR sczCacheDirectory = NULL;
1096
1097 // save registration key root
1098 pRegistration->hkRoot = pRegistration->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1099
1100 // build uninstall registry key path
1101 hr = StrAllocFormatted(&pRegistration->sczRegistrationKey, L"%s\\%s", BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, pRegistration->sczId);
1102 ExitOnFailure(hr, "Failed to build uninstall registry key path.");
1103
1104 // build cache directory
1105 hr = CacheGetCompletedPath(pRegistration->fPerMachine, pRegistration->sczId, &sczCacheDirectory);
1106 ExitOnFailure(hr, "Failed to build cache directory.");
1107
1108 // build cached executable path
1109 hr = PathConcat(sczCacheDirectory, pRegistration->sczExecutableName, &pRegistration->sczCacheExecutablePath);
1110 ExitOnFailure(hr, "Failed to build cached executable path.");
1111
1112 // build state file path
1113 hr = StrAllocFormatted(&pRegistration->sczStateFile, L"%s\\state.rsm", sczCacheDirectory);
1114 ExitOnFailure(hr, "Failed to build state file path.");
1115
1116LExit:
1117 ReleaseStr(sczCacheDirectory);
1118 return hr;
1119}
1120
1121static HRESULT GetBundleManufacturer(
1122 __in BURN_REGISTRATION* pRegistration,
1123 __in BURN_VARIABLES* pVariables,
1124 __out LPWSTR* psczBundleManufacturer
1125 )
1126{
1127 HRESULT hr = S_OK;
1128
1129 hr = VariableGetString(pVariables, BURN_BUNDLE_MANUFACTURER, psczBundleManufacturer);
1130 if (E_NOTFOUND == hr)
1131 {
1132 hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_MANUFACTURER, pRegistration->sczPublisher, FALSE);
1133 ExitOnFailure(hr, "Failed to set bundle manufacturer.");
1134
1135 hr = StrAllocString(psczBundleManufacturer, pRegistration->sczPublisher, 0);
1136 }
1137 ExitOnFailure(hr, "Failed to get bundle manufacturer.");
1138
1139LExit:
1140 return hr;
1141}
1142
1143static HRESULT GetBundleName(
1144 __in BURN_REGISTRATION* pRegistration,
1145 __in BURN_VARIABLES* pVariables,
1146 __out LPWSTR* psczBundleName
1147 )
1148{
1149 HRESULT hr = S_OK;
1150
1151 hr = VariableGetString(pVariables, BURN_BUNDLE_NAME, psczBundleName);
1152 if (E_NOTFOUND == hr)
1153 {
1154 hr = VariableSetLiteralString(pVariables, BURN_BUNDLE_NAME, pRegistration->sczDisplayName, FALSE);
1155 ExitOnFailure(hr, "Failed to set bundle name.");
1156
1157 hr = StrAllocString(psczBundleName, pRegistration->sczDisplayName, 0);
1158 }
1159 ExitOnFailure(hr, "Failed to get bundle name.");
1160
1161LExit:
1162 return hr;
1163}
1164
1165static HRESULT UpdateResumeMode(
1166 __in BURN_REGISTRATION* pRegistration,
1167 __in HKEY hkRegistration,
1168 __in BURN_RESUME_MODE resumeMode,
1169 __in BOOL fRestartInitiated
1170 )
1171{
1172 HRESULT hr = S_OK;
1173 DWORD er = ERROR_SUCCESS;
1174 HKEY hkRebootRequired = NULL;
1175 HKEY hkRun = NULL;
1176 LPWSTR sczResumeCommandLine = NULL;
1177 LPCWSTR sczResumeKey = REGISTRY_RUN_ONCE_KEY;
1178 OS_VERSION osv = OS_VERSION_UNKNOWN;
1179 DWORD dwServicePack = 0;
1180
1181 LogId(REPORT_STANDARD, MSG_SESSION_UPDATE, pRegistration->sczRegistrationKey, LoggingResumeModeToString(resumeMode), LoggingBoolToString(fRestartInitiated), LoggingBoolToString(pRegistration->fDisableResume));
1182
1183 // On Windows XP and Server 2003, write the resume information to the Run key
1184 // instead of RunOnce. That avoids the problem that driver installation might
1185 // trigger RunOnce commands to be executed before the reboot.
1186 OsGetVersion(&osv, &dwServicePack);
1187 if (osv < OS_VERSION_VISTA)
1188 {
1189 sczResumeKey = REGISTRY_RUN_KEY;
1190 }
1191
1192 // write resume information
1193 if (hkRegistration)
1194 {
1195 // write Resume value
1196 hr = RegWriteNumber(hkRegistration, L"Resume", (DWORD)resumeMode);
1197 ExitOnFailure(hr, "Failed to write Resume value.");
1198
1199 // Write the Installed value *only* when the mode is ARP. This will tell us
1200 // that the bundle considers itself "installed" on the machine. Note that we
1201 // never change the value to "0" after that. The bundle will be considered
1202 // "uninstalled" when all of the registration is removed.
1203 if (BURN_RESUME_MODE_ARP == resumeMode)
1204 {
1205 // Write Installed value.
1206 hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_INSTALLED, 1);
1207 ExitOnFailure(hr, "Failed to write Installed value.");
1208 }
1209 }
1210
1211 // If the engine is active write the run key so we resume if there is an unexpected
1212 // power loss. Also, if a restart was initiated in the middle of the chain then
1213 // ensure the run key exists (it should since going active would have written it).
1214 // Do not write the run key when embedded since the containing bundle
1215 // is expected to detect for and restart the embedded bundle.
1216 if ((BURN_RESUME_MODE_ACTIVE == resumeMode || fRestartInitiated) && !pRegistration->fDisableResume)
1217 {
1218 // append RunOnce switch
1219 hr = StrAllocFormatted(&sczResumeCommandLine, L"\"%ls\" /%ls", pRegistration->sczCacheExecutablePath, BURN_COMMANDLINE_SWITCH_RUNONCE);
1220 ExitOnFailure(hr, "Failed to format resume command line for RunOnce.");
1221
1222 // write run key
1223 hr = RegCreate(pRegistration->hkRoot, sczResumeKey, KEY_WRITE, &hkRun);
1224 ExitOnFailure(hr, "Failed to create run key.");
1225
1226 hr = RegWriteString(hkRun, pRegistration->sczId, sczResumeCommandLine);
1227 ExitOnFailure(hr, "Failed to write run key value.");
1228
1229 hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_RESUME_COMMAND_LINE, pRegistration->sczResumeCommandLine);
1230 ExitOnFailure(hr, "Failed to write resume command line value.");
1231 }
1232 else // delete run key value
1233 {
1234 hr = RegOpen(pRegistration->hkRoot, sczResumeKey, KEY_WRITE, &hkRun);
1235 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
1236 {
1237 hr = S_OK;
1238 }
1239 else
1240 {
1241 ExitOnWin32Error(er, hr, "Failed to open run key.");
1242
1243 er = ::RegDeleteValueW(hkRun, pRegistration->sczId);
1244 if (ERROR_FILE_NOT_FOUND == er)
1245 {
1246 er = ERROR_SUCCESS;
1247 }
1248 ExitOnWin32Error(er, hr, "Failed to delete run key value.");
1249 }
1250
1251 if (hkRegistration)
1252 {
1253 er = ::RegDeleteValueW(hkRegistration, REGISTRY_BUNDLE_RESUME_COMMAND_LINE);
1254 if (ERROR_FILE_NOT_FOUND == er)
1255 {
1256 er = ERROR_SUCCESS;
1257 }
1258 ExitOnWin32Error(er, hr, "Failed to delete resume command line value.");
1259 }
1260 }
1261
1262LExit:
1263 ReleaseStr(sczResumeCommandLine);
1264 ReleaseRegKey(hkRebootRequired);
1265 ReleaseRegKey(hkRun);
1266
1267 return hr;
1268}
1269
1270static HRESULT ParseRelatedCodes(
1271 __in BURN_REGISTRATION* pRegistration,
1272 __in IXMLDOMNode* pixnBundle
1273 )
1274{
1275 HRESULT hr = S_OK;
1276 IXMLDOMNodeList* pixnNodes = NULL;
1277 IXMLDOMNode* pixnElement = NULL;
1278 LPWSTR sczAction = NULL;
1279 LPWSTR sczId = NULL;
1280 DWORD cElements = 0;
1281
1282 hr = XmlSelectNodes(pixnBundle, L"RelatedBundle", &pixnNodes);
1283 ExitOnFailure(hr, "Failed to get RelatedBundle nodes");
1284
1285 hr = pixnNodes->get_length((long*)&cElements);
1286 ExitOnFailure(hr, "Failed to get RelatedBundle element count.");
1287
1288 for (DWORD i = 0; i < cElements; ++i)
1289 {
1290 hr = XmlNextElement(pixnNodes, &pixnElement, NULL);
1291 ExitOnFailure(hr, "Failed to get next RelatedBundle element.");
1292
1293 hr = XmlGetAttributeEx(pixnElement, L"Action", &sczAction);
1294 ExitOnFailure(hr, "Failed to get @Action.");
1295
1296 hr = XmlGetAttributeEx(pixnElement, L"Id", &sczId);
1297 ExitOnFailure(hr, "Failed to get @Id.");
1298
1299 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Detect", -1))
1300 {
1301 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes + 1, sizeof(LPWSTR), 5);
1302 ExitOnFailure(hr, "Failed to resize Detect code array in registration");
1303
1304 pRegistration->rgsczDetectCodes[pRegistration->cDetectCodes] = sczId;
1305 sczId = NULL;
1306 ++pRegistration->cDetectCodes;
1307 }
1308 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Upgrade", -1))
1309 {
1310 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes + 1, sizeof(LPWSTR), 5);
1311 ExitOnFailure(hr, "Failed to resize Upgrade code array in registration");
1312
1313 pRegistration->rgsczUpgradeCodes[pRegistration->cUpgradeCodes] = sczId;
1314 sczId = NULL;
1315 ++pRegistration->cUpgradeCodes;
1316 }
1317 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Addon", -1))
1318 {
1319 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes + 1, sizeof(LPWSTR), 5);
1320 ExitOnFailure(hr, "Failed to resize Addon code array in registration");
1321
1322 pRegistration->rgsczAddonCodes[pRegistration->cAddonCodes] = sczId;
1323 sczId = NULL;
1324 ++pRegistration->cAddonCodes;
1325 }
1326 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Patch", -1))
1327 {
1328 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes + 1, sizeof(LPWSTR), 5);
1329 ExitOnFailure(hr, "Failed to resize Patch code array in registration");
1330
1331 pRegistration->rgsczPatchCodes[pRegistration->cPatchCodes] = sczId;
1332 sczId = NULL;
1333 ++pRegistration->cPatchCodes;
1334 }
1335 else
1336 {
1337 hr = E_INVALIDARG;
1338 ExitOnFailure(hr, "Invalid value for @Action: %ls", sczAction);
1339 }
1340 }
1341
1342LExit:
1343 ReleaseObject(pixnNodes);
1344 ReleaseObject(pixnElement);
1345 ReleaseStr(sczAction);
1346 ReleaseStr(sczId);
1347
1348 return hr;
1349}
1350
1351static HRESULT FormatUpdateRegistrationKey(
1352 __in BURN_REGISTRATION* pRegistration,
1353 __out_z LPWSTR* psczKey
1354 )
1355{
1356 HRESULT hr = S_OK;
1357 LPWSTR sczKey = NULL;
1358
1359 hr = StrAllocFormatted(&sczKey, L"SOFTWARE\\%ls\\Updates\\", pRegistration->update.sczManufacturer);
1360 ExitOnFailure(hr, "Failed to format the key path for update registration.");
1361
1362 if (pRegistration->update.sczProductFamily)
1363 {
1364 hr = StrAllocFormatted(&sczKey, L"%ls%ls\\", sczKey, pRegistration->update.sczProductFamily);
1365 ExitOnFailure(hr, "Failed to format the key path for update registration.");
1366 }
1367
1368 hr = StrAllocConcat(&sczKey, pRegistration->update.sczName, 0);
1369 ExitOnFailure(hr, "Failed to format the key path for update registration.");
1370
1371 *psczKey = sczKey;
1372 sczKey = NULL;
1373
1374LExit:
1375 ReleaseStr(sczKey);
1376
1377 return hr;
1378}
1379
1380static HRESULT WriteSoftwareTags(
1381 __in BOOL fPerMachine,
1382 __in BURN_SOFTWARE_TAGS* pSoftwareTags
1383 )
1384{
1385 HRESULT hr = S_OK;
1386 LPWSTR sczRootFolder = NULL;
1387 LPWSTR sczRegidFolder = NULL;
1388 LPWSTR sczPath = NULL;
1389
1390 hr = PathGetKnownFolder(fPerMachine ? CSIDL_COMMON_APPDATA : CSIDL_LOCAL_APPDATA, &sczRootFolder);
1391 ExitOnFailure(hr, "Failed to find local %hs appdata directory.", fPerMachine ? "per-machine" : "per-user");
1392
1393 for (DWORD iTag = 0; iTag < pSoftwareTags->cSoftwareTags; ++iTag)
1394 {
1395 BURN_SOFTWARE_TAG* pSoftwareTag = pSoftwareTags->rgSoftwareTags + iTag;
1396
1397 hr = PathConcat(sczRootFolder, pSoftwareTag->sczRegid, &sczRegidFolder);
1398 ExitOnFailure(hr, "Failed to allocate regid folder path.");
1399
1400 hr = PathConcat(sczRegidFolder, pSoftwareTag->sczFilename, &sczPath);
1401 ExitOnFailure(hr, "Failed to allocate regid folder path.");
1402
1403 hr = DirEnsureExists(sczRegidFolder, NULL);
1404 ExitOnFailure(hr, "Failed to create regid folder: %ls", sczRegidFolder);
1405
1406 hr = FileWrite(sczPath, FILE_ATTRIBUTE_NORMAL, reinterpret_cast<LPBYTE>(pSoftwareTag->sczTag), lstrlenA(pSoftwareTag->sczTag), NULL);
1407 ExitOnFailure(hr, "Failed to write tag xml to file: %ls", sczPath);
1408 }
1409
1410LExit:
1411 ReleaseStr(sczPath);
1412 ReleaseStr(sczRegidFolder);
1413 ReleaseStr(sczRootFolder);
1414
1415 return hr;
1416}
1417
1418static HRESULT RemoveSoftwareTags(
1419 __in BOOL fPerMachine,
1420 __in BURN_SOFTWARE_TAGS* pSoftwareTags
1421 )
1422{
1423 HRESULT hr = S_OK;
1424 LPWSTR sczRootFolder = NULL;
1425 LPWSTR sczRegidFolder = NULL;
1426 LPWSTR sczPath = NULL;
1427
1428 hr = PathGetKnownFolder(fPerMachine ? CSIDL_COMMON_APPDATA : CSIDL_LOCAL_APPDATA, &sczRootFolder);
1429 ExitOnFailure(hr, "Failed to find local %hs appdata directory.", fPerMachine ? "per-machine" : "per-user");
1430
1431 for (DWORD iTag = 0; iTag < pSoftwareTags->cSoftwareTags; ++iTag)
1432 {
1433 BURN_SOFTWARE_TAG* pSoftwareTag = pSoftwareTags->rgSoftwareTags + iTag;
1434
1435 hr = PathConcat(sczRootFolder, pSoftwareTag->sczRegid, &sczRegidFolder);
1436 ExitOnFailure(hr, "Failed to allocate regid folder path.");
1437
1438 hr = PathConcat(sczRegidFolder, pSoftwareTag->sczFilename, &sczPath);
1439 ExitOnFailure(hr, "Failed to allocate regid folder path.");
1440
1441 // Best effort to delete the software tag file and the regid folder.
1442 FileEnsureDelete(sczPath);
1443
1444 ::RemoveDirectoryW(sczRegidFolder);
1445 }
1446
1447LExit:
1448 ReleaseStr(sczPath);
1449 ReleaseStr(sczRegidFolder);
1450 ReleaseStr(sczRootFolder);
1451
1452 return hr;
1453}
1454
1455static HRESULT WriteUpdateRegistration(
1456 __in BURN_REGISTRATION* pRegistration,
1457 __in BURN_VARIABLES* pVariables
1458 )
1459{
1460 HRESULT hr = S_OK;
1461 LPWSTR sczKey = NULL;
1462 HKEY hkKey = NULL;
1463
1464 hr = FormatUpdateRegistrationKey(pRegistration, &sczKey);
1465 ExitOnFailure(hr, "Failed to get the formatted key path for update registration.");
1466
1467 hr = RegCreate(pRegistration->hkRoot, sczKey, KEY_WRITE, &hkKey);
1468 ExitOnFailure(hr, "Failed to create the key for update registration.");
1469
1470 hr = RegWriteString(hkKey, L"ThisVersionInstalled", L"Y");
1471 ExitOnFailure(hr, "Failed to write %ls value.", L"ThisVersionInstalled");
1472
1473 hr = RegWriteString(hkKey, L"PackageName", pRegistration->sczDisplayName);
1474 ExitOnFailure(hr, "Failed to write %ls value.", L"PackageName");
1475
1476 hr = RegWriteString(hkKey, L"PackageVersion", pRegistration->sczDisplayVersion);
1477 ExitOnFailure(hr, "Failed to write %ls value.", L"PackageVersion");
1478
1479 hr = RegWriteString(hkKey, L"Publisher", pRegistration->sczPublisher);
1480 ExitOnFailure(hr, "Failed to write %ls value.", L"Publisher");
1481
1482 if (pRegistration->update.sczDepartment)
1483 {
1484 hr = RegWriteString(hkKey, L"PublishingGroup", pRegistration->update.sczDepartment);
1485 ExitOnFailure(hr, "Failed to write %ls value.", L"PublishingGroup");
1486 }
1487
1488 hr = RegWriteString(hkKey, L"ReleaseType", pRegistration->update.sczClassification);
1489 ExitOnFailure(hr, "Failed to write %ls value.", L"ReleaseType");
1490
1491 hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_LOGONUSER, L"InstalledBy");
1492 ExitOnFailure(hr, "Failed to write %ls value.", L"InstalledBy");
1493
1494 hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_DATE, L"InstalledDate");
1495 ExitOnFailure(hr, "Failed to write %ls value.", L"InstalledDate");
1496
1497 hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_INSTALLERNAME, L"InstallerName");
1498 ExitOnFailure(hr, "Failed to write %ls value.", L"InstallerName");
1499
1500 hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_INSTALLERVERSION, L"InstallerVersion");
1501 ExitOnFailure(hr, "Failed to write %ls value.", L"InstallerVersion");
1502
1503LExit:
1504 ReleaseRegKey(hkKey);
1505 ReleaseStr(sczKey);
1506
1507 return hr;
1508}
1509
1510static HRESULT RemoveUpdateRegistration(
1511 __in BURN_REGISTRATION* pRegistration
1512 )
1513{
1514 HRESULT hr = S_OK;
1515 LPWSTR sczKey = NULL;
1516 LPWSTR sczPackageVersion = NULL;
1517 HKEY hkKey = NULL;
1518 BOOL fDeleteRegKey = TRUE;
1519
1520 hr = FormatUpdateRegistrationKey(pRegistration, &sczKey);
1521 ExitOnFailure(hr, "Failed to format key for update registration.");
1522
1523 // Only delete if the uninstalling bundle's PackageVersion is the same as the
1524 // PackageVersion in the update registration key.
1525 // This is to support build to build upgrades
1526 hr = RegOpen(pRegistration->hkRoot, sczKey, KEY_QUERY_VALUE, &hkKey);
1527 if (SUCCEEDED(hr))
1528 {
1529 hr = RegReadString(hkKey, L"PackageVersion", &sczPackageVersion);
1530 if (SUCCEEDED(hr))
1531 {
1532 if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, sczPackageVersion, -1, pRegistration->sczDisplayVersion, -1))
1533 {
1534 fDeleteRegKey = FALSE;
1535 }
1536 }
1537 ReleaseRegKey(hkKey);
1538 }
1539
1540 // Unable to open the key or read the value is okay.
1541 hr = S_OK;
1542
1543 if (fDeleteRegKey)
1544 {
1545 hr = RegDelete(pRegistration->hkRoot, sczKey, REG_KEY_DEFAULT, FALSE);
1546 if (E_FILENOTFOUND != hr)
1547 {
1548 ExitOnFailure(hr, "Failed to remove update registration key: %ls", sczKey);
1549 }
1550 }
1551
1552LExit:
1553 ReleaseStr(sczPackageVersion);
1554 ReleaseStr(sczKey);
1555
1556 return hr;
1557}
1558
1559static HRESULT RegWriteStringVariable(
1560 __in HKEY hk,
1561 __in BURN_VARIABLES* pVariables,
1562 __in LPCWSTR wzVariable,
1563 __in LPCWSTR wzName
1564 )
1565{
1566 HRESULT hr = S_OK;
1567 LPWSTR sczValue = NULL;
1568
1569 hr = VariableGetString(pVariables, wzVariable, &sczValue);
1570 ExitOnFailure(hr, "Failed to get the %ls variable.", wzVariable);
1571
1572 hr = RegWriteString(hk, wzName, sczValue);
1573 ExitOnFailure(hr, "Failed to write %ls value.", wzName);
1574
1575LExit:
1576 StrSecureZeroFreeString(sczValue);
1577
1578 return hr;
1579}
1580
1581static HRESULT UpdateBundleNameRegistration(
1582 __in BURN_REGISTRATION* pRegistration,
1583 __in BURN_VARIABLES* pVariables,
1584 __in HKEY hkRegistration
1585 )
1586{
1587 HRESULT hr = S_OK;
1588 LPWSTR sczDisplayName = NULL;
1589
1590 // DisplayName: provided by UI
1591 hr = GetBundleName(pRegistration, pVariables, &sczDisplayName);
1592 hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME, SUCCEEDED(hr) ? sczDisplayName : pRegistration->sczDisplayName);
1593 ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME);
1594
1595LExit:
1596 ReleaseStr(sczDisplayName);
1597
1598 return hr;
1599}
diff --git a/src/engine/registration.h b/src/engine/registration.h
new file mode 100644
index 00000000..c830a06d
--- /dev/null
+++ b/src/engine/registration.h
@@ -0,0 +1,214 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10enum BURN_MODE;
11enum BURN_DEPENDENCY_REGISTRATION_ACTION;
12struct _BURN_LOGGING;
13typedef _BURN_LOGGING BURN_LOGGING;
14
15// constants
16
17const LPCWSTR BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
18const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH = L"BundleCachePath";
19const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE = L"BundleAddonCode";
20const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE = L"BundleDetectCode";
21const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE = L"BundlePatchCode";
22const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgradeCode";
23const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME = L"DisplayName";
24const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION = L"BundleVersion";
25const LPCWSTR BURN_REGISTRATION_REGISTRY_ENGINE_VERSION = L"EngineVersion";
26const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey";
27const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_TAG = L"BundleTag";
28
29enum BURN_RESUME_MODE
30{
31 BURN_RESUME_MODE_NONE,
32 BURN_RESUME_MODE_ACTIVE,
33 BURN_RESUME_MODE_SUSPEND,
34 BURN_RESUME_MODE_ARP,
35 BURN_RESUME_MODE_REBOOT_PENDING,
36};
37
38enum BURN_REGISTRATION_MODIFY_TYPE
39{
40 BURN_REGISTRATION_MODIFY_ENABLED,
41 BURN_REGISTRATION_MODIFY_DISABLE,
42 BURN_REGISTRATION_MODIFY_DISABLE_BUTTON,
43};
44
45
46// structs
47
48typedef struct _BURN_UPDATE_REGISTRATION
49{
50 BOOL fRegisterUpdate;
51 LPWSTR sczManufacturer;
52 LPWSTR sczDepartment;
53 LPWSTR sczProductFamily;
54 LPWSTR sczName;
55 LPWSTR sczClassification;
56} BURN_UPDATE_REGISTRATION;
57
58typedef struct _BURN_RELATED_BUNDLE
59{
60 BOOTSTRAPPER_RELATION_TYPE relationType;
61
62 DWORD64 qwVersion;
63 LPWSTR sczTag;
64
65 BURN_PACKAGE package;
66} BURN_RELATED_BUNDLE;
67
68typedef struct _BURN_RELATED_BUNDLES
69{
70 BURN_RELATED_BUNDLE* rgRelatedBundles;
71 DWORD cRelatedBundles;
72} BURN_RELATED_BUNDLES;
73
74typedef struct _BURN_SOFTWARE_TAG
75{
76 LPWSTR sczFilename;
77 LPWSTR sczRegid;
78 LPSTR sczTag;
79} BURN_SOFTWARE_TAG;
80
81typedef struct _BURN_SOFTWARE_TAGS
82{
83 BURN_SOFTWARE_TAG* rgSoftwareTags;
84 DWORD cSoftwareTags;
85} BURN_SOFTWARE_TAGS;
86
87typedef struct _BURN_REGISTRATION
88{
89 BOOL fPerMachine;
90 BOOL fRegisterArp;
91 BOOL fDisableResume;
92 BOOL fInstalled;
93 LPWSTR sczId;
94 LPWSTR sczTag;
95
96 LPWSTR *rgsczDetectCodes;
97 DWORD cDetectCodes;
98
99 LPWSTR *rgsczUpgradeCodes;
100 DWORD cUpgradeCodes;
101
102 LPWSTR *rgsczAddonCodes;
103 DWORD cAddonCodes;
104
105 LPWSTR *rgsczPatchCodes;
106 DWORD cPatchCodes;
107
108 DWORD64 qwVersion;
109 LPWSTR sczActiveParent;
110 LPWSTR sczProviderKey;
111 LPWSTR sczExecutableName;
112
113 // paths
114 HKEY hkRoot;
115 LPWSTR sczRegistrationKey;
116 LPWSTR sczCacheExecutablePath;
117 LPWSTR sczResumeCommandLine;
118 LPWSTR sczStateFile;
119
120 // ARP registration
121 LPWSTR sczDisplayName;
122 LPWSTR sczDisplayVersion;
123 LPWSTR sczPublisher;
124 LPWSTR sczHelpLink;
125 LPWSTR sczHelpTelephone;
126 LPWSTR sczAboutUrl;
127 LPWSTR sczUpdateUrl;
128 LPWSTR sczParentDisplayName;
129 LPWSTR sczComments;
130 //LPWSTR sczReadme; // TODO: this would be a file path
131 LPWSTR sczContact;
132 //DWORD64 qwEstimatedSize; // TODO: size should come from disk cost calculation
133 BURN_REGISTRATION_MODIFY_TYPE modify;
134 BOOL fNoRemoveDefined;
135 BOOL fNoRemove;
136
137 BURN_SOFTWARE_TAGS softwareTags;
138
139 // Update registration
140 BURN_UPDATE_REGISTRATION update;
141
142 // Only valid after detect.
143 BURN_RELATED_BUNDLES relatedBundles;
144
145 LPWSTR sczDetectedProviderKeyBundleId;
146 LPWSTR sczAncestors;
147
148 BOOL fEnabledForwardCompatibleBundle;
149 BURN_PACKAGE forwardCompatibleBundle;
150} BURN_REGISTRATION;
151
152
153// functions
154
155HRESULT RegistrationParseFromXml(
156 __in BURN_REGISTRATION* pRegistration,
157 __in IXMLDOMNode* pixnBundle
158 );
159void RegistrationUninitialize(
160 __in BURN_REGISTRATION* pRegistration
161 );
162HRESULT RegistrationSetVariables(
163 __in BURN_REGISTRATION* pRegistration,
164 __in BURN_VARIABLES* pVariables
165 );
166HRESULT RegistrationDetectInstalled(
167 __in BURN_REGISTRATION* pRegistration,
168 __out BOOL* pfInstalled
169 );
170HRESULT RegistrationDetectResumeType(
171 __in BURN_REGISTRATION* pRegistration,
172 __out BOOTSTRAPPER_RESUME_TYPE* pResumeType
173 );
174HRESULT RegistrationDetectRelatedBundles(
175 __in BURN_REGISTRATION* pRegistration
176 );
177HRESULT RegistrationSessionBegin(
178 __in_z LPCWSTR wzEngineWorkingPath,
179 __in BURN_REGISTRATION* pRegistration,
180 __in BURN_VARIABLES* pVariables,
181 __in BURN_USER_EXPERIENCE* pUserExperience,
182 __in DWORD dwRegistrationOptions,
183 __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction,
184 __in DWORD64 qwEstimatedSize
185 );
186HRESULT RegistrationSessionResume(
187 __in BURN_REGISTRATION* pRegistration,
188 __in BURN_VARIABLES* pVariables
189 );
190HRESULT RegistrationSessionEnd(
191 __in BURN_REGISTRATION* pRegistration,
192 __in BURN_RESUME_MODE resumeMode,
193 __in BOOTSTRAPPER_APPLY_RESTART restart,
194 __in BURN_DEPENDENCY_REGISTRATION_ACTION dependencyRegistrationAction
195 );
196HRESULT RegistrationSaveState(
197 __in BURN_REGISTRATION* pRegistration,
198 __in_bcount_opt(cbBuffer) BYTE* pbBuffer,
199 __in_opt DWORD cbBuffer
200 );
201HRESULT RegistrationLoadState(
202 __in BURN_REGISTRATION* pRegistration,
203 __out_bcount(*pcbBuffer) BYTE** ppbBuffer,
204 __out DWORD* pcbBuffer
205 );
206HRESULT RegistrationGetResumeCommandLine(
207 __in const BURN_REGISTRATION* pRegistration,
208 __deref_out_z LPWSTR* psczResumeCommandLine
209 );
210
211
212#if defined(__cplusplus)
213}
214#endif
diff --git a/src/engine/relatedbundle.cpp b/src/engine/relatedbundle.cpp
new file mode 100644
index 00000000..87794177
--- /dev/null
+++ b/src/engine/relatedbundle.cpp
@@ -0,0 +1,457 @@
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#include "precomp.h"
4
5// internal function declarations
6
7static HRESULT LoadIfRelatedBundle(
8 __in BOOL fPerMachine,
9 __in HKEY hkUninstallKey,
10 __in_z LPCWSTR sczRelatedBundleId,
11 __in BURN_REGISTRATION* pRegistration,
12 __in BURN_RELATED_BUNDLES* pRelatedBundles
13 );
14static HRESULT DetermineRelationType(
15 __in HKEY hkBundleId,
16 __in BURN_REGISTRATION* pRegistration,
17 __out BOOTSTRAPPER_RELATION_TYPE* pRelationType
18 );
19static HRESULT LoadRelatedBundleFromKey(
20 __in_z LPCWSTR wzRelatedBundleId,
21 __in HKEY hkBundleId,
22 __in BOOL fPerMachine,
23 __in BOOTSTRAPPER_RELATION_TYPE relationType,
24 __inout BURN_RELATED_BUNDLE *pRelatedBundle
25 );
26
27
28// function definitions
29
30extern "C" HRESULT RelatedBundlesInitializeForScope(
31 __in BOOL fPerMachine,
32 __in BURN_REGISTRATION* pRegistration,
33 __in BURN_RELATED_BUNDLES* pRelatedBundles
34 )
35{
36 HRESULT hr = S_OK;
37 HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
38 HKEY hkUninstallKey = NULL;
39 LPWSTR sczRelatedBundleId = NULL;
40
41 hr = RegOpen(hkRoot, BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, KEY_READ, &hkUninstallKey);
42 if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
43 {
44 ExitFunction1(hr = S_OK);
45 }
46 ExitOnFailure(hr, "Failed to open uninstall registry key.");
47
48 for (DWORD dwIndex = 0; /* exit via break below */; ++dwIndex)
49 {
50 hr = RegKeyEnum(hkUninstallKey, dwIndex, &sczRelatedBundleId);
51 if (E_NOMOREITEMS == hr)
52 {
53 hr = S_OK;
54 break;
55 }
56 ExitOnFailure(hr, "Failed to enumerate uninstall key for related bundles.");
57
58 // If we did not find our bundle id, try to load the subkey as a related bundle.
59 if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczRelatedBundleId, -1, pRegistration->sczId, -1))
60 {
61 // Ignore failures here since we'll often find products that aren't actually
62 // related bundles (or even bundles at all).
63 HRESULT hrRelatedBundle = LoadIfRelatedBundle(fPerMachine, hkUninstallKey, sczRelatedBundleId, pRegistration, pRelatedBundles);
64 UNREFERENCED_PARAMETER(hrRelatedBundle);
65 }
66 }
67
68LExit:
69 ReleaseStr(sczRelatedBundleId);
70 ReleaseRegKey(hkUninstallKey);
71
72 return hr;
73}
74
75extern "C" void RelatedBundlesUninitialize(
76 __in BURN_RELATED_BUNDLES* pRelatedBundles
77 )
78{
79 if (pRelatedBundles->rgRelatedBundles)
80 {
81 for (DWORD i = 0; i < pRelatedBundles->cRelatedBundles; ++i)
82 {
83 PackageUninitialize(&pRelatedBundles->rgRelatedBundles[i].package);
84 ReleaseStr(pRelatedBundles->rgRelatedBundles[i].sczTag);
85 }
86
87 MemFree(pRelatedBundles->rgRelatedBundles);
88 }
89
90 memset(pRelatedBundles, 0, sizeof(BURN_RELATED_BUNDLES));
91}
92
93
94// internal helper functions
95
96static HRESULT LoadIfRelatedBundle(
97 __in BOOL fPerMachine,
98 __in HKEY hkUninstallKey,
99 __in_z LPCWSTR sczRelatedBundleId,
100 __in BURN_REGISTRATION* pRegistration,
101 __in BURN_RELATED_BUNDLES* pRelatedBundles
102 )
103{
104 HRESULT hr = S_OK;
105 HKEY hkBundleId = NULL;
106 BOOTSTRAPPER_RELATION_TYPE relationType = BOOTSTRAPPER_RELATION_NONE;
107
108 hr = RegOpen(hkUninstallKey, sczRelatedBundleId, KEY_READ, &hkBundleId);
109 ExitOnFailure(hr, "Failed to open uninstall key for potential related bundle: %ls", sczRelatedBundleId);
110
111 hr = DetermineRelationType(hkBundleId, pRegistration, &relationType);
112 if (FAILED(hr) || BOOTSTRAPPER_RELATION_NONE == relationType)
113 {
114 // Must not be a related bundle.
115 hr = E_NOTFOUND;
116 }
117 else // load the related bundle.
118 {
119 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRelatedBundles->rgRelatedBundles), pRelatedBundles->cRelatedBundles + 1, sizeof(BURN_RELATED_BUNDLE), 5);
120 ExitOnFailure(hr, "Failed to ensure there is space for related bundles.");
121
122 BURN_RELATED_BUNDLE* pRelatedBundle = pRelatedBundles->rgRelatedBundles + pRelatedBundles->cRelatedBundles;
123
124 hr = LoadRelatedBundleFromKey(sczRelatedBundleId, hkBundleId, fPerMachine, relationType, pRelatedBundle);
125 ExitOnFailure(hr, "Failed to initialize package from related bundle id: %ls", sczRelatedBundleId);
126
127 ++pRelatedBundles->cRelatedBundles;
128 }
129
130LExit:
131 ReleaseRegKey(hkBundleId);
132
133 return hr;
134}
135
136static HRESULT DetermineRelationType(
137 __in HKEY hkBundleId,
138 __in BURN_REGISTRATION* pRegistration,
139 __out BOOTSTRAPPER_RELATION_TYPE* pRelationType
140 )
141{
142 HRESULT hr = S_OK;
143 LPWSTR* rgsczUpgradeCodes = NULL;
144 DWORD cUpgradeCodes = 0;
145 STRINGDICT_HANDLE sdUpgradeCodes = NULL;
146 LPWSTR* rgsczAddonCodes = NULL;
147 DWORD cAddonCodes = 0;
148 STRINGDICT_HANDLE sdAddonCodes = NULL;
149 LPWSTR* rgsczDetectCodes = NULL;
150 DWORD cDetectCodes = 0;
151 STRINGDICT_HANDLE sdDetectCodes = NULL;
152 LPWSTR* rgsczPatchCodes = NULL;
153 DWORD cPatchCodes = 0;
154 STRINGDICT_HANDLE sdPatchCodes = NULL;
155
156 *pRelationType = BOOTSTRAPPER_RELATION_NONE;
157
158 // All remaining operations should treat all related bundles as non-vital.
159 hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes, &cUpgradeCodes);
160 if (HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE) == hr)
161 {
162 TraceError(hr, "Failed to read upgrade codes as REG_MULTI_SZ. Trying again as REG_SZ in case of older bundles.");
163
164 rgsczUpgradeCodes = reinterpret_cast<LPWSTR*>(MemAlloc(sizeof(LPWSTR), TRUE));
165 ExitOnNull(rgsczUpgradeCodes, hr, E_OUTOFMEMORY, "Failed to allocate list for a single upgrade code from older bundle.");
166
167 hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes[0]);
168 if (SUCCEEDED(hr))
169 {
170 cUpgradeCodes = 1;
171 }
172 }
173
174 // Compare upgrade codes.
175 if (SUCCEEDED(hr))
176 {
177 hr = DictCreateStringListFromArray(&sdUpgradeCodes, rgsczUpgradeCodes, cUpgradeCodes, DICT_FLAG_CASEINSENSITIVE);
178 ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "upgrade codes");
179
180 // Upgrade relationship: when their upgrade codes match our upgrade codes.
181 hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes);
182 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
183 {
184 hr = S_OK;
185 }
186 else
187 {
188 ExitOnFailure(hr, "Failed to do array search for upgrade code match.");
189
190 *pRelationType = BOOTSTRAPPER_RELATION_UPGRADE;
191 ExitFunction();
192 }
193
194 // Detect relationship: when their upgrade codes match our detect codes.
195 hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
196 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
197 {
198 hr = S_OK;
199 }
200 else
201 {
202 ExitOnFailure(hr, "Failed to do array search for detect code match.");
203
204 *pRelationType = BOOTSTRAPPER_RELATION_DETECT;
205 ExitFunction();
206 }
207
208 // Dependent relationship: when their upgrade codes match our addon codes.
209 hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes);
210 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
211 {
212 hr = S_OK;
213 }
214 else
215 {
216 ExitOnFailure(hr, "Failed to do array search for addon code match.");
217
218 *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
219 ExitFunction();
220 }
221
222 // Dependent relationship: when their upgrade codes match our patch codes.
223 hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes);
224 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
225 {
226 hr = S_OK;
227 }
228 else
229 {
230 ExitOnFailure(hr, "Failed to do array search for addon code match.");
231
232 *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
233 ExitFunction();
234 }
235
236 ReleaseNullDict(sdUpgradeCodes);
237 ReleaseNullStrArray(rgsczUpgradeCodes, cUpgradeCodes);
238 }
239
240 // Compare addon codes.
241 hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE, &rgsczAddonCodes, &cAddonCodes);
242 if (SUCCEEDED(hr))
243 {
244 hr = DictCreateStringListFromArray(&sdAddonCodes, rgsczAddonCodes, cAddonCodes, DICT_FLAG_CASEINSENSITIVE);
245 ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "addon codes");
246
247 // Addon relationship: when their addon codes match our detect codes.
248 hr = DictCompareStringListToArray(sdAddonCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
249 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
250 {
251 hr = S_OK;
252 }
253 else
254 {
255 ExitOnFailure(hr, "Failed to do array search for addon code match.");
256
257 *pRelationType = BOOTSTRAPPER_RELATION_ADDON;
258 ExitFunction();
259 }
260
261 // Addon relationship: when their addon codes match our upgrade codes.
262 hr = DictCompareStringListToArray(sdAddonCodes, const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes);
263 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
264 {
265 hr = S_OK;
266 }
267 else
268 {
269 ExitOnFailure(hr, "Failed to do array search for addon code match.");
270
271 *pRelationType = BOOTSTRAPPER_RELATION_ADDON;
272 ExitFunction();
273 }
274
275 ReleaseNullDict(sdAddonCodes);
276 ReleaseNullStrArray(rgsczAddonCodes, cAddonCodes);
277 }
278
279 // Compare patch codes.
280 hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE, &rgsczPatchCodes, &cPatchCodes);
281 if (SUCCEEDED(hr))
282 {
283 hr = DictCreateStringListFromArray(&sdPatchCodes, rgsczPatchCodes, cPatchCodes, DICT_FLAG_CASEINSENSITIVE);
284 ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "patch codes");
285
286 // Patch relationship: when their patch codes match our detect codes.
287 hr = DictCompareStringListToArray(sdPatchCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
288 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
289 {
290 hr = S_OK;
291 }
292 else
293 {
294 ExitOnFailure(hr, "Failed to do array search for patch code match.");
295
296 *pRelationType = BOOTSTRAPPER_RELATION_PATCH;
297 ExitFunction();
298 }
299
300 // Patch relationship: when their patch codes match our upgrade codes.
301 hr = DictCompareStringListToArray(sdPatchCodes, const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes);
302 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
303 {
304 hr = S_OK;
305 }
306 else
307 {
308 ExitOnFailure(hr, "Failed to do array search for patch code match.");
309
310 *pRelationType = BOOTSTRAPPER_RELATION_PATCH;
311 ExitFunction();
312 }
313
314 ReleaseNullDict(sdPatchCodes);
315 ReleaseNullStrArray(rgsczPatchCodes, cPatchCodes);
316 }
317
318 // Compare detect codes.
319 hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE, &rgsczDetectCodes, &cDetectCodes);
320 if (SUCCEEDED(hr))
321 {
322 hr = DictCreateStringListFromArray(&sdDetectCodes, rgsczDetectCodes, cDetectCodes, DICT_FLAG_CASEINSENSITIVE);
323 ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "detect codes");
324
325 // Detect relationship: when their detect codes match our detect codes.
326 hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes);
327 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
328 {
329 hr = S_OK;
330 }
331 else
332 {
333 ExitOnFailure(hr, "Failed to do array search for detect code match.");
334
335 *pRelationType = BOOTSTRAPPER_RELATION_DETECT;
336 ExitFunction();
337 }
338
339 // Dependent relationship: when their detect codes match our addon codes.
340 hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes);
341 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
342 {
343 hr = S_OK;
344 }
345 else
346 {
347 ExitOnFailure(hr, "Failed to do array search for addon code match.");
348
349 *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
350 ExitFunction();
351 }
352
353 // Dependent relationship: when their detect codes match our patch codes.
354 hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes);
355 if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr)
356 {
357 hr = S_OK;
358 }
359 else
360 {
361 ExitOnFailure(hr, "Failed to do array search for addon code match.");
362
363 *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT;
364 ExitFunction();
365 }
366
367 ReleaseNullDict(sdDetectCodes);
368 ReleaseNullStrArray(rgsczDetectCodes, cDetectCodes);
369 }
370
371LExit:
372 if (SUCCEEDED(hr) && BOOTSTRAPPER_RELATION_NONE == *pRelationType)
373 {
374 hr = E_NOTFOUND;
375 }
376
377 ReleaseDict(sdUpgradeCodes);
378 ReleaseStrArray(rgsczUpgradeCodes, cUpgradeCodes);
379 ReleaseDict(sdAddonCodes);
380 ReleaseStrArray(rgsczAddonCodes, cAddonCodes);
381 ReleaseDict(sdDetectCodes);
382 ReleaseStrArray(rgsczDetectCodes, cDetectCodes);
383 ReleaseDict(sdPatchCodes);
384 ReleaseStrArray(rgsczPatchCodes, cPatchCodes);
385
386 return hr;
387}
388
389static HRESULT LoadRelatedBundleFromKey(
390 __in_z LPCWSTR wzRelatedBundleId,
391 __in HKEY hkBundleId,
392 __in BOOL fPerMachine,
393 __in BOOTSTRAPPER_RELATION_TYPE relationType,
394 __inout BURN_RELATED_BUNDLE *pRelatedBundle
395 )
396{
397 HRESULT hr = S_OK;
398 DWORD64 qwEngineVersion = 0;
399 LPWSTR sczCachePath = NULL;
400 DWORD64 qwFileSize = 0;
401 BURN_DEPENDENCY_PROVIDER dependencyProvider = { };
402
403 hr = RegReadVersion(hkBundleId, BURN_REGISTRATION_REGISTRY_ENGINE_VERSION, &qwEngineVersion);
404 if (FAILED(hr))
405 {
406 qwEngineVersion = 0;
407 hr = S_OK;
408 }
409
410 hr = RegReadVersion(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, &pRelatedBundle->qwVersion);
411 ExitOnFailure(hr, "Failed to read version from registry for bundle: %ls", wzRelatedBundleId);
412
413 hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH, &sczCachePath);
414 ExitOnFailure(hr, "Failed to read cache path from registry for bundle: %ls", wzRelatedBundleId);
415
416 hr = FileSize(sczCachePath, reinterpret_cast<LONGLONG *>(&qwFileSize));
417 ExitOnFailure(hr, "Failed to get size of pseudo bundle: %ls", sczCachePath);
418
419 hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY, &dependencyProvider.sczKey);
420 if (E_FILENOTFOUND != hr)
421 {
422 ExitOnFailure(hr, "Failed to read provider key from registry for bundle: %ls", wzRelatedBundleId);
423
424 dependencyProvider.fImported = TRUE;
425
426 hr = FileVersionToStringEx(pRelatedBundle->qwVersion, &dependencyProvider.sczVersion);
427 ExitOnFailure(hr, "Failed to copy version for bundle: %ls", wzRelatedBundleId);
428
429 hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME, &dependencyProvider.sczDisplayName);
430 if (E_FILENOTFOUND != hr)
431 {
432 ExitOnFailure(hr, "Failed to copy display name for bundle: %ls", wzRelatedBundleId);
433 }
434 }
435
436 hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_TAG, &pRelatedBundle->sczTag);
437 if (E_FILENOTFOUND == hr)
438 {
439 hr = S_OK;
440 }
441 ExitOnFailure(hr, "Failed to read tag from registry for bundle: %ls", wzRelatedBundleId);
442
443 pRelatedBundle->relationType = relationType;
444
445 hr = PseudoBundleInitialize(qwEngineVersion, &pRelatedBundle->package, fPerMachine, wzRelatedBundleId, pRelatedBundle->relationType,
446 BOOTSTRAPPER_PACKAGE_STATE_PRESENT, sczCachePath, sczCachePath, NULL, qwFileSize, FALSE,
447 L"-quiet", L"-repair -quiet", L"-uninstall -quiet",
448 (dependencyProvider.sczKey && *dependencyProvider.sczKey) ? &dependencyProvider : NULL,
449 NULL, 0);
450 ExitOnFailure(hr, "Failed to initialize related bundle to represent bundle: %ls", wzRelatedBundleId);
451
452LExit:
453 DependencyUninitialize(&dependencyProvider);
454 ReleaseStr(sczCachePath);
455
456 return hr;
457}
diff --git a/src/engine/relatedbundle.h b/src/engine/relatedbundle.h
new file mode 100644
index 00000000..01691c25
--- /dev/null
+++ b/src/engine/relatedbundle.h
@@ -0,0 +1,20 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9HRESULT RelatedBundlesInitializeForScope(
10 __in BOOL fPerMachine,
11 __in BURN_REGISTRATION* pRegistration,
12 __in BURN_RELATED_BUNDLES* pRelatedBundles
13 );
14void RelatedBundlesUninitialize(
15 __in BURN_RELATED_BUNDLES* pRelatedBundles
16 );
17
18#if defined(__cplusplus)
19}
20#endif
diff --git a/src/engine/search.cpp b/src/engine/search.cpp
new file mode 100644
index 00000000..c50790fd
--- /dev/null
+++ b/src/engine/search.cpp
@@ -0,0 +1,1195 @@
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#include "precomp.h"
4
5
6// internal function declarations
7
8static HRESULT DirectorySearchExists(
9 __in BURN_SEARCH* pSearch,
10 __in BURN_VARIABLES* pVariables
11 );
12static HRESULT DirectorySearchPath(
13 __in BURN_SEARCH* pSearch,
14 __in BURN_VARIABLES* pVariables
15 );
16static HRESULT FileSearchExists(
17 __in BURN_SEARCH* pSearch,
18 __in BURN_VARIABLES* pVariables
19 );
20static HRESULT FileSearchVersion(
21 __in BURN_SEARCH* pSearch,
22 __in BURN_VARIABLES* pVariables
23 );
24static HRESULT FileSearchPath(
25 __in BURN_SEARCH* pSearch,
26 __in BURN_VARIABLES* pVariables
27 );
28static HRESULT RegistrySearchExists(
29 __in BURN_SEARCH* pSearch,
30 __in BURN_VARIABLES* pVariables
31 );
32static HRESULT RegistrySearchValue(
33 __in BURN_SEARCH* pSearch,
34 __in BURN_VARIABLES* pVariables
35 );
36static HRESULT MsiComponentSearch(
37 __in BURN_SEARCH* pSearch,
38 __in BURN_VARIABLES* pVariables
39 );
40static HRESULT MsiProductSearch(
41 __in BURN_SEARCH* pSearch,
42 __in BURN_VARIABLES* pVariables
43 );
44static HRESULT MsiFeatureSearch(
45 __in BURN_SEARCH* pSearch,
46 __in BURN_VARIABLES* pVariables
47 );
48
49
50// function definitions
51
52extern "C" HRESULT SearchesParseFromXml(
53 __in BURN_SEARCHES* pSearches,
54 __in IXMLDOMNode* pixnBundle
55 )
56{
57 HRESULT hr = S_OK;
58 IXMLDOMNodeList* pixnNodes = NULL;
59 IXMLDOMNode* pixnNode = NULL;
60 DWORD cNodes = 0;
61 BSTR bstrNodeName = NULL;
62 LPWSTR scz = NULL;
63
64 // select search nodes
65 hr = XmlSelectNodes(pixnBundle, L"DirectorySearch|FileSearch|RegistrySearch|MsiComponentSearch|MsiProductSearch|MsiFeatureSearch", &pixnNodes);
66 ExitOnFailure(hr, "Failed to select search nodes.");
67
68 // get search node count
69 hr = pixnNodes->get_length((long*)&cNodes);
70 ExitOnFailure(hr, "Failed to get search node count.");
71
72 if (!cNodes)
73 {
74 ExitFunction();
75 }
76
77 // allocate memory for searches
78 pSearches->rgSearches = (BURN_SEARCH*)MemAlloc(sizeof(BURN_SEARCH) * cNodes, TRUE);
79 ExitOnNull(pSearches->rgSearches, hr, E_OUTOFMEMORY, "Failed to allocate memory for search structs.");
80
81 pSearches->cSearches = cNodes;
82
83 // parse search elements
84 for (DWORD i = 0; i < cNodes; ++i)
85 {
86 BURN_SEARCH* pSearch = &pSearches->rgSearches[i];
87
88 hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName);
89 ExitOnFailure(hr, "Failed to get next node.");
90
91 // @Id
92 hr = XmlGetAttributeEx(pixnNode, L"Id", &pSearch->sczKey);
93 ExitOnFailure(hr, "Failed to get @Id.");
94
95 // @Variable
96 hr = XmlGetAttributeEx(pixnNode, L"Variable", &pSearch->sczVariable);
97 ExitOnFailure(hr, "Failed to get @Variable.");
98
99 // @Condition
100 hr = XmlGetAttributeEx(pixnNode, L"Condition", &pSearch->sczCondition);
101 if (E_NOTFOUND != hr)
102 {
103 ExitOnFailure(hr, "Failed to get @Condition.");
104 }
105
106 // read type specific attributes
107 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"DirectorySearch", -1))
108 {
109 pSearch->Type = BURN_SEARCH_TYPE_DIRECTORY;
110
111 // @Path
112 hr = XmlGetAttributeEx(pixnNode, L"Path", &pSearch->DirectorySearch.sczPath);
113 ExitOnFailure(hr, "Failed to get @Path.");
114
115 // @Type
116 hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
117 ExitOnFailure(hr, "Failed to get @Type.");
118
119 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"exists", -1))
120 {
121 pSearch->DirectorySearch.Type = BURN_DIRECTORY_SEARCH_TYPE_EXISTS;
122 }
123 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"path", -1))
124 {
125 pSearch->DirectorySearch.Type = BURN_DIRECTORY_SEARCH_TYPE_PATH;
126 }
127 else
128 {
129 hr = E_INVALIDARG;
130 ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
131 }
132 }
133 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"FileSearch", -1))
134 {
135 pSearch->Type = BURN_SEARCH_TYPE_FILE;
136
137 // @Path
138 hr = XmlGetAttributeEx(pixnNode, L"Path", &pSearch->FileSearch.sczPath);
139 ExitOnFailure(hr, "Failed to get @Path.");
140
141 // @Type
142 hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
143 ExitOnFailure(hr, "Failed to get @Type.");
144
145 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"exists", -1))
146 {
147 pSearch->FileSearch.Type = BURN_FILE_SEARCH_TYPE_EXISTS;
148 }
149 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1))
150 {
151 pSearch->FileSearch.Type = BURN_FILE_SEARCH_TYPE_VERSION;
152 }
153 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"path", -1))
154 {
155 pSearch->FileSearch.Type = BURN_FILE_SEARCH_TYPE_PATH;
156 }
157 else
158 {
159 hr = E_INVALIDARG;
160 ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
161 }
162 }
163 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"RegistrySearch", -1))
164 {
165 pSearch->Type = BURN_SEARCH_TYPE_REGISTRY;
166
167 // @Root
168 hr = XmlGetAttributeEx(pixnNode, L"Root", &scz);
169 ExitOnFailure(hr, "Failed to get @Root.");
170
171 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKCR", -1))
172 {
173 pSearch->RegistrySearch.hRoot = HKEY_CLASSES_ROOT;
174 }
175 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKCU", -1))
176 {
177 pSearch->RegistrySearch.hRoot = HKEY_CURRENT_USER;
178 }
179 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKLM", -1))
180 {
181 pSearch->RegistrySearch.hRoot = HKEY_LOCAL_MACHINE;
182 }
183 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"HKU", -1))
184 {
185 pSearch->RegistrySearch.hRoot = HKEY_USERS;
186 }
187 else
188 {
189 hr = E_INVALIDARG;
190 ExitOnFailure(hr, "Invalid value for @Root: %ls", scz);
191 }
192
193 // @Key
194 hr = XmlGetAttributeEx(pixnNode, L"Key", &pSearch->RegistrySearch.sczKey);
195 ExitOnFailure(hr, "Failed to get Key attribute.");
196
197 // @Value
198 hr = XmlGetAttributeEx(pixnNode, L"Value", &pSearch->RegistrySearch.sczValue);
199 if (E_NOTFOUND != hr)
200 {
201 ExitOnFailure(hr, "Failed to get Value attribute.");
202 }
203
204 // @Type
205 hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
206 ExitOnFailure(hr, "Failed to get @Type.");
207
208 hr = XmlGetYesNoAttribute(pixnNode, L"Win64", &pSearch->RegistrySearch.fWin64);
209 if (E_NOTFOUND != hr)
210 {
211 ExitOnFailure(hr, "Failed to get Win64 attribute.");
212 }
213
214 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"exists", -1))
215 {
216 pSearch->RegistrySearch.Type = BURN_REGISTRY_SEARCH_TYPE_EXISTS;
217 }
218 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"value", -1))
219 {
220 pSearch->RegistrySearch.Type = BURN_REGISTRY_SEARCH_TYPE_VALUE;
221
222 // @ExpandEnvironment
223 hr = XmlGetYesNoAttribute(pixnNode, L"ExpandEnvironment", &pSearch->RegistrySearch.fExpandEnvironment);
224 if (E_NOTFOUND != hr)
225 {
226 ExitOnFailure(hr, "Failed to get @ExpandEnvironment.");
227 }
228
229 // @VariableType
230 hr = XmlGetAttributeEx(pixnNode, L"VariableType", &scz);
231 ExitOnFailure(hr, "Failed to get @VariableType.");
232
233 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1))
234 {
235 pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_NUMERIC;
236 }
237 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"string", -1))
238 {
239 pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_STRING;
240 }
241 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1))
242 {
243 pSearch->RegistrySearch.VariableType = BURN_VARIANT_TYPE_VERSION;
244 }
245 else
246 {
247 hr = E_INVALIDARG;
248 ExitOnFailure(hr, "Invalid value for @VariableType: %ls", scz);
249 }
250 }
251 else
252 {
253 hr = E_INVALIDARG;
254 ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
255 }
256 }
257 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiComponentSearch", -1))
258 {
259 pSearch->Type = BURN_SEARCH_TYPE_MSI_COMPONENT;
260
261 // @ProductCode
262 hr = XmlGetAttributeEx(pixnNode, L"ProductCode", &pSearch->MsiComponentSearch.sczProductCode);
263 if (E_NOTFOUND != hr)
264 {
265 ExitOnFailure(hr, "Failed to get @ProductCode.");
266 }
267
268 // @ComponentId
269 hr = XmlGetAttributeEx(pixnNode, L"ComponentId", &pSearch->MsiComponentSearch.sczComponentId);
270 ExitOnFailure(hr, "Failed to get @ComponentId.");
271
272 // @Type
273 hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
274 ExitOnFailure(hr, "Failed to get @Type.");
275
276 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"keyPath", -1))
277 {
278 pSearch->MsiComponentSearch.Type = BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH;
279 }
280 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"state", -1))
281 {
282 pSearch->MsiComponentSearch.Type = BURN_MSI_COMPONENT_SEARCH_TYPE_STATE;
283 }
284 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"directory", -1))
285 {
286 pSearch->MsiComponentSearch.Type = BURN_MSI_COMPONENT_SEARCH_TYPE_DIRECTORY;
287 }
288 else
289 {
290 hr = E_INVALIDARG;
291 ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
292 }
293 }
294 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiProductSearch", -1))
295 {
296 pSearch->Type = BURN_SEARCH_TYPE_MSI_PRODUCT;
297 pSearch->MsiProductSearch.GuidType = BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_NONE;
298
299 // @ProductCode (if we don't find a product code then look for an upgrade code)
300 hr = XmlGetAttributeEx(pixnNode, L"ProductCode", &pSearch->MsiProductSearch.sczGuid);
301 if (E_NOTFOUND != hr)
302 {
303 ExitOnFailure(hr, "Failed to get @ProductCode.");
304 pSearch->MsiProductSearch.GuidType = BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_PRODUCTCODE;
305 }
306 else
307 {
308 // @UpgradeCode
309 hr = XmlGetAttributeEx(pixnNode, L"UpgradeCode", &pSearch->MsiProductSearch.sczGuid);
310 if (E_NOTFOUND != hr)
311 {
312 ExitOnFailure(hr, "Failed to get @UpgradeCode.");
313 pSearch->MsiProductSearch.GuidType = BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_UPGRADECODE;
314 }
315 }
316
317 // make sure we found either a product or upgrade code
318 if (BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_NONE == pSearch->MsiProductSearch.GuidType)
319 {
320 hr = E_NOTFOUND;
321 ExitOnFailure(hr, "Failed to get @ProductCode or @UpgradeCode.");
322 }
323
324 // @Type
325 hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
326 ExitOnFailure(hr, "Failed to get @Type.");
327
328 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1))
329 {
330 pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION;
331 }
332 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"language", -1))
333 {
334 pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE;
335 }
336 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"state", -1))
337 {
338 pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_STATE;
339 }
340 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"assignment", -1))
341 {
342 pSearch->MsiProductSearch.Type = BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT;
343 }
344 else
345 {
346 hr = E_INVALIDARG;
347 ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
348 }
349 }
350 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiFeatureSearch", -1))
351 {
352 pSearch->Type = BURN_SEARCH_TYPE_MSI_FEATURE;
353
354 // @ProductCode
355 hr = XmlGetAttributeEx(pixnNode, L"ProductCode", &pSearch->MsiFeatureSearch.sczProductCode);
356 ExitOnFailure(hr, "Failed to get @ProductCode.");
357
358 // @FeatureId
359 hr = XmlGetAttributeEx(pixnNode, L"FeatureId", &pSearch->MsiFeatureSearch.sczFeatureId);
360 ExitOnFailure(hr, "Failed to get @FeatureId.");
361
362 // @Type
363 hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
364 ExitOnFailure(hr, "Failed to get @Type.");
365
366 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"state", -1))
367 {
368 pSearch->MsiFeatureSearch.Type = BURN_MSI_FEATURE_SEARCH_TYPE_STATE;
369 }
370 else
371 {
372 hr = E_INVALIDARG;
373 ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
374 }
375 }
376 else
377 {
378 hr = E_UNEXPECTED;
379 ExitOnFailure(hr, "Unexpected element name: %ls", bstrNodeName);
380 }
381
382 // prepare next iteration
383 ReleaseNullObject(pixnNode);
384 ReleaseNullBSTR(bstrNodeName);
385 }
386
387 hr = S_OK;
388
389LExit:
390 ReleaseObject(pixnNodes);
391 ReleaseObject(pixnNode);
392 ReleaseBSTR(bstrNodeName);
393 ReleaseStr(scz);
394 return hr;
395}
396
397extern "C" HRESULT SearchesExecute(
398 __in BURN_SEARCHES* pSearches,
399 __in BURN_VARIABLES* pVariables
400 )
401{
402 HRESULT hr = S_OK;
403 BOOL f = FALSE;
404
405 for (DWORD i = 0; i < pSearches->cSearches; ++i)
406 {
407 BURN_SEARCH* pSearch = &pSearches->rgSearches[i];
408
409 // evaluate condition
410 if (pSearch->sczCondition && *pSearch->sczCondition)
411 {
412 hr = ConditionEvaluate(pVariables, pSearch->sczCondition, &f);
413 if (E_INVALIDDATA == hr)
414 {
415 TraceError(hr, "Failed to parse search condition. Id = '%ls', Condition = '%ls'", pSearch->sczKey, pSearch->sczCondition);
416 hr = S_OK;
417 continue;
418 }
419 ExitOnFailure(hr, "Failed to evaluate search condition. Id = '%ls', Condition = '%ls'", pSearch->sczKey, pSearch->sczCondition);
420
421 if (!f)
422 {
423 continue; // condition evaluated to false, skip
424 }
425 }
426
427 switch (pSearch->Type)
428 {
429 case BURN_SEARCH_TYPE_DIRECTORY:
430 switch (pSearch->DirectorySearch.Type)
431 {
432 case BURN_DIRECTORY_SEARCH_TYPE_EXISTS:
433 hr = DirectorySearchExists(pSearch, pVariables);
434 break;
435 case BURN_DIRECTORY_SEARCH_TYPE_PATH:
436 hr = DirectorySearchPath(pSearch, pVariables);
437 break;
438 default:
439 hr = E_UNEXPECTED;
440 }
441 break;
442 case BURN_SEARCH_TYPE_FILE:
443 switch (pSearch->FileSearch.Type)
444 {
445 case BURN_FILE_SEARCH_TYPE_EXISTS:
446 hr = FileSearchExists(pSearch, pVariables);
447 break;
448 case BURN_FILE_SEARCH_TYPE_VERSION:
449 hr = FileSearchVersion(pSearch, pVariables);
450 break;
451 case BURN_FILE_SEARCH_TYPE_PATH:
452 hr = FileSearchPath(pSearch, pVariables);
453 break;
454 default:
455 hr = E_UNEXPECTED;
456 }
457 break;
458 case BURN_SEARCH_TYPE_REGISTRY:
459 switch (pSearch->RegistrySearch.Type)
460 {
461 case BURN_REGISTRY_SEARCH_TYPE_EXISTS:
462 hr = RegistrySearchExists(pSearch, pVariables);
463 break;
464 case BURN_REGISTRY_SEARCH_TYPE_VALUE:
465 hr = RegistrySearchValue(pSearch, pVariables);
466 break;
467 default:
468 hr = E_UNEXPECTED;
469 }
470 break;
471 case BURN_SEARCH_TYPE_MSI_COMPONENT:
472 hr = MsiComponentSearch(pSearch, pVariables);
473 break;
474 case BURN_SEARCH_TYPE_MSI_PRODUCT:
475 hr = MsiProductSearch(pSearch, pVariables);
476 break;
477 case BURN_SEARCH_TYPE_MSI_FEATURE:
478 hr = MsiFeatureSearch(pSearch, pVariables);
479 break;
480 default:
481 hr = E_UNEXPECTED;
482 }
483
484 if (FAILED(hr))
485 {
486 TraceError(hr, "Search failed. Id = '%ls'", pSearch->sczKey);
487 continue;
488 }
489 }
490
491 hr = S_OK;
492
493LExit:
494 return hr;
495}
496
497extern "C" void SearchesUninitialize(
498 __in BURN_SEARCHES* pSearches
499 )
500{
501 if (pSearches->rgSearches)
502 {
503 for (DWORD i = 0; i < pSearches->cSearches; ++i)
504 {
505 BURN_SEARCH* pSearch = &pSearches->rgSearches[i];
506
507 ReleaseStr(pSearch->sczKey);
508 ReleaseStr(pSearch->sczVariable);
509 ReleaseStr(pSearch->sczCondition);
510
511 switch (pSearch->Type)
512 {
513 case BURN_SEARCH_TYPE_DIRECTORY:
514 ReleaseStr(pSearch->DirectorySearch.sczPath);
515 break;
516 case BURN_SEARCH_TYPE_FILE:
517 ReleaseStr(pSearch->FileSearch.sczPath);
518 break;
519 case BURN_SEARCH_TYPE_REGISTRY:
520 ReleaseStr(pSearch->RegistrySearch.sczKey);
521 ReleaseStr(pSearch->RegistrySearch.sczValue);
522 break;
523 case BURN_SEARCH_TYPE_MSI_COMPONENT:
524 ReleaseStr(pSearch->MsiComponentSearch.sczProductCode);
525 ReleaseStr(pSearch->MsiComponentSearch.sczComponentId);
526 break;
527 case BURN_SEARCH_TYPE_MSI_PRODUCT:
528 ReleaseStr(pSearch->MsiProductSearch.sczGuid);
529 break;
530 case BURN_SEARCH_TYPE_MSI_FEATURE:
531 ReleaseStr(pSearch->MsiFeatureSearch.sczProductCode);
532 ReleaseStr(pSearch->MsiFeatureSearch.sczFeatureId);
533 break;
534 }
535 }
536 MemFree(pSearches->rgSearches);
537 }
538}
539
540
541// internal function definitions
542
543static HRESULT DirectorySearchExists(
544 __in BURN_SEARCH* pSearch,
545 __in BURN_VARIABLES* pVariables
546 )
547{
548 HRESULT hr = S_OK;
549 LPWSTR sczPath = NULL;
550 BOOL fExists = FALSE;
551
552 // format path
553 hr = VariableFormatString(pVariables, pSearch->DirectorySearch.sczPath, &sczPath, NULL);
554 ExitOnFailure(hr, "Failed to format variable string.");
555
556 DWORD dwAttributes = ::GetFileAttributesW(sczPath);
557 if (INVALID_FILE_ATTRIBUTES == dwAttributes)
558 {
559 hr = HRESULT_FROM_WIN32(::GetLastError());
560 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
561 {
562 hr = S_OK; // didn't find file, fExists still is false.
563 }
564 }
565 else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)
566 {
567 fExists = TRUE;
568 }
569
570 // else must have found a file.
571 // What if there is a hidden variable in sczPath?
572 ExitOnFailure(hr, "Failed while searching directory search: %ls, for path: %ls", pSearch->sczKey, sczPath);
573
574 // set variable
575 hr = VariableSetNumeric(pVariables, pSearch->sczVariable, fExists, FALSE);
576 ExitOnFailure(hr, "Failed to set variable.");
577
578LExit:
579 StrSecureZeroFreeString(sczPath);
580
581 return hr;
582}
583
584static HRESULT DirectorySearchPath(
585 __in BURN_SEARCH* pSearch,
586 __in BURN_VARIABLES* pVariables
587 )
588{
589 HRESULT hr = S_OK;
590 LPWSTR sczPath = NULL;
591
592 // format path
593 hr = VariableFormatString(pVariables, pSearch->DirectorySearch.sczPath, &sczPath, NULL);
594 ExitOnFailure(hr, "Failed to format variable string.");
595
596 DWORD dwAttributes = ::GetFileAttributesW(sczPath);
597 if (INVALID_FILE_ATTRIBUTES == dwAttributes)
598 {
599 hr = HRESULT_FROM_WIN32(::GetLastError());
600 }
601 else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)
602 {
603 hr = VariableSetLiteralString(pVariables, pSearch->sczVariable, sczPath, FALSE);
604 ExitOnFailure(hr, "Failed to set directory search path variable.");
605 }
606 else // must have found a file.
607 {
608 hr = E_PATHNOTFOUND;
609 }
610
611 // What if there is a hidden variable in sczPath?
612 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
613 {
614 LogStringLine(REPORT_STANDARD, "Directory search: %ls, did not find path: %ls, reason: 0x%x", pSearch->sczKey, sczPath, hr);
615 ExitFunction1(hr = S_OK);
616 }
617 ExitOnFailure(hr, "Failed while searching directory search: %ls, for path: %ls", pSearch->sczKey, sczPath);
618
619LExit:
620 StrSecureZeroFreeString(sczPath);
621
622 return hr;
623}
624
625static HRESULT FileSearchExists(
626 __in BURN_SEARCH* pSearch,
627 __in BURN_VARIABLES* pVariables
628 )
629{
630 HRESULT hr = S_OK;
631 DWORD er = ERROR_SUCCESS;
632 LPWSTR sczPath = NULL;
633 BOOL fExists = FALSE;
634
635 // format path
636 hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL);
637 ExitOnFailure(hr, "Failed to format variable string.");
638
639 // find file
640 DWORD dwAttributes = ::GetFileAttributesW(sczPath);
641 if (INVALID_FILE_ATTRIBUTES == dwAttributes)
642 {
643 er = ::GetLastError();
644 if (ERROR_FILE_NOT_FOUND == er || ERROR_PATH_NOT_FOUND == er)
645 {
646 // What if there is a hidden variable in sczPath?
647 LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath);
648 }
649 else
650 {
651 ExitOnWin32Error(er, hr, "Failed get to file attributes. '%ls'", pSearch->DirectorySearch.sczPath);
652 }
653 }
654 else if (FILE_ATTRIBUTE_DIRECTORY != (dwAttributes & FILE_ATTRIBUTE_DIRECTORY))
655 {
656 fExists = TRUE;
657 }
658
659 // set variable
660 hr = VariableSetNumeric(pVariables, pSearch->sczVariable, fExists, FALSE);
661 ExitOnFailure(hr, "Failed to set variable.");
662
663LExit:
664 StrSecureZeroFreeString(sczPath);
665 return hr;
666}
667
668static HRESULT FileSearchVersion(
669 __in BURN_SEARCH* pSearch,
670 __in BURN_VARIABLES* pVariables
671 )
672{
673 HRESULT hr = S_OK;
674 ULARGE_INTEGER uliVersion = { };
675 LPWSTR sczPath = NULL;
676
677 // format path
678 hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL);
679 ExitOnFailure(hr, "Failed to format path string.");
680
681 // get file version
682 hr = FileVersion(sczPath, &uliVersion.HighPart, &uliVersion.LowPart);
683 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
684 {
685 // What if there is a hidden variable in sczPath?
686 LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath);
687 ExitFunction1(hr = S_OK);
688 }
689 ExitOnFailure(hr, "Failed get file version.");
690
691 // set variable
692 hr = VariableSetVersion(pVariables, pSearch->sczVariable, uliVersion.QuadPart, FALSE);
693 ExitOnFailure(hr, "Failed to set variable.");
694
695LExit:
696 StrSecureZeroFreeString(sczPath);
697 return hr;
698}
699
700static HRESULT FileSearchPath(
701 __in BURN_SEARCH* pSearch,
702 __in BURN_VARIABLES* pVariables
703 )
704{
705 HRESULT hr = S_OK;
706 LPWSTR sczPath = NULL;
707
708 // format path
709 hr = VariableFormatString(pVariables, pSearch->FileSearch.sczPath, &sczPath, NULL);
710 ExitOnFailure(hr, "Failed to format variable string.");
711
712 DWORD dwAttributes = ::GetFileAttributesW(sczPath);
713 if (INVALID_FILE_ATTRIBUTES == dwAttributes)
714 {
715 hr = HRESULT_FROM_WIN32(::GetLastError());
716 }
717 else if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) // found a directory.
718 {
719 hr = E_FILENOTFOUND;
720 }
721 else // found our file.
722 {
723 hr = VariableSetLiteralString(pVariables, pSearch->sczVariable, sczPath, FALSE);
724 ExitOnFailure(hr, "Failed to set variable to file search path.");
725 }
726
727 // What if there is a hidden variable in sczPath?
728 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
729 {
730 LogStringLine(REPORT_STANDARD, "File search: %ls, did not find path: %ls", pSearch->sczKey, sczPath);
731 ExitFunction1(hr = S_OK);
732 }
733 ExitOnFailure(hr, "Failed while searching file search: %ls, for path: %ls", pSearch->sczKey, sczPath);
734
735LExit:
736 StrSecureZeroFreeString(sczPath);
737
738 return hr;
739}
740
741static HRESULT RegistrySearchExists(
742 __in BURN_SEARCH* pSearch,
743 __in BURN_VARIABLES* pVariables
744 )
745{
746 HRESULT hr = S_OK;
747 DWORD er = ERROR_SUCCESS;
748 LPWSTR sczKey = NULL;
749 LPWSTR sczValue = NULL;
750 HKEY hKey = NULL;
751 DWORD dwType = 0;
752 BOOL fExists = FALSE;
753 REGSAM samDesired = KEY_QUERY_VALUE;
754
755 if (pSearch->RegistrySearch.fWin64)
756 {
757 samDesired = samDesired | KEY_WOW64_64KEY;
758 }
759
760 // format key string
761 hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczKey, &sczKey, NULL);
762 ExitOnFailure(hr, "Failed to format key string.");
763
764 // open key
765 hr = RegOpen(pSearch->RegistrySearch.hRoot, sczKey, samDesired, &hKey);
766 if (SUCCEEDED(hr))
767 {
768 fExists = TRUE;
769 }
770 else if (E_FILENOTFOUND == hr)
771 {
772 // What if there is a hidden variable in sczKey?
773 LogStringLine(REPORT_STANDARD, "Registry key not found. Key = '%ls'", sczKey);
774 fExists = FALSE;
775 hr = S_OK;
776 }
777 else
778 {
779 // What if there is a hidden variable in sczKey?
780 ExitOnFailure(hr, "Failed to open registry key. Key = '%ls'", sczKey);
781 }
782
783 if (fExists && pSearch->RegistrySearch.sczValue)
784 {
785 // format value string
786 hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczValue, &sczValue, NULL);
787 ExitOnFailure(hr, "Failed to format value string.");
788
789 // query value
790 er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, NULL, NULL);
791 switch (er)
792 {
793 case ERROR_SUCCESS:
794 fExists = TRUE;
795 break;
796 case ERROR_FILE_NOT_FOUND:
797 // What if there is a hidden variable in sczKey or sczValue?
798 LogStringLine(REPORT_STANDARD, "Registry value not found. Key = '%ls', Value = '%ls'", sczKey, sczValue);
799 fExists = FALSE;
800 break;
801 default:
802 ExitOnWin32Error(er, hr, "Failed to query registry key value.");
803 }
804 }
805
806 // set variable
807 hr = VariableSetNumeric(pVariables, pSearch->sczVariable, fExists, FALSE);
808 ExitOnFailure(hr, "Failed to set variable.");
809
810LExit:
811 if (FAILED(hr))
812 {
813 // What if there is a hidden variable in sczKey?
814 LogStringLine(REPORT_STANDARD, "RegistrySearchExists failed: ID '%ls', HRESULT 0x%x", sczKey, hr);
815 }
816
817 StrSecureZeroFreeString(sczKey);
818 StrSecureZeroFreeString(sczValue);
819 ReleaseRegKey(hKey);
820
821 return hr;
822}
823
824static HRESULT RegistrySearchValue(
825 __in BURN_SEARCH* pSearch,
826 __in BURN_VARIABLES* pVariables
827 )
828{
829 HRESULT hr = S_OK;
830 DWORD er = ERROR_SUCCESS;
831 LPWSTR sczKey = NULL;
832 LPWSTR sczValue = NULL;
833 HKEY hKey = NULL;
834 DWORD dwType = 0;
835 DWORD cbData = 0;
836 LPBYTE pData = NULL;
837 DWORD cch = 0;
838 BURN_VARIANT value = { };
839 REGSAM samDesired = KEY_QUERY_VALUE;
840
841 if (pSearch->RegistrySearch.fWin64)
842 {
843 samDesired = samDesired | KEY_WOW64_64KEY;
844 }
845
846 // format key string
847 hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczKey, &sczKey, NULL);
848 ExitOnFailure(hr, "Failed to format key string.");
849
850 // format value string
851 if (pSearch->RegistrySearch.sczValue)
852 {
853 hr = VariableFormatString(pVariables, pSearch->RegistrySearch.sczValue, &sczValue, NULL);
854 ExitOnFailure(hr, "Failed to format value string.");
855 }
856
857 // open key
858 hr = RegOpen(pSearch->RegistrySearch.hRoot, sczKey, samDesired, &hKey);
859 if (E_FILENOTFOUND == hr)
860 {
861 // What if there is a hidden variable in sczKey?
862 LogStringLine(REPORT_STANDARD, "Registry key not found. Key = '%ls'", sczKey);
863 hr = VariableSetLiteralVariant(pVariables, pSearch->sczVariable, &value);
864 ExitOnFailure(hr, "Failed to clear variable.");
865 ExitFunction1(hr = S_OK);
866 }
867 ExitOnFailure(hr, "Failed to open registry key.");
868
869 // get value
870 er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, NULL, &cbData);
871 if (ERROR_FILE_NOT_FOUND == er)
872 {
873 // What if there is a hidden variable in sczKey or sczValue?
874 LogStringLine(REPORT_STANDARD, "Registry value not found. Key = '%ls', Value = '%ls'", sczKey, sczValue);
875 hr = VariableSetLiteralVariant(pVariables, pSearch->sczVariable, &value);
876 ExitOnFailure(hr, "Failed to clear variable.");
877 ExitFunction1(hr = S_OK);
878 }
879 ExitOnWin32Error(er, hr, "Failed to query registry key value size.");
880
881 pData = (LPBYTE)MemAlloc(cbData + sizeof(WCHAR), TRUE); // + sizeof(WCHAR) here to ensure that we always have a null terminator for REG_SZ
882 ExitOnNull(pData, hr, E_OUTOFMEMORY, "Failed to allocate memory registry value.");
883
884 er = ::RegQueryValueExW(hKey, sczValue, NULL, &dwType, pData, &cbData);
885 ExitOnWin32Error(er, hr, "Failed to query registry key value.");
886
887 switch (dwType)
888 {
889 case REG_DWORD:
890 if (sizeof(LONG) != cbData)
891 {
892 ExitFunction1(hr = E_UNEXPECTED);
893 }
894 hr = BVariantSetNumeric(&value, *((LONG*)pData));
895 break;
896 case REG_QWORD:
897 if (sizeof(LONGLONG) != cbData)
898 {
899 ExitFunction1(hr = E_UNEXPECTED);
900 }
901 hr = BVariantSetNumeric(&value, *((LONGLONG*)pData));
902 break;
903 case REG_EXPAND_SZ:
904 if (pSearch->RegistrySearch.fExpandEnvironment)
905 {
906 hr = StrAlloc(&value.sczValue, cbData);
907 ExitOnFailure(hr, "Failed to allocate string buffer.");
908 value.Type = BURN_VARIANT_TYPE_STRING;
909
910 cch = ::ExpandEnvironmentStringsW((LPCWSTR)pData, value.sczValue, cbData);
911 if (cch > cbData)
912 {
913 hr = StrAlloc(&value.sczValue, cch);
914 ExitOnFailure(hr, "Failed to allocate string buffer.");
915
916 if (cch != ::ExpandEnvironmentStringsW((LPCWSTR)pData, value.sczValue, cch))
917 {
918 ExitWithLastError(hr, "Failed to get expand environment string.");
919 }
920 }
921 break;
922 }
923 __fallthrough;
924 case REG_SZ:
925 hr = BVariantSetString(&value, (LPCWSTR)pData, 0);
926 break;
927 default:
928 ExitOnFailure(hr = E_NOTIMPL, "Unsupported registry key value type. Type = '%u'", dwType);
929 }
930 ExitOnFailure(hr, "Failed to read registry value.");
931
932 // change value to requested type
933 hr = BVariantChangeType(&value, pSearch->RegistrySearch.VariableType);
934 ExitOnFailure(hr, "Failed to change value type.");
935
936 // Set variable as a literal.
937 hr = VariableSetLiteralVariant(pVariables, pSearch->sczVariable, &value);
938 ExitOnFailure(hr, "Failed to set variable.");
939
940LExit:
941 if (FAILED(hr))
942 {
943 // What if there is a hidden variable in sczKey?
944 LogStringLine(REPORT_STANDARD, "RegistrySearchValue failed: ID '%ls', HRESULT 0x%x", sczKey, hr);
945 }
946
947 StrSecureZeroFreeString(sczKey);
948 StrSecureZeroFreeString(sczValue);
949 ReleaseRegKey(hKey);
950 ReleaseMem(pData);
951 BVariantUninitialize(&value);
952
953 return hr;
954}
955
956static HRESULT MsiComponentSearch(
957 __in BURN_SEARCH* pSearch,
958 __in BURN_VARIABLES* pVariables
959 )
960{
961 HRESULT hr = S_OK;
962 INSTALLSTATE is = INSTALLSTATE_BROKEN;
963 LPWSTR sczComponentId = NULL;
964 LPWSTR sczProductCode = NULL;
965 LPWSTR sczPath = NULL;
966
967 // format component id string
968 hr = VariableFormatString(pVariables, pSearch->MsiComponentSearch.sczComponentId, &sczComponentId, NULL);
969 ExitOnFailure(hr, "Failed to format component id string.");
970
971 if (pSearch->MsiComponentSearch.sczProductCode)
972 {
973 // format product code string
974 hr = VariableFormatString(pVariables, pSearch->MsiComponentSearch.sczProductCode, &sczProductCode, NULL);
975 ExitOnFailure(hr, "Failed to format product code string.");
976 }
977
978 if (sczProductCode)
979 {
980 hr = WiuGetComponentPath(sczProductCode, sczComponentId, &is, &sczPath);
981 }
982 else
983 {
984 hr = WiuLocateComponent(sczComponentId, &is, &sczPath);
985 }
986
987 if (INSTALLSTATE_SOURCEABSENT == is)
988 {
989 is = INSTALLSTATE_SOURCE;
990 }
991 else if (INSTALLSTATE_UNKNOWN == is || INSTALLSTATE_NOTUSED == is)
992 {
993 is = INSTALLSTATE_ABSENT;
994 }
995 else if (INSTALLSTATE_ABSENT != is && INSTALLSTATE_LOCAL != is && INSTALLSTATE_SOURCE != is)
996 {
997 hr = E_INVALIDARG;
998 ExitOnFailure(hr, "Failed to get component path: %d", is);
999 }
1000
1001 // set variable
1002 switch (pSearch->MsiComponentSearch.Type)
1003 {
1004 case BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH:
1005 if (INSTALLSTATE_ABSENT == is || INSTALLSTATE_LOCAL == is || INSTALLSTATE_SOURCE == is)
1006 {
1007 hr = VariableSetLiteralString(pVariables, pSearch->sczVariable, sczPath, FALSE);
1008 }
1009 break;
1010 case BURN_MSI_COMPONENT_SEARCH_TYPE_STATE:
1011 hr = VariableSetNumeric(pVariables, pSearch->sczVariable, is, FALSE);
1012 break;
1013 case BURN_MSI_COMPONENT_SEARCH_TYPE_DIRECTORY:
1014 if (INSTALLSTATE_ABSENT == is || INSTALLSTATE_LOCAL == is || INSTALLSTATE_SOURCE == is)
1015 {
1016 // remove file part from path, if any
1017 LPWSTR wz = wcsrchr(sczPath, L'\\');
1018 if (wz)
1019 {
1020 wz[1] = L'\0';
1021 }
1022
1023 hr = VariableSetLiteralString(pVariables, pSearch->sczVariable, sczPath, FALSE);
1024 }
1025 break;
1026 }
1027 ExitOnFailure(hr, "Failed to set variable.");
1028
1029LExit:
1030 if (FAILED(hr))
1031 {
1032 LogStringLine(REPORT_STANDARD, "MsiComponentSearch failed: ID '%ls', HRESULT 0x%x", pSearch->sczKey, hr);
1033 }
1034
1035 StrSecureZeroFreeString(sczComponentId);
1036 StrSecureZeroFreeString(sczProductCode);
1037 ReleaseStr(sczPath);
1038 return hr;
1039}
1040
1041static HRESULT MsiProductSearch(
1042 __in BURN_SEARCH* pSearch,
1043 __in BURN_VARIABLES* pVariables
1044 )
1045{
1046 HRESULT hr = S_OK;
1047 LPWSTR sczGuid = NULL;
1048 LPCWSTR wzProperty = NULL;
1049 LPWSTR *rgsczRelatedProductCodes = NULL;
1050 DWORD dwRelatedProducts = 0;
1051 BURN_VARIANT_TYPE type = BURN_VARIANT_TYPE_NONE;
1052 BURN_VARIANT value = { };
1053 // We're not going to encrypt this value, so can access the value directly.
1054
1055 switch (pSearch->MsiProductSearch.Type)
1056 {
1057 case BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION:
1058 wzProperty = INSTALLPROPERTY_VERSIONSTRING;
1059 break;
1060 case BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE:
1061 wzProperty = INSTALLPROPERTY_LANGUAGE;
1062 break;
1063 case BURN_MSI_PRODUCT_SEARCH_TYPE_STATE:
1064 wzProperty = INSTALLPROPERTY_PRODUCTSTATE;
1065 break;
1066 case BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT:
1067 wzProperty = INSTALLPROPERTY_ASSIGNMENTTYPE;
1068 break;
1069 default:
1070 ExitOnFailure(hr = E_NOTIMPL, "Unsupported product search type: %u", pSearch->MsiProductSearch.Type);
1071 }
1072
1073 // format guid string
1074 hr = VariableFormatString(pVariables, pSearch->MsiProductSearch.sczGuid, &sczGuid, NULL);
1075 ExitOnFailure(hr, "Failed to format GUID string.");
1076
1077 // get product info
1078 value.Type = BURN_VARIANT_TYPE_STRING;
1079
1080 // if this is an upgrade code then get the product code of the highest versioned related product
1081 if (BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_UPGRADECODE == pSearch->MsiProductSearch.GuidType)
1082 {
1083 // WiuEnumRelatedProductCodes will log sczGuid on errors, what if there's a hidden variable in there?
1084 hr = WiuEnumRelatedProductCodes(sczGuid, &rgsczRelatedProductCodes, &dwRelatedProducts, TRUE);
1085 ExitOnFailure(hr, "Failed to enumerate related products for upgrade code.");
1086
1087 // if we actually found a related product then use its upgrade code for the rest of the search
1088 if (1 == dwRelatedProducts)
1089 {
1090 hr = StrAllocStringSecure(&sczGuid, rgsczRelatedProductCodes[0], 0);
1091 ExitOnFailure(hr, "Failed to copy upgrade code.");
1092 }
1093 else
1094 {
1095 // set this here so we have a way of knowing that we don't need to bother
1096 // querying for the product information below
1097 hr = HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT);
1098 }
1099 }
1100
1101 if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr)
1102 {
1103 hr = WiuGetProductInfo(sczGuid, wzProperty, &value.sczValue);
1104 if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) == hr)
1105 {
1106 // product state is available only through MsiGetProductInfoEx
1107 // What if there is a hidden variable in sczGuid?
1108 LogStringLine(REPORT_VERBOSE, "Trying per-machine extended info for property '%ls' for product: %ls", wzProperty, sczGuid);
1109 hr = WiuGetProductInfoEx(sczGuid, NULL, MSIINSTALLCONTEXT_MACHINE, wzProperty, &value.sczValue);
1110
1111 // if not in per-machine context, try per-user (unmanaged)
1112 if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr)
1113 {
1114 // What if there is a hidden variable in sczGuid?
1115 LogStringLine(REPORT_STANDARD, "Trying per-user extended info for property '%ls' for product: %ls", wzProperty, sczGuid);
1116 hr = WiuGetProductInfoEx(sczGuid, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, wzProperty, &value.sczValue);
1117 }
1118 }
1119 }
1120
1121 if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr)
1122 {
1123 // What if there is a hidden variable in sczGuid?
1124 LogStringLine(REPORT_STANDARD, "Product or related product not found: %ls", sczGuid);
1125
1126 // set value to indicate absent
1127 switch (pSearch->MsiProductSearch.Type)
1128 {
1129 case BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT: __fallthrough;
1130 case BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION:
1131 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1132 value.llValue = 0;
1133 break;
1134 case BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE:
1135 // is supposed to remain empty
1136 break;
1137 case BURN_MSI_PRODUCT_SEARCH_TYPE_STATE:
1138 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1139 value.llValue = INSTALLSTATE_ABSENT;
1140 break;
1141 }
1142
1143 hr = S_OK;
1144 }
1145 ExitOnFailure(hr, "Failed to get product info.");
1146
1147 // change value type
1148 switch (pSearch->MsiProductSearch.Type)
1149 {
1150 case BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION:
1151 type = BURN_VARIANT_TYPE_VERSION;
1152 break;
1153 case BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE:
1154 type = BURN_VARIANT_TYPE_STRING;
1155 break;
1156 case BURN_MSI_PRODUCT_SEARCH_TYPE_STATE: __fallthrough;
1157 case BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT:
1158 type = BURN_VARIANT_TYPE_NUMERIC;
1159 break;
1160 }
1161 hr = BVariantChangeType(&value, type);
1162 ExitOnFailure(hr, "Failed to change value type.");
1163
1164 // Set variable as a literal.
1165 hr = VariableSetLiteralVariant(pVariables, pSearch->sczVariable, &value);
1166 ExitOnFailure(hr, "Failed to set variable.");
1167
1168LExit:
1169 if (FAILED(hr))
1170 {
1171 LogStringLine(REPORT_STANDARD, "MsiProductSearch failed: ID '%ls', HRESULT 0x%x", pSearch->sczKey, hr);
1172 }
1173
1174 StrSecureZeroFreeString(sczGuid);
1175 ReleaseStrArray(rgsczRelatedProductCodes, dwRelatedProducts);
1176 BVariantUninitialize(&value);
1177
1178 return hr;
1179}
1180
1181static HRESULT MsiFeatureSearch(
1182 __in BURN_SEARCH* pSearch,
1183 __in BURN_VARIABLES* /*pVariables*/
1184 )
1185{
1186 HRESULT hr = E_NOTIMPL;
1187
1188//LExit:
1189 if (FAILED(hr))
1190 {
1191 LogStringLine(REPORT_STANDARD, "MsiFeatureSearch failed: ID '%ls', HRESULT 0x%x", pSearch->sczKey, hr);
1192 }
1193
1194 return hr;
1195}
diff --git a/src/engine/search.h b/src/engine/search.h
new file mode 100644
index 00000000..65dfb18f
--- /dev/null
+++ b/src/engine/search.h
@@ -0,0 +1,152 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// constants
11
12enum BURN_SEARCH_TYPE
13{
14 BURN_SEARCH_TYPE_NONE,
15 BURN_SEARCH_TYPE_DIRECTORY,
16 BURN_SEARCH_TYPE_FILE,
17 BURN_SEARCH_TYPE_REGISTRY,
18 BURN_SEARCH_TYPE_MSI_COMPONENT,
19 BURN_SEARCH_TYPE_MSI_PRODUCT,
20 BURN_SEARCH_TYPE_MSI_FEATURE,
21};
22
23enum BURN_DIRECTORY_SEARCH_TYPE
24{
25 BURN_DIRECTORY_SEARCH_TYPE_NONE,
26 BURN_DIRECTORY_SEARCH_TYPE_EXISTS,
27 BURN_DIRECTORY_SEARCH_TYPE_PATH,
28};
29
30enum BURN_FILE_SEARCH_TYPE
31{
32 BURN_FILE_SEARCH_TYPE_NONE,
33 BURN_FILE_SEARCH_TYPE_EXISTS,
34 BURN_FILE_SEARCH_TYPE_VERSION,
35 BURN_FILE_SEARCH_TYPE_PATH,
36};
37
38enum BURN_REGISTRY_SEARCH_TYPE
39{
40 BURN_REGISTRY_SEARCH_TYPE_NONE,
41 BURN_REGISTRY_SEARCH_TYPE_EXISTS,
42 BURN_REGISTRY_SEARCH_TYPE_VALUE,
43};
44
45enum BURN_MSI_COMPONENT_SEARCH_TYPE
46{
47 BURN_MSI_COMPONENT_SEARCH_TYPE_NONE,
48 BURN_MSI_COMPONENT_SEARCH_TYPE_KEYPATH,
49 BURN_MSI_COMPONENT_SEARCH_TYPE_STATE,
50 BURN_MSI_COMPONENT_SEARCH_TYPE_DIRECTORY,
51};
52
53enum BURN_MSI_PRODUCT_SEARCH_TYPE
54{
55 BURN_MSI_PRODUCT_SEARCH_TYPE_NONE,
56 BURN_MSI_PRODUCT_SEARCH_TYPE_VERSION,
57 BURN_MSI_PRODUCT_SEARCH_TYPE_LANGUAGE,
58 BURN_MSI_PRODUCT_SEARCH_TYPE_STATE,
59 BURN_MSI_PRODUCT_SEARCH_TYPE_ASSIGNMENT,
60};
61
62enum BURN_MSI_PRODUCT_SEARCH_GUID_TYPE
63{
64 BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_NONE,
65 BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_PRODUCTCODE,
66 BURN_MSI_PRODUCT_SEARCH_GUID_TYPE_UPGRADECODE
67};
68
69enum BURN_MSI_FEATURE_SEARCH_TYPE
70{
71 BURN_MSI_FEATURE_SEARCH_TYPE_NONE,
72 BURN_MSI_FEATURE_SEARCH_TYPE_STATE,
73};
74
75
76// structs
77
78typedef struct _BURN_SEARCH
79{
80 LPWSTR sczKey;
81 LPWSTR sczVariable;
82 LPWSTR sczCondition;
83
84 BURN_SEARCH_TYPE Type;
85 union
86 {
87 struct
88 {
89 BURN_DIRECTORY_SEARCH_TYPE Type;
90 LPWSTR sczPath;
91 } DirectorySearch;
92 struct
93 {
94 BURN_FILE_SEARCH_TYPE Type;
95 LPWSTR sczPath;
96 } FileSearch;
97 struct
98 {
99 BURN_REGISTRY_SEARCH_TYPE Type;
100 BURN_VARIANT_TYPE VariableType;
101 HKEY hRoot;
102 LPWSTR sczKey;
103 LPWSTR sczValue;
104 BOOL fWin64;
105 BOOL fExpandEnvironment;
106 } RegistrySearch;
107 struct
108 {
109 BURN_MSI_COMPONENT_SEARCH_TYPE Type;
110 LPWSTR sczProductCode;
111 LPWSTR sczComponentId;
112 } MsiComponentSearch;
113 struct
114 {
115 BURN_MSI_PRODUCT_SEARCH_TYPE Type;
116 BURN_MSI_PRODUCT_SEARCH_GUID_TYPE GuidType;
117 LPWSTR sczGuid;
118 } MsiProductSearch;
119 struct
120 {
121 BURN_MSI_FEATURE_SEARCH_TYPE Type;
122 LPWSTR sczProductCode;
123 LPWSTR sczFeatureId;
124 } MsiFeatureSearch;
125 };
126} BURN_SEARCH;
127
128typedef struct _BURN_SEARCHES
129{
130 BURN_SEARCH* rgSearches;
131 DWORD cSearches;
132} BURN_SEARCHES;
133
134
135// function declarations
136
137HRESULT SearchesParseFromXml(
138 __in BURN_SEARCHES* pSearches,
139 __in IXMLDOMNode* pixnBundle
140 );
141HRESULT SearchesExecute(
142 __in BURN_SEARCHES* pSearches,
143 __in BURN_VARIABLES* pVariables
144 );
145void SearchesUninitialize(
146 __in BURN_SEARCHES* pSearches
147 );
148
149
150#if defined(__cplusplus)
151}
152#endif
diff --git a/src/engine/section.cpp b/src/engine/section.cpp
new file mode 100644
index 00000000..3adcdd14
--- /dev/null
+++ b/src/engine/section.cpp
@@ -0,0 +1,399 @@
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#include "precomp.h"
4
5
6// constants
7
8// If these defaults ever change, be sure to update constants in burn\stub\StubSection.cpp as well.
9#define BURN_SECTION_NAME ".wixburn"
10#define BURN_SECTION_MAGIC 0x00f14300
11#define BURN_SECTION_VERSION 0x00000002
12#define MANIFEST_CABINET_TOKEN L"0"
13
14// structs
15typedef struct _BURN_SECTION_HEADER
16{
17 DWORD dwMagic;
18 DWORD dwVersion;
19
20 GUID guidBundleId;
21
22 DWORD dwStubSize;
23 DWORD dwOriginalChecksum;
24 DWORD dwOriginalSignatureOffset;
25 DWORD dwOriginalSignatureSize;
26
27 DWORD dwFormat;
28 DWORD cContainers;
29 DWORD rgcbContainers[1];
30} BURN_SECTION_HEADER;
31
32static HRESULT VerifySectionMatchesMemoryPEHeader(
33 __in REFGUID pSection
34 );
35
36
37extern "C" HRESULT SectionInitialize(
38 __in BURN_SECTION* pSection,
39 __in HANDLE hEngineFile,
40 __in HANDLE hSourceEngineFile
41 )
42{
43 HRESULT hr = S_OK;
44 DWORD cbRead = 0;
45 LARGE_INTEGER li = { };
46 LONGLONG llSize = 0;
47 IMAGE_DOS_HEADER dosHeader = { };
48 IMAGE_NT_HEADERS ntHeader = { };
49 DWORD dwChecksumOffset = 0;
50 DWORD dwCertificateTableOffset = 0;
51 DWORD dwSignatureOffset = 0;
52 DWORD cbSignature = 0;
53 IMAGE_SECTION_HEADER sectionHeader = { };
54 DWORD dwOriginalChecksumAndSignatureOffset = 0;
55 BURN_SECTION_HEADER* pBurnSectionHeader = NULL;
56
57 pSection->hEngineFile = hEngineFile;
58 ExitOnInvalidHandleWithLastError(pSection->hEngineFile, hr, "Failed to open handle to engine process path.");
59
60 pSection->hSourceEngineFile = INVALID_HANDLE_VALUE == hSourceEngineFile ? hEngineFile : hSourceEngineFile;
61
62 //
63 // First, make sure we have a valid DOS signature.
64 //
65 if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN))
66 {
67 ExitWithLastError(hr, "Failed to seek to start of file.");
68 }
69
70 // read DOS header
71 if (!::ReadFile(pSection->hEngineFile, &dosHeader, sizeof(IMAGE_DOS_HEADER), &cbRead, NULL))
72 {
73 ExitWithLastError(hr, "Failed to read DOS header.");
74 }
75 else if (sizeof(IMAGE_DOS_HEADER) > cbRead || IMAGE_DOS_SIGNATURE != dosHeader.e_magic)
76 {
77 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
78 ExitOnRootFailure(hr, "Failed to find valid DOS image header in buffer.");
79 }
80
81 //
82 // Now, make sure we have a valid NT signature.
83 //
84
85 // seek to new header
86 li.QuadPart = dosHeader.e_lfanew;
87 if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN))
88 {
89 ExitWithLastError(hr, "Failed to seek to NT header.");
90 }
91
92 // read NT header
93 if (!::ReadFile(pSection->hEngineFile, &ntHeader, sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER), &cbRead, NULL))
94 {
95 ExitWithLastError(hr, "Failed to read NT header.");
96 }
97 else if ((sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER)) > cbRead || IMAGE_NT_SIGNATURE != ntHeader.Signature)
98 {
99 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
100 ExitOnRootFailure(hr, "Failed to find valid NT image header in buffer.");
101 }
102
103 // Get the table offsets.
104 dwChecksumOffset = dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER) + (sizeof(DWORD) * 16);
105 dwCertificateTableOffset = dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS) - (sizeof(IMAGE_DATA_DIRECTORY) * (IMAGE_NUMBEROF_DIRECTORY_ENTRIES - IMAGE_DIRECTORY_ENTRY_SECURITY));
106
107 // Seek into the certificate table to get the signature size.
108 li.QuadPart = dwCertificateTableOffset;
109 if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN))
110 {
111 ExitWithLastError(hr, "Failed to seek to section info.");
112 }
113
114 if (!::ReadFile(pSection->hEngineFile, &dwSignatureOffset, sizeof(dwSignatureOffset), &cbRead, NULL))
115 {
116 ExitWithLastError(hr, "Failed to read signature offset.");
117 }
118
119 if (!::ReadFile(pSection->hEngineFile, &cbSignature, sizeof(cbSignature), &cbRead, NULL))
120 {
121 ExitWithLastError(hr, "Failed to read signature size.");
122 }
123
124 //
125 // Finally, get into the section table and look for the Burn section info.
126 //
127
128 // seek past optional headers
129 li.QuadPart = dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER) + ntHeader.FileHeader.SizeOfOptionalHeader;
130 if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN))
131 {
132 ExitWithLastError(hr, "Failed to seek past optional headers.");
133 }
134
135 // read sections one by one until we find our section
136 for (DWORD i = 0; ; ++i)
137 {
138 // read section
139 if (!::ReadFile(pSection->hEngineFile, &sectionHeader, sizeof(IMAGE_SECTION_HEADER), &cbRead, NULL))
140 {
141 ExitWithLastError(hr, "Failed to read image section header, index: %u", i);
142 }
143 if (sizeof(IMAGE_SECTION_HEADER) > cbRead)
144 {
145 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
146 ExitOnRootFailure(hr, "Failed to read complete image section header, index: %u", i);
147 }
148
149 // compare header name
150 C_ASSERT(sizeof(sectionHeader.Name) == sizeof(BURN_SECTION_NAME) - 1);
151 if (0 == memcmp(sectionHeader.Name, BURN_SECTION_NAME, sizeof(sectionHeader.Name)))
152 {
153 break;
154 }
155
156 // fail if we hit the end
157 if (i + 1 >= ntHeader.FileHeader.NumberOfSections)
158 {
159 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
160 ExitOnRootFailure(hr, "Failed to find Burn section.");
161 }
162 }
163
164 //
165 // We've arrived at the section info.
166 //
167
168 // check size of section
169 if (sizeof(BURN_SECTION_HEADER) > sectionHeader.SizeOfRawData)
170 {
171 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
172 ExitOnRootFailure(hr, "Failed to read section info, data to short: %u", sectionHeader.SizeOfRawData);
173 }
174
175 // allocate buffer for section info
176 pBurnSectionHeader = (BURN_SECTION_HEADER*)MemAlloc(sectionHeader.SizeOfRawData, TRUE);
177 ExitOnNull(pBurnSectionHeader, hr, E_OUTOFMEMORY, "Failed to allocate buffer for section info.");
178
179 // seek to section info
180 li.QuadPart = sectionHeader.PointerToRawData;
181 if (!::SetFilePointerEx(pSection->hEngineFile, li, NULL, FILE_BEGIN))
182 {
183 ExitWithLastError(hr, "Failed to seek to section info.");
184 }
185
186 // Note the location of original checksum and signature information in the burn section header.
187 dwOriginalChecksumAndSignatureOffset = sectionHeader.PointerToRawData + (reinterpret_cast<LPBYTE>(&pBurnSectionHeader->dwOriginalChecksum) - reinterpret_cast<LPBYTE>(pBurnSectionHeader));
188
189 // read section info
190 if (!::ReadFile(pSection->hEngineFile, pBurnSectionHeader, sectionHeader.SizeOfRawData, &cbRead, NULL))
191 {
192 ExitWithLastError(hr, "Failed to read section info.");
193 }
194 else if (sectionHeader.SizeOfRawData > cbRead)
195 {
196 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
197 ExitOnRootFailure(hr, "Failed to read complete section info.");
198 }
199
200 // validate version of section info
201 if (BURN_SECTION_VERSION != pBurnSectionHeader->dwVersion)
202 {
203 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
204 ExitOnRootFailure(hr, "Failed to read section info, unsupported version: %08x", pBurnSectionHeader->dwVersion);
205 }
206
207 hr = FileSizeByHandle(pSection->hSourceEngineFile, &llSize);
208 ExitOnFailure(hr, "Failed to get total size of bundle.");
209
210 pSection->cbStub = pBurnSectionHeader->dwStubSize;
211
212 // If there is an original signature use that to determine the engine size.
213 if (pBurnSectionHeader->dwOriginalSignatureOffset)
214 {
215 pSection->cbEngineSize = pBurnSectionHeader->dwOriginalSignatureOffset + pBurnSectionHeader->dwOriginalSignatureSize;
216 }
217 else if (dwSignatureOffset) // if there is a signature, use it.
218 {
219 pSection->cbEngineSize = dwSignatureOffset + cbSignature;
220 }
221 else // just use the stub and UX container as the size of the engine.
222 {
223 pSection->cbEngineSize = pSection->cbStub + pBurnSectionHeader->rgcbContainers[0];
224 }
225
226 pSection->qwBundleSize = static_cast<DWORD64>(llSize);
227
228 pSection->dwChecksumOffset = dwChecksumOffset;
229 pSection->dwCertificateTableOffset = dwCertificateTableOffset;
230 pSection->dwOriginalChecksumAndSignatureOffset = dwOriginalChecksumAndSignatureOffset;
231
232 pSection->dwOriginalChecksum = pBurnSectionHeader->dwOriginalChecksum;
233 pSection->dwOriginalSignatureOffset = pBurnSectionHeader->dwOriginalSignatureOffset;
234 pSection->dwOriginalSignatureSize = pBurnSectionHeader->dwOriginalSignatureSize;
235
236 pSection->dwFormat = pBurnSectionHeader->dwFormat;
237 pSection->cContainers = pBurnSectionHeader->cContainers;
238 pSection->rgcbContainers = (DWORD*)MemAlloc(sizeof(DWORD) * pSection->cContainers, TRUE);
239 ExitOnNull(pSection->rgcbContainers, hr, E_OUTOFMEMORY, "Failed to allocate memory for container sizes.");
240
241 memcpy(pSection->rgcbContainers, pBurnSectionHeader->rgcbContainers, sizeof(DWORD) * pSection->cContainers);
242
243 // TODO: verify more than just the GUID.
244 hr = VerifySectionMatchesMemoryPEHeader(pBurnSectionHeader->guidBundleId);
245 ExitOnRootFailure(hr, "PE Header from file didn't match PE Header in memory.");
246
247LExit:
248 ReleaseMem(pBurnSectionHeader);
249
250 return hr;
251}
252
253extern "C" void SectionUninitialize(
254 __out BURN_SECTION* pSection
255 )
256{
257 ReleaseMem(pSection->rgcbContainers);
258 memset(pSection, 0, sizeof(BURN_SECTION));
259}
260
261extern "C" HRESULT SectionGetAttachedContainerInfo(
262 __in BURN_SECTION* pSection,
263 __in DWORD iContainerIndex,
264 __in DWORD dwExpectedType,
265 __out DWORD64* pqwOffset,
266 __out DWORD64* pqwSize,
267 __out BOOL* pfPresent
268 )
269{
270 HRESULT hr = S_OK;
271
272 // validate container info
273 if (iContainerIndex >= pSection->cContainers)
274 {
275 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
276 ExitOnRootFailure(hr, "Failed to find container info, too few elements: %u", pSection->cContainers);
277 }
278 else if (dwExpectedType != pSection->dwFormat)
279 {
280 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
281 ExitOnRootFailure(hr, "Unexpected container format.");
282 }
283
284 // If we are asking for the UX container, find it right after the stub.
285 if (0 == iContainerIndex)
286 {
287 *pqwOffset = pSection->cbStub;
288 }
289 else // attached containers start after the whole engine.
290 {
291 *pqwOffset = pSection->cbEngineSize;
292 for (DWORD i = 1; i < iContainerIndex; ++i)
293 {
294 *pqwOffset += pSection->rgcbContainers[i];
295 }
296 }
297
298 *pqwSize = pSection->rgcbContainers[iContainerIndex];
299 *pfPresent = (*pqwOffset + *pqwSize) <= pSection->qwBundleSize;
300
301 AssertSz(*pfPresent || pSection->qwBundleSize <= *pqwOffset, "An attached container should either be present or completely absent from the bundle. Found a case where the attached container is partially present which is wrong.");
302
303LExit:
304 return hr;
305}
306
307HRESULT VerifySectionMatchesMemoryPEHeader(
308 __in REFGUID pBundleId
309 )
310{
311 HRESULT hr = S_OK;
312 BYTE* pbPEHeader = NULL;
313 PIMAGE_DOS_HEADER pDosHeader = NULL;
314 PIMAGE_NT_HEADERS pNtHeader = NULL;
315 PIMAGE_SECTION_HEADER pSections = NULL;
316 PIMAGE_SECTION_HEADER pSectionHeader = NULL;
317 BURN_SECTION_HEADER* pBurnSectionHeader = NULL;
318
319 pbPEHeader = reinterpret_cast<BYTE*>(::GetModuleHandleW(NULL));
320 ExitOnNullWithLastError(pbPEHeader, hr, "Failed to get module handle to process.");
321
322 //
323 // First, make sure we have a valid DOS signature.
324 //
325
326 pDosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(pbPEHeader);
327 if (IMAGE_DOS_SIGNATURE != pDosHeader->e_magic)
328 {
329 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
330 ExitOnRootFailure(hr, "Failed to find valid DOS image header in buffer.");
331 }
332
333 //
334 // Now, make sure we have a valid NT signature.
335 //
336
337 pNtHeader = reinterpret_cast<PIMAGE_NT_HEADERS>(pbPEHeader + pDosHeader->e_lfanew);
338 if (IMAGE_NT_SIGNATURE != pNtHeader->Signature)
339 {
340 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
341 ExitOnRootFailure(hr, "Failed to find valid NT image header in buffer.");
342 }
343
344 //
345 // Finally, get into the section table and look for the Burn section info.
346 //
347
348 pSections = reinterpret_cast<PIMAGE_SECTION_HEADER>(pbPEHeader + pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER) + pNtHeader->FileHeader.SizeOfOptionalHeader);
349
350 // Read sections one by one until we find our section.
351 for (DWORD i = 0; ; ++i)
352 {
353 pSectionHeader = pSections + i;
354
355 // Compare header name.
356 C_ASSERT(sizeof(pSectionHeader->Name) == sizeof(BURN_SECTION_NAME) - 1);
357 if (0 == memcmp(pSectionHeader->Name, BURN_SECTION_NAME, sizeof(pSectionHeader->Name)))
358 {
359 break;
360 }
361
362 // Fail if we hit the end.
363 if (i + 1 >= pNtHeader->FileHeader.NumberOfSections)
364 {
365 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
366 ExitOnRootFailure(hr, "Failed to find Burn section.");
367 }
368 }
369
370 //
371 // We've arrived at the section info.
372 //
373
374 // Check size of section.
375 if (sizeof(BURN_SECTION_HEADER) > pSectionHeader->SizeOfRawData)
376 {
377 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
378 ExitOnRootFailure(hr, "Failed to read section info, data to short: %u", pSectionHeader->SizeOfRawData);
379 }
380
381 // Get Burn section info.
382 pBurnSectionHeader = reinterpret_cast<BURN_SECTION_HEADER*>(pbPEHeader + pSectionHeader->VirtualAddress);
383
384 // Validate version of section info.
385 if (BURN_SECTION_VERSION != pBurnSectionHeader->dwVersion)
386 {
387 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
388 ExitOnRootFailure(hr, "Failed to read section info, unsupported version: %08x", pBurnSectionHeader->dwVersion);
389 }
390
391 if (!::IsEqualGUID(pBundleId, pBurnSectionHeader->guidBundleId))
392 {
393 hr = E_INVALIDDATA;
394 ExitOnRootFailure(hr, "Bundle guid didn't match the guid in the PE Header in memory.");
395 }
396
397LExit:
398 return hr;
399}
diff --git a/src/engine/section.h b/src/engine/section.h
new file mode 100644
index 00000000..78331469
--- /dev/null
+++ b/src/engine/section.h
@@ -0,0 +1,54 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// structs
11
12typedef struct _BURN_SECTION
13{
14 HANDLE hEngineFile;
15 HANDLE hSourceEngineFile;
16
17 DWORD cbStub;
18 DWORD cbEngineSize; // stub + UX container + original certficiate
19 DWORD64 qwBundleSize; // stub + UX container + original certificate [+ attached containers* + final certificate]
20
21 DWORD dwChecksumOffset;
22 DWORD dwCertificateTableOffset;
23 DWORD dwOriginalChecksumAndSignatureOffset;
24
25 DWORD dwOriginalChecksum;
26 DWORD dwOriginalSignatureOffset;
27 DWORD dwOriginalSignatureSize;
28
29 DWORD dwFormat;
30 DWORD cContainers;
31 DWORD* rgcbContainers;
32} BURN_SECTION;
33
34
35HRESULT SectionInitialize(
36 __in BURN_SECTION* pSection,
37 __in HANDLE hEngineFile,
38 __in HANDLE hSourceEngineFile
39 );
40void SectionUninitialize(
41 __in BURN_SECTION* pSection
42 );
43HRESULT SectionGetAttachedContainerInfo(
44 __in BURN_SECTION* pSection,
45 __in DWORD iContainerIndex,
46 __in DWORD dwExpectedType,
47 __out DWORD64* pqwOffset,
48 __out DWORD64* pqwSize,
49 __out BOOL* pfPresent
50 );
51
52#if defined(__cplusplus)
53}
54#endif
diff --git a/src/engine/splashscreen.cpp b/src/engine/splashscreen.cpp
new file mode 100644
index 00000000..1f95886a
--- /dev/null
+++ b/src/engine/splashscreen.cpp
@@ -0,0 +1,316 @@
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#include "precomp.h"
4
5using namespace Gdiplus;
6
7#define BURN_SPLASHSCREEN_CLASS_WINDOW L"WixBurnSplashScreen"
8#define IDB_SPLASHSCREEN 1
9
10// struct
11
12struct SPLASHSCREEN_INFO
13{
14 Bitmap* pBitmap;
15 Point pt;
16 Size size;
17};
18
19struct SPLASHSCREEN_CONTEXT
20{
21 HANDLE hIntializedEvent;
22 HINSTANCE hInstance;
23 LPCWSTR wzCaption;
24
25 HWND* pHwnd;
26};
27
28// internal function definitions
29
30static DWORD WINAPI ThreadProc(
31 __in LPVOID pvContext
32 );
33static LRESULT CALLBACK WndProc(
34 __in HWND hWnd,
35 __in UINT uMsg,
36 __in WPARAM wParam,
37 __in LPARAM lParam
38 );
39static void OnPaint(
40 __in HDC hdc,
41 __in SPLASHSCREEN_INFO* pSplashScreen
42 );
43static HRESULT LoadSplashScreen(
44 __in HMODULE hInstance,
45 __in SPLASHSCREEN_INFO* pSplashScreen
46 );
47
48
49// function definitions
50
51extern "C" void SplashScreenCreate(
52 __in HINSTANCE hInstance,
53 __in_z_opt LPCWSTR wzCaption,
54 __out HWND* pHwnd
55 )
56{
57 HRESULT hr = S_OK;
58 SPLASHSCREEN_CONTEXT context = { };
59 HANDLE rgSplashScreenEvents[2] = { };
60 DWORD dwSplashScreenThreadId = 0;
61
62 rgSplashScreenEvents[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL);
63 ExitOnNullWithLastError(rgSplashScreenEvents[0], hr, "Failed to create modal event.");
64
65 // create splash screen thread.
66 context.hIntializedEvent = rgSplashScreenEvents[0];
67 context.hInstance = hInstance;
68 context.wzCaption = wzCaption;
69 context.pHwnd = pHwnd;
70
71 rgSplashScreenEvents[1] = ::CreateThread(NULL, 0, ThreadProc, &context, 0, &dwSplashScreenThreadId);
72 ExitOnNullWithLastError(rgSplashScreenEvents[1], hr, "Failed to create UI thread.");
73
74 // It doesn't really matter if the thread gets initialized (WAIT_OBJECT_0) or fails and exits
75 // prematurely (WAIT_OBJECT_0 + 1), we just want to wait long enough for one of those two
76 // events to happen.
77 ::WaitForMultipleObjects(countof(rgSplashScreenEvents), rgSplashScreenEvents, FALSE, INFINITE);
78
79LExit:
80 ReleaseHandle(rgSplashScreenEvents[1]);
81 ReleaseHandle(rgSplashScreenEvents[0]);
82}
83
84extern "C" HRESULT SplashScreenDisplayError(
85 __in BOOTSTRAPPER_DISPLAY display,
86 __in_z LPCWSTR wzBundleName,
87 __in HRESULT hrError
88 )
89{
90 HRESULT hr = S_OK;
91 LPWSTR sczDisplayString = NULL;
92
93 hr = StrAllocFromError(&sczDisplayString, hrError, NULL);
94 ExitOnFailure(hr, "Failed to allocate string to display error message");
95
96 Trace(REPORT_STANDARD, "Error message displayed because: %ls", sczDisplayString);
97
98 if (BOOTSTRAPPER_DISPLAY_NONE == display || BOOTSTRAPPER_DISPLAY_PASSIVE == display || BOOTSTRAPPER_DISPLAY_EMBEDDED == display)
99 {
100 // Don't display the error dialog in these modes
101 ExitFunction1(hr = S_OK);
102 }
103
104 ::MessageBoxW(NULL, sczDisplayString, wzBundleName, MB_OK | MB_ICONERROR | MB_SYSTEMMODAL);
105
106LExit:
107 ReleaseStr(sczDisplayString);
108
109 return hr;
110}
111
112
113static DWORD WINAPI ThreadProc(
114 __in LPVOID pvContext
115 )
116{
117 HRESULT hr = S_OK;
118
119 ULONG_PTR token = 0;
120 GdiplusStartupInput input;
121 GdiplusStartupOutput output = { };
122
123 SPLASHSCREEN_CONTEXT* pContext = static_cast<SPLASHSCREEN_CONTEXT*>(pvContext);
124 SPLASHSCREEN_INFO splashScreen = { };
125
126 WNDCLASSW wc = { };
127 BOOL fRegistered = TRUE;
128 HWND hWnd = NULL;
129
130 BOOL fRet = FALSE;
131 MSG msg = { };
132
133 input.GdiplusVersion = 1;
134
135 hr = GdipInitialize(&input, &token, &output);
136 ExitOnFailure(hr, "Failed to initialize GDI+.");
137
138 hr = LoadSplashScreen(pContext->hInstance, &splashScreen);
139 ExitOnFailure(hr, "Failed to load splash screen.");
140
141 // Register the window class and create the window.
142 wc.lpfnWndProc = WndProc;
143 wc.hInstance = pContext->hInstance;
144 wc.hCursor = ::LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
145 wc.lpszClassName = BURN_SPLASHSCREEN_CLASS_WINDOW;
146 if (!::RegisterClassW(&wc))
147 {
148 ExitWithLastError(hr, "Failed to register window.");
149 }
150
151 fRegistered = TRUE;
152
153 hWnd = ::CreateWindowExW(WS_EX_TOOLWINDOW, wc.lpszClassName, pContext->wzCaption, WS_POPUP | WS_VISIBLE, splashScreen.pt.X, splashScreen.pt.Y, splashScreen.size.Width, splashScreen.size.Height, HWND_DESKTOP, NULL, pContext->hInstance, &splashScreen);
154 ExitOnNullWithLastError(hWnd, hr, "Failed to create window.");
155
156 // Return the splash screen window and free the main thread waiting for us to be initialized.
157 *pContext->pHwnd = hWnd;
158 ::SetEvent(pContext->hIntializedEvent);
159
160 // Pump messages until the bootstrapper application destroys the window.
161 while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
162 {
163 if (-1 == fRet)
164 {
165 hr = E_UNEXPECTED;
166 ExitOnFailure(hr, "Unexpected return value from message pump.");
167 }
168 else if (!::IsDialogMessageW(hWnd, &msg))
169 {
170 ::TranslateMessage(&msg);
171 ::DispatchMessageW(&msg);
172 }
173 }
174
175LExit:
176 if (fRegistered)
177 {
178 ::UnregisterClassW(BURN_SPLASHSCREEN_CLASS_WINDOW, pContext->hInstance);
179 }
180
181 if (splashScreen.pBitmap)
182 {
183 delete splashScreen.pBitmap;
184 }
185
186 if (token)
187 {
188 GdipUninitialize(token);
189 }
190
191 return hr;
192}
193
194static LRESULT CALLBACK WndProc(
195 __in HWND hWnd,
196 __in UINT uMsg,
197 __in WPARAM wParam,
198 __in LPARAM lParam
199 )
200{
201 LRESULT lres = 0;
202 SPLASHSCREEN_INFO* pSplashScreen = reinterpret_cast<SPLASHSCREEN_INFO*>(::GetWindowLongW(hWnd, GWLP_USERDATA));
203
204 switch (uMsg)
205 {
206 case WM_NCCREATE:
207 {
208 LPCREATESTRUCTW lpcs = reinterpret_cast<LPCREATESTRUCTW>(lParam);
209 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(lpcs->lpCreateParams));
210 }
211 break;
212
213 case WM_NCDESTROY:
214 lres = ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
215 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0);
216 return lres;
217
218 case WM_NCHITTEST:
219 return HTCAPTION; // allow window to be moved by grabbing any pixel.
220
221 case WM_DESTROY:
222 ::PostQuitMessage(0);
223 return 0;
224
225 case WM_ERASEBKGND:
226 // The splash screen image will be repainted in its entirety.
227 return 1;
228
229 case WM_PAINT:
230 {
231 PAINTSTRUCT ps = { };
232
233 HDC hdc = BeginPaint(hWnd, &ps);
234 OnPaint(hdc, pSplashScreen);
235 EndPaint(hWnd, &ps);
236 }
237 return 0;
238 }
239
240 return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
241}
242
243static void OnPaint(
244 __in HDC hdc,
245 __in SPLASHSCREEN_INFO* pSplashScreen
246 )
247{
248 // Use high-quality bicubuc stretching from GDI+ which looks better than GDI.
249 Graphics graphics(hdc);
250 graphics.SetInterpolationMode(InterpolationModeHighQualityBicubic);
251
252 Rect dst(0, 0, pSplashScreen->size.Width, pSplashScreen->size.Height);
253 Status status = graphics.DrawImage(pSplashScreen->pBitmap, dst);
254
255#if DEBUG
256 HRESULT hr = GdipHresultFromStatus(status);
257 TraceError(hr, "Failed to draw splash screen bitmap.");
258#else
259 UNREFERENCED_PARAMETER(status);
260#endif
261}
262
263static HRESULT LoadSplashScreen(
264 __in HMODULE hInstance,
265 __in SPLASHSCREEN_INFO* pSplashScreen
266 )
267{
268 HRESULT hr = S_OK;
269 POINT ptCursor = { };
270 HMONITOR hMonitor = NULL;
271 MONITORINFOEXW mi;
272 HDC hdc = NULL;
273 UINT dpiX = 0;
274 UINT dpiY = 0;
275
276 pSplashScreen->pBitmap = Bitmap::FromResource(hInstance, MAKEINTRESOURCEW(IDB_SPLASHSCREEN));
277 ExitOnNull(pSplashScreen->pBitmap, hr, E_INVALIDDATA, "Failed to find the splash screen bitmap.");
278 ExitOnGdipFailure(pSplashScreen->pBitmap->GetLastStatus(), hr, "Failed to load the splash screen bitmap.");
279
280 pSplashScreen->pt.X = CW_USEDEFAULT;
281 pSplashScreen->pt.Y = CW_USEDEFAULT;
282 pSplashScreen->size.Width = pSplashScreen->pBitmap->GetWidth();
283 pSplashScreen->size.Height = pSplashScreen->pBitmap->GetHeight();
284
285 // Stretch and center the window on the monitor with the mouse.
286 if (::GetCursorPos(&ptCursor))
287 {
288 hMonitor = ::MonitorFromPoint(ptCursor, MONITOR_DEFAULTTONEAREST);
289 if (hMonitor)
290 {
291 ZeroMemory(&mi, sizeof(mi));
292 mi.cbSize = sizeof(mi);
293
294 if (::GetMonitorInfoW(hMonitor, &mi))
295 {
296 hdc = ::CreateDCW(L"DISPLAY", mi.szDevice, NULL, NULL);
297 if (hdc)
298 {
299 dpiX = ::GetDeviceCaps(hdc, LOGPIXELSX);
300 dpiY = ::GetDeviceCaps(hdc, LOGPIXELSY);
301
302 pSplashScreen->size.Width = pSplashScreen->size.Width * dpiX / 96;
303 pSplashScreen->size.Height = pSplashScreen->size.Height * dpiY / 96;
304
305 ::ReleaseDC(NULL, hdc);
306 }
307
308 pSplashScreen->pt.X = mi.rcWork.left + (mi.rcWork.right - mi.rcWork.left - pSplashScreen->size.Width) / 2;
309 pSplashScreen->pt.Y = mi.rcWork.top + (mi.rcWork.bottom - mi.rcWork.top - pSplashScreen->size.Height) / 2;
310 }
311 }
312 }
313
314LExit:
315 return hr;
316}
diff --git a/src/engine/splashscreen.h b/src/engine/splashscreen.h
new file mode 100644
index 00000000..8f8817c7
--- /dev/null
+++ b/src/engine/splashscreen.h
@@ -0,0 +1,31 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// constants
11
12
13// structs
14
15
16// functions
17
18void SplashScreenCreate(
19 __in HINSTANCE hInstance,
20 __in_z_opt LPCWSTR wzCaption,
21 __out HWND* pHwnd
22 );
23HRESULT SplashScreenDisplayError(
24 __in BOOTSTRAPPER_DISPLAY display,
25 __in_z LPCWSTR wzBundleName,
26 __in HRESULT hrError
27 );
28
29#if defined(__cplusplus)
30}
31#endif
diff --git a/src/engine/uithread.cpp b/src/engine/uithread.cpp
new file mode 100644
index 00000000..cf111745
--- /dev/null
+++ b/src/engine/uithread.cpp
@@ -0,0 +1,220 @@
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#include "precomp.h"
4
5#define BURN_UITHREAD_CLASS_WINDOW L"WixBurnMessageWindow"
6
7
8// structs
9
10struct UITHREAD_CONTEXT
11{
12 HANDLE hInitializedEvent;
13 HINSTANCE hInstance;
14 BURN_ENGINE_STATE* pEngineState;
15};
16
17struct UITHREAD_INFO
18{
19 BOOL fElevated;
20 BURN_USER_EXPERIENCE* pUserExperience;
21};
22
23
24// internal function declarations
25
26static DWORD WINAPI ThreadProc(
27 __in LPVOID pvContext
28 );
29
30static LRESULT CALLBACK WndProc(
31 __in HWND hWnd,
32 __in UINT uMsg,
33 __in WPARAM wParam,
34 __in LPARAM lParam
35 );
36
37
38// function definitions
39
40HRESULT UiCreateMessageWindow(
41 __in HINSTANCE hInstance,
42 __in BURN_ENGINE_STATE* pEngineState
43 )
44{
45 HRESULT hr = S_OK;
46 HANDLE rgWaitHandles[2] = { };
47 UITHREAD_CONTEXT context = { };
48
49 // Create event to signal after the UI thread / window is initialized.
50 rgWaitHandles[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL);
51 ExitOnNullWithLastError(rgWaitHandles[0], hr, "Failed to create initialization event.");
52
53 // Pass necessary information to create the window.
54 context.hInitializedEvent = rgWaitHandles[0];
55 context.hInstance = hInstance;
56 context.pEngineState = pEngineState;
57
58 // Create our separate UI thread.
59 rgWaitHandles[1] = ::CreateThread(NULL, 0, ThreadProc, &context, 0, NULL);
60 ExitOnNullWithLastError(rgWaitHandles[1], hr, "Failed to create the UI thread.");
61
62 // Wait for either the thread to be initialized or the window to exit / fail prematurely.
63 ::WaitForMultipleObjects(countof(rgWaitHandles), rgWaitHandles, FALSE, INFINITE);
64
65 pEngineState->hMessageWindowThread = rgWaitHandles[1];
66 rgWaitHandles[1] = NULL;
67
68LExit:
69 ReleaseHandle(rgWaitHandles[1]);
70 ReleaseHandle(rgWaitHandles[0]);
71
72 return hr;
73}
74
75void UiCloseMessageWindow(
76 __in BURN_ENGINE_STATE* pEngineState
77 )
78{
79 if (::IsWindow(pEngineState->hMessageWindow))
80 {
81 ::PostMessageW(pEngineState->hMessageWindow, WM_CLOSE, 0, 0);
82
83 // Give the window 15 seconds to close because if it stays open it can prevent
84 // the engine from starting a reboot (should a reboot actually be necessary).
85 ::WaitForSingleObject(pEngineState->hMessageWindowThread, 15 * 1000);
86 }
87}
88
89
90// internal function definitions
91
92static DWORD WINAPI ThreadProc(
93 __in LPVOID pvContext
94 )
95{
96 HRESULT hr = S_OK;
97 UITHREAD_CONTEXT* pContext = static_cast<UITHREAD_CONTEXT*>(pvContext);
98 UITHREAD_INFO info = { };
99
100 WNDCLASSW wc = { };
101 BOOL fRegistered = TRUE;
102 HWND hWnd = NULL;
103
104 BOOL fRet = FALSE;
105 MSG msg = { };
106
107 BURN_ENGINE_STATE* pEngineState = pContext->pEngineState;
108 BOOL fElevated = BURN_MODE_ELEVATED == pContext->pEngineState->mode;
109
110 // If elevated, set up the thread local storage to store the correct pipe to communicate logging.
111 if (fElevated)
112 {
113 Assert(TLS_OUT_OF_INDEXES != pEngineState->dwElevatedLoggingTlsId);
114
115 if (!::TlsSetValue(pEngineState->dwElevatedLoggingTlsId, pEngineState->companionConnection.hPipe))
116 {
117 // If the function failed we cannot write to the pipe so just terminate.
118 ExitFunction1(hr = E_INVALIDSTATE);
119 }
120 }
121
122 wc.lpfnWndProc = WndProc;
123 wc.hInstance = pContext->hInstance;
124 wc.lpszClassName = BURN_UITHREAD_CLASS_WINDOW;
125
126 if (!::RegisterClassW(&wc))
127 {
128 ExitWithLastError(hr, "Failed to register window.");
129 }
130
131 fRegistered = TRUE;
132
133 info.fElevated = fElevated;
134 info.pUserExperience = &pEngineState->userExperience;
135
136 // Create the window to handle reboots without activating it.
137 hWnd = ::CreateWindowExW(WS_EX_TOOLWINDOW, wc.lpszClassName, NULL, WS_POPUP | WS_VISIBLE, CW_USEDEFAULT, SW_SHOWNA, 0, 0, HWND_DESKTOP, NULL, pContext->hInstance, &info);
138 ExitOnNullWithLastError(hWnd, hr, "Failed to create window.");
139
140 // Persist the window handle and let the caller know we've initialized.
141 pEngineState->hMessageWindow = hWnd;
142 ::SetEvent(pContext->hInitializedEvent);
143
144 // Pump messages until the window is closed.
145 while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
146 {
147 if (-1 == fRet)
148 {
149 hr = E_UNEXPECTED;
150 ExitOnFailure(hr, "Unexpected return value from message pump.");
151 }
152 else if (!::IsDialogMessageW(msg.hwnd, &msg))
153 {
154 ::TranslateMessage(&msg);
155 ::DispatchMessageW(&msg);
156 }
157 }
158
159LExit:
160 if (fRegistered)
161 {
162 ::UnregisterClassW(BURN_UITHREAD_CLASS_WINDOW, pContext->hInstance);
163 }
164
165 return hr;
166}
167
168static LRESULT CALLBACK WndProc(
169 __in HWND hWnd,
170 __in UINT uMsg,
171 __in WPARAM wParam,
172 __in LPARAM lParam
173 )
174{
175 switch (uMsg)
176 {
177 case WM_NCCREATE:
178 {
179 LPCREATESTRUCTW lpcs = reinterpret_cast<LPCREATESTRUCTW>(lParam);
180 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(lpcs->lpCreateParams));
181 break;
182 }
183
184 case WM_NCDESTROY:
185 {
186 LRESULT lRes = ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
187 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0);
188 return lRes;
189 }
190
191 case WM_QUERYENDSESSION:
192 {
193 DWORD dwEndSession = static_cast<DWORD>(lParam);
194 BOOL fCritical = ENDSESSION_CRITICAL & dwEndSession;
195 BOOL fCancel = TRUE;
196 BOOL fRet = FALSE;
197
198 // Always block shutdown in the elevated process, but ask the BA in the non-elevated.
199 UITHREAD_INFO* pInfo = reinterpret_cast<UITHREAD_INFO*>(::GetWindowLongW(hWnd, GWLP_USERDATA));
200 if (!pInfo->fElevated)
201 {
202 // TODO: instead of recommending canceling all non-critical shutdowns, maybe we should only recommend cancel
203 // when the engine is doing work?
204 fCancel = !fCritical;
205 // TODO: There's a race condition here where the BA may not have been loaded, or already was unloaded.
206 UserExperienceOnSystemShutdown(pInfo->pUserExperience, dwEndSession, &fCancel);
207 }
208
209 fRet = !fCancel;
210 LogId(REPORT_STANDARD, MSG_SYSTEM_SHUTDOWN, LoggingBoolToString(fCritical), LoggingBoolToString(pInfo->fElevated), LoggingBoolToString(fRet));
211 return fRet;
212 }
213
214 case WM_DESTROY:
215 ::PostQuitMessage(0);
216 return 0;
217 }
218
219 return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
220}
diff --git a/src/engine/uithread.h b/src/engine/uithread.h
new file mode 100644
index 00000000..41168d52
--- /dev/null
+++ b/src/engine/uithread.h
@@ -0,0 +1,23 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// functions
11
12HRESULT UiCreateMessageWindow(
13 __in HINSTANCE hInstance,
14 __in BURN_ENGINE_STATE* pEngineState
15 );
16
17void UiCloseMessageWindow(
18 __in BURN_ENGINE_STATE* pEngineState
19 );
20
21#if defined(__cplusplus)
22}
23#endif
diff --git a/src/engine/update.cpp b/src/engine/update.cpp
new file mode 100644
index 00000000..b04fa9a4
--- /dev/null
+++ b/src/engine/update.cpp
@@ -0,0 +1,44 @@
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#include "precomp.h"
4
5
6// internal function declarations
7
8
9// function definitions
10
11extern "C" HRESULT UpdateParseFromXml(
12 __in BURN_UPDATE* pUpdate,
13 __in IXMLDOMNode* pixnBundle
14 )
15{
16 HRESULT hr = S_OK;
17 IXMLDOMNode* pixnUpdateNode = NULL;
18
19 hr = XmlSelectSingleNode(pixnBundle, L"Update", &pixnUpdateNode);
20 if (S_FALSE == hr)
21 {
22 ExitFunction1(hr = S_OK);
23 }
24 ExitOnFailure(hr, "Failed to select Bundle/Update node.");
25
26 // @Location
27 hr = XmlGetAttributeEx(pixnUpdateNode, L"Location", &pUpdate->sczUpdateSource);
28 ExitOnFailure(hr, "Failed to get Update@Location.");
29
30LExit:
31 ReleaseObject(pixnUpdateNode);
32
33 return hr;
34}
35
36extern "C" void UpdateUninitialize(
37 __in BURN_UPDATE* pUpdate
38 )
39{
40 PackageUninitialize(&pUpdate->package);
41
42 ReleaseStr(pUpdate->sczUpdateSource);
43 memset(pUpdate, 0, sizeof(BURN_UPDATE));
44}
diff --git a/src/engine/update.h b/src/engine/update.h
new file mode 100644
index 00000000..67d40481
--- /dev/null
+++ b/src/engine/update.h
@@ -0,0 +1,33 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// structs
11
12typedef struct _BURN_UPDATE
13{
14 BOOL fUpdateAvailable;
15 LPWSTR sczUpdateSource;
16
17 BURN_PACKAGE package;
18} BURN_UPDATE;
19
20
21// function declarations
22
23HRESULT UpdateParseFromXml(
24 __in BURN_UPDATE* pUpdate,
25 __in IXMLDOMNode* pixnBundle
26 );
27void UpdateUninitialize(
28 __in BURN_UPDATE* pUpdate
29 );
30
31#if defined(__cplusplus)
32}
33#endif
diff --git a/src/engine/userexperience.cpp b/src/engine/userexperience.cpp
new file mode 100644
index 00000000..8d5271aa
--- /dev/null
+++ b/src/engine/userexperience.cpp
@@ -0,0 +1,2122 @@
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#include "precomp.h"
4
5// internal function declarations
6
7static int FilterResult(
8 __in DWORD dwAllowedResults,
9 __in int nResult
10 );
11
12static HRESULT FilterExecuteResult(
13 __in BURN_USER_EXPERIENCE* pUserExperience,
14 __in HRESULT hrStatus,
15 __in BOOL fRollback,
16 __in BOOL fCancel,
17 __in LPCWSTR sczEventName
18 );
19
20
21// function definitions
22
23/*******************************************************************
24 UserExperienceParseFromXml -
25
26*******************************************************************/
27extern "C" HRESULT UserExperienceParseFromXml(
28 __in BURN_USER_EXPERIENCE* pUserExperience,
29 __in IXMLDOMNode* pixnBundle
30 )
31{
32 HRESULT hr = S_OK;
33 IXMLDOMNode* pixnUserExperienceNode = NULL;
34
35 // select UX node
36 hr = XmlSelectSingleNode(pixnBundle, L"UX", &pixnUserExperienceNode);
37 if (S_FALSE == hr)
38 {
39 hr = E_NOTFOUND;
40 }
41 ExitOnFailure(hr, "Failed to select user experience node.");
42
43 // parse splash screen
44 hr = XmlGetYesNoAttribute(pixnUserExperienceNode, L"SplashScreen", &pUserExperience->fSplashScreen);
45 if (E_NOTFOUND != hr)
46 {
47 ExitOnFailure(hr, "Failed to to get UX/@SplashScreen");
48 }
49
50 // parse payloads
51 hr = PayloadsParseFromXml(&pUserExperience->payloads, NULL, NULL, pixnUserExperienceNode);
52 ExitOnFailure(hr, "Failed to parse user experience payloads.");
53
54 // make sure we have at least one payload
55 if (0 == pUserExperience->payloads.cPayloads)
56 {
57 hr = E_UNEXPECTED;
58 ExitOnFailure(hr, "Too few UX payloads.");
59 }
60
61LExit:
62 ReleaseObject(pixnUserExperienceNode);
63
64 return hr;
65}
66
67/*******************************************************************
68 UserExperienceUninitialize -
69
70*******************************************************************/
71extern "C" void UserExperienceUninitialize(
72 __in BURN_USER_EXPERIENCE* pUserExperience
73 )
74{
75 ReleaseStr(pUserExperience->sczTempDirectory);
76 PayloadsUninitialize(&pUserExperience->payloads);
77
78 // clear struct
79 memset(pUserExperience, 0, sizeof(BURN_USER_EXPERIENCE));
80}
81
82/*******************************************************************
83 UserExperienceLoad -
84
85*******************************************************************/
86extern "C" HRESULT UserExperienceLoad(
87 __in BURN_USER_EXPERIENCE* pUserExperience,
88 __in BOOTSTRAPPER_ENGINE_CONTEXT* pEngineContext,
89 __in BOOTSTRAPPER_COMMAND* pCommand
90 )
91{
92 HRESULT hr = S_OK;
93 BOOTSTRAPPER_CREATE_ARGS args = { };
94 BOOTSTRAPPER_CREATE_RESULTS results = { };
95
96 args.cbSize = sizeof(BOOTSTRAPPER_CREATE_ARGS);
97 args.pCommand = pCommand;
98 args.pfnBootstrapperEngineProc = EngineForApplicationProc;
99 args.pvBootstrapperEngineProcContext = pEngineContext;
100 args.qwEngineAPIVersion = MAKEQWORDVERSION(0, 0, 0, 5); // TODO: need to decide whether to keep this, and if so when to update it.
101
102 results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS);
103
104 // Load BA DLL.
105 pUserExperience->hUXModule = ::LoadLibraryExW(pUserExperience->payloads.rgPayloads[0].sczLocalFilePath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
106 ExitOnNullWithLastError(pUserExperience->hUXModule, hr, "Failed to load UX DLL.");
107
108 // Get BootstrapperApplicationCreate entry-point.
109 PFN_BOOTSTRAPPER_APPLICATION_CREATE pfnCreate = (PFN_BOOTSTRAPPER_APPLICATION_CREATE)::GetProcAddress(pUserExperience->hUXModule, "BootstrapperApplicationCreate");
110 ExitOnNullWithLastError(pfnCreate, hr, "Failed to get BootstrapperApplicationCreate entry-point");
111
112 // Create BA.
113 hr = pfnCreate(&args, &results);
114 ExitOnFailure(hr, "Failed to create BA.");
115
116 pUserExperience->pfnBAProc = results.pfnBootstrapperApplicationProc;
117 pUserExperience->pvBAProcContext = results.pvBootstrapperApplicationProcContext;
118
119LExit:
120 return hr;
121}
122
123/*******************************************************************
124 UserExperienceUnload -
125
126*******************************************************************/
127extern "C" HRESULT UserExperienceUnload(
128 __in BURN_USER_EXPERIENCE* pUserExperience
129 )
130{
131 HRESULT hr = S_OK;
132
133 if (pUserExperience->hUXModule)
134 {
135 // Get BootstrapperApplicationDestroy entry-point and call it if it exists.
136 PFN_BOOTSTRAPPER_APPLICATION_DESTROY pfnDestroy = (PFN_BOOTSTRAPPER_APPLICATION_DESTROY)::GetProcAddress(pUserExperience->hUXModule, "BootstrapperApplicationDestroy");
137 if (pfnDestroy)
138 {
139 pfnDestroy();
140 }
141
142 // Free BA DLL.
143 if (!::FreeLibrary(pUserExperience->hUXModule))
144 {
145 hr = HRESULT_FROM_WIN32(::GetLastError());
146 TraceError(hr, "Failed to unload BA DLL.");
147 }
148 pUserExperience->hUXModule = NULL;
149 }
150
151//LExit:
152 return hr;
153}
154
155extern "C" HRESULT UserExperienceEnsureWorkingFolder(
156 __in LPCWSTR wzBundleId,
157 __deref_out_z LPWSTR* psczUserExperienceWorkingFolder
158 )
159{
160 HRESULT hr = S_OK;
161 LPWSTR sczWorkingFolder = NULL;
162
163 hr = CacheEnsureWorkingFolder(wzBundleId, &sczWorkingFolder);
164 ExitOnFailure(hr, "Failed to create working folder.");
165
166 hr = StrAllocFormatted(psczUserExperienceWorkingFolder, L"%ls%ls\\", sczWorkingFolder, L".ba");
167 ExitOnFailure(hr, "Failed to calculate the bootstrapper application working path.");
168
169 hr = DirEnsureExists(*psczUserExperienceWorkingFolder, NULL);
170 ExitOnFailure(hr, "Failed create bootstrapper application working folder.");
171
172LExit:
173 ReleaseStr(sczWorkingFolder);
174
175 return hr;
176}
177
178
179extern "C" HRESULT UserExperienceRemove(
180 __in BURN_USER_EXPERIENCE* pUserExperience
181 )
182{
183 HRESULT hr = S_OK;
184
185 // Remove temporary UX directory
186 if (pUserExperience->sczTempDirectory)
187 {
188 hr = DirEnsureDeleteEx(pUserExperience->sczTempDirectory, DIR_DELETE_FILES | DIR_DELETE_RECURSE | DIR_DELETE_SCHEDULE);
189 TraceError(hr, "Could not delete bootstrapper application folder. Some files will be left in the temp folder.");
190 }
191
192//LExit:
193 return hr;
194}
195
196extern "C" int UserExperienceSendError(
197 __in BURN_USER_EXPERIENCE* pUserExperience,
198 __in BOOTSTRAPPER_ERROR_TYPE errorType,
199 __in_z_opt LPCWSTR wzPackageId,
200 __in HRESULT hrCode,
201 __in_z_opt LPCWSTR wzError,
202 __in DWORD uiFlags,
203 __in int nRecommendation
204 )
205{
206 int nResult = nRecommendation;
207 DWORD dwCode = HRESULT_CODE(hrCode);
208 LPWSTR sczError = NULL;
209
210 // If no error string was provided, try to get the error string from the HRESULT.
211 if (!wzError)
212 {
213 if (SUCCEEDED(StrAllocFromError(&sczError, hrCode, NULL)))
214 {
215 wzError = sczError;
216 }
217 }
218
219 UserExperienceOnError(pUserExperience, errorType, wzPackageId, dwCode, wzError, uiFlags, 0, NULL, &nResult); // ignore return value.
220
221 ReleaseStr(sczError);
222 return nResult;
223}
224
225extern "C" HRESULT UserExperienceActivateEngine(
226 __in BURN_USER_EXPERIENCE* pUserExperience,
227 __out_opt BOOL* pfActivated
228 )
229{
230 HRESULT hr = S_OK;
231 BOOL fActivated;
232
233 ::EnterCriticalSection(&pUserExperience->csEngineActive);
234 if (InterlockedCompareExchange(reinterpret_cast<LONG*>(&pUserExperience->fEngineActive), TRUE, FALSE))
235 {
236 AssertSz(FALSE, "Engine should have been deactivated before activating it.");
237
238 fActivated = FALSE;
239 hr = HRESULT_FROM_WIN32(ERROR_INVALID_STATE);
240 }
241 else
242 {
243 fActivated = TRUE;
244 }
245 ::LeaveCriticalSection(&pUserExperience->csEngineActive);
246
247 if (pfActivated)
248 {
249 *pfActivated = fActivated;
250 }
251 ExitOnRootFailure(hr, "Engine active cannot be changed because it was already in that state.");
252
253LExit:
254 return hr;
255}
256
257extern "C" void UserExperienceDeactivateEngine(
258 __in BURN_USER_EXPERIENCE* pUserExperience
259 )
260{
261 BOOL fActive = InterlockedExchange(reinterpret_cast<LONG*>(&pUserExperience->fEngineActive), FALSE);
262 fActive = fActive; // prevents warning in "ship" build.
263 AssertSz(fActive, "Engine should have be active before deactivating it.");
264}
265
266extern "C" HRESULT UserExperienceEnsureEngineInactive(
267 __in BURN_USER_EXPERIENCE* pUserExperience
268 )
269{
270 HRESULT hr = pUserExperience->fEngineActive ? HRESULT_FROM_WIN32(ERROR_BUSY) : S_OK;
271 ExitOnRootFailure(hr, "Engine is active, cannot proceed.");
272
273LExit:
274 return hr;
275}
276
277extern "C" void UserExperienceExecuteReset(
278 __in BURN_USER_EXPERIENCE* pUserExperience
279 )
280{
281 pUserExperience->hrApplyError = S_OK;
282 pUserExperience->hwndApply = NULL;
283}
284
285extern "C" void UserExperienceExecutePhaseComplete(
286 __in BURN_USER_EXPERIENCE* pUserExperience,
287 __in HRESULT hrResult
288 )
289{
290 if (FAILED(hrResult))
291 {
292 pUserExperience->hrApplyError = hrResult;
293 }
294}
295
296EXTERN_C BAAPI UserExperienceOnApplyBegin(
297 __in BURN_USER_EXPERIENCE* pUserExperience,
298 __in DWORD dwPhaseCount
299 )
300{
301 HRESULT hr = S_OK;
302 BA_ONAPPLYBEGIN_ARGS args = { };
303 BA_ONAPPLYBEGIN_RESULTS results = { };
304
305 args.cbSize = sizeof(args);
306 args.dwPhaseCount = dwPhaseCount;
307
308 results.cbSize = sizeof(results);
309
310 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYBEGIN, &args, &results, pUserExperience->pvBAProcContext);
311 ExitOnFailure(hr, "BA OnApplyBegin failed.");
312
313 if (results.fCancel)
314 {
315 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
316 }
317
318LExit:
319 return hr;
320}
321
322EXTERN_C BAAPI UserExperienceOnApplyComplete(
323 __in BURN_USER_EXPERIENCE* pUserExperience,
324 __in HRESULT hrStatus,
325 __in BOOTSTRAPPER_APPLY_RESTART restart,
326 __inout BOOTSTRAPPER_APPLYCOMPLETE_ACTION* pAction
327 )
328{
329 HRESULT hr = S_OK;
330 BA_ONAPPLYCOMPLETE_ARGS args = { };
331 BA_ONAPPLYCOMPLETE_RESULTS results = { };
332
333 args.cbSize = sizeof(args);
334 args.hrStatus = hrStatus;
335 args.restart = restart;
336 args.recommendation = *pAction;
337
338 results.cbSize = sizeof(results);
339 results.action = *pAction;
340
341 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYCOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
342 ExitOnFailure(hr, "BA OnApplyComplete failed.");
343
344 *pAction = results.action;
345
346LExit:
347 return hr;
348}
349
350EXTERN_C BAAPI UserExperienceOnCacheAcquireBegin(
351 __in BURN_USER_EXPERIENCE* pUserExperience,
352 __in_z_opt LPCWSTR wzPackageOrContainerId,
353 __in_z_opt LPCWSTR wzPayloadId,
354 __in BOOTSTRAPPER_CACHE_OPERATION operation,
355 __in_z LPCWSTR wzSource
356 )
357{
358 HRESULT hr = S_OK;
359 BA_ONCACHEACQUIREBEGIN_ARGS args = { };
360 BA_ONCACHEACQUIREBEGIN_RESULTS results = { };
361
362 args.cbSize = sizeof(args);
363 args.wzPackageOrContainerId = wzPackageOrContainerId;
364 args.wzPayloadId = wzPayloadId;
365 args.operation = operation;
366 args.wzSource = wzSource;
367
368 results.cbSize = sizeof(results);
369
370 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREBEGIN, &args, &results, pUserExperience->pvBAProcContext);
371 ExitOnFailure(hr, "BA OnCacheAcquireBegin failed.");
372
373 if (results.fCancel)
374 {
375 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
376 }
377
378LExit:
379 return hr;
380}
381
382EXTERN_C BAAPI UserExperienceOnCacheAcquireComplete(
383 __in BURN_USER_EXPERIENCE* pUserExperience,
384 __in_z_opt LPCWSTR wzPackageOrContainerId,
385 __in_z_opt LPCWSTR wzPayloadId,
386 __in HRESULT hrStatus,
387 __inout BOOL* pfRetry
388 )
389{
390 HRESULT hr = S_OK;
391 BA_ONCACHEACQUIRECOMPLETE_ARGS args = { };
392 BA_ONCACHEACQUIRECOMPLETE_RESULTS results = { };
393
394 args.cbSize = sizeof(args);
395 args.wzPackageOrContainerId = wzPackageOrContainerId;
396 args.wzPayloadId = wzPayloadId;
397 args.hrStatus = hrStatus;
398 args.recommendation = *pfRetry ? BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_RETRY : BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_NONE;
399
400 results.cbSize = sizeof(results);
401 results.action = args.recommendation;
402
403 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIRECOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
404 ExitOnFailure(hr, "BA OnCacheAcquireComplete failed.");
405
406 if (FAILED(hrStatus))
407 {
408 *pfRetry = BOOTSTRAPPER_CACHEACQUIRECOMPLETE_ACTION_RETRY == results.action;
409 }
410
411LExit:
412 return hr;
413}
414
415EXTERN_C BAAPI UserExperienceOnCacheAcquireProgress(
416 __in BURN_USER_EXPERIENCE* pUserExperience,
417 __in_z_opt LPCWSTR wzPackageOrContainerId,
418 __in_z_opt LPCWSTR wzPayloadId,
419 __in DWORD64 dw64Progress,
420 __in DWORD64 dw64Total,
421 __in DWORD dwOverallPercentage
422 )
423{
424 HRESULT hr = S_OK;
425 BA_ONCACHEACQUIREPROGRESS_ARGS args = { };
426 BA_ONCACHEACQUIREPROGRESS_RESULTS results = { };
427
428 args.cbSize = sizeof(args);
429 args.wzPackageOrContainerId = wzPackageOrContainerId;
430 args.wzPayloadId = wzPayloadId;
431 args.dw64Progress = dw64Progress;
432 args.dw64Total = dw64Total;
433 args.dwOverallPercentage = dwOverallPercentage;
434
435 results.cbSize = sizeof(results);
436
437 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEACQUIREPROGRESS, &args, &results, pUserExperience->pvBAProcContext);
438 ExitOnFailure(hr, "BA OnCacheAcquireProgress failed.");
439
440 if (results.fCancel)
441 {
442 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
443 }
444
445LExit:
446 return hr;
447}
448
449EXTERN_C BAAPI UserExperienceOnCacheBegin(
450 __in BURN_USER_EXPERIENCE* pUserExperience
451 )
452{
453 HRESULT hr = S_OK;
454 BA_ONCACHEBEGIN_ARGS args = { };
455 BA_ONCACHEBEGIN_RESULTS results = { };
456
457 args.cbSize = sizeof(args);
458
459 results.cbSize = sizeof(results);
460
461 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEBEGIN, &args, &results, pUserExperience->pvBAProcContext);
462 ExitOnFailure(hr, "BA OnCacheBegin failed.");
463
464 if (results.fCancel)
465 {
466 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
467 }
468
469LExit:
470 return hr;
471}
472
473EXTERN_C BAAPI UserExperienceOnCacheComplete(
474 __in BURN_USER_EXPERIENCE* pUserExperience,
475 __in HRESULT hrStatus
476 )
477{
478 HRESULT hr = S_OK;
479 BA_ONCACHECOMPLETE_ARGS args = { };
480 BA_ONCACHECOMPLETE_RESULTS results = { };
481
482 args.cbSize = sizeof(args);
483 args.hrStatus = hrStatus;
484
485 results.cbSize = sizeof(results);
486
487 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHECOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
488 ExitOnFailure(hr, "BA OnCacheComplete failed.");
489
490LExit:
491 return hr;
492}
493
494EXTERN_C BAAPI UserExperienceOnCachePackageBegin(
495 __in BURN_USER_EXPERIENCE* pUserExperience,
496 __in_z LPCWSTR wzPackageId,
497 __in DWORD cCachePayloads,
498 __in DWORD64 dw64PackageCacheSize
499 )
500{
501 HRESULT hr = S_OK;
502 BA_ONCACHEPACKAGEBEGIN_ARGS args = { };
503 BA_ONCACHEPACKAGEBEGIN_RESULTS results = { };
504
505 args.cbSize = sizeof(args);
506 args.wzPackageId = wzPackageId;
507 args.cCachePayloads = cCachePayloads;
508 args.dw64PackageCacheSize = dw64PackageCacheSize;
509
510 results.cbSize = sizeof(results);
511
512 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGEBEGIN, &args, &results, pUserExperience->pvBAProcContext);
513 ExitOnFailure(hr, "BA OnCachePackageBegin failed.");
514
515 if (results.fCancel)
516 {
517 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
518 }
519
520LExit:
521 return hr;
522}
523
524EXTERN_C BAAPI UserExperienceOnCachePackageComplete(
525 __in BURN_USER_EXPERIENCE* pUserExperience,
526 __in_z LPCWSTR wzPackageId,
527 __in HRESULT hrStatus,
528 __inout BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION* pAction
529 )
530{
531 HRESULT hr = S_OK;
532 BA_ONCACHEPACKAGECOMPLETE_ARGS args = { };
533 BA_ONCACHEPACKAGECOMPLETE_RESULTS results = { };
534
535 args.cbSize = sizeof(args);
536 args.wzPackageId = wzPackageId;
537 args.hrStatus = hrStatus;
538 args.recommendation = *pAction;
539
540 results.cbSize = sizeof(results);
541 results.action = *pAction;
542
543 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGECOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
544 ExitOnFailure(hr, "BA OnCachePackageComplete failed.");
545
546 if (FAILED(hrStatus))
547 {
548 *pAction = results.action;
549 }
550
551LExit:
552 return hr;
553}
554
555EXTERN_C BAAPI UserExperienceOnCacheVerifyBegin(
556 __in BURN_USER_EXPERIENCE* pUserExperience,
557 __in_z_opt LPCWSTR wzPackageOrContainerId,
558 __in_z_opt LPCWSTR wzPayloadId
559 )
560{
561 HRESULT hr = S_OK;
562 BA_ONCACHEVERIFYBEGIN_ARGS args = { };
563 BA_ONCACHEVERIFYBEGIN_RESULTS results = { };
564
565 args.cbSize = sizeof(args);
566 args.wzPackageOrContainerId = wzPackageOrContainerId;
567 args.wzPayloadId = wzPayloadId;
568
569 results.cbSize = sizeof(results);
570
571 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYBEGIN, &args, &results, pUserExperience->pvBAProcContext);
572 ExitOnFailure(hr, "BA OnCacheVerifyBegin failed.");
573
574 if (results.fCancel)
575 {
576 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
577 }
578
579LExit:
580 return hr;
581}
582
583EXTERN_C BAAPI UserExperienceOnCacheVerifyComplete(
584 __in BURN_USER_EXPERIENCE* pUserExperience,
585 __in_z_opt LPCWSTR wzPackageOrContainerId,
586 __in_z_opt LPCWSTR wzPayloadId,
587 __in HRESULT hrStatus,
588 __inout BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION* pAction
589 )
590{
591 HRESULT hr = S_OK;
592 BA_ONCACHEVERIFYCOMPLETE_ARGS args = { };
593 BA_ONCACHEVERIFYCOMPLETE_RESULTS results = { };
594
595 args.cbSize = sizeof(args);
596 args.wzPackageOrContainerId = wzPackageOrContainerId;
597 args.wzPayloadId = wzPayloadId;
598 args.hrStatus = hrStatus;
599 args.recommendation = *pAction;
600
601 results.cbSize = sizeof(results);
602 results.action = *pAction;
603
604 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEVERIFYCOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
605 ExitOnFailure(hr, "BA OnCacheVerifyComplete failed.");
606
607 if (FAILED(hrStatus))
608 {
609 *pAction = results.action;
610 }
611
612LExit:
613 return hr;
614}
615
616EXTERN_C BAAPI UserExperienceOnDetectBegin(
617 __in BURN_USER_EXPERIENCE* pUserExperience,
618 __in BOOL fInstalled,
619 __in DWORD cPackages
620 )
621{
622 HRESULT hr = S_OK;
623 BA_ONDETECTBEGIN_ARGS args = { };
624 BA_ONDETECTBEGIN_RESULTS results = { };
625
626 args.cbSize = sizeof(args);
627 args.cPackages = cPackages;
628 args.fInstalled = fInstalled;
629
630 results.cbSize = sizeof(results);
631
632 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTBEGIN, &args, &results, pUserExperience->pvBAProcContext);
633 ExitOnFailure(hr, "BA OnDetectBegin failed.");
634
635 if (results.fCancel)
636 {
637 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
638 }
639
640LExit:
641 return hr;
642}
643
644EXTERN_C BAAPI UserExperienceOnDetectCompatibleMsiPackage(
645 __in BURN_USER_EXPERIENCE* pUserExperience,
646 __in_z LPCWSTR wzPackageId,
647 __in_z LPCWSTR wzCompatiblePackageId,
648 __in DWORD64 dw64CompatiblePackageVersion
649 )
650{
651 HRESULT hr = S_OK;
652 BA_ONDETECTCOMPATIBLEMSIPACKAGE_ARGS args = { };
653 BA_ONDETECTCOMPATIBLEMSIPACKAGE_RESULTS results = { };
654
655 args.cbSize = sizeof(args);
656 args.wzPackageId = wzPackageId;
657 args.wzCompatiblePackageId = wzCompatiblePackageId;
658 args.dw64CompatiblePackageVersion = dw64CompatiblePackageVersion;
659
660 results.cbSize = sizeof(results);
661
662 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTCOMPATIBLEMSIPACKAGE, &args, &results, pUserExperience->pvBAProcContext);
663 ExitOnFailure(hr, "BA OnDetectCompatibleMsiPackage failed.");
664
665 if (results.fCancel)
666 {
667 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
668 }
669
670LExit:
671 return hr;
672}
673
674EXTERN_C BAAPI UserExperienceOnDetectComplete(
675 __in BURN_USER_EXPERIENCE* pUserExperience,
676 __in HRESULT hrStatus
677 )
678{
679 HRESULT hr = S_OK;
680 BA_ONDETECTCOMPLETE_ARGS args = { };
681 BA_ONDETECTCOMPLETE_RESULTS results = { };
682
683 args.cbSize = sizeof(args);
684 args.hrStatus = hrStatus;
685
686 results.cbSize = sizeof(results);
687
688 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTCOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
689 ExitOnFailure(hr, "BA OnDetectComplete failed.");
690
691LExit:
692 return hr;
693}
694
695EXTERN_C BAAPI UserExperienceOnDetectForwardCompatibleBundle(
696 __in BURN_USER_EXPERIENCE* pUserExperience,
697 __in_z LPCWSTR wzBundleId,
698 __in BOOTSTRAPPER_RELATION_TYPE relationType,
699 __in_z LPCWSTR wzBundleTag,
700 __in BOOL fPerMachine,
701 __in DWORD64 dw64Version,
702 __inout BOOL* pfIgnoreBundle
703 )
704{
705 HRESULT hr = S_OK;
706 BA_ONDETECTFORWARDCOMPATIBLEBUNDLE_ARGS args = { };
707 BA_ONDETECTFORWARDCOMPATIBLEBUNDLE_RESULTS results = { };
708
709 args.cbSize = sizeof(args);
710 args.wzBundleId = wzBundleId;
711 args.relationType = relationType;
712 args.wzBundleTag = wzBundleTag;
713 args.fPerMachine = fPerMachine;
714 args.dw64Version = dw64Version;
715
716 results.cbSize = sizeof(results);
717 results.fIgnoreBundle = *pfIgnoreBundle;
718
719 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTFORWARDCOMPATIBLEBUNDLE, &args, &results, pUserExperience->pvBAProcContext);
720 ExitOnFailure(hr, "BA OnDetectForwardCompatibleBundle failed.");
721
722 if (results.fCancel)
723 {
724 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
725 }
726 *pfIgnoreBundle = results.fIgnoreBundle;
727
728LExit:
729 return hr;
730}
731
732EXTERN_C BAAPI UserExperienceOnDetectMsiFeature(
733 __in BURN_USER_EXPERIENCE* pUserExperience,
734 __in_z LPCWSTR wzPackageId,
735 __in_z LPCWSTR wzFeatureId,
736 __in BOOTSTRAPPER_FEATURE_STATE state
737 )
738{
739 HRESULT hr = S_OK;
740 BA_ONDETECTMSIFEATURE_ARGS args = { };
741 BA_ONDETECTMSIFEATURE_RESULTS results = { };
742
743 args.cbSize = sizeof(args);
744 args.wzPackageId = wzPackageId;
745 args.wzFeatureId = wzFeatureId;
746 args.state = state;
747
748 results.cbSize = sizeof(results);
749
750 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTMSIFEATURE, &args, &results, pUserExperience->pvBAProcContext);
751 ExitOnFailure(hr, "BA OnDetectMsiFeature failed.");
752
753 if (results.fCancel)
754 {
755 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
756 }
757
758LExit:
759 return hr;
760}
761
762EXTERN_C BAAPI UserExperienceOnDetectPackageBegin(
763 __in BURN_USER_EXPERIENCE* pUserExperience,
764 __in_z LPCWSTR wzPackageId
765 )
766{
767 HRESULT hr = S_OK;
768 BA_ONDETECTPACKAGEBEGIN_ARGS args = { };
769 BA_ONDETECTPACKAGEBEGIN_RESULTS results = { };
770
771 args.cbSize = sizeof(args);
772 args.wzPackageId = wzPackageId;
773
774 results.cbSize = sizeof(results);
775
776 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPACKAGEBEGIN, &args, &results, pUserExperience->pvBAProcContext);
777 ExitOnFailure(hr, "BA OnDetectPackageBegin failed.");
778
779 if (results.fCancel)
780 {
781 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
782 }
783
784LExit:
785 return hr;
786}
787
788EXTERN_C BAAPI UserExperienceOnDetectPackageComplete(
789 __in BURN_USER_EXPERIENCE* pUserExperience,
790 __in_z LPCWSTR wzPackageId,
791 __in HRESULT hrStatus,
792 __in BOOTSTRAPPER_PACKAGE_STATE state
793 )
794{
795 HRESULT hr = S_OK;
796 BA_ONDETECTPACKAGECOMPLETE_ARGS args = { };
797 BA_ONDETECTPACKAGECOMPLETE_RESULTS results = { };
798
799 args.cbSize = sizeof(args);
800 args.wzPackageId = wzPackageId;
801 args.hrStatus = hrStatus;
802 args.state = state;
803
804 results.cbSize = sizeof(results);
805
806 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTPACKAGECOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
807 ExitOnFailure(hr, "BA OnDetectPackageComplete failed.");
808
809LExit:
810 return hr;
811}
812
813EXTERN_C BAAPI UserExperienceOnDetectRelatedBundle(
814 __in BURN_USER_EXPERIENCE* pUserExperience,
815 __in_z LPCWSTR wzBundleId,
816 __in BOOTSTRAPPER_RELATION_TYPE relationType,
817 __in_z LPCWSTR wzBundleTag,
818 __in BOOL fPerMachine,
819 __in DWORD64 dw64Version,
820 __in BOOTSTRAPPER_RELATED_OPERATION operation
821 )
822{
823 HRESULT hr = S_OK;
824 BA_ONDETECTRELATEDBUNDLE_ARGS args = { };
825 BA_ONDETECTRELATEDBUNDLE_RESULTS results = { };
826
827 args.cbSize = sizeof(args);
828 args.wzBundleId = wzBundleId;
829 args.relationType = relationType;
830 args.wzBundleTag = wzBundleTag;
831 args.fPerMachine = fPerMachine;
832 args.dw64Version = dw64Version;
833 args.operation = operation;
834
835 results.cbSize = sizeof(results);
836
837 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDBUNDLE, &args, &results, pUserExperience->pvBAProcContext);
838 ExitOnFailure(hr, "BA OnDetectRelatedBundle failed.");
839
840 if (results.fCancel)
841 {
842 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
843 }
844
845LExit:
846 return hr;
847}
848
849EXTERN_C BAAPI UserExperienceOnDetectRelatedMsiPackage(
850 __in BURN_USER_EXPERIENCE* pUserExperience,
851 __in_z LPCWSTR wzPackageId,
852 __in_z LPCWSTR wzUpgradeCode,
853 __in_z LPCWSTR wzProductCode,
854 __in BOOL fPerMachine,
855 __in DWORD64 dw64Version,
856 __in BOOTSTRAPPER_RELATED_OPERATION operation
857 )
858{
859 HRESULT hr = S_OK;
860 BA_ONDETECTRELATEDMSIPACKAGE_ARGS args = { };
861 BA_ONDETECTRELATEDMSIPACKAGE_RESULTS results = { };
862
863 args.cbSize = sizeof(args);
864 args.wzPackageId = wzPackageId;
865 args.wzUpgradeCode = wzUpgradeCode;
866 args.wzProductCode = wzProductCode;
867 args.fPerMachine = fPerMachine;
868 args.dw64Version = dw64Version;
869 args.operation = operation;
870
871 results.cbSize = sizeof(results);
872
873 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDMSIPACKAGE, &args, &results, pUserExperience->pvBAProcContext);
874 ExitOnFailure(hr, "BA OnDetectRelatedMsiPackage failed.");
875
876 if (results.fCancel)
877 {
878 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
879 }
880
881LExit:
882 return hr;
883}
884
885EXTERN_C BAAPI UserExperienceOnDetectTargetMsiPackage(
886 __in BURN_USER_EXPERIENCE* pUserExperience,
887 __in_z LPCWSTR wzPackageId,
888 __in_z LPCWSTR wzProductCode,
889 __in BOOTSTRAPPER_PACKAGE_STATE patchState
890 )
891{
892 HRESULT hr = S_OK;
893 BA_ONDETECTTARGETMSIPACKAGE_ARGS args = { };
894 BA_ONDETECTTARGETMSIPACKAGE_RESULTS results = { };
895
896 args.cbSize = sizeof(args);
897 args.wzPackageId = wzPackageId;
898 args.wzProductCode = wzProductCode;
899 args.patchState = patchState;
900
901 results.cbSize = sizeof(results);
902
903 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTTARGETMSIPACKAGE, &args, &results, pUserExperience->pvBAProcContext);
904 ExitOnFailure(hr, "BA OnDetectTargetMsiPackage failed.");
905
906 if (results.fCancel)
907 {
908 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
909 }
910
911LExit:
912 return hr;
913}
914
915EXTERN_C BAAPI UserExperienceOnDetectUpdate(
916 __in BURN_USER_EXPERIENCE* pUserExperience,
917 __in_z LPCWSTR wzUpdateLocation,
918 __in DWORD64 dw64Size,
919 __in DWORD64 dw64Version,
920 __in_z_opt LPCWSTR wzTitle,
921 __in_z_opt LPCWSTR wzSummary,
922 __in_z_opt LPCWSTR wzContentType,
923 __in_z_opt LPCWSTR wzContent,
924 __inout BOOL* pfStopProcessingUpdates
925 )
926{
927 HRESULT hr = S_OK;
928 BA_ONDETECTUPDATE_ARGS args = { };
929 BA_ONDETECTUPDATE_RESULTS results = { };
930
931 args.cbSize = sizeof(args);
932 args.wzUpdateLocation = wzUpdateLocation;
933 args.dw64Size = dw64Size;
934 args.dw64Version = dw64Version;
935 args.wzTitle = wzTitle;
936 args.wzSummary = wzSummary;
937 args.wzContentType = wzContentType;
938 args.wzContent = wzContent;
939
940 results.cbSize = sizeof(results);
941 results.fStopProcessingUpdates = *pfStopProcessingUpdates;
942
943 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATE, &args, &results, pUserExperience->pvBAProcContext);
944 ExitOnFailure(hr, "BA OnDetectUpdate failed.");
945
946 if (results.fCancel)
947 {
948 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
949 }
950 *pfStopProcessingUpdates = results.fStopProcessingUpdates;
951
952LExit:
953 return hr;
954}
955
956EXTERN_C BAAPI UserExperienceOnDetectUpdateBegin(
957 __in BURN_USER_EXPERIENCE* pUserExperience,
958 __in_z LPCWSTR wzUpdateLocation,
959 __inout BOOL* pfSkip
960 )
961{
962 HRESULT hr = S_OK;
963 BA_ONDETECTUPDATEBEGIN_ARGS args = { };
964 BA_ONDETECTUPDATEBEGIN_RESULTS results = { };
965
966 args.cbSize = sizeof(args);
967 args.wzUpdateLocation = wzUpdateLocation;
968
969 results.cbSize = sizeof(results);
970 results.fSkip = *pfSkip;
971
972 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATEBEGIN, &args, &results, pUserExperience->pvBAProcContext);
973 ExitOnFailure(hr, "BA OnDetectUpdateBegin failed.");
974
975 if (results.fCancel)
976 {
977 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
978 }
979 *pfSkip = results.fSkip;
980
981LExit:
982 return hr;
983}
984
985EXTERN_C BAAPI UserExperienceOnDetectUpdateComplete(
986 __in BURN_USER_EXPERIENCE* pUserExperience,
987 __in HRESULT hrStatus,
988 __inout BOOL* pfIgnoreError
989 )
990{
991 HRESULT hr = S_OK;
992 BA_ONDETECTUPDATECOMPLETE_ARGS args = { };
993 BA_ONDETECTUPDATECOMPLETE_RESULTS results = { };
994
995 args.cbSize = sizeof(args);
996 args.hrStatus = hrStatus;
997
998 results.cbSize = sizeof(results);
999 results.fIgnoreError = *pfIgnoreError;
1000
1001 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTUPDATECOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
1002 ExitOnFailure(hr, "BA OnDetectUpdateComplete failed.");
1003
1004 if (FAILED(hrStatus))
1005 {
1006 *pfIgnoreError = results.fIgnoreError;
1007 }
1008
1009LExit:
1010 return hr;
1011}
1012
1013EXTERN_C BAAPI UserExperienceOnElevateBegin(
1014 __in BURN_USER_EXPERIENCE* pUserExperience
1015 )
1016{
1017 HRESULT hr = S_OK;
1018 BA_ONELEVATEBEGIN_ARGS args = { };
1019 BA_ONELEVATEBEGIN_RESULTS results = { };
1020
1021 args.cbSize = sizeof(args);
1022
1023 results.cbSize = sizeof(results);
1024
1025 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONELEVATEBEGIN, &args, &results, pUserExperience->pvBAProcContext);
1026 ExitOnFailure(hr, "BA OnElevateBegin failed.");
1027
1028 if (results.fCancel)
1029 {
1030 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1031 }
1032
1033LExit:
1034 return hr;
1035}
1036
1037EXTERN_C BAAPI UserExperienceOnElevateComplete(
1038 __in BURN_USER_EXPERIENCE* pUserExperience,
1039 __in HRESULT hrStatus
1040 )
1041{
1042 HRESULT hr = S_OK;
1043 BA_ONELEVATECOMPLETE_ARGS args = { };
1044 BA_ONELEVATECOMPLETE_RESULTS results = { };
1045
1046 args.cbSize = sizeof(args);
1047 args.hrStatus = hrStatus;
1048
1049 results.cbSize = sizeof(results);
1050
1051 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONELEVATECOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
1052 ExitOnFailure(hr, "BA OnElevateComplete failed.");
1053
1054LExit:
1055 return hr;
1056}
1057
1058EXTERN_C BAAPI UserExperienceOnError(
1059 __in BURN_USER_EXPERIENCE* pUserExperience,
1060 __in BOOTSTRAPPER_ERROR_TYPE errorType,
1061 __in_z_opt LPCWSTR wzPackageId,
1062 __in DWORD dwCode,
1063 __in_z_opt LPCWSTR wzError,
1064 __in DWORD dwUIHint,
1065 __in DWORD cData,
1066 __in_ecount_z_opt(cData) LPCWSTR* rgwzData,
1067 __inout int* pnResult
1068 )
1069{
1070 HRESULT hr = S_OK;
1071 BA_ONERROR_ARGS args = { };
1072 BA_ONERROR_RESULTS results = { };
1073
1074 args.cbSize = sizeof(args);
1075 args.errorType = errorType;
1076 args.wzPackageId = wzPackageId;
1077 args.dwCode = dwCode;
1078 args.wzError = wzError;
1079 args.dwUIHint = dwUIHint;
1080 args.cData = cData;
1081 args.rgwzData = rgwzData;
1082 args.nRecommendation = *pnResult;
1083
1084 results.cbSize = sizeof(results);
1085 results.nResult = *pnResult;
1086
1087 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONERROR, &args, &results, pUserExperience->pvBAProcContext);
1088 ExitOnFailure(hr, "BA OnError failed.");
1089
1090 *pnResult = results.nResult;
1091
1092LExit:
1093 return hr;
1094}
1095
1096EXTERN_C BAAPI UserExperienceOnExecuteBegin(
1097 __in BURN_USER_EXPERIENCE* pUserExperience,
1098 __in DWORD cExecutingPackages
1099 )
1100{
1101 HRESULT hr = S_OK;
1102 BA_ONEXECUTEBEGIN_ARGS args = { };
1103 BA_ONEXECUTEBEGIN_RESULTS results = { };
1104
1105 args.cbSize = sizeof(args);
1106 args.cExecutingPackages = cExecutingPackages;
1107
1108 results.cbSize = sizeof(results);
1109
1110 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEBEGIN, &args, &results, pUserExperience->pvBAProcContext);
1111 ExitOnFailure(hr, "BA OnExecuteBegin failed.");
1112
1113 if (results.fCancel)
1114 {
1115 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1116 }
1117
1118LExit:
1119 return hr;
1120}
1121
1122EXTERN_C BAAPI UserExperienceOnExecuteComplete(
1123 __in BURN_USER_EXPERIENCE* pUserExperience,
1124 __in HRESULT hrStatus
1125 )
1126{
1127 HRESULT hr = S_OK;
1128 BA_ONEXECUTECOMPLETE_ARGS args = { };
1129 BA_ONEXECUTECOMPLETE_RESULTS results = { };
1130
1131 args.cbSize = sizeof(args);
1132 args.hrStatus = hrStatus;
1133
1134 results.cbSize = sizeof(results);
1135
1136 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTECOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
1137 ExitOnFailure(hr, "BA OnExecuteComplete failed.");
1138
1139LExit:
1140 return hr;
1141}
1142
1143EXTERN_C BAAPI UserExperienceOnExecuteFilesInUse(
1144 __in BURN_USER_EXPERIENCE* pUserExperience,
1145 __in_z LPCWSTR wzPackageId,
1146 __in DWORD cFiles,
1147 __in_ecount_z_opt(cFiles) LPCWSTR* rgwzFiles,
1148 __inout int* pnResult
1149 )
1150{
1151 HRESULT hr = S_OK;
1152 BA_ONEXECUTEFILESINUSE_ARGS args = { };
1153 BA_ONEXECUTEFILESINUSE_RESULTS results = { };
1154
1155 args.cbSize = sizeof(args);
1156 args.wzPackageId = wzPackageId;
1157 args.cFiles = cFiles;
1158 args.rgwzFiles = rgwzFiles;
1159 args.nRecommendation = *pnResult;
1160
1161 results.cbSize = sizeof(results);
1162 results.nResult = *pnResult;
1163
1164 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEFILESINUSE, &args, &results, pUserExperience->pvBAProcContext);
1165 ExitOnFailure(hr, "BA OnExecuteFilesInUse failed.");
1166
1167 *pnResult = results.nResult;
1168
1169LExit:
1170 return hr;
1171}
1172
1173EXTERN_C BAAPI UserExperienceOnExecuteMsiMessage(
1174 __in BURN_USER_EXPERIENCE* pUserExperience,
1175 __in_z LPCWSTR wzPackageId,
1176 __in INSTALLMESSAGE messageType,
1177 __in DWORD dwUIHint,
1178 __in_z LPCWSTR wzMessage,
1179 __in DWORD cData,
1180 __in_ecount_z_opt(cData) LPCWSTR* rgwzData,
1181 __inout int* pnResult
1182 )
1183{
1184 HRESULT hr = S_OK;
1185 BA_ONEXECUTEMSIMESSAGE_ARGS args = { };
1186 BA_ONEXECUTEMSIMESSAGE_RESULTS results = { };
1187
1188 args.cbSize = sizeof(args);
1189 args.wzPackageId = wzPackageId;
1190 args.messageType = messageType;
1191 args.dwUIHint = dwUIHint;
1192 args.wzMessage = wzMessage;
1193 args.cData = cData;
1194 args.rgwzData = rgwzData;
1195 args.nRecommendation = *pnResult;
1196
1197 results.cbSize = sizeof(results);
1198 results.nResult = *pnResult;
1199
1200 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEMSIMESSAGE, &args, &results, pUserExperience->pvBAProcContext);
1201 ExitOnFailure(hr, "BA OnExecuteMsiMessage failed.");
1202
1203 *pnResult = results.nResult;
1204
1205LExit:
1206 return hr;
1207}
1208
1209EXTERN_C BAAPI UserExperienceOnExecutePackageBegin(
1210 __in BURN_USER_EXPERIENCE* pUserExperience,
1211 __in_z LPCWSTR wzPackageId,
1212 __in BOOL fExecute
1213 )
1214{
1215 HRESULT hr = S_OK;
1216 BA_ONEXECUTEPACKAGEBEGIN_ARGS args = { };
1217 BA_ONEXECUTEPACKAGEBEGIN_RESULTS results = { };
1218
1219 args.cbSize = sizeof(args);
1220 args.wzPackageId = wzPackageId;
1221 args.fExecute = fExecute;
1222
1223 results.cbSize = sizeof(results);
1224
1225 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPACKAGEBEGIN, &args, &results, pUserExperience->pvBAProcContext);
1226 ExitOnFailure(hr, "BA OnExecutePackageBegin failed.");
1227
1228 if (results.fCancel)
1229 {
1230 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1231 }
1232
1233LExit:
1234 return hr;
1235}
1236
1237EXTERN_C BAAPI UserExperienceOnExecutePackageComplete(
1238 __in BURN_USER_EXPERIENCE* pUserExperience,
1239 __in_z LPCWSTR wzPackageId,
1240 __in HRESULT hrStatus,
1241 __in BOOTSTRAPPER_APPLY_RESTART restart,
1242 __inout BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION* pAction
1243 )
1244{
1245 HRESULT hr = S_OK;
1246 BA_ONEXECUTEPACKAGECOMPLETE_ARGS args = { };
1247 BA_ONEXECUTEPACKAGECOMPLETE_RESULTS results = { };
1248
1249 args.cbSize = sizeof(args);
1250 args.wzPackageId = wzPackageId;
1251 args.hrStatus = hrStatus;
1252 args.restart = restart;
1253 args.recommendation = *pAction;
1254
1255 results.cbSize = sizeof(results);
1256 results.action = *pAction;
1257
1258 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPACKAGECOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
1259 ExitOnFailure(hr, "BA OnExecutePackageComplete failed.");
1260
1261 *pAction = results.action;
1262
1263LExit:
1264 return hr;
1265}
1266
1267EXTERN_C BAAPI UserExperienceOnExecutePatchTarget(
1268 __in BURN_USER_EXPERIENCE* pUserExperience,
1269 __in_z LPCWSTR wzPackageId,
1270 __in_z LPCWSTR wzTargetProductCode
1271 )
1272{
1273 HRESULT hr = S_OK;
1274 BA_ONEXECUTEPATCHTARGET_ARGS args = { };
1275 BA_ONEXECUTEPATCHTARGET_RESULTS results = { };
1276
1277 args.cbSize = sizeof(args);
1278 args.wzPackageId = wzPackageId;
1279 args.wzTargetProductCode = wzTargetProductCode;
1280
1281 results.cbSize = sizeof(results);
1282
1283 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPATCHTARGET, &args, &results, pUserExperience->pvBAProcContext);
1284 ExitOnFailure(hr, "BA OnExecutePatchTarget failed.");
1285
1286 if (results.fCancel)
1287 {
1288 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1289 }
1290
1291LExit:
1292 return hr;
1293}
1294
1295EXTERN_C BAAPI UserExperienceOnExecuteProgress(
1296 __in BURN_USER_EXPERIENCE* pUserExperience,
1297 __in_z LPCWSTR wzPackageId,
1298 __in DWORD dwProgressPercentage,
1299 __in DWORD dwOverallPercentage,
1300 __out int* pnResult
1301 )
1302{
1303 HRESULT hr = S_OK;
1304 BA_ONEXECUTEPROGRESS_ARGS args = { };
1305 BA_ONEXECUTEPROGRESS_RESULTS results = { };
1306
1307 args.cbSize = sizeof(args);
1308 args.wzPackageId = wzPackageId;
1309 args.dwProgressPercentage = dwProgressPercentage;
1310 args.dwOverallPercentage = dwOverallPercentage;
1311
1312 results.cbSize = sizeof(results);
1313
1314 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPROGRESS, &args, &results, pUserExperience->pvBAProcContext);
1315 ExitOnFailure(hr, "BA OnExecuteProgress failed.");
1316
1317LExit:
1318 if (FAILED(hr))
1319 {
1320 *pnResult = IDERROR;
1321 }
1322 else if (results.fCancel)
1323 {
1324 *pnResult = IDCANCEL;
1325 }
1326 else
1327 {
1328 *pnResult = IDNOACTION;
1329 }
1330 return hr;
1331}
1332
1333EXTERN_C BAAPI UserExperienceOnLaunchApprovedExeBegin(
1334 __in BURN_USER_EXPERIENCE* pUserExperience
1335 )
1336{
1337 HRESULT hr = S_OK;
1338 BA_ONLAUNCHAPPROVEDEXEBEGIN_ARGS args = { };
1339 BA_ONLAUNCHAPPROVEDEXEBEGIN_RESULTS results = { };
1340
1341 args.cbSize = sizeof(args);
1342
1343 results.cbSize = sizeof(results);
1344
1345 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXEBEGIN, &args, &results, pUserExperience->pvBAProcContext);
1346 ExitOnFailure(hr, "BA OnLaunchApprovedExeBegin failed.");
1347
1348 if (results.fCancel)
1349 {
1350 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1351 }
1352
1353LExit:
1354 return hr;
1355}
1356
1357EXTERN_C BAAPI UserExperienceOnLaunchApprovedExeComplete(
1358 __in BURN_USER_EXPERIENCE* pUserExperience,
1359 __in HRESULT hrStatus,
1360 __in DWORD dwProcessId
1361 )
1362{
1363 HRESULT hr = S_OK;
1364 BA_ONLAUNCHAPPROVEDEXECOMPLETE_ARGS args = { };
1365 BA_ONLAUNCHAPPROVEDEXECOMPLETE_RESULTS results = { };
1366
1367 args.cbSize = sizeof(args);
1368 args.hrStatus = hrStatus;
1369 args.dwProcessId = dwProcessId;
1370
1371 results.cbSize = sizeof(results);
1372
1373 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXECOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
1374 ExitOnFailure(hr, "BA OnLaunchApprovedExeComplete failed.");
1375
1376LExit:
1377 return hr;
1378}
1379
1380EXTERN_C BAAPI UserExperienceOnPlanBegin(
1381 __in BURN_USER_EXPERIENCE* pUserExperience,
1382 __in DWORD cPackages
1383 )
1384{
1385 HRESULT hr = S_OK;
1386 BA_ONPLANBEGIN_ARGS args = { };
1387 BA_ONPLANBEGIN_RESULTS results = { };
1388
1389 args.cbSize = sizeof(args);
1390 args.cPackages = cPackages;
1391
1392 results.cbSize = sizeof(results);
1393
1394 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANBEGIN, &args, &results, pUserExperience->pvBAProcContext);
1395 ExitOnFailure(hr, "BA OnPlanBegin failed.");
1396
1397 if (results.fCancel)
1398 {
1399 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1400 }
1401
1402LExit:
1403 return hr;
1404}
1405
1406EXTERN_C BAAPI UserExperienceOnPlanCompatibleMsiPackageBegin(
1407 __in BURN_USER_EXPERIENCE* pUserExperience,
1408 __in_z LPCWSTR wzPackageId,
1409 __in_z LPCWSTR wzCompatiblePackageId,
1410 __in DWORD64 dw64CompatiblePackageVersion,
1411 __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
1412 )
1413{
1414 HRESULT hr = S_OK;
1415 BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_ARGS args = { };
1416 BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_RESULTS results = { };
1417
1418 args.cbSize = sizeof(args);
1419 args.wzPackageId = wzPackageId;
1420 args.wzCompatiblePackageId = wzCompatiblePackageId;
1421 args.dw64CompatiblePackageVersion = dw64CompatiblePackageVersion;
1422 args.recommendedState = *pRequestedState;
1423
1424 results.cbSize = sizeof(results);
1425 results.requestedState = *pRequestedState;
1426
1427 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGEBEGIN, &args, &results, pUserExperience->pvBAProcContext);
1428 ExitOnFailure(hr, "BA OnPlanCompatibleMsiPackageBegin failed.");
1429
1430 if (results.fCancel)
1431 {
1432 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1433 }
1434 *pRequestedState = results.requestedState;
1435
1436LExit:
1437 return hr;
1438}
1439
1440EXTERN_C BAAPI UserExperienceOnPlanCompatibleMsiPackageComplete(
1441 __in BURN_USER_EXPERIENCE* pUserExperience,
1442 __in_z LPCWSTR wzPackageId,
1443 __in_z LPCWSTR wzCompatiblePackageId,
1444 __in HRESULT hrStatus,
1445 __in BOOTSTRAPPER_PACKAGE_STATE state,
1446 __in BOOTSTRAPPER_REQUEST_STATE requested,
1447 __in BOOTSTRAPPER_ACTION_STATE execute,
1448 __in BOOTSTRAPPER_ACTION_STATE rollback
1449 )
1450{
1451 HRESULT hr = S_OK;
1452 BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_ARGS args = { };
1453 BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_RESULTS results = { };
1454
1455 args.cbSize = sizeof(args);
1456 args.wzPackageId = wzPackageId;
1457 args.wzCompatiblePackageId = wzCompatiblePackageId;
1458 args.hrStatus = hrStatus;
1459 args.state = state;
1460 args.requested = requested;
1461 args.execute = execute;
1462 args.rollback = rollback;
1463
1464 results.cbSize = sizeof(results);
1465
1466 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
1467 ExitOnFailure(hr, "BA OnPlanCompatibleMsiPackageComplete failed.");
1468
1469LExit:
1470 return hr;
1471}
1472
1473EXTERN_C BAAPI UserExperienceOnPlanMsiFeature(
1474 __in BURN_USER_EXPERIENCE* pUserExperience,
1475 __in_z LPCWSTR wzPackageId,
1476 __in_z LPCWSTR wzFeatureId,
1477 __inout BOOTSTRAPPER_FEATURE_STATE* pRequestedState
1478 )
1479{
1480 HRESULT hr = S_OK;
1481 BA_ONPLANMSIFEATURE_ARGS args = { };
1482 BA_ONPLANMSIFEATURE_RESULTS results = { };
1483
1484 args.cbSize = sizeof(args);
1485 args.wzPackageId = wzPackageId;
1486 args.wzFeatureId = wzFeatureId;
1487 args.recommendedState = *pRequestedState;
1488
1489 results.cbSize = sizeof(results);
1490 results.requestedState = *pRequestedState;
1491
1492 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANMSIFEATURE, &args, &results, pUserExperience->pvBAProcContext);
1493 ExitOnFailure(hr, "BA OnPlanMsiFeature failed.");
1494
1495 if (results.fCancel)
1496 {
1497 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1498 }
1499 *pRequestedState = results.requestedState;
1500
1501LExit:
1502 return hr;
1503}
1504
1505EXTERN_C BAAPI UserExperienceOnPlanComplete(
1506 __in BURN_USER_EXPERIENCE* pUserExperience,
1507 __in HRESULT hrStatus
1508 )
1509{
1510 HRESULT hr = S_OK;
1511 BA_ONPLANCOMPLETE_ARGS args = { };
1512 BA_ONPLANCOMPLETE_RESULTS results = { };
1513
1514 args.cbSize = sizeof(args);
1515 args.hrStatus = hrStatus;
1516
1517 results.cbSize = sizeof(results);
1518
1519 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
1520 ExitOnFailure(hr, "BA OnPlanComplete failed.");
1521
1522LExit:
1523 return hr;
1524}
1525
1526EXTERN_C BAAPI UserExperienceOnPlanPackageBegin(
1527 __in BURN_USER_EXPERIENCE* pUserExperience,
1528 __in_z LPCWSTR wzPackageId,
1529 __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
1530 )
1531{
1532 HRESULT hr = S_OK;
1533 BA_ONPLANPACKAGEBEGIN_ARGS args = { };
1534 BA_ONPLANPACKAGEBEGIN_RESULTS results = { };
1535
1536 args.cbSize = sizeof(args);
1537 args.wzPackageId = wzPackageId;
1538 args.recommendedState = *pRequestedState;
1539
1540 results.cbSize = sizeof(results);
1541 results.requestedState = *pRequestedState;
1542
1543 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPACKAGEBEGIN, &args, &results, pUserExperience->pvBAProcContext);
1544 ExitOnFailure(hr, "BA OnPlanPackageBegin failed.");
1545
1546 if (results.fCancel)
1547 {
1548 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1549 }
1550 *pRequestedState = results.requestedState;
1551
1552LExit:
1553 return hr;
1554}
1555
1556EXTERN_C BAAPI UserExperienceOnPlanPackageComplete(
1557 __in BURN_USER_EXPERIENCE* pUserExperience,
1558 __in_z LPCWSTR wzPackageId,
1559 __in HRESULT hrStatus,
1560 __in BOOTSTRAPPER_PACKAGE_STATE state,
1561 __in BOOTSTRAPPER_REQUEST_STATE requested,
1562 __in BOOTSTRAPPER_ACTION_STATE execute,
1563 __in BOOTSTRAPPER_ACTION_STATE rollback
1564 )
1565{
1566 HRESULT hr = S_OK;
1567 BA_ONPLANPACKAGECOMPLETE_ARGS args = { };
1568 BA_ONPLANPACKAGECOMPLETE_RESULTS results = { };
1569
1570 args.cbSize = sizeof(args);
1571 args.wzPackageId = wzPackageId;
1572 args.hrStatus = hrStatus;
1573 args.state = state;
1574 args.requested = requested;
1575 args.execute = execute;
1576 args.rollback = rollback;
1577
1578 results.cbSize = sizeof(results);
1579
1580 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANPACKAGECOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
1581 ExitOnFailure(hr, "BA OnPlanPackageComplete failed.");
1582
1583LExit:
1584 return hr;
1585}
1586
1587EXTERN_C BAAPI UserExperienceOnPlanRelatedBundle(
1588 __in BURN_USER_EXPERIENCE* pUserExperience,
1589 __in_z LPCWSTR wzBundleId,
1590 __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
1591 )
1592{
1593 HRESULT hr = S_OK;
1594 BA_ONPLANRELATEDBUNDLE_ARGS args = { };
1595 BA_ONPLANRELATEDBUNDLE_RESULTS results = { };
1596
1597 args.cbSize = sizeof(args);
1598 args.wzBundleId = wzBundleId;
1599 args.recommendedState = *pRequestedState;
1600
1601 results.cbSize = sizeof(results);
1602 results.requestedState = *pRequestedState;
1603
1604 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLE, &args, &results, pUserExperience->pvBAProcContext);
1605 ExitOnFailure(hr, "BA OnPlanRelatedBundle failed.");
1606
1607 if (results.fCancel)
1608 {
1609 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1610 }
1611 *pRequestedState = results.requestedState;
1612
1613LExit:
1614 return hr;
1615}
1616
1617EXTERN_C BAAPI UserExperienceOnPlanTargetMsiPackage(
1618 __in BURN_USER_EXPERIENCE* pUserExperience,
1619 __in_z LPCWSTR wzPackageId,
1620 __in_z LPCWSTR wzProductCode,
1621 __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
1622 )
1623{
1624 HRESULT hr = S_OK;
1625 BA_ONPLANTARGETMSIPACKAGE_ARGS args = { };
1626 BA_ONPLANTARGETMSIPACKAGE_RESULTS results = { };
1627
1628 args.cbSize = sizeof(args);
1629 args.wzPackageId = wzPackageId;
1630 args.wzProductCode = wzProductCode;
1631 args.recommendedState = *pRequestedState;
1632
1633 results.cbSize = sizeof(results);
1634 results.requestedState = *pRequestedState;
1635
1636 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANTARGETMSIPACKAGE, &args, &results, pUserExperience->pvBAProcContext);
1637 ExitOnFailure(hr, "BA OnPlanTargetMsiPackage failed.");
1638
1639 if (results.fCancel)
1640 {
1641 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1642 }
1643 *pRequestedState = results.requestedState;
1644
1645LExit:
1646 return hr;
1647}
1648
1649EXTERN_C BAAPI UserExperienceOnProgress(
1650 __in BURN_USER_EXPERIENCE* pUserExperience,
1651 __in BOOL fRollback,
1652 __in DWORD dwProgressPercentage,
1653 __in DWORD dwOverallPercentage
1654 )
1655{
1656 HRESULT hr = S_OK;
1657 BA_ONPROGRESS_ARGS args = { };
1658 BA_ONPROGRESS_RESULTS results = { };
1659
1660 args.cbSize = sizeof(args);
1661 args.dwProgressPercentage = dwProgressPercentage;
1662 args.dwOverallPercentage = dwOverallPercentage;
1663
1664 results.cbSize = sizeof(results);
1665
1666 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONPROGRESS, &args, &results, pUserExperience->pvBAProcContext);
1667 hr = FilterExecuteResult(pUserExperience, hr, fRollback, results.fCancel, L"OnProgress");
1668
1669 return hr;
1670}
1671
1672EXTERN_C BAAPI UserExperienceOnRegisterBegin(
1673 __in BURN_USER_EXPERIENCE* pUserExperience
1674 )
1675{
1676 HRESULT hr = S_OK;
1677 BA_ONREGISTERBEGIN_ARGS args = { };
1678 BA_ONREGISTERBEGIN_RESULTS results = { };
1679
1680 args.cbSize = sizeof(args);
1681
1682 results.cbSize = sizeof(results);
1683
1684 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONREGISTERBEGIN, &args, &results, pUserExperience->pvBAProcContext);
1685 ExitOnFailure(hr, "BA OnRegisterBegin failed.");
1686
1687 if (results.fCancel)
1688 {
1689 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1690 }
1691
1692LExit:
1693 return hr;
1694}
1695
1696EXTERN_C BAAPI UserExperienceOnRegisterComplete(
1697 __in BURN_USER_EXPERIENCE* pUserExperience,
1698 __in HRESULT hrStatus
1699 )
1700{
1701 HRESULT hr = S_OK;
1702 BA_ONREGISTERCOMPLETE_ARGS args = { };
1703 BA_ONREGISTERCOMPLETE_RESULTS results = { };
1704
1705 args.cbSize = sizeof(args);
1706 args.hrStatus = hrStatus;
1707
1708 results.cbSize = sizeof(results);
1709
1710 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONREGISTERCOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
1711 ExitOnFailure(hr, "BA OnRegisterComplete failed.");
1712
1713LExit:
1714 return hr;
1715}
1716
1717EXTERN_C BAAPI UserExperienceOnResolveSource(
1718 __in BURN_USER_EXPERIENCE* pUserExperience,
1719 __in_z LPCWSTR wzPackageOrContainerId,
1720 __in_z_opt LPCWSTR wzPayloadId,
1721 __in_z LPCWSTR wzLocalSource,
1722 __in_z_opt LPCWSTR wzDownloadSource,
1723 __inout BOOTSTRAPPER_RESOLVESOURCE_ACTION* pAction
1724 )
1725{
1726 HRESULT hr = S_OK;
1727 BA_ONRESOLVESOURCE_ARGS args = { };
1728 BA_ONRESOLVESOURCE_RESULTS results = { };
1729
1730 args.cbSize = sizeof(args);
1731 args.wzPackageOrContainerId = wzPackageOrContainerId;
1732 args.wzPayloadId = wzPayloadId;
1733 args.wzLocalSource = wzLocalSource;
1734 args.wzDownloadSource = wzDownloadSource;
1735 args.recommendation = *pAction;
1736
1737 results.cbSize = sizeof(results);
1738 results.action = *pAction;
1739
1740 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONRESOLVESOURCE, &args, &results, pUserExperience->pvBAProcContext);
1741 ExitOnFailure(hr, "BA OnResolveSource failed.");
1742
1743 if (results.fCancel)
1744 {
1745 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1746 }
1747 else
1748 {
1749 *pAction = results.action;
1750 }
1751
1752LExit:
1753 return hr;
1754}
1755
1756EXTERN_C BAAPI UserExperienceOnShutdown(
1757 __in BURN_USER_EXPERIENCE* pUserExperience,
1758 __inout BOOTSTRAPPER_SHUTDOWN_ACTION* pAction
1759 )
1760{
1761 HRESULT hr = S_OK;
1762 BA_ONSHUTDOWN_ARGS args = { };
1763 BA_ONSHUTDOWN_RESULTS results = { };
1764
1765 args.cbSize = sizeof(args);
1766
1767 results.cbSize = sizeof(results);
1768 results.action = *pAction;
1769
1770 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONSHUTDOWN, &args, &results, pUserExperience->pvBAProcContext);
1771 ExitOnFailure(hr, "BA OnShutdown failed.");
1772
1773 *pAction = results.action;
1774
1775LExit:
1776 return hr;
1777}
1778
1779EXTERN_C BAAPI UserExperienceOnStartup(
1780 __in BURN_USER_EXPERIENCE* pUserExperience
1781 )
1782{
1783 HRESULT hr = S_OK;
1784 BA_ONSTARTUP_ARGS args = { };
1785 BA_ONSTARTUP_RESULTS results = { };
1786
1787 args.cbSize = sizeof(args);
1788
1789 results.cbSize = sizeof(results);
1790
1791 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONSTARTUP, &args, &results, pUserExperience->pvBAProcContext);
1792 ExitOnFailure(hr, "BA OnStartup failed.");
1793
1794LExit:
1795 return hr;
1796}
1797
1798EXTERN_C BAAPI UserExperienceOnSystemShutdown(
1799 __in BURN_USER_EXPERIENCE* pUserExperience,
1800 __in DWORD dwEndSession,
1801 __inout BOOL* pfCancel
1802 )
1803{
1804 HRESULT hr = S_OK;
1805 BA_ONSYSTEMSHUTDOWN_ARGS args = { };
1806 BA_ONSYSTEMSHUTDOWN_RESULTS results = { };
1807
1808 args.cbSize = sizeof(args);
1809 args.dwEndSession = dwEndSession;
1810
1811 results.cbSize = sizeof(results);
1812 results.fCancel = *pfCancel;
1813
1814 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONSYSTEMSHUTDOWN, &args, &results, pUserExperience->pvBAProcContext);
1815 ExitOnFailure(hr, "BA OnSystemShutdown failed.");
1816
1817 *pfCancel = results.fCancel;
1818
1819LExit:
1820 return hr;
1821}
1822
1823EXTERN_C BAAPI UserExperienceOnUnregisterBegin(
1824 __in BURN_USER_EXPERIENCE* pUserExperience
1825 )
1826{
1827 HRESULT hr = S_OK;
1828 BA_ONUNREGISTERBEGIN_ARGS args = { };
1829 BA_ONUNREGISTERBEGIN_RESULTS results = { };
1830
1831 args.cbSize = sizeof(args);
1832
1833 results.cbSize = sizeof(results);
1834
1835 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONUNREGISTERBEGIN, &args, &results, pUserExperience->pvBAProcContext);
1836 ExitOnFailure(hr, "BA OnUnregisterBegin failed.");
1837
1838 if (results.fCancel)
1839 {
1840 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1841 }
1842
1843LExit:
1844 return hr;
1845}
1846
1847EXTERN_C BAAPI UserExperienceOnUnregisterComplete(
1848 __in BURN_USER_EXPERIENCE* pUserExperience,
1849 __in HRESULT hrStatus
1850 )
1851{
1852 HRESULT hr = S_OK;
1853 BA_ONUNREGISTERCOMPLETE_ARGS args = { };
1854 BA_ONUNREGISTERCOMPLETE_RESULTS results = { };
1855
1856 args.cbSize = sizeof(args);
1857 args.hrStatus = hrStatus;
1858
1859 results.cbSize = sizeof(results);
1860
1861 hr = pUserExperience->pfnBAProc(BOOTSTRAPPER_APPLICATION_MESSAGE_ONUNREGISTERCOMPLETE, &args, &results, pUserExperience->pvBAProcContext);
1862 ExitOnFailure(hr, "BA OnUnregisterComplete failed.");
1863
1864LExit:
1865 return hr;
1866}
1867
1868extern "C" int UserExperienceCheckExecuteResult(
1869 __in BURN_USER_EXPERIENCE* pUserExperience,
1870 __in BOOL fRollback,
1871 __in DWORD dwAllowedResults,
1872 __in int nResult
1873 )
1874{
1875 // Do not allow canceling while rolling back.
1876 if (fRollback && (IDCANCEL == nResult || IDABORT == nResult))
1877 {
1878 nResult = IDNOACTION;
1879 }
1880 else if (FAILED(pUserExperience->hrApplyError) && !fRollback) // if we failed cancel except not during rollback.
1881 {
1882 nResult = IDCANCEL;
1883 }
1884
1885 nResult = FilterResult(dwAllowedResults, nResult);
1886 return nResult;
1887}
1888
1889extern "C" HRESULT UserExperienceInterpretResult(
1890 __in BURN_USER_EXPERIENCE* /*pUserExperience*/,
1891 __in DWORD dwAllowedResults,
1892 __in int nResult
1893 )
1894{
1895 int nFilteredResult = FilterResult(dwAllowedResults, nResult);
1896 return IDOK == nFilteredResult || IDNOACTION == nFilteredResult ? S_OK : IDCANCEL == nFilteredResult || IDABORT == nFilteredResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE);
1897}
1898
1899extern "C" HRESULT UserExperienceInterpretExecuteResult(
1900 __in BURN_USER_EXPERIENCE* pUserExperience,
1901 __in BOOL fRollback,
1902 __in DWORD dwAllowedResults,
1903 __in int nResult
1904 )
1905{
1906 HRESULT hr = S_OK;
1907
1908 // If we failed return that error unless this is rollback which should roll on.
1909 if (FAILED(pUserExperience->hrApplyError) && !fRollback)
1910 {
1911 hr = pUserExperience->hrApplyError;
1912 }
1913 else
1914 {
1915 int nCheckedResult = UserExperienceCheckExecuteResult(pUserExperience, fRollback, dwAllowedResults, nResult);
1916 hr = IDOK == nCheckedResult || IDNOACTION == nCheckedResult ? S_OK : IDCANCEL == nCheckedResult || IDABORT == nCheckedResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE);
1917 }
1918
1919 return hr;
1920}
1921
1922
1923// internal functions
1924
1925static int FilterResult(
1926 __in DWORD dwAllowedResults,
1927 __in int nResult
1928 )
1929{
1930 if (IDNOACTION == nResult || IDERROR == nResult) // do nothing and errors pass through.
1931 {
1932 }
1933 else
1934 {
1935 switch (dwAllowedResults)
1936 {
1937 case MB_OK:
1938 nResult = IDOK;
1939 break;
1940
1941 case MB_OKCANCEL:
1942 if (IDOK == nResult || IDYES == nResult)
1943 {
1944 nResult = IDOK;
1945 }
1946 else if (IDCANCEL == nResult || IDABORT == nResult || IDNO == nResult)
1947 {
1948 nResult = IDCANCEL;
1949 }
1950 else
1951 {
1952 nResult = IDNOACTION;
1953 }
1954 break;
1955
1956 case MB_ABORTRETRYIGNORE:
1957 if (IDCANCEL == nResult || IDABORT == nResult)
1958 {
1959 nResult = IDABORT;
1960 }
1961 else if (IDRETRY == nResult || IDTRYAGAIN == nResult)
1962 {
1963 nResult = IDRETRY;
1964 }
1965 else if (IDIGNORE == nResult)
1966 {
1967 nResult = IDIGNORE;
1968 }
1969 else
1970 {
1971 nResult = IDNOACTION;
1972 }
1973 break;
1974
1975 case MB_YESNO:
1976 if (IDOK == nResult || IDYES == nResult)
1977 {
1978 nResult = IDYES;
1979 }
1980 else if (IDCANCEL == nResult || IDABORT == nResult || IDNO == nResult)
1981 {
1982 nResult = IDNO;
1983 }
1984 else
1985 {
1986 nResult = IDNOACTION;
1987 }
1988 break;
1989
1990 case MB_YESNOCANCEL:
1991 if (IDOK == nResult || IDYES == nResult)
1992 {
1993 nResult = IDYES;
1994 }
1995 else if (IDNO == nResult)
1996 {
1997 nResult = IDNO;
1998 }
1999 else if (IDCANCEL == nResult || IDABORT == nResult)
2000 {
2001 nResult = IDCANCEL;
2002 }
2003 else
2004 {
2005 nResult = IDNOACTION;
2006 }
2007 break;
2008
2009 case MB_RETRYCANCEL:
2010 if (IDRETRY == nResult || IDTRYAGAIN == nResult)
2011 {
2012 nResult = IDRETRY;
2013 }
2014 else if (IDCANCEL == nResult || IDABORT == nResult)
2015 {
2016 nResult = IDABORT;
2017 }
2018 else
2019 {
2020 nResult = IDNOACTION;
2021 }
2022 break;
2023
2024 case MB_CANCELTRYCONTINUE:
2025 if (IDCANCEL == nResult || IDABORT == nResult)
2026 {
2027 nResult = IDABORT;
2028 }
2029 else if (IDRETRY == nResult || IDTRYAGAIN == nResult)
2030 {
2031 nResult = IDRETRY;
2032 }
2033 else if (IDCONTINUE == nResult || IDIGNORE == nResult)
2034 {
2035 nResult = IDCONTINUE;
2036 }
2037 else
2038 {
2039 nResult = IDNOACTION;
2040 }
2041 break;
2042
2043 case WIU_MB_OKIGNORECANCELRETRY: // custom Windows Installer utility return code.
2044 if (IDOK == nResult || IDYES == nResult)
2045 {
2046 nResult = IDOK;
2047 }
2048 else if (IDCONTINUE == nResult || IDIGNORE == nResult)
2049 {
2050 nResult = IDIGNORE;
2051 }
2052 else if (IDCANCEL == nResult || IDABORT == nResult)
2053 {
2054 nResult = IDCANCEL;
2055 }
2056 else if (IDRETRY == nResult || IDTRYAGAIN == nResult || IDNO == nResult)
2057 {
2058 nResult = IDRETRY;
2059 }
2060 else
2061 {
2062 nResult = IDNOACTION;
2063 }
2064 break;
2065
2066 case MB_RETRYTRYAGAIN: // custom return code.
2067 if (IDRETRY != nResult && IDTRYAGAIN != nResult)
2068 {
2069 nResult = IDNOACTION;
2070 }
2071 break;
2072
2073 default:
2074 AssertSz(FALSE, "Unknown allowed results.");
2075 break;
2076 }
2077 }
2078
2079 return nResult;
2080}
2081
2082// This filters the BA's responses to events during apply.
2083// If an apply thread failed, then return its error so this thread will bail out.
2084// During rollback, the BA can't cancel.
2085static HRESULT FilterExecuteResult(
2086 __in BURN_USER_EXPERIENCE* pUserExperience,
2087 __in HRESULT hrStatus,
2088 __in BOOL fRollback,
2089 __in BOOL fCancel,
2090 __in LPCWSTR sczEventName
2091 )
2092{
2093 HRESULT hr = hrStatus;
2094 HRESULT hrApplyError = pUserExperience->hrApplyError; // make sure to use the same value for the whole method, since it can be changed in other threads.
2095
2096 // If we failed return that error unless this is rollback which should roll on.
2097 if (FAILED(hrApplyError) && !fRollback)
2098 {
2099 hr = hrApplyError;
2100 }
2101 else if (fRollback)
2102 {
2103 if (fCancel)
2104 {
2105 LogId(REPORT_STANDARD, MSG_APPLY_CANCEL_IGNORED_DURING_ROLLBACK, sczEventName);
2106 }
2107 // TODO: since cancel isn't allowed, should the BA's HRESULT be ignored as well?
2108 // In the previous code, they could still alter rollback by returning IDERROR.
2109 }
2110 else
2111 {
2112 ExitOnFailure(hr, "BA %ls failed.", sczEventName);
2113
2114 if (fCancel)
2115 {
2116 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
2117 }
2118 }
2119
2120LExit:
2121 return hr;
2122}
diff --git a/src/engine/userexperience.h b/src/engine/userexperience.h
new file mode 100644
index 00000000..27a94115
--- /dev/null
+++ b/src/engine/userexperience.h
@@ -0,0 +1,439 @@
1#pragma once
2// 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.
3
4#define BAAPI HRESULT __stdcall
5
6#if defined(__cplusplus)
7extern "C" {
8#endif
9
10
11// constants
12
13const DWORD MB_RETRYTRYAGAIN = 0xF;
14
15
16// structs
17
18struct BOOTSTRAPPER_ENGINE_CONTEXT;
19
20typedef struct _BURN_USER_EXPERIENCE
21{
22 BOOL fSplashScreen;
23 BURN_PAYLOADS payloads;
24
25 HMODULE hUXModule;
26 PFN_BOOTSTRAPPER_APPLICATION_PROC pfnBAProc;
27 LPVOID pvBAProcContext;
28 LPWSTR sczTempDirectory;
29
30 CRITICAL_SECTION csEngineActive; // Changing the engine active state in the user experience must be
31 // syncronized through this critical section.
32 // Note: The engine must never do a UX callback while in this critical section.
33
34 BOOL fEngineActive; // Indicates that the engine is currently active with one of the execution
35 // steps (detect, plan, apply), and cannot accept requests from the UX.
36 // This flag should be cleared by the engine prior to UX callbacks that
37 // allows altering of the engine state.
38
39 HRESULT hrApplyError; // Tracks is an error occurs during apply that requires the cache or
40 // execute threads to bail.
41
42 HWND hwndApply; // The window handle provided at the beginning of Apply(). Only valid
43 // during apply.
44
45 HWND hwndDetect; // The window handle provided at the beginning of Detect(). Only valid
46 // during Detect.
47
48 DWORD dwExitCode; // Exit code returned by the user experience for the engine overall.
49} BURN_USER_EXPERIENCE;
50
51// functions
52
53HRESULT UserExperienceParseFromXml(
54 __in BURN_USER_EXPERIENCE* pUserExperience,
55 __in IXMLDOMNode* pixnBundle
56 );
57void UserExperienceUninitialize(
58 __in BURN_USER_EXPERIENCE* pUserExperience
59 );
60HRESULT UserExperienceLoad(
61 __in BURN_USER_EXPERIENCE* pUserExperience,
62 __in BOOTSTRAPPER_ENGINE_CONTEXT* pEngineContext,
63 __in BOOTSTRAPPER_COMMAND* pCommand
64 );
65HRESULT UserExperienceUnload(
66 __in BURN_USER_EXPERIENCE* pUserExperience
67 );
68HRESULT UserExperienceEnsureWorkingFolder(
69 __in LPCWSTR wzBundleId,
70 __deref_out_z LPWSTR* psczUserExperienceWorkingFolder
71 );
72HRESULT UserExperienceRemove(
73 __in BURN_USER_EXPERIENCE* pUserExperience
74 );
75int UserExperienceSendError(
76 __in BURN_USER_EXPERIENCE* pUserExperience,
77 __in BOOTSTRAPPER_ERROR_TYPE errorType,
78 __in_z_opt LPCWSTR wzPackageId,
79 __in HRESULT hrCode,
80 __in_z_opt LPCWSTR wzError,
81 __in DWORD uiFlags,
82 __in int nRecommendation
83 );
84HRESULT UserExperienceActivateEngine(
85 __in BURN_USER_EXPERIENCE* pUserExperience,
86 __out_opt BOOL* pfActivated
87 );
88void UserExperienceDeactivateEngine(
89 __in BURN_USER_EXPERIENCE* pUserExperience
90 );
91HRESULT UserExperienceEnsureEngineInactive(
92 __in BURN_USER_EXPERIENCE* pUserExperience
93 );
94void UserExperienceExecuteReset(
95 __in BURN_USER_EXPERIENCE* pUserExperience
96 );
97void UserExperienceExecutePhaseComplete(
98 __in BURN_USER_EXPERIENCE* pUserExperience,
99 __in HRESULT hrResult
100 );
101BAAPI UserExperienceOnApplyBegin(
102 __in BURN_USER_EXPERIENCE* pUserExperience,
103 __in DWORD dwPhaseCount
104 );
105BAAPI UserExperienceOnApplyComplete(
106 __in BURN_USER_EXPERIENCE* pUserExperience,
107 __in HRESULT hrStatus,
108 __in BOOTSTRAPPER_APPLY_RESTART restart,
109 __inout BOOTSTRAPPER_APPLYCOMPLETE_ACTION* pAction
110);
111BAAPI UserExperienceOnCacheAcquireBegin(
112 __in BURN_USER_EXPERIENCE* pUserExperience,
113 __in_z_opt LPCWSTR wzPackageOrContainerId,
114 __in_z_opt LPCWSTR wzPayloadId,
115 __in BOOTSTRAPPER_CACHE_OPERATION operation,
116 __in_z LPCWSTR wzSource
117 );
118BAAPI UserExperienceOnCacheAcquireComplete(
119 __in BURN_USER_EXPERIENCE* pUserExperience,
120 __in_z_opt LPCWSTR wzPackageOrContainerId,
121 __in_z_opt LPCWSTR wzPayloadId,
122 __in HRESULT hrStatus,
123 __inout BOOL* pfRetry
124 );
125BAAPI UserExperienceOnCacheAcquireProgress(
126 __in BURN_USER_EXPERIENCE* pUserExperience,
127 __in_z_opt LPCWSTR wzPackageOrContainerId,
128 __in_z_opt LPCWSTR wzPayloadId,
129 __in DWORD64 dw64Progress,
130 __in DWORD64 dw64Total,
131 __in DWORD dwOverallPercentage
132 );
133BAAPI UserExperienceOnCacheBegin(
134 __in BURN_USER_EXPERIENCE* pUserExperience
135 );
136BAAPI UserExperienceOnCacheComplete(
137 __in BURN_USER_EXPERIENCE* pUserExperience,
138 __in HRESULT hrStatus
139 );
140BAAPI UserExperienceOnCachePackageBegin(
141 __in BURN_USER_EXPERIENCE* pUserExperience,
142 __in_z LPCWSTR wzPackageId,
143 __in DWORD cCachePayloads,
144 __in DWORD64 dw64PackageCacheSize
145 );
146BAAPI UserExperienceOnCachePackageComplete(
147 __in BURN_USER_EXPERIENCE* pUserExperience,
148 __in_z LPCWSTR wzPackageId,
149 __in HRESULT hrStatus,
150 __inout BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION* pAction
151 );
152BAAPI UserExperienceOnCacheVerifyBegin(
153 __in BURN_USER_EXPERIENCE* pUserExperience,
154 __in_z_opt LPCWSTR wzPackageOrContainerId,
155 __in_z_opt LPCWSTR wzPayloadId
156 );
157BAAPI UserExperienceOnCacheVerifyComplete(
158 __in BURN_USER_EXPERIENCE* pUserExperience,
159 __in_z_opt LPCWSTR wzPackageOrContainerId,
160 __in_z_opt LPCWSTR wzPayloadId,
161 __in HRESULT hrStatus,
162 __inout BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION* pAction
163 );
164BAAPI UserExperienceOnDetectBegin(
165 __in BURN_USER_EXPERIENCE* pUserExperience,
166 __in BOOL fInstalled,
167 __in DWORD cPackages
168 );
169BAAPI UserExperienceOnDetectCompatibleMsiPackage(
170 __in BURN_USER_EXPERIENCE* pUserExperience,
171 __in_z LPCWSTR wzPackageId,
172 __in_z LPCWSTR wzCompatiblePackageId,
173 __in DWORD64 dw64CompatiblePackageVersion
174 );
175BAAPI UserExperienceOnDetectComplete(
176 __in BURN_USER_EXPERIENCE* pUserExperience,
177 __in HRESULT hrStatus
178 );
179BAAPI UserExperienceOnDetectForwardCompatibleBundle(
180 __in BURN_USER_EXPERIENCE* pUserExperience,
181 __in_z LPCWSTR wzBundleId,
182 __in BOOTSTRAPPER_RELATION_TYPE relationType,
183 __in_z LPCWSTR wzBundleTag,
184 __in BOOL fPerMachine,
185 __in DWORD64 dw64Version,
186 __inout BOOL* pfIgnoreBundle
187 );
188BAAPI UserExperienceOnDetectMsiFeature(
189 __in BURN_USER_EXPERIENCE* pUserExperience,
190 __in_z LPCWSTR wzPackageId,
191 __in_z LPCWSTR wzFeatureId,
192 __in BOOTSTRAPPER_FEATURE_STATE state
193 );
194BAAPI UserExperienceOnDetectPackageBegin(
195 __in BURN_USER_EXPERIENCE* pUserExperience,
196 __in_z LPCWSTR wzPackageId
197 );
198BAAPI UserExperienceOnDetectPackageComplete(
199 __in BURN_USER_EXPERIENCE* pUserExperience,
200 __in_z LPCWSTR wzPackageId,
201 __in HRESULT hrStatus,
202 __in BOOTSTRAPPER_PACKAGE_STATE state
203 );
204BAAPI UserExperienceOnDetectRelatedBundle(
205 __in BURN_USER_EXPERIENCE* pUserExperience,
206 __in_z LPCWSTR wzBundleId,
207 __in BOOTSTRAPPER_RELATION_TYPE relationType,
208 __in_z LPCWSTR wzBundleTag,
209 __in BOOL fPerMachine,
210 __in DWORD64 dw64Version,
211 __in BOOTSTRAPPER_RELATED_OPERATION operation
212 );
213BAAPI UserExperienceOnDetectRelatedMsiPackage(
214 __in BURN_USER_EXPERIENCE* pUserExperience,
215 __in_z LPCWSTR wzPackageId,
216 __in_z LPCWSTR wzUpgradeCode,
217 __in_z LPCWSTR wzProductCode,
218 __in BOOL fPerMachine,
219 __in DWORD64 dw64Version,
220 __in BOOTSTRAPPER_RELATED_OPERATION operation
221 );
222BAAPI UserExperienceOnDetectTargetMsiPackage(
223 __in BURN_USER_EXPERIENCE* pUserExperience,
224 __in_z LPCWSTR wzPackageId,
225 __in_z LPCWSTR wzProductCode,
226 __in BOOTSTRAPPER_PACKAGE_STATE patchState
227 );
228BAAPI UserExperienceOnDetectUpdate(
229 __in BURN_USER_EXPERIENCE* pUserExperience,
230 __in_z LPCWSTR wzUpdateLocation,
231 __in DWORD64 dw64Size,
232 __in DWORD64 dw64Version,
233 __in_z_opt LPCWSTR wzTitle,
234 __in_z_opt LPCWSTR wzSummary,
235 __in_z_opt LPCWSTR wzContentType,
236 __in_z_opt LPCWSTR wzContent,
237 __inout BOOL* pfStopProcessingUpdates
238 );
239BAAPI UserExperienceOnDetectUpdateBegin(
240 __in BURN_USER_EXPERIENCE* pUserExperience,
241 __in_z LPCWSTR wzUpdateLocation,
242 __inout BOOL* pfSkip
243 );
244BAAPI UserExperienceOnDetectUpdateComplete(
245 __in BURN_USER_EXPERIENCE* pUserExperience,
246 __in HRESULT hrStatus,
247 __inout BOOL* pfIgnoreError
248 );
249BAAPI UserExperienceOnElevateBegin(
250 __in BURN_USER_EXPERIENCE* pUserExperience
251 );
252BAAPI UserExperienceOnElevateComplete(
253 __in BURN_USER_EXPERIENCE* pUserExperience,
254 __in HRESULT hrStatus
255 );
256BAAPI UserExperienceOnError(
257 __in BURN_USER_EXPERIENCE* pUserExperience,
258 __in BOOTSTRAPPER_ERROR_TYPE errorType,
259 __in_z_opt LPCWSTR wzPackageId,
260 __in DWORD dwCode,
261 __in_z_opt LPCWSTR wzError,
262 __in DWORD dwUIHint,
263 __in DWORD cData,
264 __in_ecount_z_opt(cData) LPCWSTR* rgwzData,
265 __inout int* pnResult
266 );
267BAAPI UserExperienceOnExecuteBegin(
268 __in BURN_USER_EXPERIENCE* pUserExperience,
269 __in DWORD cExecutingPackages
270 );
271BAAPI UserExperienceOnExecuteComplete(
272 __in BURN_USER_EXPERIENCE* pUserExperience,
273 __in HRESULT hrStatus
274);
275BAAPI UserExperienceOnExecuteFilesInUse(
276 __in BURN_USER_EXPERIENCE* pUserExperience,
277 __in_z LPCWSTR wzPackageId,
278 __in DWORD cFiles,
279 __in_ecount_z_opt(cFiles) LPCWSTR* rgwzFiles,
280 __inout int* pnResult
281 );
282BAAPI UserExperienceOnExecuteMsiMessage(
283 __in BURN_USER_EXPERIENCE* pUserExperience,
284 __in_z LPCWSTR wzPackageId,
285 __in INSTALLMESSAGE messageType,
286 __in DWORD dwUIHint,
287 __in_z LPCWSTR wzMessage,
288 __in DWORD cData,
289 __in_ecount_z_opt(cData) LPCWSTR* rgwzData,
290 __inout int* pnResult
291 );
292BAAPI UserExperienceOnExecutePackageBegin(
293 __in BURN_USER_EXPERIENCE* pUserExperience,
294 __in_z LPCWSTR wzPackageId,
295 __in BOOL fExecute
296 );
297BAAPI UserExperienceOnExecutePackageComplete(
298 __in BURN_USER_EXPERIENCE* pUserExperience,
299 __in_z LPCWSTR wzPackageId,
300 __in HRESULT hrStatus,
301 __in BOOTSTRAPPER_APPLY_RESTART restart,
302 __inout BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION* pAction
303 );
304BAAPI UserExperienceOnExecutePatchTarget(
305 __in BURN_USER_EXPERIENCE* pUserExperience,
306 __in_z LPCWSTR wzPackageId,
307 __in_z LPCWSTR wzTargetProductCode
308 );
309BAAPI UserExperienceOnExecuteProgress(
310 __in BURN_USER_EXPERIENCE* pUserExperience,
311 __in_z LPCWSTR wzPackageId,
312 __in DWORD dwProgressPercentage,
313 __in DWORD dwOverallPercentage,
314 __inout int* pnResult
315 );
316BAAPI UserExperienceOnLaunchApprovedExeBegin(
317 __in BURN_USER_EXPERIENCE* pUserExperience
318 );
319BAAPI UserExperienceOnLaunchApprovedExeComplete(
320 __in BURN_USER_EXPERIENCE* pUserExperience,
321 __in HRESULT hrStatus,
322 __in DWORD dwProcessId
323 );
324BAAPI UserExperienceOnPlanBegin(
325 __in BURN_USER_EXPERIENCE* pUserExperience,
326 __in DWORD cPackages
327 );
328BAAPI UserExperienceOnPlanCompatibleMsiPackageBegin(
329 __in BURN_USER_EXPERIENCE* pUserExperience,
330 __in_z LPCWSTR wzPackageId,
331 __in_z LPCWSTR wzCompatiblePackageId,
332 __in DWORD64 dw64CompatiblePackageVersion,
333 __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
334 );
335BAAPI UserExperienceOnPlanCompatibleMsiPackageComplete(
336 __in BURN_USER_EXPERIENCE* pUserExperience,
337 __in_z LPCWSTR wzPackageId,
338 __in_z LPCWSTR wzCompatiblePackageId,
339 __in HRESULT hrStatus,
340 __in BOOTSTRAPPER_PACKAGE_STATE state,
341 __in BOOTSTRAPPER_REQUEST_STATE requested,
342 __in BOOTSTRAPPER_ACTION_STATE execute,
343 __in BOOTSTRAPPER_ACTION_STATE rollback
344 );
345BAAPI UserExperienceOnPlanComplete(
346 __in BURN_USER_EXPERIENCE* pUserExperience,
347 __in HRESULT hrStatus
348 );
349BAAPI UserExperienceOnPlanMsiFeature(
350 __in BURN_USER_EXPERIENCE* pUserExperience,
351 __in_z LPCWSTR wzPackageId,
352 __in_z LPCWSTR wzFeatureId,
353 __inout BOOTSTRAPPER_FEATURE_STATE* pRequestedState
354 );
355BAAPI UserExperienceOnPlanPackageBegin(
356 __in BURN_USER_EXPERIENCE* pUserExperience,
357 __in_z LPCWSTR wzPackageId,
358 __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
359 );
360BAAPI UserExperienceOnPlanPackageComplete(
361 __in BURN_USER_EXPERIENCE* pUserExperience,
362 __in_z LPCWSTR wzPackageId,
363 __in HRESULT hrStatus,
364 __in BOOTSTRAPPER_PACKAGE_STATE state,
365 __in BOOTSTRAPPER_REQUEST_STATE requested,
366 __in BOOTSTRAPPER_ACTION_STATE execute,
367 __in BOOTSTRAPPER_ACTION_STATE rollback
368 );
369BAAPI UserExperienceOnPlanRelatedBundle(
370 __in BURN_USER_EXPERIENCE* pUserExperience,
371 __in_z LPCWSTR wzBundleId,
372 __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
373 );
374BAAPI UserExperienceOnPlanTargetMsiPackage(
375 __in BURN_USER_EXPERIENCE* pUserExperience,
376 __in_z LPCWSTR wzPackageId,
377 __in_z LPCWSTR wzProductCode,
378 __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
379 );
380BAAPI UserExperienceOnProgress(
381 __in BURN_USER_EXPERIENCE* pUserExperience,
382 __in BOOL fRollback,
383 __in DWORD dwProgressPercentage,
384 __in DWORD dwOverallPercentage
385 );
386BAAPI UserExperienceOnRegisterBegin(
387 __in BURN_USER_EXPERIENCE* pUserExperience
388 );
389BAAPI UserExperienceOnRegisterComplete(
390 __in BURN_USER_EXPERIENCE* pUserExperience,
391 __in HRESULT hrStatus
392 );
393BAAPI UserExperienceOnResolveSource(
394 __in BURN_USER_EXPERIENCE* pUserExperience,
395 __in_z LPCWSTR wzPackageOrContainerId,
396 __in_z_opt LPCWSTR wzPayloadId,
397 __in_z LPCWSTR wzLocalSource,
398 __in_z_opt LPCWSTR wzDownloadSource,
399 __inout BOOTSTRAPPER_RESOLVESOURCE_ACTION* pAction
400 );
401BAAPI UserExperienceOnShutdown(
402 __in BURN_USER_EXPERIENCE* pUserExperience,
403 __inout BOOTSTRAPPER_SHUTDOWN_ACTION* pAction
404 );
405BAAPI UserExperienceOnStartup(
406 __in BURN_USER_EXPERIENCE* pUserExperience
407 );
408BAAPI UserExperienceOnSystemShutdown(
409 __in BURN_USER_EXPERIENCE* pUserExperience,
410 __in DWORD dwEndSession,
411 __inout BOOL* pfCancel
412 );
413BAAPI UserExperienceOnUnregisterBegin(
414 __in BURN_USER_EXPERIENCE* pUserExperience
415 );
416BAAPI UserExperienceOnUnregisterComplete(
417 __in BURN_USER_EXPERIENCE* pUserExperience,
418 __in HRESULT hrStatus
419 );
420HRESULT UserExperienceInterpretResult(
421 __in BURN_USER_EXPERIENCE* pUserExperience,
422 __in DWORD dwAllowedResults,
423 __in int nResult
424 );
425int UserExperienceCheckExecuteResult(
426 __in BURN_USER_EXPERIENCE* pUserExperience,
427 __in BOOL fRollback,
428 __in DWORD dwAllowedResults,
429 __in int nResult
430 );
431HRESULT UserExperienceInterpretExecuteResult(
432 __in BURN_USER_EXPERIENCE* pUserExperience,
433 __in BOOL fRollback,
434 __in DWORD dwAllowedResults,
435 __in int nResult
436 );
437#if defined(__cplusplus)
438}
439#endif
diff --git a/src/engine/variable.cpp b/src/engine/variable.cpp
new file mode 100644
index 00000000..ed4abea2
--- /dev/null
+++ b/src/engine/variable.cpp
@@ -0,0 +1,2345 @@
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#include "precomp.h"
4
5
6// structs
7
8typedef const struct _BUILT_IN_VARIABLE_DECLARATION
9{
10 LPCWSTR wzVariable;
11 PFN_INITIALIZEVARIABLE pfnInitialize;
12 DWORD_PTR dwpInitializeData;
13 BOOL fPersist;
14 BOOL fOverridable;
15} BUILT_IN_VARIABLE_DECLARATION;
16
17
18// constants
19
20const DWORD GROW_VARIABLE_ARRAY = 3;
21
22enum OS_INFO_VARIABLE
23{
24 OS_INFO_VARIABLE_NONE,
25 OS_INFO_VARIABLE_VersionNT,
26 OS_INFO_VARIABLE_VersionNT64,
27 OS_INFO_VARIABLE_ServicePackLevel,
28 OS_INFO_VARIABLE_NTProductType,
29 OS_INFO_VARIABLE_NTSuiteBackOffice,
30 OS_INFO_VARIABLE_NTSuiteDataCenter,
31 OS_INFO_VARIABLE_NTSuiteEnterprise,
32 OS_INFO_VARIABLE_NTSuitePersonal,
33 OS_INFO_VARIABLE_NTSuiteSmallBusiness,
34 OS_INFO_VARIABLE_NTSuiteSmallBusinessRestricted,
35 OS_INFO_VARIABLE_NTSuiteWebServer,
36 OS_INFO_VARIABLE_CompatibilityMode,
37 OS_INFO_VARIABLE_TerminalServer,
38 OS_INFO_VARIABLE_ProcessorArchitecture,
39};
40
41enum SET_VARIABLE
42{
43 SET_VARIABLE_NOT_BUILTIN,
44 SET_VARIABLE_OVERRIDE_BUILTIN,
45 SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS,
46 SET_VARIABLE_ANY,
47};
48
49// internal function declarations
50
51static HRESULT FormatString(
52 __in BURN_VARIABLES* pVariables,
53 __in_z LPCWSTR wzIn,
54 __out_z_opt LPWSTR* psczOut,
55 __out_opt DWORD* pcchOut,
56 __in BOOL fObfuscateHiddenVariables
57 );
58static HRESULT AddBuiltInVariable(
59 __in BURN_VARIABLES* pVariables,
60 __in LPCWSTR wzVariable,
61 __in PFN_INITIALIZEVARIABLE pfnInitialize,
62 __in DWORD_PTR dwpInitializeData,
63 __in BOOL fPersist,
64 __in BOOL fOverridable
65 );
66static HRESULT GetVariable(
67 __in BURN_VARIABLES* pVariables,
68 __in_z LPCWSTR wzVariable,
69 __out BURN_VARIABLE** ppVariable
70 );
71static HRESULT FindVariableIndexByName(
72 __in BURN_VARIABLES* pVariables,
73 __in_z LPCWSTR wzVariable,
74 __out DWORD* piVariable
75 );
76static HRESULT InsertVariable(
77 __in BURN_VARIABLES* pVariables,
78 __in_z LPCWSTR wzVariable,
79 __in DWORD iPosition
80 );
81static HRESULT SetVariableValue(
82 __in BURN_VARIABLES* pVariables,
83 __in_z LPCWSTR wzVariable,
84 __in BURN_VARIANT* pVariant,
85 __in BOOL fLiteral,
86 __in SET_VARIABLE setBuiltin,
87 __in BOOL fLog
88 );
89static HRESULT InitializeVariableVersionNT(
90 __in DWORD_PTR dwpData,
91 __inout BURN_VARIANT* pValue
92 );
93static HRESULT InitializeVariableOsInfo(
94 __in DWORD_PTR dwpData,
95 __inout BURN_VARIANT* pValue
96 );
97static HRESULT InitializeVariableSystemInfo(
98 __in DWORD_PTR dwpData,
99 __inout BURN_VARIANT* pValue
100 );
101static HRESULT InitializeVariableComputerName(
102 __in DWORD_PTR dwpData,
103 __inout BURN_VARIANT* pValue
104 );
105static HRESULT InitializeVariableVersionMsi(
106 __in DWORD_PTR dwpData,
107 __inout BURN_VARIANT* pValue
108 );
109static HRESULT InitializeVariableCsidlFolder(
110 __in DWORD_PTR dwpData,
111 __inout BURN_VARIANT* pValue
112 );
113static HRESULT InitializeVariableWindowsVolumeFolder(
114 __in DWORD_PTR dwpData,
115 __inout BURN_VARIANT* pValue
116 );
117static HRESULT InitializeVariableTempFolder(
118 __in DWORD_PTR dwpData,
119 __inout BURN_VARIANT* pValue
120 );
121static HRESULT InitializeVariableSystemFolder(
122 __in DWORD_PTR dwpData,
123 __inout BURN_VARIANT* pValue
124 );
125static HRESULT InitializeVariablePrivileged(
126 __in DWORD_PTR dwpData,
127 __inout BURN_VARIANT* pValue
128 );
129static HRESULT InitializeVariableRebootPending(
130 __in DWORD_PTR dwpData,
131 __inout BURN_VARIANT* pValue
132 );
133static HRESULT InitializeSystemLanguageID(
134 __in DWORD_PTR dwpData,
135 __inout BURN_VARIANT* pValue
136 );
137static HRESULT InitializeUserUILanguageID(
138 __in DWORD_PTR dwpData,
139 __inout BURN_VARIANT* pValue
140 );
141static HRESULT InitializeUserLanguageID(
142 __in DWORD_PTR dwpData,
143 __inout BURN_VARIANT* pValue
144 );
145static HRESULT InitializeVariableString(
146 __in DWORD_PTR dwpData,
147 __inout BURN_VARIANT* pValue
148 );
149static HRESULT InitializeVariableNumeric(
150 __in DWORD_PTR dwpData,
151 __inout BURN_VARIANT* pValue
152 );
153static HRESULT InitializeVariableRegistryFolder(
154 __in DWORD_PTR dwpData,
155 __inout BURN_VARIANT* pValue
156 );
157static HRESULT InitializeVariable6432Folder(
158 __in DWORD_PTR dwpData,
159 __inout BURN_VARIANT* pValue
160 );
161static HRESULT InitializeVariableDate(
162 __in DWORD_PTR dwpData,
163 __inout BURN_VARIANT* pValue
164 );
165static HRESULT InitializeVariableInstallerName(
166 __in DWORD_PTR dwpData,
167 __inout BURN_VARIANT* pValue
168 );
169static HRESULT InitializeVariableInstallerVersion(
170 __in DWORD_PTR dwpData,
171 __inout BURN_VARIANT* pValue
172 );
173static HRESULT InitializeVariableVersion(
174 __in DWORD_PTR dwpData,
175 __inout BURN_VARIANT* pValue
176 );
177static HRESULT InitializeVariableLogonUser(
178 __in DWORD_PTR dwpData,
179 __inout BURN_VARIANT* pValue
180 );
181static HRESULT Get64bitFolderFromRegistry(
182 __in int nFolder,
183 __deref_out_z LPWSTR* psczPath
184 );
185
186
187// function definitions
188
189extern "C" HRESULT VariableInitialize(
190 __in BURN_VARIABLES* pVariables
191 )
192{
193 HRESULT hr = S_OK;
194
195 ::InitializeCriticalSection(&pVariables->csAccess);
196
197 const BUILT_IN_VARIABLE_DECLARATION vrgBuiltInVariables[] = {
198 {L"AdminToolsFolder", InitializeVariableCsidlFolder, CSIDL_ADMINTOOLS},
199 {L"AppDataFolder", InitializeVariableCsidlFolder, CSIDL_APPDATA},
200 {L"CommonAppDataFolder", InitializeVariableCsidlFolder, CSIDL_COMMON_APPDATA},
201#if defined(_WIN64)
202 {L"CommonFiles64Folder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES_COMMON},
203 {L"CommonFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES_COMMONX86},
204#else
205 {L"CommonFiles64Folder", InitializeVariableRegistryFolder, CSIDL_PROGRAM_FILES_COMMON},
206 {L"CommonFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES_COMMON},
207#endif
208 {L"CommonFiles6432Folder", InitializeVariable6432Folder, CSIDL_PROGRAM_FILES_COMMON},
209 {L"CompatibilityMode", InitializeVariableOsInfo, OS_INFO_VARIABLE_CompatibilityMode},
210 {VARIABLE_DATE, InitializeVariableDate, 0},
211 {L"ComputerName", InitializeVariableComputerName, 0},
212 {L"DesktopFolder", InitializeVariableCsidlFolder, CSIDL_DESKTOP},
213 {L"FavoritesFolder", InitializeVariableCsidlFolder, CSIDL_FAVORITES},
214 {L"FontsFolder", InitializeVariableCsidlFolder, CSIDL_FONTS},
215 {VARIABLE_INSTALLERNAME, InitializeVariableInstallerName, 0},
216 {VARIABLE_INSTALLERVERSION, InitializeVariableInstallerVersion, 0},
217 {L"LocalAppDataFolder", InitializeVariableCsidlFolder, CSIDL_LOCAL_APPDATA},
218 {VARIABLE_LOGONUSER, InitializeVariableLogonUser, 0},
219 {L"MyPicturesFolder", InitializeVariableCsidlFolder, CSIDL_MYPICTURES},
220 {L"NTProductType", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTProductType},
221 {L"NTSuiteBackOffice", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteBackOffice},
222 {L"NTSuiteDataCenter", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteDataCenter},
223 {L"NTSuiteEnterprise", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteEnterprise},
224 {L"NTSuitePersonal", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuitePersonal},
225 {L"NTSuiteSmallBusiness", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteSmallBusiness},
226 {L"NTSuiteSmallBusinessRestricted", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteSmallBusinessRestricted},
227 {L"NTSuiteWebServer", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteWebServer},
228 {L"PersonalFolder", InitializeVariableCsidlFolder, CSIDL_PERSONAL},
229 {L"Privileged", InitializeVariablePrivileged, 0},
230 {L"ProcessorArchitecture", InitializeVariableSystemInfo, OS_INFO_VARIABLE_ProcessorArchitecture},
231#if defined(_WIN64)
232 {L"ProgramFiles64Folder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES},
233 {L"ProgramFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILESX86},
234#else
235 {L"ProgramFiles64Folder", InitializeVariableRegistryFolder, CSIDL_PROGRAM_FILES},
236 {L"ProgramFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES},
237#endif
238 {L"ProgramFiles6432Folder", InitializeVariable6432Folder, CSIDL_PROGRAM_FILES},
239 {L"ProgramMenuFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAMS},
240 {L"RebootPending", InitializeVariableRebootPending, 0},
241 {L"SendToFolder", InitializeVariableCsidlFolder, CSIDL_SENDTO},
242 {L"ServicePackLevel", InitializeVariableVersionNT, OS_INFO_VARIABLE_ServicePackLevel},
243 {L"StartMenuFolder", InitializeVariableCsidlFolder, CSIDL_STARTMENU},
244 {L"StartupFolder", InitializeVariableCsidlFolder, CSIDL_STARTUP},
245 {L"SystemFolder", InitializeVariableSystemFolder, FALSE},
246 {L"System64Folder", InitializeVariableSystemFolder, TRUE},
247 {L"SystemLanguageID", InitializeSystemLanguageID, 0},
248 {L"TempFolder", InitializeVariableTempFolder, 0},
249 {L"TemplateFolder", InitializeVariableCsidlFolder, CSIDL_TEMPLATES},
250 {L"TerminalServer", InitializeVariableOsInfo, OS_INFO_VARIABLE_TerminalServer},
251 {L"UserUILanguageID", InitializeUserUILanguageID, 0},
252 {L"UserLanguageID", InitializeUserLanguageID, 0},
253 {L"VersionMsi", InitializeVariableVersionMsi, 0},
254 {L"VersionNT", InitializeVariableVersionNT, OS_INFO_VARIABLE_VersionNT},
255 {L"VersionNT64", InitializeVariableVersionNT, OS_INFO_VARIABLE_VersionNT64},
256 {L"WindowsFolder", InitializeVariableCsidlFolder, CSIDL_WINDOWS},
257 {L"WindowsVolume", InitializeVariableWindowsVolumeFolder, 0},
258 {BURN_BUNDLE_ACTION, InitializeVariableNumeric, 0, FALSE, TRUE},
259 {BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, InitializeVariableString, NULL, FALSE, TRUE},
260 {BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, InitializeVariableString, NULL, FALSE, TRUE},
261 {BURN_BUNDLE_FORCED_RESTART_PACKAGE, InitializeVariableString, NULL, TRUE, TRUE},
262 {BURN_BUNDLE_INSTALLED, InitializeVariableNumeric, 0, FALSE, TRUE},
263 {BURN_BUNDLE_ELEVATED, InitializeVariableNumeric, 0, FALSE, TRUE},
264 {BURN_BUNDLE_ACTIVE_PARENT, InitializeVariableString, NULL, FALSE, TRUE},
265 {BURN_BUNDLE_PROVIDER_KEY, InitializeVariableString, (DWORD_PTR)L"", FALSE, TRUE},
266 {BURN_BUNDLE_SOURCE_PROCESS_PATH, InitializeVariableString, NULL, FALSE, TRUE},
267 {BURN_BUNDLE_SOURCE_PROCESS_FOLDER, InitializeVariableString, NULL, FALSE, TRUE},
268 {BURN_BUNDLE_TAG, InitializeVariableString, (DWORD_PTR)L"", FALSE, TRUE},
269 {BURN_BUNDLE_UILEVEL, InitializeVariableNumeric, 0, FALSE, TRUE},
270 {BURN_BUNDLE_VERSION, InitializeVariableVersion, 0, FALSE, TRUE},
271 };
272
273 for (DWORD i = 0; i < countof(vrgBuiltInVariables); ++i)
274 {
275 BUILT_IN_VARIABLE_DECLARATION* pBuiltInVariable = &vrgBuiltInVariables[i];
276
277 hr = AddBuiltInVariable(pVariables, pBuiltInVariable->wzVariable, pBuiltInVariable->pfnInitialize, pBuiltInVariable->dwpInitializeData, pBuiltInVariable->fPersist, pBuiltInVariable->fOverridable);
278 ExitOnFailure(hr, "Failed to add built-in variable: %ls.", pBuiltInVariable->wzVariable);
279 }
280
281LExit:
282 return hr;
283}
284
285extern "C" HRESULT VariablesParseFromXml(
286 __in BURN_VARIABLES* pVariables,
287 __in IXMLDOMNode* pixnBundle
288 )
289{
290 HRESULT hr = S_OK;
291 IXMLDOMNodeList* pixnNodes = NULL;
292 IXMLDOMNode* pixnNode = NULL;
293 DWORD cNodes = 0;
294 LPWSTR sczId = NULL;
295 LPWSTR scz = NULL;
296 BURN_VARIANT value = { };
297 BURN_VARIANT_TYPE valueType = BURN_VARIANT_TYPE_NONE;
298 BOOL fHidden = FALSE;
299 BOOL fPersisted = FALSE;
300 DWORD iVariable = 0;
301
302 ::EnterCriticalSection(&pVariables->csAccess);
303
304 // select variable nodes
305 hr = XmlSelectNodes(pixnBundle, L"Variable", &pixnNodes);
306 ExitOnFailure(hr, "Failed to select variable nodes.");
307
308 // get variable node count
309 hr = pixnNodes->get_length((long*)&cNodes);
310 ExitOnFailure(hr, "Failed to get variable node count.");
311
312 // parse package elements
313 for (DWORD i = 0; i < cNodes; ++i)
314 {
315 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
316 ExitOnFailure(hr, "Failed to get next node.");
317
318 // @Id
319 hr = XmlGetAttributeEx(pixnNode, L"Id", &sczId);
320 ExitOnFailure(hr, "Failed to get @Id.");
321
322 // @Hidden
323 hr = XmlGetYesNoAttribute(pixnNode, L"Hidden", &fHidden);
324 ExitOnFailure(hr, "Failed to get @Hidden.");
325
326 // @Persisted
327 hr = XmlGetYesNoAttribute(pixnNode, L"Persisted", &fPersisted);
328 ExitOnFailure(hr, "Failed to get @Persisted.");
329
330 // @Value
331 hr = XmlGetAttributeEx(pixnNode, L"Value", &scz);
332 if (E_NOTFOUND != hr)
333 {
334 ExitOnFailure(hr, "Failed to get @Value.");
335
336 hr = BVariantSetString(&value, scz, 0);
337 ExitOnFailure(hr, "Failed to set variant value.");
338
339 // @Type
340 hr = XmlGetAttributeEx(pixnNode, L"Type", &scz);
341 ExitOnFailure(hr, "Failed to get @Type.");
342
343 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1))
344 {
345 if (!fHidden)
346 {
347 LogStringLine(REPORT_STANDARD, "Initializing numeric variable '%ls' to value '%ls'", sczId, value.sczValue);
348 }
349 valueType = BURN_VARIANT_TYPE_NUMERIC;
350 }
351 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"string", -1))
352 {
353 if (!fHidden)
354 {
355 LogStringLine(REPORT_STANDARD, "Initializing string variable '%ls' to value '%ls'", sczId, value.sczValue);
356 }
357 valueType = BURN_VARIANT_TYPE_STRING;
358 }
359 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1))
360 {
361 if (!fHidden)
362 {
363 LogStringLine(REPORT_STANDARD, "Initializing version variable '%ls' to value '%ls'", sczId, value.sczValue);
364 }
365 valueType = BURN_VARIANT_TYPE_VERSION;
366 }
367 else
368 {
369 hr = E_INVALIDARG;
370 ExitOnFailure(hr, "Invalid value for @Type: %ls", scz);
371 }
372 }
373 else
374 {
375 valueType = BURN_VARIANT_TYPE_NONE;
376 }
377
378 if (fHidden)
379 {
380 LogStringLine(REPORT_STANDARD, "Initializing hidden variable '%ls'", sczId);
381 }
382
383 // change value variant to correct type
384 hr = BVariantChangeType(&value, valueType);
385 ExitOnFailure(hr, "Failed to change variant type.");
386
387 // find existing variable
388 hr = FindVariableIndexByName(pVariables, sczId, &iVariable);
389 ExitOnFailure(hr, "Failed to find variable value '%ls'.", sczId);
390
391 // insert element if not found
392 if (S_FALSE == hr)
393 {
394 hr = InsertVariable(pVariables, sczId, iVariable);
395 ExitOnFailure(hr, "Failed to insert variable '%ls'.", sczId);
396 }
397 else if (BURN_VARIABLE_INTERNAL_TYPE_NORMAL < pVariables->rgVariables[iVariable].internalType)
398 {
399 hr = E_INVALIDARG;
400 ExitOnRootFailure(hr, "Attempt to set built-in variable value: %ls", sczId);
401 }
402 pVariables->rgVariables[iVariable].fHidden = fHidden;
403 pVariables->rgVariables[iVariable].fPersisted = fPersisted;
404
405 // update variable value
406 hr = BVariantSetValue(&pVariables->rgVariables[iVariable].Value, &value);
407 ExitOnFailure(hr, "Failed to set value of variable: %ls", sczId);
408
409 hr = BVariantSetEncryption(&pVariables->rgVariables[iVariable].Value, fHidden);
410 ExitOnFailure(hr, "Failed to set variant encryption");
411
412 // prepare next iteration
413 ReleaseNullObject(pixnNode);
414 BVariantUninitialize(&value);
415 ReleaseNullStrSecure(scz);
416 }
417
418LExit:
419 ::LeaveCriticalSection(&pVariables->csAccess);
420
421 ReleaseObject(pixnNodes);
422 ReleaseObject(pixnNode);
423 ReleaseStr(scz);
424 ReleaseStr(sczId);
425 BVariantUninitialize(&value);
426
427 return hr;
428}
429
430extern "C" void VariablesUninitialize(
431 __in BURN_VARIABLES* pVariables
432 )
433{
434 ::DeleteCriticalSection(&pVariables->csAccess);
435
436 if (pVariables->rgVariables)
437 {
438 for (DWORD i = 0; i < pVariables->cVariables; ++i)
439 {
440 BURN_VARIABLE* pVariable = &pVariables->rgVariables[i];
441 if (pVariable)
442 {
443 ReleaseStr(pVariable->sczName);
444 BVariantUninitialize(&pVariable->Value);
445 }
446 }
447 MemFree(pVariables->rgVariables);
448 }
449}
450
451extern "C" void VariablesDump(
452 __in BURN_VARIABLES* pVariables
453 )
454{
455 HRESULT hr = S_OK;
456 LPWSTR sczValue = NULL;
457
458 for (DWORD i = 0; i < pVariables->cVariables; ++i)
459 {
460 BURN_VARIABLE* pVariable = &pVariables->rgVariables[i];
461 if (pVariable && BURN_VARIANT_TYPE_NONE != pVariable->Value.Type)
462 {
463 hr = StrAllocFormatted(&sczValue, L"%ls = [%ls]", pVariable->sczName, pVariable->sczName);
464 if (SUCCEEDED(hr))
465 {
466 if (pVariable->fHidden)
467 {
468 hr = VariableFormatStringObfuscated(pVariables, sczValue, &sczValue, NULL);
469 }
470 else
471 {
472 hr = VariableFormatString(pVariables, sczValue, &sczValue, NULL);
473 }
474 }
475
476 if (FAILED(hr))
477 {
478 // already logged; best-effort to dump the rest on our way out the door
479 continue;
480 }
481
482 LogId(REPORT_VERBOSE, MSG_VARIABLE_DUMP, sczValue);
483
484 ReleaseNullStrSecure(sczValue);
485 }
486 }
487
488 StrSecureZeroFreeString(sczValue);
489}
490
491// The contents of pllValue may be sensitive, if variable is hidden should keep value encrypted and SecureZeroMemory.
492extern "C" HRESULT VariableGetNumeric(
493 __in BURN_VARIABLES* pVariables,
494 __in_z LPCWSTR wzVariable,
495 __out LONGLONG* pllValue
496 )
497{
498 HRESULT hr = S_OK;
499 BURN_VARIABLE* pVariable = NULL;
500
501 ::EnterCriticalSection(&pVariables->csAccess);
502
503 hr = GetVariable(pVariables, wzVariable, &pVariable);
504 if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type)
505 {
506 ExitFunction1(hr = E_NOTFOUND);
507 }
508 else if (E_NOTFOUND == hr)
509 {
510 ExitFunction();
511 }
512 ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable);
513
514 hr = BVariantGetNumeric(&pVariable->Value, pllValue);
515 ExitOnFailure(hr, "Failed to get value as numeric for variable: %ls", wzVariable);
516
517LExit:
518 ::LeaveCriticalSection(&pVariables->csAccess);
519
520 return hr;
521}
522
523// The contents of psczValue may be sensitive, if variable is hidden should keep encrypted and SecureZeroFree.
524extern "C" HRESULT VariableGetString(
525 __in BURN_VARIABLES* pVariables,
526 __in_z LPCWSTR wzVariable,
527 __out_z LPWSTR* psczValue
528 )
529{
530 HRESULT hr = S_OK;
531 BURN_VARIABLE* pVariable = NULL;
532
533 ::EnterCriticalSection(&pVariables->csAccess);
534
535 hr = GetVariable(pVariables, wzVariable, &pVariable);
536 if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type)
537 {
538 ExitFunction1(hr = E_NOTFOUND);
539 }
540 else if (E_NOTFOUND == hr)
541 {
542 ExitFunction();
543 }
544 ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable);
545
546 hr = BVariantGetString(&pVariable->Value, psczValue);
547 ExitOnFailure(hr, "Failed to get value as string for variable: %ls", wzVariable);
548
549LExit:
550 ::LeaveCriticalSection(&pVariables->csAccess);
551
552 return hr;
553}
554
555// The contents of pqwValue may be sensitive, if variable is hidden should keep value encrypted and SecureZeroMemory.
556extern "C" HRESULT VariableGetVersion(
557 __in BURN_VARIABLES* pVariables,
558 __in_z LPCWSTR wzVariable,
559 __in DWORD64* pqwValue
560 )
561{
562 HRESULT hr = S_OK;
563 BURN_VARIABLE* pVariable = NULL;
564
565 ::EnterCriticalSection(&pVariables->csAccess);
566
567 hr = GetVariable(pVariables, wzVariable, &pVariable);
568 if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type)
569 {
570 ExitFunction1(hr = E_NOTFOUND);
571 }
572 else if (E_NOTFOUND == hr)
573 {
574 ExitFunction();
575 }
576 ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable);
577
578 hr = BVariantGetVersion(&pVariable->Value, pqwValue);
579 ExitOnFailure(hr, "Failed to get value as version for variable: %ls", wzVariable);
580
581LExit:
582 ::LeaveCriticalSection(&pVariables->csAccess);
583
584 return hr;
585}
586
587extern "C" HRESULT VariableGetVariant(
588 __in BURN_VARIABLES* pVariables,
589 __in_z LPCWSTR wzVariable,
590 __in BURN_VARIANT* pValue
591 )
592{
593 HRESULT hr = S_OK;
594 BURN_VARIABLE* pVariable = NULL;
595
596 ::EnterCriticalSection(&pVariables->csAccess);
597
598 hr = GetVariable(pVariables, wzVariable, &pVariable);
599 if (E_NOTFOUND == hr)
600 {
601 ExitFunction();
602 }
603 ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable);
604
605 hr = BVariantCopy(&pVariable->Value, pValue);
606 ExitOnFailure(hr, "Failed to copy value of variable: %ls", wzVariable);
607
608LExit:
609 ::LeaveCriticalSection(&pVariables->csAccess);
610
611 return hr;
612}
613
614// The contents of psczValue may be sensitive, should keep encrypted and SecureZeroFree.
615extern "C" HRESULT VariableGetFormatted(
616 __in BURN_VARIABLES* pVariables,
617 __in_z LPCWSTR wzVariable,
618 __out_z LPWSTR* psczValue
619 )
620{
621 HRESULT hr = S_OK;
622 BURN_VARIABLE* pVariable = NULL;
623 LPWSTR scz = NULL;
624
625 ::EnterCriticalSection(&pVariables->csAccess);
626
627 hr = GetVariable(pVariables, wzVariable, &pVariable);
628 if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type)
629 {
630 ExitFunction1(hr = E_NOTFOUND);
631 }
632 else if (E_NOTFOUND == hr)
633 {
634 ExitFunction();
635 }
636 ExitOnFailure(hr, "Failed to get variable: %ls", wzVariable);
637
638 // Strings need to get expanded unless they're built-in or literal because they're guaranteed not to have embedded variables.
639 if (BURN_VARIANT_TYPE_STRING == pVariable->Value.Type &&
640 BURN_VARIABLE_INTERNAL_TYPE_NORMAL == pVariable->internalType &&
641 !pVariable->fLiteral)
642 {
643 hr = BVariantGetString(&pVariable->Value, &scz);
644 ExitOnFailure(hr, "Failed to get unformatted string.");
645
646 hr = VariableFormatString(pVariables, scz, psczValue, NULL);
647 ExitOnFailure(hr, "Failed to format value '%ls' of variable: %ls", pVariable->fHidden ? L"*****" : pVariable->Value.sczValue, wzVariable);
648 }
649 else
650 {
651 hr = BVariantGetString(&pVariable->Value, psczValue);
652 ExitOnFailure(hr, "Failed to get value as string for variable: %ls", wzVariable);
653 }
654
655LExit:
656 ::LeaveCriticalSection(&pVariables->csAccess);
657 StrSecureZeroFreeString(scz);
658
659 return hr;
660}
661
662extern "C" HRESULT VariableSetNumeric(
663 __in BURN_VARIABLES* pVariables,
664 __in_z LPCWSTR wzVariable,
665 __in LONGLONG llValue,
666 __in BOOL fOverwriteBuiltIn
667 )
668{
669 BURN_VARIANT variant = { };
670
671 // We're not going to encrypt this value, so can access the value directly.
672 variant.llValue = llValue;
673 variant.Type = BURN_VARIANT_TYPE_NUMERIC;
674
675 return SetVariableValue(pVariables, wzVariable, &variant, FALSE, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE);
676}
677
678extern "C" HRESULT VariableSetLiteralString(
679 __in BURN_VARIABLES* pVariables,
680 __in_z LPCWSTR wzVariable,
681 __in_z_opt LPCWSTR wzValue,
682 __in BOOL fOverwriteBuiltIn
683 )
684{
685 BURN_VARIANT variant = { };
686
687 // We're not going to encrypt this value, so can access the value directly.
688 variant.sczValue = (LPWSTR)wzValue;
689 variant.Type = BURN_VARIANT_TYPE_STRING;
690
691 return SetVariableValue(pVariables, wzVariable, &variant, TRUE, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE);
692}
693
694extern "C" HRESULT VariableSetString(
695 __in BURN_VARIABLES* pVariables,
696 __in_z LPCWSTR wzVariable,
697 __in_z_opt LPCWSTR wzValue,
698 __in BOOL fOverwriteBuiltIn
699 )
700{
701 BURN_VARIANT variant = { };
702
703 // We're not going to encrypt this value, so can access the value directly.
704 variant.sczValue = (LPWSTR)wzValue;
705 variant.Type = BURN_VARIANT_TYPE_STRING;
706
707 return SetVariableValue(pVariables, wzVariable, &variant, FALSE, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE);
708}
709
710extern "C" HRESULT VariableSetVersion(
711 __in BURN_VARIABLES* pVariables,
712 __in_z LPCWSTR wzVariable,
713 __in DWORD64 qwValue,
714 __in BOOL fOverwriteBuiltIn
715 )
716{
717 BURN_VARIANT variant = { };
718
719 // We're not going to encrypt this value, so can access the value directly.
720 variant.qwValue = qwValue;
721 variant.Type = BURN_VARIANT_TYPE_VERSION;
722
723 return SetVariableValue(pVariables, wzVariable, &variant, FALSE, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE);
724}
725
726extern "C" HRESULT VariableSetLiteralVariant(
727 __in BURN_VARIABLES* pVariables,
728 __in_z LPCWSTR wzVariable,
729 __in BURN_VARIANT* pVariant
730 )
731{
732 return SetVariableValue(pVariables, wzVariable, pVariant, TRUE, SET_VARIABLE_NOT_BUILTIN, TRUE);
733}
734
735// The contents of psczOut may be sensitive, should keep encrypted and SecureZeroFree
736extern "C" HRESULT VariableFormatString(
737 __in BURN_VARIABLES* pVariables,
738 __in_z LPCWSTR wzIn,
739 __out_z_opt LPWSTR* psczOut,
740 __out_opt DWORD* pcchOut
741 )
742{
743 return FormatString(pVariables, wzIn, psczOut, pcchOut, FALSE);
744}
745
746extern "C" HRESULT VariableFormatStringObfuscated(
747 __in BURN_VARIABLES* pVariables,
748 __in_z LPCWSTR wzIn,
749 __out_z_opt LPWSTR* psczOut,
750 __out_opt DWORD* pcchOut
751 )
752{
753 return FormatString(pVariables, wzIn, psczOut, pcchOut, TRUE);
754}
755
756extern "C" HRESULT VariableEscapeString(
757 __in_z LPCWSTR wzIn,
758 __out_z LPWSTR* psczOut
759 )
760{
761 HRESULT hr = S_OK;
762 LPCWSTR wzRead = NULL;
763 LPWSTR pwzEscaped = NULL;
764 LPWSTR pwz = NULL;
765 SIZE_T i = 0;
766
767 // allocate buffer for escaped string
768 hr = StrAlloc(&pwzEscaped, lstrlenW(wzIn) + 1);
769 ExitOnFailure(hr, "Failed to allocate buffer for escaped string.");
770
771 // read through string and move characters, inserting escapes as needed
772 wzRead = wzIn;
773 for (;;)
774 {
775 // find next character needing escaping
776 i = wcscspn(wzRead, L"[]{}");
777
778 // copy skipped characters
779 if (0 < i)
780 {
781 hr = StrAllocConcat(&pwzEscaped, wzRead, i);
782 ExitOnFailure(hr, "Failed to append characters.");
783 }
784
785 if (L'\0' == wzRead[i])
786 {
787 break; // end reached
788 }
789
790 // escape character
791 hr = StrAllocFormatted(&pwz, L"[\\%c]", wzRead[i]);
792 ExitOnFailure(hr, "Failed to format escape sequence.");
793
794 hr = StrAllocConcat(&pwzEscaped, pwz, 0);
795 ExitOnFailure(hr, "Failed to append escape sequence.");
796
797 // update read pointer
798 wzRead += i + 1;
799 }
800
801 // return value
802 hr = StrAllocString(psczOut, pwzEscaped, 0);
803 ExitOnFailure(hr, "Failed to copy string.");
804
805LExit:
806 ReleaseStr(pwzEscaped);
807 ReleaseStr(pwz);
808 return hr;
809}
810
811extern "C" HRESULT VariableSerialize(
812 __in BURN_VARIABLES* pVariables,
813 __in BOOL fPersisting,
814 __inout BYTE** ppbBuffer,
815 __inout SIZE_T* piBuffer
816 )
817{
818 HRESULT hr = S_OK;
819 BOOL fIncluded = FALSE;
820 LONGLONG ll = 0;
821 LPWSTR scz = NULL;
822 DWORD64 qw = 0;
823
824 ::EnterCriticalSection(&pVariables->csAccess);
825
826 // Write variable count.
827 hr = BuffWriteNumber(ppbBuffer, piBuffer, pVariables->cVariables);
828 ExitOnFailure(hr, "Failed to write variable count.");
829
830 // Write variables.
831 for (DWORD i = 0; i < pVariables->cVariables; ++i)
832 {
833 BURN_VARIABLE* pVariable = &pVariables->rgVariables[i];
834
835 // If we aren't persisting, include only variables that aren't rejected by the elevated process.
836 // If we are persisting, include only variables that should be persisted.
837 fIncluded = (!fPersisting && BURN_VARIABLE_INTERNAL_TYPE_BUILTIN != pVariable->internalType) ||
838 (fPersisting && pVariable->fPersisted);
839
840 // Write included flag.
841 hr = BuffWriteNumber(ppbBuffer, piBuffer, (DWORD)fIncluded);
842 ExitOnFailure(hr, "Failed to write included flag.");
843
844 if (!fIncluded)
845 {
846 continue;
847 }
848
849 // Write variable name.
850 hr = BuffWriteString(ppbBuffer, piBuffer, pVariable->sczName);
851 ExitOnFailure(hr, "Failed to write variable name.");
852
853 // Write variable value type.
854 hr = BuffWriteNumber(ppbBuffer, piBuffer, (DWORD)pVariable->Value.Type);
855 ExitOnFailure(hr, "Failed to write variable value type.");
856
857 // Write variable value.
858 switch (pVariable->Value.Type)
859 {
860 case BURN_VARIANT_TYPE_NONE:
861 break;
862 case BURN_VARIANT_TYPE_NUMERIC:
863 hr = BVariantGetNumeric(&pVariable->Value, &ll);
864 ExitOnFailure(hr, "Failed to get numeric.");
865
866 hr = BuffWriteNumber64(ppbBuffer, piBuffer, static_cast<DWORD64>(ll));
867 ExitOnFailure(hr, "Failed to write variable value as number.");
868
869 SecureZeroMemory(&ll, sizeof(ll));
870 break;
871 case BURN_VARIANT_TYPE_VERSION:
872 hr = BVariantGetVersion(&pVariable->Value, &qw);
873 ExitOnFailure(hr, "Failed to get version.");
874
875 hr = BuffWriteNumber64(ppbBuffer, piBuffer, qw);
876 ExitOnFailure(hr, "Failed to write variable value as number.");
877
878 SecureZeroMemory(&qw, sizeof(qw));
879 break;
880 case BURN_VARIANT_TYPE_STRING:
881 hr = BVariantGetString(&pVariable->Value, &scz);
882 ExitOnFailure(hr, "Failed to get string.");
883
884 hr = BuffWriteString(ppbBuffer, piBuffer, scz);
885 ExitOnFailure(hr, "Failed to write variable value as string.");
886
887 ReleaseNullStrSecure(scz);
888 break;
889 default:
890 hr = E_INVALIDARG;
891 ExitOnFailure(hr, "Unsupported variable type.");
892 }
893
894 // Write literal flag.
895 hr = BuffWriteNumber(ppbBuffer, piBuffer, (DWORD)pVariable->fLiteral);
896 ExitOnFailure(hr, "Failed to write literal flag.");
897 }
898
899LExit:
900 ::LeaveCriticalSection(&pVariables->csAccess);
901 SecureZeroMemory(&ll, sizeof(ll));
902 SecureZeroMemory(&qw, sizeof(qw));
903 StrSecureZeroFreeString(scz);
904
905 return hr;
906}
907
908extern "C" HRESULT VariableDeserialize(
909 __in BURN_VARIABLES* pVariables,
910 __in BOOL fWasPersisted,
911 __in_bcount(cbBuffer) BYTE* pbBuffer,
912 __in SIZE_T cbBuffer,
913 __inout SIZE_T* piBuffer
914 )
915{
916 HRESULT hr = S_OK;
917 DWORD cVariables = 0;
918 LPWSTR sczName = NULL;
919 BOOL fIncluded = FALSE;
920 BOOL fLiteral = FALSE;
921 BURN_VARIANT value = { };
922 LPWSTR scz = NULL;
923 DWORD64 qw = 0;
924
925 ::EnterCriticalSection(&pVariables->csAccess);
926
927 // Read variable count.
928 hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, &cVariables);
929 ExitOnFailure(hr, "Failed to read variable count.");
930
931 // Read variables.
932 for (DWORD i = 0; i < cVariables; ++i)
933 {
934 // Read variable included flag.
935 hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, (DWORD*)&fIncluded);
936 ExitOnFailure(hr, "Failed to read variable included flag.");
937
938 if (!fIncluded)
939 {
940 continue; // if variable is not included, skip.
941 }
942
943 // Read variable name.
944 hr = BuffReadString(pbBuffer, cbBuffer, piBuffer, &sczName);
945 ExitOnFailure(hr, "Failed to read variable name.");
946
947 // Read variable value type.
948 hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, (DWORD*)&value.Type);
949 ExitOnFailure(hr, "Failed to read variable value type.");
950
951 // Read variable value.
952 switch (value.Type)
953 {
954 case BURN_VARIANT_TYPE_NONE:
955 break;
956 case BURN_VARIANT_TYPE_NUMERIC:
957 hr = BuffReadNumber64(pbBuffer, cbBuffer, piBuffer, &qw);
958 ExitOnFailure(hr, "Failed to read variable value as number.");
959
960 hr = BVariantSetNumeric(&value, static_cast<LONGLONG>(qw));
961 ExitOnFailure(hr, "Failed to set variable value.");
962
963 SecureZeroMemory(&qw, sizeof(qw));
964 break;
965 case BURN_VARIANT_TYPE_VERSION:
966 hr = BuffReadNumber64(pbBuffer, cbBuffer, piBuffer, &qw);
967 ExitOnFailure(hr, "Failed to read variable value as number.");
968
969 hr = BVariantSetVersion(&value, qw);
970 ExitOnFailure(hr, "Failed to set variable value.");
971
972 SecureZeroMemory(&qw, sizeof(qw));
973 break;
974 case BURN_VARIANT_TYPE_STRING:
975 hr = BuffReadString(pbBuffer, cbBuffer, piBuffer, &scz);
976 ExitOnFailure(hr, "Failed to read variable value as string.");
977
978 hr = BVariantSetString(&value, scz, NULL);
979 ExitOnFailure(hr, "Failed to set variable value.");
980
981 ReleaseNullStrSecure(scz);
982 break;
983 default:
984 hr = E_INVALIDARG;
985 ExitOnFailure(hr, "Unsupported variable type.");
986 }
987
988 // Read variable literal flag.
989 hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, (DWORD*)&fLiteral);
990 ExitOnFailure(hr, "Failed to read variable literal flag.");
991
992 // Set variable.
993 hr = SetVariableValue(pVariables, sczName, &value, fLiteral, fWasPersisted ? SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS : SET_VARIABLE_ANY, FALSE);
994 ExitOnFailure(hr, "Failed to set variable.");
995
996 // Clean up.
997 BVariantUninitialize(&value);
998 }
999
1000LExit:
1001 ::LeaveCriticalSection(&pVariables->csAccess);
1002
1003 ReleaseStr(sczName);
1004 BVariantUninitialize(&value);
1005 SecureZeroMemory(&qw, sizeof(qw));
1006 StrSecureZeroFreeString(scz);
1007
1008 return hr;
1009}
1010
1011extern "C" HRESULT VariableStrAlloc(
1012 __in BOOL fZeroOnRealloc,
1013 __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
1014 __in DWORD_PTR cch
1015 )
1016{
1017 HRESULT hr = S_OK;
1018
1019 if (fZeroOnRealloc)
1020 {
1021 hr = StrAllocSecure(ppwz, cch);
1022 }
1023 else
1024 {
1025 hr = StrAlloc(ppwz, cch);
1026 }
1027
1028 return hr;
1029}
1030
1031extern "C" HRESULT VariableStrAllocString(
1032 __in BOOL fZeroOnRealloc,
1033 __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz,
1034 __in_z LPCWSTR wzSource,
1035 __in DWORD_PTR cchSource
1036 )
1037{
1038 HRESULT hr = S_OK;
1039
1040 if (fZeroOnRealloc)
1041 {
1042 hr = StrAllocStringSecure(ppwz, wzSource, cchSource);
1043 }
1044 else
1045 {
1046 hr = StrAllocString(ppwz, wzSource, cchSource);
1047 }
1048
1049 return hr;
1050}
1051
1052extern "C" HRESULT VariableStrAllocConcat(
1053 __in BOOL fZeroOnRealloc,
1054 __deref_out_z LPWSTR* ppwz,
1055 __in_z LPCWSTR wzSource,
1056 __in DWORD_PTR cchSource
1057 )
1058{
1059 HRESULT hr = S_OK;
1060
1061 if (fZeroOnRealloc)
1062 {
1063 hr = StrAllocConcatSecure(ppwz, wzSource, cchSource);
1064 }
1065 else
1066 {
1067 hr = StrAllocConcat(ppwz, wzSource, cchSource);
1068 }
1069
1070 return hr;
1071}
1072
1073extern "C" HRESULT __cdecl VariableStrAllocFormatted(
1074 __in BOOL fZeroOnRealloc,
1075 __deref_out_z LPWSTR* ppwz,
1076 __in __format_string LPCWSTR wzFormat,
1077 ...
1078 )
1079{
1080 HRESULT hr = S_OK;
1081 va_list args;
1082
1083 va_start(args, wzFormat);
1084 if (fZeroOnRealloc)
1085 {
1086 hr = StrAllocFormattedArgsSecure(ppwz, wzFormat, args);
1087 }
1088 else
1089 {
1090 hr = StrAllocFormattedArgs(ppwz, wzFormat, args);
1091 }
1092 va_end(args);
1093
1094 return hr;
1095}
1096
1097extern "C" HRESULT VariableIsHidden(
1098 __in BURN_VARIABLES* pVariables,
1099 __in_z LPCWSTR wzVariable,
1100 __out BOOL* pfHidden
1101 )
1102{
1103 HRESULT hr = S_OK;
1104 BURN_VARIABLE* pVariable = NULL;
1105
1106 ::EnterCriticalSection(&pVariables->csAccess);
1107
1108 hr = GetVariable(pVariables, wzVariable, &pVariable);
1109 if (E_NOTFOUND == hr)
1110 {
1111 // A missing variable does not need its data hidden.
1112 *pfHidden = FALSE;
1113
1114 ExitFunction1(hr = S_OK);
1115 }
1116 ExitOnFailure(hr, "Failed to get visibility of variable: %ls", wzVariable);
1117
1118 *pfHidden = pVariable->fHidden;
1119
1120LExit:
1121 ::LeaveCriticalSection(&pVariables->csAccess);
1122
1123 return hr;
1124}
1125
1126
1127// internal function definitions
1128
1129// The contents of psczOut may be sensitive, should keep encrypted and SecureZeroFree.
1130static HRESULT FormatString(
1131 __in BURN_VARIABLES* pVariables,
1132 __in_z LPCWSTR wzIn,
1133 __out_z_opt LPWSTR* psczOut,
1134 __out_opt DWORD* pcchOut,
1135 __in BOOL fObfuscateHiddenVariables
1136 )
1137{
1138 HRESULT hr = S_OK;
1139 DWORD er = ERROR_SUCCESS;
1140 LPWSTR sczUnformatted = NULL;
1141 LPWSTR sczFormat = NULL;
1142 LPCWSTR wzRead = NULL;
1143 LPCWSTR wzOpen = NULL;
1144 LPCWSTR wzClose = NULL;
1145 LPWSTR scz = NULL;
1146 LPWSTR* rgVariables = NULL;
1147 DWORD cVariables = 0;
1148 DWORD cch = 0;
1149 BOOL fHidden = FALSE;
1150 MSIHANDLE hRecord = NULL;
1151
1152 ::EnterCriticalSection(&pVariables->csAccess);
1153
1154 // allocate buffer for format string
1155 hr = StrAlloc(&sczFormat, lstrlenW(wzIn) + 1);
1156 ExitOnFailure(hr, "Failed to allocate buffer for format string.");
1157
1158 // read out variables from the unformatted string and build a format string
1159 wzRead = wzIn;
1160 for (;;)
1161 {
1162 // scan for opening '['
1163 wzOpen = wcschr(wzRead, L'[');
1164 if (!wzOpen)
1165 {
1166 // end reached, append the remainder of the string and end loop
1167 hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, 0);
1168 ExitOnFailure(hr, "Failed to append string.");
1169 break;
1170 }
1171
1172 // scan for closing ']'
1173 wzClose = wcschr(wzOpen + 1, L']');
1174 if (!wzClose)
1175 {
1176 // end reached, treat unterminated expander as literal
1177 hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, 0);
1178 ExitOnFailure(hr, "Failed to append string.");
1179 break;
1180 }
1181 cch = wzClose - wzOpen - 1;
1182
1183 if (0 == cch)
1184 {
1185 // blank, copy all text including the terminator
1186 hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, (DWORD_PTR)(wzClose - wzRead) + 1);
1187 ExitOnFailure(hr, "Failed to append string.");
1188 }
1189 else
1190 {
1191 // append text preceding expander
1192 if (wzOpen > wzRead)
1193 {
1194 hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, (DWORD_PTR)(wzOpen - wzRead));
1195 ExitOnFailure(hr, "Failed to append string.");
1196 }
1197
1198 // get variable name
1199 hr = VariableStrAllocString(!fObfuscateHiddenVariables, &scz, wzOpen + 1, cch);
1200 ExitOnFailure(hr, "Failed to get variable name.");
1201
1202 // allocate space in variable array
1203 if (rgVariables)
1204 {
1205 LPVOID pv = MemReAlloc(rgVariables, sizeof(LPWSTR) * (cVariables + 1), TRUE);
1206 ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate variable array.");
1207 rgVariables = (LPWSTR*)pv;
1208 }
1209 else
1210 {
1211 rgVariables = (LPWSTR*)MemAlloc(sizeof(LPWSTR) * (cVariables + 1), TRUE);
1212 ExitOnNull(rgVariables, hr, E_OUTOFMEMORY, "Failed to allocate variable array.");
1213 }
1214
1215 // set variable value
1216 if (2 <= cch && L'\\' == wzOpen[1])
1217 {
1218 // escape sequence, copy character
1219 hr = VariableStrAllocString(!fObfuscateHiddenVariables, &rgVariables[cVariables], &wzOpen[2], 1);
1220 }
1221 else
1222 {
1223 if (fObfuscateHiddenVariables)
1224 {
1225 hr = VariableIsHidden(pVariables, scz, &fHidden);
1226 ExitOnFailure(hr, "Failed to determine variable visibility: '%ls'.", scz);
1227 }
1228
1229 if (fHidden)
1230 {
1231 hr = StrAllocString(&rgVariables[cVariables], L"*****", 0);
1232 }
1233 else
1234 {
1235 // get formatted variable value
1236 hr = VariableGetFormatted(pVariables, scz, &rgVariables[cVariables]);
1237 if (E_NOTFOUND == hr) // variable not found
1238 {
1239 hr = StrAllocStringSecure(&rgVariables[cVariables], L"", 0);
1240 }
1241 }
1242 }
1243 ExitOnFailure(hr, "Failed to set variable value.");
1244 ++cVariables;
1245
1246 // append placeholder to format string
1247 hr = VariableStrAllocFormatted(!fObfuscateHiddenVariables, &scz, L"[%d]", cVariables);
1248 ExitOnFailure(hr, "Failed to format placeholder string.");
1249
1250 hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, scz, 0);
1251 ExitOnFailure(hr, "Failed to append placeholder.");
1252 }
1253
1254 // update read pointer
1255 wzRead = wzClose + 1;
1256 }
1257
1258 // create record
1259 hRecord = ::MsiCreateRecord(cVariables);
1260 ExitOnNull(hRecord, hr, E_OUTOFMEMORY, "Failed to allocate record.");
1261
1262 // set format string
1263 er = ::MsiRecordSetStringW(hRecord, 0, sczFormat);
1264 ExitOnWin32Error(er, hr, "Failed to set record format string.");
1265
1266 // copy record fields
1267 for (DWORD i = 0; i < cVariables; ++i)
1268 {
1269 if (*rgVariables[i]) // not setting if blank
1270 {
1271 er = ::MsiRecordSetStringW(hRecord, i + 1, rgVariables[i]);
1272 ExitOnWin32Error(er, hr, "Failed to set record string.");
1273 }
1274 }
1275
1276 // get formatted character count
1277 cch = 0;
1278#pragma prefast(push)
1279#pragma prefast(disable:6298)
1280 er = ::MsiFormatRecordW(NULL, hRecord, L"", &cch);
1281#pragma prefast(pop)
1282 if (ERROR_MORE_DATA != er)
1283 {
1284 ExitOnWin32Error(er, hr, "Failed to get formatted length.");
1285 }
1286
1287 // return formatted string
1288 if (psczOut)
1289 {
1290 hr = VariableStrAlloc(!fObfuscateHiddenVariables, &scz, ++cch);
1291 ExitOnFailure(hr, "Failed to allocate string.");
1292
1293 er = ::MsiFormatRecordW(NULL, hRecord, scz, &cch);
1294 ExitOnWin32Error(er, hr, "Failed to format record.");
1295
1296 hr = VariableStrAllocString(!fObfuscateHiddenVariables, psczOut, scz, 0);
1297 ExitOnFailure(hr, "Failed to copy string.");
1298 }
1299
1300 // return character count
1301 if (pcchOut)
1302 {
1303 *pcchOut = cch;
1304 }
1305
1306LExit:
1307 ::LeaveCriticalSection(&pVariables->csAccess);
1308
1309 if (rgVariables)
1310 {
1311 for (DWORD i = 0; i < cVariables; ++i)
1312 {
1313 if (fObfuscateHiddenVariables)
1314 {
1315 ReleaseStr(rgVariables[i]);
1316 }
1317 else
1318 {
1319 StrSecureZeroFreeString(rgVariables[i]);
1320 }
1321 }
1322 MemFree(rgVariables);
1323 }
1324
1325 if (hRecord)
1326 {
1327 ::MsiCloseHandle(hRecord);
1328 }
1329
1330 if (fObfuscateHiddenVariables)
1331 {
1332 ReleaseStr(sczUnformatted);
1333 ReleaseStr(sczFormat);
1334 ReleaseStr(scz);
1335 }
1336 else
1337 {
1338 StrSecureZeroFreeString(sczUnformatted);
1339 StrSecureZeroFreeString(sczFormat);
1340 StrSecureZeroFreeString(scz);
1341 }
1342
1343 return hr;
1344}
1345
1346static HRESULT AddBuiltInVariable(
1347 __in BURN_VARIABLES* pVariables,
1348 __in LPCWSTR wzVariable,
1349 __in PFN_INITIALIZEVARIABLE pfnInitialize,
1350 __in DWORD_PTR dwpInitializeData,
1351 __in BOOL fPersist,
1352 __in BOOL fOverridable
1353 )
1354{
1355 HRESULT hr = S_OK;
1356 DWORD iVariable = 0;
1357 BURN_VARIABLE* pVariable = NULL;
1358
1359 hr = FindVariableIndexByName(pVariables, wzVariable, &iVariable);
1360 ExitOnFailure(hr, "Failed to find variable value.");
1361
1362 // insert element if not found
1363 if (S_FALSE == hr)
1364 {
1365 hr = InsertVariable(pVariables, wzVariable, iVariable);
1366 ExitOnFailure(hr, "Failed to insert variable.");
1367 }
1368
1369 // set variable values
1370 pVariable = &pVariables->rgVariables[iVariable];
1371 pVariable->fPersisted = fPersist;
1372 pVariable->internalType = fOverridable ? BURN_VARIABLE_INTERNAL_TYPE_OVERRIDABLE_BUILTIN : BURN_VARIABLE_INTERNAL_TYPE_BUILTIN;
1373 pVariable->pfnInitialize = pfnInitialize;
1374 pVariable->dwpInitializeData = dwpInitializeData;
1375
1376LExit:
1377 return hr;
1378}
1379
1380static HRESULT GetVariable(
1381 __in BURN_VARIABLES* pVariables,
1382 __in_z LPCWSTR wzVariable,
1383 __out BURN_VARIABLE** ppVariable
1384 )
1385{
1386 HRESULT hr = S_OK;
1387 DWORD iVariable = 0;
1388 BURN_VARIABLE* pVariable = NULL;
1389
1390 hr = FindVariableIndexByName(pVariables, wzVariable, &iVariable);
1391 ExitOnFailure(hr, "Failed to find variable value '%ls'.", wzVariable);
1392
1393 if (S_FALSE == hr)
1394 {
1395 ExitFunction1(hr = E_NOTFOUND);
1396 }
1397
1398 pVariable = &pVariables->rgVariables[iVariable];
1399
1400 // initialize built-in variable
1401 if (BURN_VARIANT_TYPE_NONE == pVariable->Value.Type && BURN_VARIABLE_INTERNAL_TYPE_NORMAL < pVariable->internalType)
1402 {
1403 hr = pVariable->pfnInitialize(pVariable->dwpInitializeData, &pVariable->Value);
1404 ExitOnFailure(hr, "Failed to initialize built-in variable value '%ls'.", wzVariable);
1405 }
1406
1407 *ppVariable = pVariable;
1408
1409LExit:
1410 return hr;
1411}
1412
1413static HRESULT FindVariableIndexByName(
1414 __in BURN_VARIABLES* pVariables,
1415 __in_z LPCWSTR wzVariable,
1416 __out DWORD* piVariable
1417 )
1418{
1419 HRESULT hr = S_OK;
1420 DWORD iRangeFirst = 0;
1421 DWORD cRangeLength = pVariables->cVariables;
1422
1423 while (cRangeLength)
1424 {
1425 // get variable in middle of range
1426 DWORD iPosition = cRangeLength / 2;
1427 BURN_VARIABLE* pVariable = &pVariables->rgVariables[iRangeFirst + iPosition];
1428
1429 switch (::CompareStringW(LOCALE_INVARIANT, SORT_STRINGSORT, wzVariable, -1, pVariable->sczName, -1))
1430 {
1431 case CSTR_LESS_THAN:
1432 // restrict range to elements before the current
1433 cRangeLength = iPosition;
1434 break;
1435 case CSTR_EQUAL:
1436 // variable found
1437 *piVariable = iRangeFirst + iPosition;
1438 ExitFunction1(hr = S_OK);
1439 case CSTR_GREATER_THAN:
1440 // restrict range to elements after the current
1441 iRangeFirst += iPosition + 1;
1442 cRangeLength -= iPosition + 1;
1443 break;
1444 default:
1445 ExitWithLastError(hr, "Failed to compare strings.");
1446 }
1447 }
1448
1449 *piVariable = iRangeFirst;
1450 hr = S_FALSE; // variable not found
1451
1452LExit:
1453 return hr;
1454}
1455
1456static HRESULT InsertVariable(
1457 __in BURN_VARIABLES* pVariables,
1458 __in_z LPCWSTR wzVariable,
1459 __in DWORD iPosition
1460 )
1461{
1462 HRESULT hr = S_OK;
1463 size_t cbAllocSize = 0;
1464
1465 // ensure there is room in the variable array
1466 if (pVariables->cVariables == pVariables->dwMaxVariables)
1467 {
1468 hr = ::DWordAdd(pVariables->dwMaxVariables, GROW_VARIABLE_ARRAY, &(pVariables->dwMaxVariables));
1469 ExitOnRootFailure(hr, "Overflow while growing variable array size");
1470
1471 if (pVariables->rgVariables)
1472 {
1473 hr = ::SizeTMult(sizeof(BURN_VARIABLE), pVariables->dwMaxVariables, &cbAllocSize);
1474 ExitOnRootFailure(hr, "Overflow while calculating size of variable array buffer");
1475
1476 LPVOID pv = MemReAlloc(pVariables->rgVariables, cbAllocSize, FALSE);
1477 ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate room for more variables.");
1478
1479 // Prefast claims it's possible to hit this. Putting the check in just in case.
1480 if (pVariables->dwMaxVariables < pVariables->cVariables)
1481 {
1482 hr = INTSAFE_E_ARITHMETIC_OVERFLOW;
1483 ExitOnRootFailure(hr, "Overflow while dealing with variable array buffer allocation");
1484 }
1485
1486 pVariables->rgVariables = (BURN_VARIABLE*)pv;
1487 memset(&pVariables->rgVariables[pVariables->cVariables], 0, sizeof(BURN_VARIABLE) * (pVariables->dwMaxVariables - pVariables->cVariables));
1488 }
1489 else
1490 {
1491 pVariables->rgVariables = (BURN_VARIABLE*)MemAlloc(sizeof(BURN_VARIABLE) * pVariables->dwMaxVariables, TRUE);
1492 ExitOnNull(pVariables->rgVariables, hr, E_OUTOFMEMORY, "Failed to allocate room for variables.");
1493 }
1494 }
1495
1496 // move variables
1497 if (0 < pVariables->cVariables - iPosition)
1498 {
1499 memmove(&pVariables->rgVariables[iPosition + 1], &pVariables->rgVariables[iPosition], sizeof(BURN_VARIABLE) * (pVariables->cVariables - iPosition));
1500 memset(&pVariables->rgVariables[iPosition], 0, sizeof(BURN_VARIABLE));
1501 }
1502
1503 ++pVariables->cVariables;
1504
1505 // allocate name
1506 hr = StrAllocString(&pVariables->rgVariables[iPosition].sczName, wzVariable, 0);
1507 ExitOnFailure(hr, "Failed to copy variable name.");
1508
1509LExit:
1510 return hr;
1511}
1512
1513static HRESULT SetVariableValue(
1514 __in BURN_VARIABLES* pVariables,
1515 __in_z LPCWSTR wzVariable,
1516 __in BURN_VARIANT* pVariant,
1517 __in BOOL fLiteral,
1518 __in SET_VARIABLE setBuiltin,
1519 __in BOOL fLog
1520 )
1521{
1522 HRESULT hr = S_OK;
1523 DWORD iVariable = 0;
1524
1525 ::EnterCriticalSection(&pVariables->csAccess);
1526
1527 hr = FindVariableIndexByName(pVariables, wzVariable, &iVariable);
1528 ExitOnFailure(hr, "Failed to find variable value '%ls'.", wzVariable);
1529
1530 // Insert element if not found.
1531 if (S_FALSE == hr)
1532 {
1533 hr = InsertVariable(pVariables, wzVariable, iVariable);
1534 ExitOnFailure(hr, "Failed to insert variable '%ls'.", wzVariable);
1535 }
1536 else if (BURN_VARIABLE_INTERNAL_TYPE_NORMAL < pVariables->rgVariables[iVariable].internalType) // built-in variables must be overridden.
1537 {
1538 if (SET_VARIABLE_OVERRIDE_BUILTIN == setBuiltin ||
1539 (SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS == setBuiltin && pVariables->rgVariables[iVariable].fPersisted) ||
1540 SET_VARIABLE_ANY == setBuiltin && BURN_VARIABLE_INTERNAL_TYPE_BUILTIN != pVariables->rgVariables[iVariable].internalType)
1541 {
1542 hr = S_OK;
1543 }
1544 else
1545 {
1546 hr = E_INVALIDARG;
1547 ExitOnRootFailure(hr, "Attempt to set built-in variable value: %ls", wzVariable);
1548 }
1549 }
1550 else // must *not* be a built-in variable so caller should not have tried to override it as a built-in.
1551 {
1552 // Not possible from external callers so just assert.
1553 AssertSz(SET_VARIABLE_OVERRIDE_BUILTIN != setBuiltin, "Intent to overwrite non-built-in variable.");
1554 }
1555
1556 // Log value when not overwriting a built-in variable.
1557 if (fLog && BURN_VARIABLE_INTERNAL_TYPE_NORMAL == pVariables->rgVariables[iVariable].internalType)
1558 {
1559 if (pVariables->rgVariables[iVariable].fHidden)
1560 {
1561 LogStringLine(REPORT_STANDARD, "Setting hidden variable '%ls'", wzVariable);
1562 }
1563 else
1564 {
1565 // Assume value isn't encrypted since it's not hidden.
1566 switch (pVariant->Type)
1567 {
1568 case BURN_VARIANT_TYPE_NONE:
1569 if (BURN_VARIANT_TYPE_NONE != pVariables->rgVariables[iVariable].Value.Type)
1570 {
1571 LogStringLine(REPORT_STANDARD, "Unsetting variable '%ls'", wzVariable, pVariant->sczValue);
1572 }
1573 break;
1574
1575 case BURN_VARIANT_TYPE_NUMERIC:
1576 LogStringLine(REPORT_STANDARD, "Setting numeric variable '%ls' to value %lld", wzVariable, pVariant->llValue);
1577 break;
1578
1579 case BURN_VARIANT_TYPE_STRING:
1580 if (!pVariant->sczValue)
1581 {
1582 LogStringLine(REPORT_STANDARD, "Unsetting variable '%ls'", wzVariable, pVariant->sczValue);
1583 }
1584 else
1585 {
1586 LogStringLine(REPORT_STANDARD, "Setting string variable '%ls' to value '%ls'", wzVariable, pVariant->sczValue);
1587 }
1588 break;
1589
1590 case BURN_VARIANT_TYPE_VERSION:
1591 LogStringLine(REPORT_STANDARD, "Setting version variable '%ls' to value '%hu.%hu.%hu.%hu'", wzVariable, (WORD)(pVariant->qwValue >> 48), (WORD)(pVariant->qwValue >> 32), (WORD)(pVariant->qwValue >> 16), (WORD)(pVariant->qwValue));
1592 break;
1593
1594 default:
1595 AssertSz(FALSE, "Unknown variant type.");
1596 break;
1597 }
1598 }
1599 }
1600
1601 // Update variable value.
1602 hr = BVariantSetValue(&pVariables->rgVariables[iVariable].Value, pVariant);
1603 ExitOnFailure(hr, "Failed to set value of variable: %ls", wzVariable);
1604
1605 // Update variable literal flag.
1606 pVariables->rgVariables[iVariable].fLiteral = fLiteral;
1607
1608LExit:
1609 ::LeaveCriticalSection(&pVariables->csAccess);
1610
1611 if (FAILED(hr) && fLog)
1612 {
1613 LogStringLine(REPORT_STANDARD, "Setting variable failed: ID '%ls', HRESULT 0x%x", wzVariable, hr);
1614 }
1615
1616 return hr;
1617}
1618
1619extern "C" typedef NTSTATUS (NTAPI *RTL_GET_VERSION)(_Out_ PRTL_OSVERSIONINFOEXW lpVersionInformation);
1620
1621static HRESULT InitializeVariableVersionNT(
1622 __in DWORD_PTR dwpData,
1623 __inout BURN_VARIANT* pValue
1624 )
1625{
1626 HRESULT hr = S_OK;
1627 HMODULE ntdll = NULL;
1628 RTL_GET_VERSION rtlGetVersion = NULL;
1629 RTL_OSVERSIONINFOEXW ovix = { };
1630 BURN_VARIANT value = { };
1631
1632 if (!::GetModuleHandleExW(0, L"ntdll", &ntdll))
1633 {
1634 ExitWithLastError(hr, "Failed to locate NTDLL.");
1635 }
1636
1637 rtlGetVersion = reinterpret_cast<RTL_GET_VERSION>(::GetProcAddress(ntdll, "RtlGetVersion"));
1638 if (NULL == rtlGetVersion)
1639 {
1640 ExitWithLastError(hr, "Failed to locate RtlGetVersion.");
1641 }
1642
1643 ovix.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
1644 hr = static_cast<HRESULT>(rtlGetVersion(&ovix));
1645 ExitOnFailure(hr, "Failed to get OS info.");
1646
1647 switch ((OS_INFO_VARIABLE)dwpData)
1648 {
1649 case OS_INFO_VARIABLE_ServicePackLevel:
1650 if (0 != ovix.wServicePackMajor)
1651 {
1652 value.qwValue = static_cast<DWORD64>(ovix.wServicePackMajor);
1653 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1654 }
1655 break;
1656 case OS_INFO_VARIABLE_VersionNT:
1657 value.qwValue = MAKEQWORDVERSION(ovix.dwMajorVersion, ovix.dwMinorVersion, 0, 0);
1658 value.Type = BURN_VARIANT_TYPE_VERSION;
1659 break;
1660 case OS_INFO_VARIABLE_VersionNT64:
1661 {
1662#if !defined(_WIN64)
1663 BOOL fIsWow64 = FALSE;
1664
1665 ProcWow64(::GetCurrentProcess(), &fIsWow64);
1666 if (fIsWow64)
1667#endif
1668 {
1669 value.qwValue = MAKEQWORDVERSION(ovix.dwMajorVersion, ovix.dwMinorVersion, 0, 0);
1670 value.Type = BURN_VARIANT_TYPE_VERSION;
1671 }
1672 }
1673 break;
1674 default:
1675 AssertSz(FALSE, "Unknown OS info type.");
1676 break;
1677 }
1678
1679 hr = BVariantCopy(&value, pValue);
1680 ExitOnFailure(hr, "Failed to set variant value.");
1681
1682LExit:
1683 if (NULL != ntdll)
1684 {
1685 FreeLibrary(ntdll);
1686 }
1687
1688 return hr;
1689}
1690
1691static HRESULT InitializeVariableOsInfo(
1692 __in DWORD_PTR dwpData,
1693 __inout BURN_VARIANT* pValue
1694 )
1695{
1696 HRESULT hr = S_OK;
1697 OSVERSIONINFOEXW ovix = { };
1698 BURN_VARIANT value = { };
1699
1700 ovix.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
1701 if (!::GetVersionExW((LPOSVERSIONINFOW)&ovix))
1702 {
1703 ExitWithLastError(hr, "Failed to get OS info.");
1704 }
1705
1706 switch ((OS_INFO_VARIABLE)dwpData)
1707 {
1708 case OS_INFO_VARIABLE_NTProductType:
1709 value.llValue = ovix.wProductType;
1710 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1711 break;
1712 case OS_INFO_VARIABLE_NTSuiteBackOffice:
1713 value.llValue = VER_SUITE_BACKOFFICE & ovix.wSuiteMask ? 1 : 0;
1714 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1715 break;
1716 case OS_INFO_VARIABLE_NTSuiteDataCenter:
1717 value.llValue = VER_SUITE_DATACENTER & ovix.wSuiteMask ? 1 : 0;
1718 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1719 break;
1720 case OS_INFO_VARIABLE_NTSuiteEnterprise:
1721 value.llValue = VER_SUITE_ENTERPRISE & ovix.wSuiteMask ? 1 : 0;
1722 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1723 break;
1724 case OS_INFO_VARIABLE_NTSuitePersonal:
1725 value.llValue = VER_SUITE_PERSONAL & ovix.wSuiteMask ? 1 : 0;
1726 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1727 break;
1728 case OS_INFO_VARIABLE_NTSuiteSmallBusiness:
1729 value.llValue = VER_SUITE_SMALLBUSINESS & ovix.wSuiteMask ? 1 : 0;
1730 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1731 break;
1732 case OS_INFO_VARIABLE_NTSuiteSmallBusinessRestricted:
1733 value.llValue = VER_SUITE_SMALLBUSINESS_RESTRICTED & ovix.wSuiteMask ? 1 : 0;
1734 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1735 break;
1736 case OS_INFO_VARIABLE_NTSuiteWebServer:
1737 value.llValue = VER_SUITE_BLADE & ovix.wSuiteMask ? 1 : 0;
1738 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1739 break;
1740 case OS_INFO_VARIABLE_CompatibilityMode:
1741 {
1742 DWORDLONG dwlConditionMask = 0;
1743 VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_EQUAL);
1744 VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_EQUAL);
1745 VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMAJOR, VER_EQUAL);
1746 VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMINOR, VER_EQUAL);
1747
1748 value.llValue = ::VerifyVersionInfoW(&ovix, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, dwlConditionMask);
1749 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1750 }
1751 break;
1752 case OS_INFO_VARIABLE_TerminalServer:
1753 value.llValue = (VER_SUITE_TERMINAL == (ovix.wSuiteMask & VER_SUITE_TERMINAL)) && (VER_SUITE_SINGLEUSERTS != (ovix.wSuiteMask & VER_SUITE_SINGLEUSERTS)) ? 1 : 0;
1754 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1755 break;
1756 default:
1757 AssertSz(FALSE, "Unknown OS info type.");
1758 break;
1759 }
1760
1761 hr = BVariantCopy(&value, pValue);
1762 ExitOnFailure(hr, "Failed to set variant value.");
1763
1764LExit:
1765 return hr;
1766}
1767
1768static HRESULT InitializeVariableSystemInfo(
1769 __in DWORD_PTR dwpData,
1770 __inout BURN_VARIANT* pValue
1771 )
1772{
1773 HRESULT hr = S_OK;
1774 SYSTEM_INFO si = { };
1775 BURN_VARIANT value = { };
1776
1777 ::GetNativeSystemInfo(&si);
1778
1779 switch ((OS_INFO_VARIABLE)dwpData)
1780 {
1781 case OS_INFO_VARIABLE_ProcessorArchitecture:
1782 value.llValue = si.wProcessorArchitecture;
1783 value.Type = BURN_VARIANT_TYPE_NUMERIC;
1784 break;
1785 default:
1786 AssertSz(FALSE, "Unknown OS info type.");
1787 break;
1788 }
1789
1790 hr = BVariantCopy(&value, pValue);
1791 ExitOnFailure(hr, "Failed to set variant value.");
1792
1793LExit:
1794 return hr;
1795}
1796
1797static HRESULT InitializeVariableComputerName(
1798 __in DWORD_PTR dwpData,
1799 __inout BURN_VARIANT* pValue
1800 )
1801{
1802 UNREFERENCED_PARAMETER(dwpData);
1803
1804 HRESULT hr = S_OK;
1805 WCHAR wzComputerName[MAX_COMPUTERNAME_LENGTH + 1] = { };
1806 DWORD cchComputerName = countof(wzComputerName);
1807
1808 // get computer name
1809 if (!::GetComputerNameW(wzComputerName, &cchComputerName))
1810 {
1811 ExitWithLastError(hr, "Failed to get computer name.");
1812 }
1813
1814 // set value
1815 hr = BVariantSetString(pValue, wzComputerName, 0);
1816 ExitOnFailure(hr, "Failed to set variant value.");
1817
1818LExit:
1819 return hr;
1820}
1821
1822static HRESULT InitializeVariableVersionMsi(
1823 __in DWORD_PTR dwpData,
1824 __inout BURN_VARIANT* pValue
1825 )
1826{
1827 UNREFERENCED_PARAMETER(dwpData);
1828
1829 HRESULT hr = S_OK;
1830 DLLGETVERSIONPROC pfnMsiDllGetVersion = NULL;
1831 DLLVERSIONINFO msiVersionInfo = { };
1832
1833 // get DllGetVersion proc address
1834 pfnMsiDllGetVersion = (DLLGETVERSIONPROC)::GetProcAddress(::GetModuleHandleW(L"msi"), "DllGetVersion");
1835 ExitOnNullWithLastError(pfnMsiDllGetVersion, hr, "Failed to find DllGetVersion entry point in msi.dll.");
1836
1837 // get msi.dll version info
1838 msiVersionInfo.cbSize = sizeof(DLLVERSIONINFO);
1839 hr = pfnMsiDllGetVersion(&msiVersionInfo);
1840 ExitOnFailure(hr, "Failed to get msi.dll version info.");
1841
1842 hr = BVariantSetVersion(pValue, MAKEQWORDVERSION(msiVersionInfo.dwMajorVersion, msiVersionInfo.dwMinorVersion, 0, 0));
1843 ExitOnFailure(hr, "Failed to set variant value.");
1844
1845LExit:
1846 return hr;
1847}
1848
1849static HRESULT InitializeVariableCsidlFolder(
1850 __in DWORD_PTR dwpData,
1851 __inout BURN_VARIANT* pValue
1852 )
1853{
1854 HRESULT hr = S_OK;
1855 LPWSTR sczPath = NULL;
1856 int nFolder = (int)dwpData;
1857
1858 // get folder path
1859 hr = ShelGetFolder(&sczPath, nFolder);
1860 ExitOnRootFailure(hr, "Failed to get shell folder.");
1861
1862 // set value
1863 hr = BVariantSetString(pValue, sczPath, 0);
1864 ExitOnFailure(hr, "Failed to set variant value.");
1865
1866LExit:
1867 ReleaseStr(sczPath);
1868
1869 return hr;
1870}
1871
1872static HRESULT InitializeVariableTempFolder(
1873 __in DWORD_PTR dwpData,
1874 __inout BURN_VARIANT* pValue
1875 )
1876{
1877 UNREFERENCED_PARAMETER(dwpData);
1878
1879 HRESULT hr = S_OK;
1880 WCHAR wzPath[MAX_PATH] = { };
1881
1882 // get volume path name
1883 if (!::GetTempPathW(MAX_PATH, wzPath))
1884 {
1885 ExitWithLastError(hr, "Failed to get temp path.");
1886 }
1887
1888 // set value
1889 hr = BVariantSetString(pValue, wzPath, 0);
1890 ExitOnFailure(hr, "Failed to set variant value.");
1891
1892LExit:
1893 return hr;
1894}
1895
1896static HRESULT InitializeVariableSystemFolder(
1897 __in DWORD_PTR dwpData,
1898 __inout BURN_VARIANT* pValue
1899 )
1900{
1901 HRESULT hr = S_OK;
1902 BOOL f64 = (BOOL)dwpData;
1903 WCHAR wzSystemFolder[MAX_PATH] = { };
1904
1905#if !defined(_WIN64)
1906 BOOL fIsWow64 = FALSE;
1907 ProcWow64(::GetCurrentProcess(), &fIsWow64);
1908
1909 if (fIsWow64)
1910 {
1911 if (f64)
1912 {
1913 if (!::GetSystemDirectoryW(wzSystemFolder, countof(wzSystemFolder)))
1914 {
1915 ExitWithLastError(hr, "Failed to get 64-bit system folder.");
1916 }
1917 }
1918 else
1919 {
1920 if (!::GetSystemWow64DirectoryW(wzSystemFolder, countof(wzSystemFolder)))
1921 {
1922 ExitWithLastError(hr, "Failed to get 32-bit system folder.");
1923 }
1924 }
1925 }
1926 else
1927 {
1928 if (!f64)
1929 {
1930 if (!::GetSystemDirectoryW(wzSystemFolder, countof(wzSystemFolder)))
1931 {
1932 ExitWithLastError(hr, "Failed to get 32-bit system folder.");
1933 }
1934 }
1935 }
1936#else
1937 if (f64)
1938 {
1939 if (!::GetSystemDirectoryW(wzSystemFolder, countof(wzSystemFolder)))
1940 {
1941 ExitWithLastError(hr, "Failed to get 64-bit system folder.");
1942 }
1943 }
1944 else
1945 {
1946 if (!::GetSystemWow64DirectoryW(wzSystemFolder, countof(wzSystemFolder)))
1947 {
1948 ExitWithLastError(hr, "Failed to get 32-bit system folder.");
1949 }
1950 }
1951#endif
1952
1953 if (*wzSystemFolder)
1954 {
1955 hr = PathFixedBackslashTerminate(wzSystemFolder, countof(wzSystemFolder));
1956 ExitOnFailure(hr, "Failed to backslash terminate system folder.");
1957 }
1958
1959 // set value
1960 hr = BVariantSetString(pValue, wzSystemFolder, 0);
1961 ExitOnFailure(hr, "Failed to set system folder variant value.");
1962
1963LExit:
1964 return hr;
1965}
1966
1967static HRESULT InitializeVariableWindowsVolumeFolder(
1968 __in DWORD_PTR dwpData,
1969 __inout BURN_VARIANT* pValue
1970 )
1971{
1972 UNREFERENCED_PARAMETER(dwpData);
1973
1974 HRESULT hr = S_OK;
1975 WCHAR wzWindowsPath[MAX_PATH] = { };
1976 WCHAR wzVolumePath[MAX_PATH] = { };
1977
1978 // get windows directory
1979 if (!::GetWindowsDirectoryW(wzWindowsPath, countof(wzWindowsPath)))
1980 {
1981 ExitWithLastError(hr, "Failed to get windows directory.");
1982 }
1983
1984 // get volume path name
1985 if (!::GetVolumePathNameW(wzWindowsPath, wzVolumePath, MAX_PATH))
1986 {
1987 ExitWithLastError(hr, "Failed to get volume path name.");
1988 }
1989
1990 // set value
1991 hr = BVariantSetString(pValue, wzVolumePath, 0);
1992 ExitOnFailure(hr, "Failed to set variant value.");
1993
1994LExit:
1995 return hr;
1996}
1997
1998static HRESULT InitializeVariablePrivileged(
1999 __in DWORD_PTR dwpData,
2000 __inout BURN_VARIANT* pValue
2001 )
2002{
2003 UNREFERENCED_PARAMETER(dwpData);
2004
2005 HRESULT hr = S_OK;
2006 BOOL fPrivileged = FALSE;
2007
2008 // check if process could run privileged.
2009 hr = OsCouldRunPrivileged(&fPrivileged);
2010 ExitOnFailure(hr, "Failed to check if process could run privileged.");
2011
2012 // set value
2013 hr = BVariantSetNumeric(pValue, fPrivileged);
2014 ExitOnFailure(hr, "Failed to set variant value.");
2015
2016LExit:
2017 return hr;
2018}
2019
2020static HRESULT InitializeVariableRebootPending(
2021 __in DWORD_PTR dwpData,
2022 __inout BURN_VARIANT* pValue
2023 )
2024{
2025 UNREFERENCED_PARAMETER(dwpData);
2026
2027 HRESULT hr = S_OK;
2028 BOOL fRebootPending = FALSE;
2029 BOOL fComInitialized = FALSE;
2030
2031 // Do a best effort to ask WU if a reboot is required. If anything goes
2032 // wrong then let's pretend a reboot is not required.
2033 hr = ::CoInitialize(NULL);
2034 if (SUCCEEDED(hr) || RPC_E_CHANGED_MODE == hr)
2035 {
2036 fComInitialized = TRUE;
2037
2038 hr = WuaRestartRequired(&fRebootPending);
2039 if (FAILED(hr))
2040 {
2041 fRebootPending = FALSE;
2042 hr = S_OK;
2043 }
2044 }
2045
2046 hr = BVariantSetNumeric(pValue, fRebootPending);
2047 ExitOnFailure(hr, "Failed to set reboot pending variant value.");
2048
2049LExit:
2050 if (fComInitialized)
2051 {
2052 ::CoUninitialize();
2053 }
2054
2055 return hr;
2056}
2057
2058static HRESULT InitializeSystemLanguageID(
2059 __in DWORD_PTR dwpData,
2060 __inout BURN_VARIANT* pValue
2061 )
2062{
2063 UNREFERENCED_PARAMETER(dwpData);
2064
2065 HRESULT hr = S_OK;
2066 LANGID langid = ::GetSystemDefaultLangID();
2067
2068 hr = BVariantSetNumeric(pValue, langid);
2069 ExitOnFailure(hr, "Failed to set variant value.");
2070
2071LExit:
2072 return hr;
2073}
2074
2075static HRESULT InitializeUserUILanguageID(
2076 __in DWORD_PTR dwpData,
2077 __inout BURN_VARIANT* pValue
2078 )
2079{
2080 UNREFERENCED_PARAMETER(dwpData);
2081
2082 HRESULT hr = S_OK;
2083 LANGID langid = ::GetUserDefaultUILanguage();
2084
2085 hr = BVariantSetNumeric(pValue, langid);
2086 ExitOnFailure(hr, "Failed to set variant value.");
2087
2088LExit:
2089 return hr;
2090}
2091
2092static HRESULT InitializeUserLanguageID(
2093 __in DWORD_PTR dwpData,
2094 __inout BURN_VARIANT* pValue
2095 )
2096{
2097 UNREFERENCED_PARAMETER(dwpData);
2098
2099 HRESULT hr = S_OK;
2100 LANGID langid = ::GetUserDefaultLangID();
2101
2102 hr = BVariantSetNumeric(pValue, langid);
2103 ExitOnFailure(hr, "Failed to set variant value.");
2104
2105LExit:
2106 return hr;
2107}
2108
2109static HRESULT InitializeVariableString(
2110 __in DWORD_PTR dwpData,
2111 __inout BURN_VARIANT* pValue
2112 )
2113{
2114 HRESULT hr = S_OK;
2115 LPCWSTR wzValue = (LPCWSTR)dwpData;
2116
2117 // set value
2118 hr = BVariantSetString(pValue, wzValue, 0);
2119 ExitOnFailure(hr, "Failed to set variant value.");
2120
2121LExit:
2122 return hr;
2123}
2124
2125static HRESULT InitializeVariableNumeric(
2126 __in DWORD_PTR dwpData,
2127 __inout BURN_VARIANT* pValue
2128 )
2129{
2130 HRESULT hr = S_OK;
2131 LONGLONG llValue = (LONGLONG)dwpData;
2132
2133 // set value
2134 hr = BVariantSetNumeric(pValue, llValue);
2135 ExitOnFailure(hr, "Failed to set variant value.");
2136
2137LExit:
2138 return hr;
2139}
2140
2141static HRESULT InitializeVariableRegistryFolder(
2142 __in DWORD_PTR dwpData,
2143 __inout BURN_VARIANT* pValue
2144 )
2145{
2146 HRESULT hr = S_OK;
2147 int nFolder = (int)dwpData;
2148 LPWSTR sczPath = NULL;
2149
2150#if !defined(_WIN64)
2151 BOOL fIsWow64 = FALSE;
2152
2153 ProcWow64(::GetCurrentProcess(), &fIsWow64);
2154 if (!fIsWow64) // on 32-bit machines, variables aren't set
2155 {
2156 ExitFunction();
2157 }
2158#endif
2159
2160 hr = Get64bitFolderFromRegistry(nFolder, &sczPath);
2161 ExitOnFailure(hr, "Failed to get 64-bit folder.");
2162
2163 // set value
2164 hr = BVariantSetString(pValue, sczPath, 0);
2165 ExitOnFailure(hr, "Failed to set variant value.");
2166
2167LExit:
2168 ReleaseStr(sczPath);
2169
2170 return hr;
2171}
2172
2173static HRESULT InitializeVariable6432Folder(
2174 __in DWORD_PTR dwpData,
2175 __inout BURN_VARIANT* pValue
2176 )
2177{
2178 HRESULT hr = S_OK;
2179 int nFolder = (int)dwpData;
2180 LPWSTR sczPath = NULL;
2181
2182#if !defined(_WIN64)
2183 BOOL fIsWow64 = FALSE;
2184
2185 // If 32-bit use shell-folder.
2186 ProcWow64(::GetCurrentProcess(), &fIsWow64);
2187 if (!fIsWow64)
2188 {
2189 hr = ShelGetFolder(&sczPath, nFolder);
2190 ExitOnRootFailure(hr, "Failed to get shell folder.");
2191 }
2192 else
2193#endif
2194 {
2195 hr = Get64bitFolderFromRegistry(nFolder, &sczPath);
2196 ExitOnFailure(hr, "Failed to get 64-bit folder.");
2197 }
2198
2199 // set value
2200 hr = BVariantSetString(pValue, sczPath, 0);
2201 ExitOnFailure(hr, "Failed to set variant value.");
2202
2203LExit:
2204 ReleaseStr(sczPath);
2205
2206 return hr;
2207}
2208
2209// Get the date in the same format as Windows Installer.
2210static HRESULT InitializeVariableDate(
2211 __in DWORD_PTR /*dwpData*/,
2212 __inout BURN_VARIANT* pValue
2213 )
2214{
2215 HRESULT hr = S_OK;
2216 SYSTEMTIME systime = { };
2217 LPWSTR sczDate = NULL;
2218 int cchDate = 0;
2219
2220 ::GetSystemTime(&systime);
2221
2222 cchDate = ::GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systime, NULL, NULL, cchDate);
2223 if (!cchDate)
2224 {
2225 ExitOnLastError(hr, "Failed to get the required buffer length for the Date.");
2226 }
2227
2228 hr = StrAlloc(&sczDate, cchDate);
2229 ExitOnFailure(hr, "Failed to allocate the buffer for the Date.");
2230
2231 if (!::GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systime, NULL, sczDate, cchDate))
2232 {
2233 ExitOnLastError(hr, "Failed to get the Date.");
2234 }
2235
2236 // set value
2237 hr = BVariantSetString(pValue, sczDate, cchDate);
2238 ExitOnFailure(hr, "Failed to set variant value.");
2239
2240LExit:
2241 ReleaseStr(sczDate);
2242
2243 return hr;
2244}
2245
2246static HRESULT InitializeVariableInstallerName(
2247 __in DWORD_PTR /*dwpData*/,
2248 __inout BURN_VARIANT* pValue
2249 )
2250{
2251 HRESULT hr = S_OK;
2252
2253 // set value
2254 hr = BVariantSetString(pValue, L"WiX Burn", 0);
2255 ExitOnFailure(hr, "Failed to set variant value.");
2256
2257LExit:
2258 return hr;
2259}
2260
2261static HRESULT InitializeVariableInstallerVersion(
2262 __in DWORD_PTR /*dwpData*/,
2263 __inout BURN_VARIANT* pValue
2264 )
2265{
2266 HRESULT hr = S_OK;
2267 LPWSTR sczVersion = NULL;
2268
2269 hr = StrAllocStringAnsi(&sczVersion, szVerMajorMinorBuild, 0, CP_ACP);
2270 ExitOnFailure(hr, "Failed to copy the engine version.");
2271
2272 // set value
2273 hr = BVariantSetString(pValue, sczVersion, 0);
2274 ExitOnFailure(hr, "Failed to set variant value.");
2275
2276LExit:
2277 ReleaseStr(sczVersion);
2278
2279 return hr;
2280}
2281
2282static HRESULT InitializeVariableVersion(
2283 __in DWORD_PTR dwpData,
2284 __inout BURN_VARIANT* pValue
2285 )
2286{
2287 HRESULT hr = S_OK;
2288
2289 // set value
2290 hr = BVariantSetVersion(pValue, static_cast<DWORD64>(dwpData));
2291 ExitOnFailure(hr, "Failed to set variant value.");
2292
2293LExit:
2294 return hr;
2295}
2296
2297// Get the current user the same as Windows Installer.
2298static HRESULT InitializeVariableLogonUser(
2299 __in DWORD_PTR /*dwpData*/,
2300 __inout BURN_VARIANT* pValue
2301 )
2302{
2303 HRESULT hr = S_OK;
2304 WCHAR wzUserName[UNLEN + 1];
2305 DWORD cchUserName = countof(wzUserName);
2306
2307 if (!::GetUserNameW(wzUserName, &cchUserName))
2308 {
2309 ExitOnLastError(hr, "Failed to get the user name.");
2310 }
2311
2312 // set value
2313 hr = BVariantSetString(pValue, wzUserName, 0);
2314 ExitOnFailure(hr, "Failed to set variant value.");
2315
2316LExit:
2317 return hr;
2318}
2319
2320static HRESULT Get64bitFolderFromRegistry(
2321 __in int nFolder,
2322 __deref_out_z LPWSTR* psczPath
2323 )
2324{
2325 HRESULT hr = S_OK;
2326 HKEY hkFolders = NULL;
2327
2328 AssertSz(CSIDL_PROGRAM_FILES == nFolder || CSIDL_PROGRAM_FILES_COMMON == nFolder, "Unknown folder CSIDL.");
2329 LPCWSTR wzFolderValue = CSIDL_PROGRAM_FILES_COMMON == nFolder ? L"CommonFilesDir" : L"ProgramFilesDir";
2330
2331 hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion", KEY_READ | KEY_WOW64_64KEY, &hkFolders);
2332 ExitOnFailure(hr, "Failed to open Windows folder key.");
2333
2334 hr = RegReadString(hkFolders, wzFolderValue, psczPath);
2335 ExitOnFailure(hr, "Failed to read folder path for '%ls'.", wzFolderValue);
2336
2337 hr = PathBackslashTerminate(psczPath);
2338 ExitOnFailure(hr, "Failed to ensure path was backslash terminated.");
2339
2340LExit:
2341 ReleaseRegKey(hkFolders);
2342
2343 return hr;
2344}
2345
diff --git a/src/engine/variable.h b/src/engine/variable.h
new file mode 100644
index 00000000..c48e0160
--- /dev/null
+++ b/src/engine/variable.h
@@ -0,0 +1,190 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// constants
11
12const LPCWSTR VARIABLE_DATE = L"Date";
13const LPCWSTR VARIABLE_LOGONUSER = L"LogonUser";
14const LPCWSTR VARIABLE_INSTALLERNAME = L"InstallerName";
15const LPCWSTR VARIABLE_INSTALLERVERSION = L"InstallerVersion";
16
17
18// typedefs
19
20typedef HRESULT (*PFN_INITIALIZEVARIABLE)(
21 __in DWORD_PTR dwpData,
22 __inout BURN_VARIANT* pValue
23 );
24
25
26// constants
27
28enum BURN_VARIABLE_INTERNAL_TYPE
29{
30 BURN_VARIABLE_INTERNAL_TYPE_NORMAL, // the BA can set this variable.
31 BURN_VARIABLE_INTERNAL_TYPE_OVERRIDABLE_BUILTIN, // the BA can't set this variable, but the unelevated process can serialize it to the elevated process.
32 BURN_VARIABLE_INTERNAL_TYPE_BUILTIN, // the BA can't set this variable, and the unelevated process can't serialize it to the elevated process.
33};
34
35
36// structs
37
38typedef struct _BURN_VARIABLE
39{
40 LPWSTR sczName;
41 BURN_VARIANT Value;
42 BOOL fHidden;
43 BOOL fLiteral; // if fLiteral, then when formatting this variable its value should be used as is (don't continue recursively formatting).
44 BOOL fPersisted;
45
46 // used for late initialization of built-in variables
47 BURN_VARIABLE_INTERNAL_TYPE internalType;
48 PFN_INITIALIZEVARIABLE pfnInitialize;
49 DWORD_PTR dwpInitializeData;
50} BURN_VARIABLE;
51
52typedef struct _BURN_VARIABLES
53{
54 CRITICAL_SECTION csAccess;
55 DWORD dwMaxVariables;
56 DWORD cVariables;
57 BURN_VARIABLE* rgVariables;
58} BURN_VARIABLES;
59
60
61// function declarations
62
63HRESULT VariableInitialize(
64 __in BURN_VARIABLES* pVariables
65 );
66HRESULT VariablesParseFromXml(
67 __in BURN_VARIABLES* pVariables,
68 __in IXMLDOMNode* pixnBundle
69 );
70void VariablesUninitialize(
71 __in BURN_VARIABLES* pVariables
72 );
73void VariablesDump(
74 __in BURN_VARIABLES* pVariables
75 );
76HRESULT VariableGetNumeric(
77 __in BURN_VARIABLES* pVariables,
78 __in_z LPCWSTR wzVariable,
79 __out LONGLONG* pllValue
80 );
81HRESULT VariableGetString(
82 __in BURN_VARIABLES* pVariables,
83 __in_z LPCWSTR wzVariable,
84 __out_z LPWSTR* psczValue
85 );
86HRESULT VariableGetVersion(
87 __in BURN_VARIABLES* pVariables,
88 __in_z LPCWSTR wzVariable,
89 __in DWORD64* pqwValue
90 );
91HRESULT VariableGetVariant(
92 __in BURN_VARIABLES* pVariables,
93 __in_z LPCWSTR wzVariable,
94 __in BURN_VARIANT* pValue
95 );
96HRESULT VariableGetFormatted(
97 __in BURN_VARIABLES* pVariables,
98 __in_z LPCWSTR wzVariable,
99 __out_z LPWSTR* psczValue
100 );
101HRESULT VariableSetNumeric(
102 __in BURN_VARIABLES* pVariables,
103 __in_z LPCWSTR wzVariable,
104 __in LONGLONG llValue,
105 __in BOOL fOverwriteBuiltIn
106 );
107HRESULT VariableSetLiteralString(
108 __in BURN_VARIABLES* pVariables,
109 __in_z LPCWSTR wzVariable,
110 __in_z_opt LPCWSTR wzValue,
111 __in BOOL fOverwriteBuiltIn
112 );
113HRESULT VariableSetString(
114 __in BURN_VARIABLES* pVariables,
115 __in_z LPCWSTR wzVariable,
116 __in_z_opt LPCWSTR wzValue,
117 __in BOOL fOverwriteBuiltIn
118 );
119HRESULT VariableSetVersion(
120 __in BURN_VARIABLES* pVariables,
121 __in_z LPCWSTR wzVariable,
122 __in DWORD64 qwValue,
123 __in BOOL fOverwriteBuiltIn
124 );
125HRESULT VariableSetLiteralVariant(
126 __in BURN_VARIABLES* pVariables,
127 __in_z LPCWSTR wzVariable,
128 __in BURN_VARIANT* pVariant
129 );
130HRESULT VariableFormatString(
131 __in BURN_VARIABLES* pVariables,
132 __in_z LPCWSTR wzIn,
133 __out_z_opt LPWSTR* psczOut,
134 __out_opt DWORD* pcchOut
135 );
136HRESULT VariableFormatStringObfuscated(
137 __in BURN_VARIABLES* pVariables,
138 __in_z LPCWSTR wzIn,
139 __out_z_opt LPWSTR* psczOut,
140 __out_opt DWORD* pcchOut
141 );
142HRESULT VariableEscapeString(
143 __in_z LPCWSTR wzIn,
144 __out_z LPWSTR* psczOut
145 );
146HRESULT VariableSerialize(
147 __in BURN_VARIABLES* pVariables,
148 __in BOOL fPersisting,
149 __inout BYTE** ppbBuffer,
150 __inout SIZE_T* piBuffer
151 );
152HRESULT VariableDeserialize(
153 __in BURN_VARIABLES* pVariables,
154 __in BOOL fWasPersisted,
155 __in_bcount(cbBuffer) BYTE* pbBuffer,
156 __in SIZE_T cbBuffer,
157 __inout SIZE_T* piBuffer
158 );
159HRESULT VariableStrAlloc(
160 __in BOOL fZeroOnRealloc,
161 __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
162 __in DWORD_PTR cch
163 );
164HRESULT VariableStrAllocString(
165 __in BOOL fZeroOnRealloc,
166 __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz,
167 __in_z LPCWSTR wzSource,
168 __in DWORD_PTR cchSource
169 );
170HRESULT VariableStrAllocConcat(
171 __in BOOL fZeroOnRealloc,
172 __deref_out_z LPWSTR* ppwz,
173 __in_z LPCWSTR wzSource,
174 __in DWORD_PTR cchSource
175 );
176HRESULT __cdecl VariableStrAllocFormatted(
177 __in BOOL fZeroOnRealloc,
178 __deref_out_z LPWSTR* ppwz,
179 __in __format_string LPCWSTR wzFormat,
180 ...
181 );
182HRESULT VariableIsHidden(
183 __in BURN_VARIABLES* pVariables,
184 __in_z LPCWSTR wzVariable,
185 __out BOOL* pfHidden
186 );
187
188#if defined(__cplusplus)
189}
190#endif
diff --git a/src/engine/variant.cpp b/src/engine/variant.cpp
new file mode 100644
index 00000000..2a9f08ed
--- /dev/null
+++ b/src/engine/variant.cpp
@@ -0,0 +1,601 @@
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#include "precomp.h"
4
5#define VARIANT_ENCRYPTION_SCOPE CRYPTPROTECTMEMORY_SAME_PROCESS
6
7// internal function declarations
8
9static HRESULT BVariantEncryptNumeric(
10 __in BURN_VARIANT* pVariant,
11 __in BOOL fEncrypt
12 );
13
14static HRESULT BVariantEncryptString(
15 __in BURN_VARIANT* pVariant,
16 __in BOOL fEncrypt
17 );
18
19static HRESULT BVariantEncryptVersion(
20 __in BURN_VARIANT* pVariant,
21 __in BOOL fEncrypt
22 );
23
24static HRESULT BVariantRetrieveDecryptedNumeric(
25 __in BURN_VARIANT* pVariant,
26 __out LONGLONG* pllValue
27 );
28
29static HRESULT BVariantRetrieveDecryptedString(
30 __in BURN_VARIANT* pVariant,
31 __out LPWSTR* psczValue
32 );
33
34static HRESULT BVariantRetrieveDecryptedVersion(
35 __in BURN_VARIANT* pVariant,
36 __out DWORD64* pqwValue
37 );
38
39// function definitions
40
41extern "C" void BVariantUninitialize(
42 __in BURN_VARIANT* pVariant
43 )
44{
45 if (BURN_VARIANT_TYPE_STRING == pVariant->Type)
46 {
47 StrSecureZeroFreeString(pVariant->sczValue);
48 }
49 SecureZeroMemory(pVariant, sizeof(BURN_VARIANT));
50}
51
52// The contents of pllValue may be sensitive, should keep encrypted and SecureZeroMemory.
53extern "C" HRESULT BVariantGetNumeric(
54 __in BURN_VARIANT* pVariant,
55 __out LONGLONG* pllValue
56 )
57{
58 HRESULT hr = S_OK;
59 LPWSTR sczValue = NULL;
60
61 switch (pVariant->Type)
62 {
63 case BURN_VARIANT_TYPE_NUMERIC:
64 BVariantRetrieveDecryptedNumeric(pVariant, pllValue);
65 break;
66 case BURN_VARIANT_TYPE_STRING:
67 hr = BVariantRetrieveDecryptedString(pVariant, &sczValue);
68 if (SUCCEEDED(hr))
69 {
70 hr = StrStringToInt64(sczValue, 0, pllValue);
71 if (FAILED(hr))
72 {
73 hr = DISP_E_TYPEMISMATCH;
74 }
75 }
76 StrSecureZeroFreeString(sczValue);
77 break;
78 case BURN_VARIANT_TYPE_VERSION:
79 BVariantRetrieveDecryptedVersion(pVariant, (DWORD64*)pllValue);
80 break;
81 default:
82 hr = E_INVALIDARG;
83 break;
84 }
85
86 return hr;
87}
88
89// The contents of psczValue may be sensitive, should keep encrypted and SecureZeroFree.
90extern "C" HRESULT BVariantGetString(
91 __in BURN_VARIANT* pVariant,
92 __out_z LPWSTR* psczValue
93 )
94{
95 HRESULT hr = S_OK;
96 LONGLONG llValue = 0;
97 DWORD64 qwValue = 0;
98
99 switch (pVariant->Type)
100 {
101 case BURN_VARIANT_TYPE_NUMERIC:
102 hr = BVariantRetrieveDecryptedNumeric(pVariant, &llValue);
103 if (SUCCEEDED(hr))
104 {
105 hr = StrAllocFormattedSecure(psczValue, L"%I64d", llValue);
106 ExitOnFailure(hr, "Failed to convert int64 to string.");
107 }
108 SecureZeroMemory(&llValue, sizeof(llValue));
109 break;
110 case BURN_VARIANT_TYPE_STRING:
111 hr = BVariantRetrieveDecryptedString(pVariant, psczValue);
112 break;
113 case BURN_VARIANT_TYPE_VERSION:
114 hr = BVariantRetrieveDecryptedVersion(pVariant, &qwValue);
115 if (SUCCEEDED(hr))
116 {
117 hr = StrAllocFormattedSecure(psczValue, L"%hu.%hu.%hu.%hu",
118 (WORD)(qwValue >> 48),
119 (WORD)(qwValue >> 32),
120 (WORD)(qwValue >> 16),
121 (WORD)qwValue);
122 ExitOnFailure(hr, "Failed to convert version to string.");
123 }
124 SecureZeroMemory(&qwValue, sizeof(qwValue));
125 break;
126 default:
127 hr = E_INVALIDARG;
128 break;
129 }
130
131LExit:
132 return hr;
133}
134
135// The contents of pqwValue may be sensitive, should keep encrypted and SecureZeroMemory.
136extern "C" HRESULT BVariantGetVersion(
137 __in BURN_VARIANT* pVariant,
138 __out DWORD64* pqwValue
139 )
140{
141 HRESULT hr = S_OK;
142 LPWSTR sczValue = NULL;
143
144 switch (pVariant->Type)
145 {
146 case BURN_VARIANT_TYPE_NUMERIC:
147 BVariantRetrieveDecryptedNumeric(pVariant, (LONGLONG*)pqwValue);
148 break;
149 case BURN_VARIANT_TYPE_STRING:
150 hr = BVariantRetrieveDecryptedString(pVariant, &sczValue);
151 if (SUCCEEDED(hr))
152 {
153 hr = FileVersionFromStringEx(sczValue, 0, pqwValue);
154 if (FAILED(hr))
155 {
156 hr = DISP_E_TYPEMISMATCH;
157 }
158 }
159 StrSecureZeroFreeString(sczValue);
160 break;
161 case BURN_VARIANT_TYPE_VERSION:
162 BVariantRetrieveDecryptedVersion(pVariant, pqwValue);
163 break;
164 default:
165 hr = E_INVALIDARG;
166 break;
167 }
168
169 return hr;
170}
171
172extern "C" HRESULT BVariantSetNumeric(
173 __in BURN_VARIANT* pVariant,
174 __in LONGLONG llValue
175 )
176{
177 HRESULT hr = S_OK;
178 BOOL fEncryptValue = pVariant->fEncryptValue;
179
180 if (BURN_VARIANT_TYPE_STRING == pVariant->Type)
181 {
182 StrSecureZeroFreeString(pVariant->sczValue);
183 }
184 memset(pVariant, 0, sizeof(BURN_VARIANT));
185 pVariant->llValue = llValue;
186 pVariant->Type = BURN_VARIANT_TYPE_NUMERIC;
187 BVariantSetEncryption(pVariant, fEncryptValue);
188
189 return hr;
190}
191
192extern "C" HRESULT BVariantSetString(
193 __in BURN_VARIANT* pVariant,
194 __in_z_opt LPCWSTR wzValue,
195 __in DWORD_PTR cchValue
196 )
197{
198 HRESULT hr = S_OK;
199 BOOL fEncryptValue = pVariant->fEncryptValue;
200
201 if (!wzValue) // if we're nulling out the string, make the variable NONE.
202 {
203 BVariantUninitialize(pVariant);
204 }
205 else // assign the value.
206 {
207 if (BURN_VARIANT_TYPE_STRING != pVariant->Type)
208 {
209 memset(pVariant, 0, sizeof(BURN_VARIANT));
210 }
211 else
212 {
213 // We're about to copy an unencrypted value.
214 pVariant->fEncryptValue = FALSE;
215 }
216
217 hr = StrAllocStringSecure(&pVariant->sczValue, wzValue, cchValue);
218 ExitOnFailure(hr, "Failed to copy string.");
219
220 pVariant->Type = BURN_VARIANT_TYPE_STRING;
221 }
222
223LExit:
224 BVariantSetEncryption(pVariant, fEncryptValue);
225 return hr;
226}
227
228extern "C" HRESULT BVariantSetVersion(
229 __in BURN_VARIANT* pVariant,
230 __in DWORD64 qwValue
231 )
232{
233 HRESULT hr = S_OK;
234 BOOL fEncryptValue = pVariant->fEncryptValue;
235
236 if (BURN_VARIANT_TYPE_STRING == pVariant->Type)
237 {
238 StrSecureZeroFreeString(pVariant->sczValue);
239 }
240 memset(pVariant, 0, sizeof(BURN_VARIANT));
241 pVariant->qwValue = qwValue;
242 pVariant->Type = BURN_VARIANT_TYPE_VERSION;
243 BVariantSetEncryption(pVariant, fEncryptValue);
244
245 return hr;
246}
247
248extern "C" HRESULT BVariantSetValue(
249 __in BURN_VARIANT* pVariant,
250 __in BURN_VARIANT* pValue
251 )
252{
253 HRESULT hr = S_OK;
254 LONGLONG llValue = 0;
255 LPWSTR sczValue = NULL;
256 DWORD64 qwValue = 0;
257 BOOL fEncrypt = pVariant->fEncryptValue;
258
259 switch (pValue->Type)
260 {
261 case BURN_VARIANT_TYPE_NONE:
262 BVariantUninitialize(pVariant);
263 break;
264 case BURN_VARIANT_TYPE_NUMERIC:
265 hr = BVariantGetNumeric(pValue, &llValue);
266 if (SUCCEEDED(hr))
267 {
268 hr = BVariantSetNumeric(pVariant, llValue);
269 }
270 SecureZeroMemory(&llValue, sizeof(llValue));
271 break;
272 case BURN_VARIANT_TYPE_STRING:
273 hr = BVariantGetString(pValue, &sczValue);
274 if (SUCCEEDED(hr))
275 {
276 hr = BVariantSetString(pVariant, sczValue, 0);
277 }
278 StrSecureZeroFreeString(sczValue);
279 break;
280 case BURN_VARIANT_TYPE_VERSION:
281 hr = BVariantGetVersion(pValue, &qwValue);
282 if (SUCCEEDED(hr))
283 {
284 hr = BVariantSetVersion(pVariant, qwValue);
285 }
286 SecureZeroMemory(&qwValue, sizeof(qwValue));
287 break;
288 default:
289 hr = E_INVALIDARG;
290 }
291 ExitOnFailure(hr, "Failed to copy variant.");
292
293 hr = BVariantSetEncryption(pVariant, fEncrypt);
294
295LExit:
296 return hr;
297}
298
299extern "C" HRESULT BVariantCopy(
300 __in BURN_VARIANT* pSource,
301 __out BURN_VARIANT* pTarget
302 )
303{
304 HRESULT hr = S_OK;
305 LONGLONG llValue = 0;
306 LPWSTR sczValue = NULL;
307 DWORD64 qwValue = 0;
308
309 BVariantUninitialize(pTarget);
310
311 switch (pSource->Type)
312 {
313 case BURN_VARIANT_TYPE_NONE:
314 break;
315 case BURN_VARIANT_TYPE_NUMERIC:
316 hr = BVariantGetNumeric(pSource, &llValue);
317 if (SUCCEEDED(hr))
318 {
319 hr = BVariantSetNumeric(pTarget, llValue);
320 }
321 SecureZeroMemory(&llValue, sizeof(llValue));
322 break;
323 case BURN_VARIANT_TYPE_STRING:
324 hr = BVariantGetString(pSource, &sczValue);
325 if (SUCCEEDED(hr))
326 {
327 hr = BVariantSetString(pTarget, sczValue, 0);
328 }
329 StrSecureZeroFreeString(sczValue);
330 break;
331 case BURN_VARIANT_TYPE_VERSION:
332 hr = BVariantGetVersion(pSource, &qwValue);
333 if (SUCCEEDED(hr))
334 {
335 hr = BVariantSetVersion(pTarget, qwValue);
336 }
337 SecureZeroMemory(&qwValue, sizeof(qwValue));
338 break;
339 default:
340 hr = E_INVALIDARG;
341 }
342 ExitOnFailure(hr, "Failed to copy variant.");
343
344 hr = BVariantSetEncryption(pTarget, pSource->fEncryptValue);
345
346LExit:
347 return hr;
348}
349
350extern "C" HRESULT BVariantChangeType(
351 __in BURN_VARIANT* pVariant,
352 __in BURN_VARIANT_TYPE type
353 )
354{
355 HRESULT hr = S_OK;
356 BURN_VARIANT variant = { };
357 BOOL fEncryptValue = pVariant->fEncryptValue;
358
359 if (pVariant->Type == type)
360 {
361 ExitFunction(); // variant already is of the requested type
362 }
363
364 switch (type)
365 {
366 case BURN_VARIANT_TYPE_NONE:
367 hr = S_OK;
368 break;
369 case BURN_VARIANT_TYPE_NUMERIC:
370 hr = BVariantGetNumeric(pVariant, &variant.llValue);
371 break;
372 case BURN_VARIANT_TYPE_STRING:
373 hr = BVariantGetString(pVariant, &variant.sczValue);
374 break;
375 case BURN_VARIANT_TYPE_VERSION:
376 hr = BVariantGetVersion(pVariant, &variant.qwValue);
377 break;
378 default:
379 ExitFunction1(hr = E_INVALIDARG);
380 }
381 ExitOnFailure(hr, "Failed to copy variant value.");
382 variant.Type = type;
383
384 BVariantUninitialize(pVariant);
385 memcpy_s(pVariant, sizeof(BURN_VARIANT), &variant, sizeof(BURN_VARIANT));
386 SecureZeroMemory(&variant, sizeof(BURN_VARIANT));
387 BVariantSetEncryption(pVariant, fEncryptValue);
388
389LExit:
390 return hr;
391}
392
393extern "C" HRESULT BVariantSetEncryption(
394 __in BURN_VARIANT* pVariant,
395 __in BOOL fEncrypt
396 )
397{
398 HRESULT hr = S_OK;
399
400 if (pVariant->fEncryptValue == fEncrypt)
401 {
402 // The requested encryption state is already applied.
403 ExitFunction();
404 }
405
406 switch (pVariant->Type)
407 {
408 case BURN_VARIANT_TYPE_NONE:
409 hr = S_OK;
410 break;
411 case BURN_VARIANT_TYPE_NUMERIC:
412 hr = BVariantEncryptNumeric(pVariant, fEncrypt);
413 break;
414 case BURN_VARIANT_TYPE_STRING:
415 hr = BVariantEncryptString(pVariant, fEncrypt);
416 break;
417 case BURN_VARIANT_TYPE_VERSION:
418 hr = BVariantEncryptVersion(pVariant, fEncrypt);
419 break;
420 default:
421 hr = E_INVALIDARG;
422 }
423 ExitOnFailure(hr, "Failed to set the variant's encryption state");
424 pVariant->fEncryptValue = fEncrypt;
425
426LExit:
427 return hr;
428}
429
430static HRESULT BVariantEncryptNumeric(
431 __in BURN_VARIANT* pVariant,
432 __in BOOL fEncrypt
433 )
434{
435 HRESULT hr = S_OK;
436
437 if (fEncrypt)
438 {
439 hr = CrypEncryptMemory(&pVariant->llValue, sizeof(pVariant->encryptionPadding), VARIANT_ENCRYPTION_SCOPE);
440 }
441 else
442 {
443 hr = CrypDecryptMemory(&pVariant->llValue, sizeof(pVariant->encryptionPadding), VARIANT_ENCRYPTION_SCOPE);
444 }
445
446//LExit:
447 return hr;
448}
449
450static HRESULT BVariantEncryptString(
451 __in BURN_VARIANT* pVariant,
452 __in BOOL fEncrypt
453 )
454{
455 HRESULT hr = S_OK;
456 SIZE_T cbData = 0;
457
458 if (NULL == pVariant->sczValue)
459 {
460 ExitFunction();
461 }
462
463 cbData = MemSize(pVariant->sczValue);
464 if (-1 == cbData)
465 {
466 hr = E_INVALIDARG;
467 ExitOnFailure(hr, "Failed to get the size of the string");
468 }
469
470 DWORD remainder = fEncrypt ? cbData % CRYP_ENCRYPT_MEMORY_SIZE : 0;
471 DWORD extraNeeded = 0 < remainder ? CRYP_ENCRYPT_MEMORY_SIZE - remainder : 0;
472 if ((MAXDWORD - extraNeeded) < cbData)
473 {
474 hr = E_INVALIDDATA;
475 ExitOnFailure(hr, "The string is too big: size %u", cbData);
476 }
477 else if (0 < extraNeeded)
478 {
479 cbData += extraNeeded;
480 LPVOID pvNew = NULL;
481 hr = MemReAllocSecure(static_cast<LPVOID>(pVariant->sczValue), cbData, TRUE, &pvNew);
482 ExitOnFailure(hr, "Failed to resize the string so it could be encrypted");
483 pVariant->sczValue = static_cast<LPWSTR>(pvNew);
484 }
485
486 if (fEncrypt)
487 {
488 hr = CrypEncryptMemory(pVariant->sczValue, static_cast<DWORD>(cbData), VARIANT_ENCRYPTION_SCOPE);
489 }
490 else
491 {
492 hr = CrypDecryptMemory(pVariant->sczValue, static_cast<DWORD>(cbData), VARIANT_ENCRYPTION_SCOPE);
493 }
494
495LExit:
496 return hr;
497}
498
499static HRESULT BVariantEncryptVersion(
500 __in BURN_VARIANT* pVariant,
501 __in BOOL fEncrypt
502 )
503{
504 HRESULT hr = S_OK;
505
506 if (fEncrypt)
507 {
508 hr = CrypEncryptMemory(&pVariant->qwValue, sizeof(pVariant->encryptionPadding), VARIANT_ENCRYPTION_SCOPE);
509 }
510 else
511 {
512 hr = CrypDecryptMemory(&pVariant->qwValue, sizeof(pVariant->encryptionPadding), VARIANT_ENCRYPTION_SCOPE);
513 }
514
515//LExit:
516 return hr;
517}
518
519// The contents of pllValue may be sensitive, should keep encrypted and SecureZeroMemory.
520static HRESULT BVariantRetrieveDecryptedNumeric(
521 __in BURN_VARIANT* pVariant,
522 __out LONGLONG* pllValue
523 )
524{
525 HRESULT hr = S_OK;
526
527 Assert(NULL != pllValue);
528 if (pVariant->fEncryptValue)
529 {
530 hr = BVariantEncryptNumeric(pVariant, FALSE);
531 ExitOnFailure(hr, "Failed to decrypt numeric");
532 }
533
534 *pllValue = pVariant->llValue;
535
536 if (pVariant->fEncryptValue)
537 {
538 hr = BVariantEncryptNumeric(pVariant, TRUE);
539 }
540
541LExit:
542 return hr;
543}
544
545// The contents of psczValue may be sensitive, should keep encrypted and SecureZeroFree.
546static HRESULT BVariantRetrieveDecryptedString(
547 __in BURN_VARIANT* pVariant,
548 __out LPWSTR* psczValue
549 )
550{
551 HRESULT hr = S_OK;
552
553 if (NULL == pVariant->sczValue)
554 {
555 *psczValue = NULL;
556 ExitFunction();
557 }
558
559 if (pVariant->fEncryptValue)
560 {
561 hr = BVariantEncryptString(pVariant, FALSE);
562 ExitOnFailure(hr, "Failed to decrypt string");
563 }
564
565 hr = StrAllocStringSecure(psczValue, pVariant->sczValue, 0);
566 ExitOnFailure(hr, "Failed to copy value.");
567
568 if (pVariant->fEncryptValue)
569 {
570 hr = BVariantEncryptString(pVariant, TRUE);
571 }
572
573LExit:
574 return hr;
575}
576
577// The contents of pqwValue may be sensitive, should keep encrypted and SecureZeroMemory.
578static HRESULT BVariantRetrieveDecryptedVersion(
579 __in BURN_VARIANT* pVariant,
580 __out DWORD64* pqwValue
581 )
582{
583 HRESULT hr = S_OK;
584
585 Assert(NULL != pqwValue);
586 if (pVariant->fEncryptValue)
587 {
588 hr = BVariantEncryptVersion(pVariant, FALSE);
589 ExitOnFailure(hr, "Failed to decrypt version");
590 }
591
592 *pqwValue = pVariant->qwValue;
593
594 if (pVariant->fEncryptValue)
595 {
596 hr = BVariantEncryptVersion(pVariant, TRUE);
597 }
598
599LExit:
600 return hr;
601}
diff --git a/src/engine/variant.h b/src/engine/variant.h
new file mode 100644
index 00000000..9259f05a
--- /dev/null
+++ b/src/engine/variant.h
@@ -0,0 +1,102 @@
1#pragma once
2// 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.
3
4
5#if defined(__cplusplus)
6extern "C" {
7#endif
8
9
10// constants
11
12enum BURN_VARIANT_TYPE
13{
14 BURN_VARIANT_TYPE_NONE,
15 BURN_VARIANT_TYPE_NUMERIC,
16 BURN_VARIANT_TYPE_STRING,
17 BURN_VARIANT_TYPE_VERSION,
18};
19
20
21// struct
22
23typedef struct _BURN_VARIANT
24{
25 union
26 {
27 LONGLONG llValue;
28 DWORD64 qwValue;
29 LPWSTR sczValue;
30 BYTE encryptionPadding[CRYP_ENCRYPT_MEMORY_SIZE];
31 };
32 BURN_VARIANT_TYPE Type;
33 BOOL fEncryptValue;
34} BURN_VARIANT;
35
36
37// function declarations
38
39void BVariantUninitialize(
40 __in BURN_VARIANT* pVariant
41 );
42HRESULT BVariantGetNumeric(
43 __in BURN_VARIANT* pVariant,
44 __out LONGLONG* pllValue
45 );
46HRESULT BVariantGetString(
47 __in BURN_VARIANT* pVariant,
48 __out_z LPWSTR* psczValue
49 );
50HRESULT BVariantGetVersion(
51 __in BURN_VARIANT* pVariant,
52 __out DWORD64* pqwValue
53 );
54HRESULT BVariantSetNumeric(
55 __in BURN_VARIANT* pVariant,
56 __in LONGLONG llValue
57 );
58HRESULT BVariantSetString(
59 __in BURN_VARIANT* pVariant,
60 __in_z_opt LPCWSTR wzValue,
61 __in DWORD_PTR cchValue
62 );
63HRESULT BVariantSetVersion(
64 __in BURN_VARIANT* pVariant,
65 __in DWORD64 qwValue
66 );
67/********************************************************************
68BVariantSetValue - Convenience function that calls BVariantUninitialize,
69 BVariantSetNumeric, BVariantSetString, or
70 BVariantSetVersion based on the type of pValue.
71 The encryption state of pVariant is preserved.
72********************************************************************/
73HRESULT BVariantSetValue(
74 __in BURN_VARIANT* pVariant,
75 __in BURN_VARIANT* pValue
76 );
77/********************************************************************
78BVariantCopy - creates a copy of pSource.
79 The encryption state of pTarget is set to
80 the encryption state of pSource.
81********************************************************************/
82HRESULT BVariantCopy(
83 __in BURN_VARIANT* pSource,
84 __out BURN_VARIANT* pTarget
85 );
86HRESULT BVariantChangeType(
87 __in BURN_VARIANT* pVariant,
88 __in BURN_VARIANT_TYPE type
89 );
90/********************************************************************
91BVariantSetEncryption - sets the encryption state of pVariant.
92 If the encryption state matches the requested
93 state, this function does nothing.
94********************************************************************/
95HRESULT BVariantSetEncryption(
96 __in BURN_VARIANT* pVariant,
97 __in BOOL fEncrypt
98 );
99
100#if defined(__cplusplus)
101}
102#endif
diff --git a/src/stub/StubSection.cpp b/src/stub/StubSection.cpp
new file mode 100644
index 00000000..962bb3cf
--- /dev/null
+++ b/src/stub/StubSection.cpp
@@ -0,0 +1,23 @@
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#include "precomp.h"
4
5#pragma section(".wixburn",read)
6
7// If these defaults ever change, be sure to update constants in burn\engine\section.cpp as well.
8#pragma data_seg(push, ".wixburn")
9static DWORD dwMagic = 0x00f14300;
10static DWORD dwVersion = 0x00000002;
11
12static GUID guidBundleId = { };
13
14static DWORD dwStubSize = 0;
15static DWORD dwOriginalChecksum = 0;
16static DWORD dwOriginalSignatureOffset = 0;
17static DWORD dwOriginalSignatureSize = 0;
18
19static DWORD dwContainerFormat = 1;
20static DWORD dwContainerCount = 0;
21static DWORD qwBootstrapperApplicationContainerSize = 0;
22static DWORD qwAttachedContainerSize = 0;
23#pragma data_seg(pop)
diff --git a/src/stub/precomp.h b/src/stub/precomp.h
new file mode 100644
index 00000000..387d4f0f
--- /dev/null
+++ b/src/stub/precomp.h
@@ -0,0 +1,13 @@
1#pragma once
2// 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.
3
4
5#include <windows.h>
6
7#include <dutil.h>
8#include <apputil.h>
9#include <strutil.h>
10#include <fileutil.h>
11#include <pathutil.h>
12
13#include "engine.h"
diff --git a/src/stub/stub.cpp b/src/stub/stub.cpp
new file mode 100644
index 00000000..2f09eede
--- /dev/null
+++ b/src/stub/stub.cpp
@@ -0,0 +1,64 @@
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#include "precomp.h"
4
5
6int WINAPI wWinMain(
7 __in HINSTANCE hInstance,
8 __in_opt HINSTANCE /* hPrevInstance */,
9 __in_z_opt LPWSTR lpCmdLine,
10 __in int nCmdShow
11 )
12{
13 HRESULT hr = S_OK;
14 DWORD dwExitCode = 0;
15 LPWSTR sczPath = NULL;
16 HANDLE hEngineFile = INVALID_HANDLE_VALUE;
17
18 LPCWSTR rgsczSafelyLoadSystemDlls[] =
19 {
20 L"cabinet.dll", // required by Burn.
21 L"msi.dll", // required by Burn.
22 L"version.dll", // required by Burn.
23 L"wininet.dll", // required by Burn.
24
25 L"comres.dll", // required by CLSIDFromProgID() when loading clbcatq.dll.
26 L"clbcatq.dll", // required by CLSIDFromProgID() when loading msxml?.dll.
27
28 L"msasn1.dll", // required by DecryptFile() when loading crypt32.dll.
29 L"crypt32.dll", // required by DecryptFile() when loading feclient.dll.
30 L"feclient.dll", // unsafely loaded by DecryptFile().
31 };
32
33 // Best effort attempt to get our file handle as soon as possible.
34 hr = PathForCurrentProcess(&sczPath, NULL);
35 if (SUCCEEDED(hr))
36 {
37 hEngineFile = ::CreateFileW(sczPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
38 }
39
40 // If the engine is in the clean room, we'll do the unsafe initialization
41 // because some systems in Windows (namely GDI+) will fail when run in
42 // a process that protects against DLL hijacking. Since we know the clean
43 // room is in a clean folder and not subject to DLL hijacking we won't
44 // make ourselves perfectly secure so that we can load BAs that still
45 // depend on those parts of Windows that are insecure to DLL hijacking.
46 if (EngineInCleanRoom(lpCmdLine))
47 {
48 AppInitializeUnsafe();
49 }
50 else
51 {
52 AppInitialize(rgsczSafelyLoadSystemDlls, countof(rgsczSafelyLoadSystemDlls));
53 }
54
55 // call run
56 hr = EngineRun(hInstance, hEngineFile, lpCmdLine, nCmdShow, &dwExitCode);
57 ExitOnFailure(hr, "Failed to run application.");
58
59LExit:
60 ReleaseFileHandle(hEngineFile);
61 ReleaseStr(sczPath);
62
63 return FAILED(hr) ? (int)hr : (int)dwExitCode;
64}
diff --git a/src/stub/stub.ico b/src/stub/stub.ico
new file mode 100644
index 00000000..c2e2717c
--- /dev/null
+++ b/src/stub/stub.ico
Binary files differ
diff --git a/src/stub/stub.manifest b/src/stub/stub.manifest
new file mode 100644
index 00000000..d5767cdb
--- /dev/null
+++ b/src/stub/stub.manifest
@@ -0,0 +1,18 @@
1<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2<!-- 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. -->
3
4
5<assembly xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" manifestVersion="1.0">
6 <assemblyIdentity name="setup.exe" version="1.0.0.0" processorArchitecture="x86" type="win32"/>
7 <description>WiX Toolset Bootstrapper</description>
8 <asmv3:application><asmv3:windowsSettings><ws:dpiAware xmlns:ws="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</ws:dpiAware></asmv3:windowsSettings></asmv3:application>
9 <dependency><dependentAssembly><assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="X86" publicKeyToken="6595b64144ccf1df" language="*" /></dependentAssembly></dependency>
10 <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"><application>
11 <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
12 <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
13 <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
14 <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
15 <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
16 </application></compatibility>
17 <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"><security><requestedPrivileges><requestedExecutionLevel level="asInvoker" uiAccess="false"/></requestedPrivileges></security></trustInfo>
18</assembly>
diff --git a/src/stub/stub.rc b/src/stub/stub.rc
new file mode 100644
index 00000000..5601703d
--- /dev/null
+++ b/src/stub/stub.rc
@@ -0,0 +1,14 @@
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#define VER_APP
4#define VER_ORIGINAL_FILENAME "setup.exe"
5#define VER_INTERNAL_NAME "setup"
6#define VER_FILE_DESCRIPTION "WiX Toolset Bootstrapper"
7#include "wix.rc"
8
91 ICON "stub.ico"
10
11//#define MANIFEST_RESOURCE_ID 1
12#ifndef ARM // the ARM manifest is automatically injected but other platforms need it done manually.
13//MANIFEST_RESOURCE_ID RT_MANIFEST "stub.manifest"
14#endif