diff options
author | Sean Hall <r.sean.hall@gmail.com> | 2018-12-29 22:12:08 -0600 |
---|---|---|
committer | Sean Hall <r.sean.hall@gmail.com> | 2018-12-29 22:12:08 -0600 |
commit | 61847dddd4fd497057c780658e383c4627de19ec (patch) | |
tree | f85a845182922538ab9aa6ee85b0db3ab40c1f6e | |
parent | 8295f5f8fd28042e1a0a172d5afbba79178064c2 (diff) | |
download | wix-61847dddd4fd497057c780658e383c4627de19ec.tar.gz wix-61847dddd4fd497057c780658e383c4627de19ec.tar.bz2 wix-61847dddd4fd497057c780658e383c4627de19ec.zip |
Import code from old v4 repo
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 | |||
3 | namespace 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 | |||
3 | namespace 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 | |||
3 | namespace 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 | |||
3 | namespace 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 | |||
3 | using System; | ||
4 | using System.Reflection; | ||
5 | using System.Runtime.CompilerServices; | ||
6 | using 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 | |||
3 | namespace 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 | |||
5 | static 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 | |||
19 | static 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 | |||
41 | static 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 | |||
88 | static 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 | |||
110 | static 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 | |||
156 | static 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 | |||
200 | static 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 | |||
222 | static 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 | |||
258 | LExit: | ||
259 | return hr; | ||
260 | } | ||
261 | |||
262 | static 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 | |||
297 | LExit: | ||
298 | ReleaseBuffer(pbData); | ||
299 | return hr; | ||
300 | } | ||
301 | |||
302 | static 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 | |||
333 | LExit: | ||
334 | ReleaseBuffer(pbData); | ||
335 | return hr; | ||
336 | } | ||
337 | |||
338 | static 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 | |||
413 | LExit: | ||
414 | ::LeaveCriticalSection(&pContext->pEngineState->csActive); | ||
415 | |||
416 | ReleaseStr(sczCommandline); | ||
417 | ReleaseStr(sczLocalSource); | ||
418 | return hr; | ||
419 | } | ||
420 | |||
421 | static 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 | |||
469 | LExit: | ||
470 | ::LeaveCriticalSection(&pContext->pEngineState->csActive); | ||
471 | return hr; | ||
472 | } | ||
473 | |||
474 | static 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 | |||
553 | LExit: | ||
554 | ::LeaveCriticalSection(&pContext->pEngineState->csActive); | ||
555 | return hr; | ||
556 | } | ||
557 | |||
558 | static 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 | |||
579 | LExit: | ||
580 | return hr; | ||
581 | } | ||
582 | |||
583 | static 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 | |||
604 | LExit: | ||
605 | return hr; | ||
606 | } | ||
607 | |||
608 | static 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 | |||
629 | LExit: | ||
630 | return hr; | ||
631 | } | ||
632 | |||
633 | static 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 | |||
648 | static 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 | |||
661 | LExit: | ||
662 | return hr; | ||
663 | } | ||
664 | |||
665 | static 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 | |||
679 | LExit: | ||
680 | return hr; | ||
681 | } | ||
682 | |||
683 | static 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 | |||
700 | LExit: | ||
701 | return hr; | ||
702 | } | ||
703 | |||
704 | static 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 | |||
717 | LExit: | ||
718 | return hr; | ||
719 | } | ||
720 | |||
721 | static 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 | |||
734 | LExit: | ||
735 | return hr; | ||
736 | } | ||
737 | |||
738 | static 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 | |||
787 | LExit: | ||
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 | |||
801 | HRESULT 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 | |||
892 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | // constants | ||
10 | |||
11 | enum 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 | |||
27 | struct BOOTSTRAPPER_ENGINE_CONTEXT | ||
28 | { | ||
29 | BURN_ENGINE_STATE* pEngineState; | ||
30 | DWORD dwThreadId; | ||
31 | }; | ||
32 | |||
33 | // function declarations | ||
34 | |||
35 | HRESULT 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 | |||
6 | const DWORD BURN_CACHE_MAX_RECOMMENDED_VERIFY_TRYAGAIN_ATTEMPTS = 2; | ||
7 | |||
8 | // structs | ||
9 | |||
10 | struct 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 | |||
23 | typedef 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 | ||
35 | static 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 | |||
43 | static 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 | ); | ||
49 | static 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 | ); | ||
56 | static void UpdateCacheSuccessProgress( | ||
57 | __in BURN_PLAN* pPlan, | ||
58 | __in BURN_CACHE_ACTION* pCacheAction, | ||
59 | __inout DWORD64* pqwSuccessfulCachedProgress | ||
60 | ); | ||
61 | static 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 | ); | ||
71 | static 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 | ); | ||
81 | static 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 | ); | ||
96 | static 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 | ); | ||
105 | static HRESULT CopyPayload( | ||
106 | __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, | ||
107 | __in_z LPCWSTR wzSourcePath, | ||
108 | __in_z LPCWSTR wzDestinationPath | ||
109 | ); | ||
110 | static HRESULT DownloadPayload( | ||
111 | __in BURN_CACHE_ACQUIRE_PROGRESS_CONTEXT* pProgress, | ||
112 | __in_z LPCWSTR wzDestinationPath | ||
113 | ); | ||
114 | static 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 | ); | ||
125 | static void DoRollbackCache( | ||
126 | __in BURN_USER_EXPERIENCE* pUX, | ||
127 | __in BURN_PLAN* pPlan, | ||
128 | __in HANDLE hPipe, | ||
129 | __in DWORD dwCheckpoint | ||
130 | ); | ||
131 | static 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 | ); | ||
142 | static 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 | ); | ||
150 | static 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 | ); | ||
159 | static 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 | ); | ||
168 | static 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 | ); | ||
177 | static 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 | ); | ||
187 | static HRESULT ExecutePackageProviderAction( | ||
188 | __in BURN_ENGINE_STATE* pEngineState, | ||
189 | __in BURN_EXECUTE_ACTION* pAction, | ||
190 | __in BURN_EXECUTE_CONTEXT* pContext | ||
191 | ); | ||
192 | static HRESULT ExecuteDependencyAction( | ||
193 | __in BURN_ENGINE_STATE* pEngineState, | ||
194 | __in BURN_EXECUTE_ACTION* pAction, | ||
195 | __in BURN_EXECUTE_CONTEXT* pContext | ||
196 | ); | ||
197 | static HRESULT ExecuteCompatiblePackageAction( | ||
198 | __in BURN_ENGINE_STATE* pEngineState, | ||
199 | __in BURN_EXECUTE_ACTION* pAction | ||
200 | ); | ||
201 | static HRESULT CleanPackage( | ||
202 | __in HANDLE hElevatedPipe, | ||
203 | __in BURN_PACKAGE* pPackage | ||
204 | ); | ||
205 | static int GenericExecuteMessageHandler( | ||
206 | __in GENERIC_EXECUTE_MESSAGE* pMessage, | ||
207 | __in LPVOID pvContext | ||
208 | ); | ||
209 | static int MsiExecuteMessageHandler( | ||
210 | __in WIU_MSI_EXECUTE_MESSAGE* pMessage, | ||
211 | __in_opt LPVOID pvContext | ||
212 | ); | ||
213 | static HRESULT ReportOverallProgressTicks( | ||
214 | __in BURN_USER_EXPERIENCE* pUX, | ||
215 | __in BOOL fRollback, | ||
216 | __in DWORD cOverallProgressTicksTotal, | ||
217 | __in DWORD cOverallProgressTicks | ||
218 | ); | ||
219 | static 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 | |||
231 | static HRESULT DoMsiBeginTransaction( | ||
232 | __in BURN_EXECUTE_CONTEXT *context | ||
233 | , __in BURN_ENGINE_STATE* pEngineState | ||
234 | ); | ||
235 | static HRESULT DoMsiCommitTransaction( | ||
236 | __in BURN_EXECUTE_CONTEXT *context | ||
237 | , __in BURN_ENGINE_STATE* pEngineState | ||
238 | ); | ||
239 | static HRESULT DoMsiRollbackTransaction( | ||
240 | __in BURN_EXECUTE_CONTEXT *context | ||
241 | , __in BURN_ENGINE_STATE* pEngineState | ||
242 | ); | ||
243 | static HRESULT ExecuteMsiBeginTransaction( | ||
244 | __in BURN_EXECUTE_CONTEXT* pContext | ||
245 | , __in BURN_ENGINE_STATE* pEngineState | ||
246 | ); | ||
247 | static HRESULT ExecuteMsiCommitTransaction( | ||
248 | __in BURN_EXECUTE_CONTEXT* pContext | ||
249 | , __in BURN_ENGINE_STATE* pEngineState | ||
250 | ); | ||
251 | static HRESULT ExecuteMsiRollbackTransaction( | ||
252 | __in BURN_EXECUTE_CONTEXT* pContext | ||
253 | , __in BURN_ENGINE_STATE* pEngineState | ||
254 | ); | ||
255 | |||
256 | // function definitions | ||
257 | |||
258 | extern "C" void ApplyInitialize() | ||
259 | { | ||
260 | // Prevent the system from sleeping. | ||
261 | ::SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED); | ||
262 | } | ||
263 | |||
264 | extern "C" void ApplyUninitialize() | ||
265 | { | ||
266 | ::SetThreadExecutionState(ES_CONTINUOUS); | ||
267 | } | ||
268 | |||
269 | extern "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 | |||
278 | LExit: | ||
279 | return hr; | ||
280 | } | ||
281 | |||
282 | extern "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 | |||
296 | extern "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 | |||
318 | LExit: | ||
319 | ReleaseHandle(hLock); | ||
320 | #endif | ||
321 | return hr; | ||
322 | } | ||
323 | |||
324 | extern "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 | |||
379 | LExit: | ||
380 | UserExperienceOnRegisterComplete(&pEngineState->userExperience, hr); | ||
381 | ReleaseStr(sczEngineWorkingPath); | ||
382 | |||
383 | return hr; | ||
384 | } | ||
385 | |||
386 | extern "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 | |||
439 | LExit: | ||
440 | UserExperienceOnUnregisterComplete(&pEngineState->userExperience, hr); | ||
441 | |||
442 | return hr; | ||
443 | } | ||
444 | |||
445 | extern "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 | |||
712 | LExit: | ||
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 | |||
735 | extern "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 | |||
863 | LExit: | ||
864 | // Send execute complete to BA. | ||
865 | UserExperienceOnExecuteComplete(&pEngineState->userExperience, hr); | ||
866 | |||
867 | return hr; | ||
868 | } | ||
869 | |||
870 | extern "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 | |||
889 | static 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 | |||
914 | LExit: | ||
915 | return hr; | ||
916 | } | ||
917 | |||
918 | static 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 | |||
971 | LExit: | ||
972 | ReleaseStr(sczExtractPayloadId); | ||
973 | ContainerClose(&context); | ||
974 | |||
975 | return hr; | ||
976 | } | ||
977 | |||
978 | static 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 | |||
1036 | static 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 | |||
1137 | LExit: | ||
1138 | ReleaseStr(sczDestinationPath); | ||
1139 | ReleaseStr(sczBundlePath); | ||
1140 | |||
1141 | return hr; | ||
1142 | } | ||
1143 | |||
1144 | static 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 | |||
1253 | LExit: | ||
1254 | ReleaseStr(sczSourceFullPath); | ||
1255 | |||
1256 | return hr; | ||
1257 | } | ||
1258 | |||
1259 | static 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 | |||
1357 | LExit: | ||
1358 | return hr; | ||
1359 | } | ||
1360 | |||
1361 | static 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 | |||
1401 | LExit: | ||
1402 | UserExperienceActivateEngine(pUX, NULL); | ||
1403 | return hr; | ||
1404 | } | ||
1405 | |||
1406 | static 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 | |||
1446 | LExit: | ||
1447 | return hr; | ||
1448 | } | ||
1449 | |||
1450 | static 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 | |||
1507 | LExit: | ||
1508 | return hr; | ||
1509 | } | ||
1510 | |||
1511 | static 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 | |||
1564 | LExit: | ||
1565 | ReleaseStr(sczError); | ||
1566 | |||
1567 | return hr; | ||
1568 | } | ||
1569 | |||
1570 | static 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 | |||
1614 | static 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 | */ | ||
1664 | static 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 | |||
1686 | LExit: | ||
1687 | return hr; | ||
1688 | } | ||
1689 | |||
1690 | static 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 | |||
1710 | LExit: | ||
1711 | return hr; | ||
1712 | } | ||
1713 | |||
1714 | static 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 | |||
1734 | LExit: | ||
1735 | return hr; | ||
1736 | } | ||
1737 | |||
1738 | // Currently, supporting only elevated transactions. | ||
1739 | static 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 | |||
1749 | LExit: | ||
1750 | return hr; | ||
1751 | } | ||
1752 | |||
1753 | static 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 | |||
1763 | LExit: | ||
1764 | return hr; | ||
1765 | } | ||
1766 | |||
1767 | static 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 | |||
1777 | LExit: | ||
1778 | return hr; | ||
1779 | } | ||
1780 | |||
1781 | |||
1782 | static 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 | |||
1896 | LExit: | ||
1897 | return hr; | ||
1898 | } | ||
1899 | |||
1900 | static 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 | |||
2036 | LExit: | ||
2037 | return hr; | ||
2038 | } | ||
2039 | |||
2040 | static 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 | |||
2102 | LExit: | ||
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 | |||
2111 | static 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 | |||
2157 | LExit: | ||
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 | |||
2166 | static 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 | |||
2221 | LExit: | ||
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 | |||
2230 | static 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 | |||
2293 | LExit: | ||
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 | |||
2302 | static 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 | |||
2321 | LExit: | ||
2322 | return hr; | ||
2323 | } | ||
2324 | |||
2325 | static 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 | |||
2344 | LExit: | ||
2345 | return hr; | ||
2346 | } | ||
2347 | |||
2348 | static 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 | |||
2363 | LExit: | ||
2364 | return hr; | ||
2365 | } | ||
2366 | |||
2367 | static 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 | |||
2386 | static 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 | |||
2416 | static 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 | |||
2452 | static 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 | |||
2468 | static 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 | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | enum 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 | |||
18 | typedef struct _APPLY_AUTHENTICATION_REQUIRED_DATA | ||
19 | { | ||
20 | BURN_USER_EXPERIENCE* pUX; | ||
21 | LPCWSTR wzPackageOrContainerId; | ||
22 | LPCWSTR wzPayloadId; | ||
23 | } APPLY_AUTHENTICATION_REQUIRED_DATA; | ||
24 | |||
25 | typedef 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 | |||
50 | typedef int (*PFN_GENERICMESSAGEHANDLER)( | ||
51 | __in GENERIC_EXECUTE_MESSAGE* pMessage, | ||
52 | __in LPVOID pvContext | ||
53 | ); | ||
54 | |||
55 | |||
56 | void ApplyInitialize(); | ||
57 | void ApplyUninitialize(); | ||
58 | HRESULT ApplySetVariables( | ||
59 | __in BURN_VARIABLES* pVariables | ||
60 | ); | ||
61 | void ApplyReset( | ||
62 | __in BURN_USER_EXPERIENCE* pUX, | ||
63 | __in BURN_PACKAGES* pPackages | ||
64 | ); | ||
65 | HRESULT ApplyLock( | ||
66 | __in BOOL fPerMachine, | ||
67 | __out HANDLE* phLock | ||
68 | ); | ||
69 | HRESULT ApplyRegister( | ||
70 | __in BURN_ENGINE_STATE* pEngineState | ||
71 | ); | ||
72 | HRESULT 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 | ); | ||
79 | HRESULT 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 | ); | ||
88 | HRESULT 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 | ); | ||
97 | void 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 | |||
8 | extern "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 | |||
75 | LExit: | ||
76 | ReleaseObject(pixnNodes); | ||
77 | ReleaseObject(pixnNode); | ||
78 | ReleaseStr(scz); | ||
79 | return hr; | ||
80 | } | ||
81 | |||
82 | extern "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 | |||
100 | extern "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 | |||
113 | extern "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 | |||
135 | LExit: | ||
136 | return hr; | ||
137 | } | ||
138 | |||
139 | extern "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 | |||
200 | LExit: | ||
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 | |||
213 | extern "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 | |||
258 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // structs | ||
11 | |||
12 | typedef struct _BURN_APPROVED_EXE | ||
13 | { | ||
14 | LPWSTR sczId; | ||
15 | LPWSTR sczKey; | ||
16 | LPWSTR sczValueName; | ||
17 | BOOL fWin64; | ||
18 | } BURN_APPROVED_EXE; | ||
19 | |||
20 | typedef struct _BURN_APPROVED_EXES | ||
21 | { | ||
22 | BURN_APPROVED_EXE* rgApprovedExes; | ||
23 | DWORD cApprovedExes; | ||
24 | } BURN_APPROVED_EXES; | ||
25 | |||
26 | typedef 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 | |||
38 | HRESULT ApprovedExesParseFromXml( | ||
39 | __in BURN_APPROVED_EXES* pApprovedExes, | ||
40 | __in IXMLDOMNode* pixnBundle | ||
41 | ); | ||
42 | |||
43 | void ApprovedExesUninitialize( | ||
44 | __in BURN_APPROVED_EXES* pApprovedExes | ||
45 | ); | ||
46 | void ApprovedExesUninitializeLaunch( | ||
47 | __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe | ||
48 | ); | ||
49 | HRESULT ApprovedExesFindById( | ||
50 | __in BURN_APPROVED_EXES* pApprovedExes, | ||
51 | __in_z LPCWSTR wzId, | ||
52 | __out BURN_APPROVED_EXE** ppApprovedExe | ||
53 | ); | ||
54 | HRESULT ApprovedExesLaunch( | ||
55 | __in BURN_VARIABLES* pVariables, | ||
56 | __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe, | ||
57 | __out DWORD* pdwProcessId | ||
58 | ); | ||
59 | HRESULT 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 | |||
7 | const DWORD BITSENGINE_NO_PROGRESS_TIMEOUT = 2 * 60; | ||
8 | const DWORD BITSENGINE_MSG_WAIT_TIMEOUT = 1; | ||
9 | |||
10 | // functions | ||
11 | |||
12 | static HRESULT CreateJob( | ||
13 | __out IBackgroundCopyJob** ppJob | ||
14 | ); | ||
15 | static HRESULT SetCredentials( | ||
16 | __in IBackgroundCopyJob* pJob, | ||
17 | __in_z_opt LPCWSTR wzUser, | ||
18 | __in_z_opt LPCWSTR wzPassword | ||
19 | ); | ||
20 | static 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 | |||
31 | class CBurnBitsCallback : public IBackgroundCopyCallback | ||
32 | { | ||
33 | public: // 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 | |||
80 | public: // 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 | |||
154 | public: | ||
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 | |||
213 | private: | ||
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 | |||
272 | public: | ||
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 | |||
302 | private: | ||
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 | |||
313 | extern "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 | |||
392 | LExit: | ||
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 | |||
411 | static 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 | |||
438 | LExit: | ||
439 | ReleaseObject(pJob); | ||
440 | ReleaseObject(pBitsManager); | ||
441 | |||
442 | return hr; | ||
443 | } | ||
444 | |||
445 | static 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 | |||
474 | LExit: | ||
475 | ReleaseObject(pJob2); | ||
476 | |||
477 | return hr; | ||
478 | } | ||
479 | |||
480 | static 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 | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | // structs | ||
10 | |||
11 | |||
12 | // functions | ||
13 | |||
14 | HRESULT 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 | |||
9 | const LPSTR INVALID_CAB_NAME = "<the>.cab"; | ||
10 | |||
11 | // structs | ||
12 | |||
13 | typedef 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 | |||
37 | static HRESULT BeginAndWaitForOperation( | ||
38 | __in BURN_CONTAINER_CONTEXT* pContext | ||
39 | ); | ||
40 | static HRESULT WaitForOperation( | ||
41 | __in BURN_CONTAINER_CONTEXT* pContext | ||
42 | ); | ||
43 | static DWORD WINAPI ExtractThreadProc( | ||
44 | __in LPVOID lpThreadParameter | ||
45 | ); | ||
46 | static INT_PTR DIAMONDAPI CabNotifyCallback( | ||
47 | __in FDINOTIFICATIONTYPE iNotification, | ||
48 | __inout FDINOTIFICATION *pFDINotify | ||
49 | ); | ||
50 | static INT_PTR CopyFileCallback( | ||
51 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
52 | __inout FDINOTIFICATION *pFDINotify | ||
53 | ); | ||
54 | static INT_PTR CloseFileInfoCallback( | ||
55 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
56 | __inout FDINOTIFICATION *pFDINotify | ||
57 | ); | ||
58 | static LPVOID DIAMONDAPI CabAlloc( | ||
59 | __in DWORD dwSize | ||
60 | ); | ||
61 | static void DIAMONDAPI CabFree( | ||
62 | __in LPVOID pvData | ||
63 | ); | ||
64 | static INT_PTR FAR DIAMONDAPI CabOpen( | ||
65 | __in char FAR *pszFile, | ||
66 | __in int /* oflag */, | ||
67 | __in int /* pmode */ | ||
68 | ); | ||
69 | static UINT FAR DIAMONDAPI CabRead( | ||
70 | __in INT_PTR hf, | ||
71 | __out void FAR *pv, | ||
72 | __in UINT cb | ||
73 | ); | ||
74 | static UINT FAR DIAMONDAPI CabWrite( | ||
75 | __in INT_PTR hf, | ||
76 | __in void FAR *pv, | ||
77 | __in UINT cb | ||
78 | ); | ||
79 | static long FAR DIAMONDAPI CabSeek( | ||
80 | __in INT_PTR hf, | ||
81 | __in long dist, | ||
82 | __in int seektype | ||
83 | ); | ||
84 | static int FAR DIAMONDAPI CabClose( | ||
85 | __in INT_PTR hf | ||
86 | ); | ||
87 | static HRESULT AddVirtualFilePointer( | ||
88 | __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, | ||
89 | __in HANDLE hFile, | ||
90 | __in LONGLONG llInitialFilePointer | ||
91 | ); | ||
92 | static HRESULT ReadIfVirtualFilePointer( | ||
93 | __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, | ||
94 | __in HANDLE hFile, | ||
95 | __in DWORD cbRead | ||
96 | ); | ||
97 | static 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 | ); | ||
104 | static HRESULT CloseIfVirturalFilePointer( | ||
105 | __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, | ||
106 | __in HANDLE hFile | ||
107 | ); | ||
108 | static 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 | |||
121 | extern "C" void CabExtractInitialize() | ||
122 | { | ||
123 | } | ||
124 | |||
125 | extern "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 | |||
153 | LExit: | ||
154 | return hr; | ||
155 | } | ||
156 | |||
157 | extern "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 | |||
175 | LExit: | ||
176 | return hr; | ||
177 | } | ||
178 | |||
179 | extern "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 | |||
197 | LExit: | ||
198 | return hr; | ||
199 | } | ||
200 | |||
201 | extern "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 | |||
225 | LExit: | ||
226 | return hr; | ||
227 | } | ||
228 | |||
229 | extern "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 | |||
242 | LExit: | ||
243 | return hr; | ||
244 | } | ||
245 | |||
246 | extern "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 | |||
271 | LExit: | ||
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 | |||
284 | static 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 | |||
299 | LExit: | ||
300 | return hr; | ||
301 | } | ||
302 | |||
303 | static 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 | |||
337 | LExit: | ||
338 | return hr; | ||
339 | } | ||
340 | |||
341 | static 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 | |||
458 | LExit: | ||
459 | if (hfdi) | ||
460 | { | ||
461 | ::FDIDestroy(hfdi); | ||
462 | } | ||
463 | if (fComInitialized) | ||
464 | { | ||
465 | ::CoUninitialize(); | ||
466 | } | ||
467 | |||
468 | return (DWORD)hr; | ||
469 | } | ||
470 | |||
471 | static 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 | |||
503 | static 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 | |||
619 | LExit: | ||
620 | ReleaseStr(pwzPath); | ||
621 | |||
622 | pContext->Cabinet.hrError = hr; | ||
623 | return SUCCEEDED(hr) ? ipResult : -1; | ||
624 | } | ||
625 | |||
626 | static 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 | |||
680 | LExit: | ||
681 | pContext->Cabinet.hrError = hr; | ||
682 | return SUCCEEDED(hr) ? ipResult : -1; | ||
683 | } | ||
684 | |||
685 | static LPVOID DIAMONDAPI CabAlloc( | ||
686 | __in DWORD dwSize | ||
687 | ) | ||
688 | { | ||
689 | return MemAlloc(dwSize, FALSE); | ||
690 | } | ||
691 | |||
692 | static void DIAMONDAPI CabFree( | ||
693 | __in LPVOID pvData | ||
694 | ) | ||
695 | { | ||
696 | MemFree(pvData); | ||
697 | } | ||
698 | |||
699 | static 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 | |||
728 | LExit: | ||
729 | pContext->Cabinet.hrError = hr; | ||
730 | return FAILED(hr) ? -1 : (INT_PTR)hFile; | ||
731 | } | ||
732 | |||
733 | static 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 | |||
751 | LExit: | ||
752 | pContext->Cabinet.hrError = hr; | ||
753 | return FAILED(hr) ? -1 : cbRead; | ||
754 | } | ||
755 | |||
756 | static 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 | |||
789 | LExit: | ||
790 | pContext->Cabinet.hrError = hr; | ||
791 | return FAILED(hr) ? -1 : cbWrite; | ||
792 | } | ||
793 | |||
794 | static 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 | |||
843 | LExit: | ||
844 | pContext->Cabinet.hrError = hr; | ||
845 | return FAILED(hr) ? -1 : liNewPointer.LowPart; | ||
846 | } | ||
847 | |||
848 | static 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 | |||
861 | static 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 | |||
876 | LExit: | ||
877 | return hr; | ||
878 | } | ||
879 | |||
880 | static 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 | |||
901 | LExit: | ||
902 | return hr; | ||
903 | } | ||
904 | |||
905 | static 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 | |||
941 | static 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 | |||
959 | static 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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // function declarations | ||
11 | |||
12 | void CabExtractInitialize(); | ||
13 | HRESULT CabExtractOpen( | ||
14 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
15 | __in LPCWSTR wzFilePath | ||
16 | ); | ||
17 | HRESULT CabExtractNextStream( | ||
18 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
19 | __inout_z LPWSTR* psczStreamName | ||
20 | ); | ||
21 | HRESULT CabExtractStreamToFile( | ||
22 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
23 | __in_z LPCWSTR wzFileName | ||
24 | ); | ||
25 | HRESULT CabExtractStreamToBuffer( | ||
26 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
27 | __out BYTE** ppbBuffer, | ||
28 | __out SIZE_T* pcbBuffer | ||
29 | ); | ||
30 | HRESULT CabExtractSkipStream( | ||
31 | __in BURN_CONTAINER_CONTEXT* pContext | ||
32 | ); | ||
33 | HRESULT 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 | |||
5 | static const LPCWSTR BUNDLE_CLEAN_ROOM_WORKING_FOLDER_NAME = L".cr"; | ||
6 | static const LPCWSTR BUNDLE_WORKING_FOLDER_NAME = L".be"; | ||
7 | static const LPCWSTR UNVERIFIED_CACHE_FOLDER_NAME = L".unverified"; | ||
8 | static const LPCWSTR PACKAGE_CACHE_FOLDER_NAME = L"Package Cache"; | ||
9 | static const DWORD FILE_OPERATION_RETRY_COUNT = 3; | ||
10 | static const DWORD FILE_OPERATION_RETRY_WAIT = 2000; | ||
11 | |||
12 | static BOOL vfInitializedCache = FALSE; | ||
13 | static BOOL vfRunningFromCache = FALSE; | ||
14 | static LPWSTR vsczSourceProcessPath = NULL; | ||
15 | static LPWSTR vsczWorkingFolder = NULL; | ||
16 | static LPWSTR vsczDefaultUserPackageCache = NULL; | ||
17 | static LPWSTR vsczDefaultMachinePackageCache = NULL; | ||
18 | static LPWSTR vsczCurrentMachinePackageCache = NULL; | ||
19 | |||
20 | static HRESULT CalculateWorkingFolder( | ||
21 | __in_z LPCWSTR wzBundleId, | ||
22 | __deref_out_z LPWSTR* psczWorkingFolder | ||
23 | ); | ||
24 | static HRESULT GetLastUsedSourceFolder( | ||
25 | __in BURN_VARIABLES* pVariables, | ||
26 | __out_z LPWSTR* psczLastSource | ||
27 | ); | ||
28 | static HRESULT CreateCompletedPath( | ||
29 | __in BOOL fPerMachine, | ||
30 | __in LPCWSTR wzCacheId, | ||
31 | __out LPWSTR* psczCacheDirectory | ||
32 | ); | ||
33 | static HRESULT CreateUnverifiedPath( | ||
34 | __in BOOL fPerMachine, | ||
35 | __in_z LPCWSTR wzPayloadId, | ||
36 | __out_z LPWSTR* psczUnverifiedPayloadPath | ||
37 | ); | ||
38 | static HRESULT GetRootPath( | ||
39 | __in BOOL fPerMachine, | ||
40 | __in BOOL fAllowRedirect, | ||
41 | __deref_out_z LPWSTR* psczRootPath | ||
42 | ); | ||
43 | static HRESULT VerifyThenTransferContainer( | ||
44 | __in BURN_CONTAINER* pContainer, | ||
45 | __in_z LPCWSTR wzCachedPath, | ||
46 | __in_z LPCWSTR wzUnverifiedContainerPath, | ||
47 | __in BOOL fMove | ||
48 | ); | ||
49 | static HRESULT VerifyThenTransferPayload( | ||
50 | __in BURN_PAYLOAD* pPayload, | ||
51 | __in_z LPCWSTR wzCachedPath, | ||
52 | __in_z LPCWSTR wzUnverifiedPayloadPath, | ||
53 | __in BOOL fMove | ||
54 | ); | ||
55 | static HRESULT TransferWorkingPathToUnverifiedPath( | ||
56 | __in_z LPCWSTR wzWorkingPath, | ||
57 | __in_z LPCWSTR wzUnverifiedPayloadPath, | ||
58 | __in BOOL fMove | ||
59 | ); | ||
60 | static HRESULT VerifyFileAgainstPayload( | ||
61 | __in BURN_PAYLOAD* pPayload, | ||
62 | __in_z LPCWSTR wzVerifyPath | ||
63 | ); | ||
64 | static HRESULT ResetPathPermissions( | ||
65 | __in BOOL fPerMachine, | ||
66 | __in_z LPCWSTR wzPath | ||
67 | ); | ||
68 | static HRESULT SecurePath( | ||
69 | __in LPCWSTR wzPath | ||
70 | ); | ||
71 | static 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 | ); | ||
79 | static HRESULT CopyEngineWithSignatureFixup( | ||
80 | __in HANDLE hEngineFile, | ||
81 | __in_z LPCWSTR wzEnginePath, | ||
82 | __in_z LPCWSTR wzTargetPath, | ||
83 | __in BURN_SECTION* pSection | ||
84 | ); | ||
85 | static HRESULT RemoveBundleOrPackage( | ||
86 | __in BOOL fBundle, | ||
87 | __in BOOL fPerMachine, | ||
88 | __in_z LPCWSTR wzBundleOrPackageId, | ||
89 | __in_z LPCWSTR wzCacheId | ||
90 | ); | ||
91 | static HRESULT VerifyHash( | ||
92 | __in BYTE* pbHash, | ||
93 | __in DWORD cbHash, | ||
94 | __in_z LPCWSTR wzUnverifiedPayloadPath, | ||
95 | __in HANDLE hFile | ||
96 | ); | ||
97 | static HRESULT VerifyPayloadWithCatalog( | ||
98 | __in BURN_PAYLOAD* pPayload, | ||
99 | __in_z LPCWSTR wzUnverifiedPayloadPath, | ||
100 | __in HANDLE hFile | ||
101 | ); | ||
102 | static HRESULT VerifyPayloadAgainstChain( | ||
103 | __in BURN_PAYLOAD* pPayload, | ||
104 | __in PCCERT_CHAIN_CONTEXT pChainContext | ||
105 | ); | ||
106 | |||
107 | |||
108 | extern "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 | |||
181 | LExit: | ||
182 | ReleaseStr(sczCurrentPath); | ||
183 | ReleaseStr(sczCompletedFolder); | ||
184 | ReleaseStr(sczCompletedPath); | ||
185 | ReleaseStr(sczOriginalSource); | ||
186 | ReleaseStr(sczOriginalSourceFolder); | ||
187 | |||
188 | return hr; | ||
189 | } | ||
190 | |||
191 | extern "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 | |||
214 | LExit: | ||
215 | ReleaseStr(sczWorkingFolder); | ||
216 | |||
217 | return hr; | ||
218 | } | ||
219 | |||
220 | extern "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 | |||
247 | LExit: | ||
248 | ReleaseStr(sczWorkingFolder); | ||
249 | |||
250 | return hr; | ||
251 | } | ||
252 | |||
253 | extern "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 | |||
267 | LExit: | ||
268 | ReleaseStr(sczWorkingFolder); | ||
269 | |||
270 | return hr; | ||
271 | } | ||
272 | |||
273 | extern "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 | |||
287 | LExit: | ||
288 | return hr; | ||
289 | } | ||
290 | |||
291 | extern "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 | |||
305 | LExit: | ||
306 | return hr; | ||
307 | } | ||
308 | |||
309 | extern "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 | |||
329 | extern "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 | |||
378 | LExit: | ||
379 | ReleaseNullStr(sczDefaultCompletedPath); | ||
380 | ReleaseNullStr(sczCurrentCompletedPath); | ||
381 | ReleaseNullStr(sczRootPath); | ||
382 | |||
383 | return hr; | ||
384 | } | ||
385 | |||
386 | extern "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 | |||
396 | LExit: | ||
397 | return hr; | ||
398 | } | ||
399 | |||
400 | extern "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 | |||
494 | LExit: | ||
495 | ReleaseStr(sczCurrentPath); | ||
496 | ReleaseStr(sczSourceProcessFolder); | ||
497 | ReleaseStr(sczLastSourceFolder); | ||
498 | ReleaseStr(sczLastSourcePath); | ||
499 | ReleaseStr(sczLayoutFolder); | ||
500 | ReleaseStr(sczLayoutPath); | ||
501 | |||
502 | return hr; | ||
503 | } | ||
504 | |||
505 | extern "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 | |||
557 | LExit: | ||
558 | ReleaseStr(sczLastSourceFolder); | ||
559 | ReleaseStr(sczSourceFolder); | ||
560 | |||
561 | return hr; | ||
562 | } | ||
563 | |||
564 | extern "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 | |||
606 | LExit: | ||
607 | return hr; | ||
608 | } | ||
609 | |||
610 | extern "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 | |||
632 | extern "C" BOOL CacheBundleRunningFromCache() | ||
633 | { | ||
634 | return vfRunningFromCache; | ||
635 | } | ||
636 | |||
637 | extern "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 | |||
655 | LExit: | ||
656 | ReleaseStr(sczSourcePath); | ||
657 | |||
658 | return hr; | ||
659 | } | ||
660 | |||
661 | extern "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 | |||
691 | LExit: | ||
692 | ReleaseStr(sczSourcePath); | ||
693 | |||
694 | return hr; | ||
695 | } | ||
696 | |||
697 | extern "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 | |||
714 | LExit: | ||
715 | ReleaseStr(sczTargetPath); | ||
716 | |||
717 | return hr; | ||
718 | } | ||
719 | |||
720 | extern "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 | |||
786 | LExit: | ||
787 | ReleaseStr(sczPayloadSourcePath); | ||
788 | ReleaseStr(sczSourceDirectory); | ||
789 | ReleaseStr(sczTargetPath); | ||
790 | ReleaseStr(sczTargetDirectory); | ||
791 | |||
792 | return hr; | ||
793 | } | ||
794 | |||
795 | extern "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 | |||
811 | LExit: | ||
812 | ReleaseStr(sczCachedPath); | ||
813 | |||
814 | return hr; | ||
815 | } | ||
816 | |||
817 | extern "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 | |||
833 | LExit: | ||
834 | ReleaseStr(sczCachedPath); | ||
835 | |||
836 | return hr; | ||
837 | } | ||
838 | |||
839 | extern "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 | |||
907 | LExit: | ||
908 | ReleaseStr(sczUnverifiedPayloadPath); | ||
909 | ReleaseStr(sczCachedPath); | ||
910 | ReleaseStr(sczCachedDirectory); | ||
911 | |||
912 | return hr; | ||
913 | } | ||
914 | |||
915 | extern "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 | |||
932 | LExit: | ||
933 | ReleaseStr(sczWorkingFolder); | ||
934 | |||
935 | return hr; | ||
936 | } | ||
937 | |||
938 | extern "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 | |||
948 | LExit: | ||
949 | return hr; | ||
950 | } | ||
951 | |||
952 | extern "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 | |||
963 | LExit: | ||
964 | return hr; | ||
965 | } | ||
966 | |||
967 | extern "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 | |||
1013 | LExit: | ||
1014 | return hr; | ||
1015 | } | ||
1016 | |||
1017 | extern "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 | |||
1086 | extern "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 | |||
1100 | static 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 | |||
1151 | LExit: | ||
1152 | return hr; | ||
1153 | } | ||
1154 | |||
1155 | static 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 | |||
1225 | LExit: | ||
1226 | ReleaseStr(sczAppData); | ||
1227 | |||
1228 | return hr; | ||
1229 | } | ||
1230 | |||
1231 | static 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 | |||
1253 | static 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 | |||
1294 | LExit: | ||
1295 | ReleaseStr(sczCacheDirectory); | ||
1296 | return hr; | ||
1297 | } | ||
1298 | |||
1299 | static 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 | |||
1324 | LExit: | ||
1325 | ReleaseStr(sczUnverifiedCacheFolder); | ||
1326 | |||
1327 | return hr; | ||
1328 | } | ||
1329 | |||
1330 | static 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 | |||
1367 | LExit: | ||
1368 | ReleaseFileHandle(hFile); | ||
1369 | |||
1370 | return hr; | ||
1371 | } | ||
1372 | |||
1373 | static 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 | |||
1420 | LExit: | ||
1421 | ReleaseFileHandle(hFile); | ||
1422 | |||
1423 | return hr; | ||
1424 | } | ||
1425 | |||
1426 | static 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 | |||
1445 | LExit: | ||
1446 | return hr; | ||
1447 | } | ||
1448 | |||
1449 | static 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 | |||
1486 | LExit: | ||
1487 | ReleaseFileHandle(hFile); | ||
1488 | |||
1489 | return hr; | ||
1490 | } | ||
1491 | |||
1492 | static 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 | |||
1512 | LExit: | ||
1513 | ReleaseMem(pAllocSid); | ||
1514 | return hr; | ||
1515 | } | ||
1516 | |||
1517 | |||
1518 | static 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 | |||
1548 | LExit: | ||
1549 | ReleaseMem(pSid); | ||
1550 | return hr; | ||
1551 | } | ||
1552 | |||
1553 | |||
1554 | static 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 | |||
1571 | LExit: | ||
1572 | return hr; | ||
1573 | } | ||
1574 | |||
1575 | |||
1576 | static 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 | |||
1606 | LExit: | ||
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 | |||
1621 | static 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 | |||
1684 | LExit: | ||
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 | |||
1696 | static 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 | |||
1757 | LExit: | ||
1758 | ReleaseFileHandle(hTarget); | ||
1759 | |||
1760 | return hr; | ||
1761 | } | ||
1762 | |||
1763 | |||
1764 | static 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 | |||
1817 | LExit: | ||
1818 | ReleaseStr(sczDirectory); | ||
1819 | ReleaseStr(sczRootCacheDirectory); | ||
1820 | |||
1821 | return hr; | ||
1822 | } | ||
1823 | |||
1824 | static 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 | |||
1860 | LExit: | ||
1861 | ReleaseStr(pszActual); | ||
1862 | ReleaseStr(pszExpected); | ||
1863 | |||
1864 | return hr; | ||
1865 | } | ||
1866 | |||
1867 | static 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 | |||
1956 | LExit: | ||
1957 | ReleaseStr(sczLowerCaseFile); | ||
1958 | ReleaseStr(sczName); | ||
1959 | ReleaseMem(pbHash); | ||
1960 | |||
1961 | return hr; | ||
1962 | } | ||
1963 | |||
1964 | static 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 | |||
2022 | LExit: | ||
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 | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | // structs | ||
10 | |||
11 | // functions | ||
12 | |||
13 | HRESULT CacheInitialize( | ||
14 | __in BURN_REGISTRATION* pRegistration, | ||
15 | __in BURN_VARIABLES* pVariables, | ||
16 | __in_z_opt LPCWSTR wzSourceProcessPath | ||
17 | ); | ||
18 | HRESULT CacheEnsureWorkingFolder( | ||
19 | __in LPCWSTR wzBundleId, | ||
20 | __deref_out_z_opt LPWSTR* psczWorkingFolder | ||
21 | ); | ||
22 | HRESULT CacheCalculateBundleWorkingPath( | ||
23 | __in_z LPCWSTR wzBundleId, | ||
24 | __in LPCWSTR wzExecutableName, | ||
25 | __deref_out_z LPWSTR* psczWorkingPath | ||
26 | ); | ||
27 | HRESULT CacheCalculateBundleLayoutWorkingPath( | ||
28 | __in_z LPCWSTR wzBundleId, | ||
29 | __deref_out_z LPWSTR* psczWorkingPath | ||
30 | ); | ||
31 | HRESULT CacheCalculatePayloadWorkingPath( | ||
32 | __in_z LPCWSTR wzBundleId, | ||
33 | __in BURN_PAYLOAD* pPayload, | ||
34 | __deref_out_z LPWSTR* psczWorkingPath | ||
35 | ); | ||
36 | HRESULT CacheCalculateContainerWorkingPath( | ||
37 | __in_z LPCWSTR wzBundleId, | ||
38 | __in BURN_CONTAINER* pContainer, | ||
39 | __deref_out_z LPWSTR* psczWorkingPath | ||
40 | ); | ||
41 | HRESULT CacheGetRootCompletedPath( | ||
42 | __in BOOL fPerMachine, | ||
43 | __in BOOL fForceInitialize, | ||
44 | __deref_out_z LPWSTR* psczRootCompletedPath | ||
45 | ); | ||
46 | HRESULT CacheGetCompletedPath( | ||
47 | __in BOOL fPerMachine, | ||
48 | __in_z LPCWSTR wzCacheId, | ||
49 | __deref_out_z LPWSTR* psczCompletedPath | ||
50 | ); | ||
51 | HRESULT CacheGetResumePath( | ||
52 | __in_z LPCWSTR wzPayloadWorkingPath, | ||
53 | __deref_out_z LPWSTR* psczResumePath | ||
54 | ); | ||
55 | HRESULT CacheFindLocalSource( | ||
56 | __in_z LPCWSTR wzSourcePath, | ||
57 | __in BURN_VARIABLES* pVariables, | ||
58 | __out BOOL* pfFound, | ||
59 | __out_z LPWSTR* psczSourceFullPath | ||
60 | ); | ||
61 | HRESULT CacheSetLastUsedSource( | ||
62 | __in BURN_VARIABLES* pVariables, | ||
63 | __in_z LPCWSTR wzSourcePath, | ||
64 | __in_z LPCWSTR wzRelativePath | ||
65 | ); | ||
66 | HRESULT CacheSendProgressCallback( | ||
67 | __in DOWNLOAD_CACHE_CALLBACK* pCallback, | ||
68 | __in DWORD64 dw64Progress, | ||
69 | __in DWORD64 dw64Total, | ||
70 | __in HANDLE hDestinationFile | ||
71 | ); | ||
72 | void CacheSendErrorCallback( | ||
73 | __in DOWNLOAD_CACHE_CALLBACK* pCallback, | ||
74 | __in HRESULT hrError, | ||
75 | __in_z_opt LPCWSTR wzError, | ||
76 | __out_opt BOOL* pfRetry | ||
77 | ); | ||
78 | BOOL CacheBundleRunningFromCache(); | ||
79 | HRESULT CacheBundleToCleanRoom( | ||
80 | __in BURN_PAYLOADS* pUxPayloads, | ||
81 | __in BURN_SECTION* pSection, | ||
82 | __deref_out_z_opt LPWSTR* psczCleanRoomBundlePath | ||
83 | ); | ||
84 | HRESULT 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 | ); | ||
91 | HRESULT CacheLayoutBundle( | ||
92 | __in_z LPCWSTR wzExecutableName, | ||
93 | __in_z LPCWSTR wzLayoutDirectory, | ||
94 | __in_z LPCWSTR wzSourceBundlePath | ||
95 | ); | ||
96 | HRESULT 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 | ); | ||
106 | HRESULT CacheLayoutContainer( | ||
107 | __in BURN_CONTAINER* pContainer, | ||
108 | __in_z_opt LPCWSTR wzLayoutDirectory, | ||
109 | __in_z LPCWSTR wzUnverifiedContainerPath, | ||
110 | __in BOOL fMove | ||
111 | ); | ||
112 | HRESULT CacheLayoutPayload( | ||
113 | __in BURN_PAYLOAD* pPayload, | ||
114 | __in_z_opt LPCWSTR wzLayoutDirectory, | ||
115 | __in_z LPCWSTR wzUnverifiedPayloadPath, | ||
116 | __in BOOL fMove | ||
117 | ); | ||
118 | HRESULT 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 | ); | ||
125 | HRESULT CacheRemoveWorkingFolder( | ||
126 | __in_z_opt LPCWSTR wzBundleId | ||
127 | ); | ||
128 | HRESULT CacheRemoveBundle( | ||
129 | __in BOOL fPerMachine, | ||
130 | __in_z LPCWSTR wzPackageId | ||
131 | ); | ||
132 | HRESULT CacheRemovePackage( | ||
133 | __in BOOL fPerMachine, | ||
134 | __in_z LPCWSTR wzPackageId, | ||
135 | __in_z LPCWSTR wzCacheId | ||
136 | ); | ||
137 | HRESULT CacheVerifyPayloadSignature( | ||
138 | __in BURN_PAYLOAD* pPayload, | ||
139 | __in_z LPCWSTR wzUnverifiedPayloadPath, | ||
140 | __in HANDLE hFile | ||
141 | ); | ||
142 | void CacheCleanup( | ||
143 | __in BOOL fPerMachine, | ||
144 | __in_z LPCWSTR wzBundleId | ||
145 | ); | ||
146 | void 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 | |||
8 | extern "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 | |||
58 | LExit: | ||
59 | ReleaseObject(pixnNodes); | ||
60 | ReleaseObject(pixnNode); | ||
61 | ReleaseStr(scz); | ||
62 | |||
63 | return hr; | ||
64 | } | ||
65 | |||
66 | extern "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 | |||
88 | LExit: | ||
89 | return hr; | ||
90 | } | ||
91 | |||
92 | extern "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 | |||
126 | LExit: | ||
127 | return hr; | ||
128 | } | ||
129 | |||
130 | extern "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 | |||
156 | LExit: | ||
157 | return hr; | ||
158 | } | ||
159 | |||
160 | extern "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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | // structs | ||
10 | |||
11 | typedef 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 | |||
21 | typedef struct _BURN_CATALOGS | ||
22 | { | ||
23 | BURN_CATALOG* rgCatalogs; | ||
24 | DWORD cCatalogs; | ||
25 | } BURN_CATALOGS; | ||
26 | |||
27 | typedef struct _BURN_PAYLOADS BURN_PAYLOADS; | ||
28 | |||
29 | |||
30 | // functions | ||
31 | |||
32 | HRESULT CatalogsParseFromXml( | ||
33 | __in BURN_CATALOGS* pCatalogs, | ||
34 | __in IXMLDOMNode* pixnBundle | ||
35 | ); | ||
36 | HRESULT CatalogFindById( | ||
37 | __in BURN_CATALOGS* pCatalogs, | ||
38 | __in_z LPCWSTR wzId, | ||
39 | __out BURN_CATALOG** ppCatalog | ||
40 | ); | ||
41 | HRESULT CatalogLoadFromPayload( | ||
42 | __in BURN_CATALOGS* pCatalogs, | ||
43 | __in BURN_PAYLOADS* pPayloads | ||
44 | ); | ||
45 | HRESULT CatalogElevatedUpdateCatalogFile( | ||
46 | __in BURN_CATALOGS* pCatalogs, | ||
47 | __in_z LPCWSTR wzId, | ||
48 | __in_z LPCWSTR wzPath | ||
49 | ); | ||
50 | void 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 | |||
23 | enum 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 | |||
60 | struct BURN_SYMBOL | ||
61 | { | ||
62 | BURN_SYMBOL_TYPE Type; | ||
63 | DWORD iPosition; | ||
64 | BURN_VARIANT Value; | ||
65 | }; | ||
66 | |||
67 | struct 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 | |||
79 | static HRESULT ParseExpression( | ||
80 | __in BURN_CONDITION_PARSE_CONTEXT* pContext, | ||
81 | __out BOOL* pf | ||
82 | ); | ||
83 | static HRESULT ParseBooleanTerm( | ||
84 | __in BURN_CONDITION_PARSE_CONTEXT* pContext, | ||
85 | __out BOOL* pf | ||
86 | ); | ||
87 | static HRESULT ParseBooleanFactor( | ||
88 | __in BURN_CONDITION_PARSE_CONTEXT* pContext, | ||
89 | __out BOOL* pf | ||
90 | ); | ||
91 | static HRESULT ParseTerm( | ||
92 | __in BURN_CONDITION_PARSE_CONTEXT* pContext, | ||
93 | __out BOOL* pf | ||
94 | ); | ||
95 | static HRESULT ParseValue( | ||
96 | __in BURN_CONDITION_PARSE_CONTEXT* pContext, | ||
97 | __out BURN_VARIANT* pValue | ||
98 | ); | ||
99 | static HRESULT Expect( | ||
100 | __in BURN_CONDITION_PARSE_CONTEXT* pContext, | ||
101 | __in BURN_SYMBOL_TYPE symbolType | ||
102 | ); | ||
103 | static HRESULT NextSymbol( | ||
104 | __in BURN_CONDITION_PARSE_CONTEXT* pContext | ||
105 | ); | ||
106 | static HRESULT CompareValues( | ||
107 | __in BURN_SYMBOL_TYPE comparison, | ||
108 | __in BURN_VARIANT leftOperand, | ||
109 | __in BURN_VARIANT rightOperand, | ||
110 | __out BOOL* pfResult | ||
111 | ); | ||
112 | static HRESULT CompareStringValues( | ||
113 | __in BURN_SYMBOL_TYPE comparison, | ||
114 | __in_z LPCWSTR wzLeftOperand, | ||
115 | __in_z LPCWSTR wzRightOperand, | ||
116 | __out BOOL* pfResult | ||
117 | ); | ||
118 | static HRESULT CompareIntegerValues( | ||
119 | __in BURN_SYMBOL_TYPE comparison, | ||
120 | __in LONGLONG llLeftOperand, | ||
121 | __in LONGLONG llRightOperand, | ||
122 | __out BOOL* pfResult | ||
123 | ); | ||
124 | static 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 | |||
134 | extern "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 | |||
162 | LExit: | ||
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 | |||
172 | extern "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 | |||
213 | LExit: | ||
214 | return hr; | ||
215 | } | ||
216 | |||
217 | HRESULT 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 | |||
241 | LExit: | ||
242 | ReleaseBSTR(bstrExpression); | ||
243 | ReleaseObject(pixnNode); | ||
244 | |||
245 | return hr; | ||
246 | } | ||
247 | |||
248 | |||
249 | // internal function definitions | ||
250 | |||
251 | static 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 | |||
278 | LExit: | ||
279 | return hr; | ||
280 | } | ||
281 | |||
282 | static 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 | |||
309 | LExit: | ||
310 | return hr; | ||
311 | } | ||
312 | |||
313 | static 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 | |||
335 | LExit: | ||
336 | return hr; | ||
337 | } | ||
338 | |||
339 | static 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 | |||
417 | LExit: | ||
418 | BVariantUninitialize(&firstValue); | ||
419 | BVariantUninitialize(&secondValue); | ||
420 | return hr; | ||
421 | } | ||
422 | |||
423 | static 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 | |||
462 | LExit: | ||
463 | return hr; | ||
464 | } | ||
465 | |||
466 | // | ||
467 | // Expect - expects a symbol. | ||
468 | // | ||
469 | static 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 | |||
486 | LExit: | ||
487 | return hr; | ||
488 | } | ||
489 | |||
490 | // | ||
491 | // NextSymbol - finds the next symbol in an expression string. | ||
492 | // | ||
493 | static 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 | |||
765 | LExit: | ||
766 | return hr; | ||
767 | } | ||
768 | |||
769 | // | ||
770 | // CompareValues - compares two variant values using a given comparison. | ||
771 | // | ||
772 | static 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 | |||
894 | LExit: | ||
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 | // | ||
908 | static 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 | |||
966 | LExit: | ||
967 | return hr; | ||
968 | } | ||
969 | |||
970 | // | ||
971 | // CompareIntegerValues - compares two integer values using a given comparison. | ||
972 | // | ||
973 | static 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 | |||
997 | LExit: | ||
998 | return hr; | ||
999 | } | ||
1000 | |||
1001 | // | ||
1002 | // CompareVersionValues - compares two quad-word version values using a given comparison. | ||
1003 | // | ||
1004 | static 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 | |||
1028 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | typedef 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 | |||
19 | HRESULT ConditionEvaluate( | ||
20 | __in BURN_VARIABLES* pVariables, | ||
21 | __in_z LPCWSTR wzCondition, | ||
22 | __out BOOL* pf | ||
23 | ); | ||
24 | HRESULT 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 | ); | ||
32 | HRESULT 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 | |||
8 | static 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 | |||
19 | extern "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 | |||
145 | LExit: | ||
146 | ReleaseObject(pixnNodes); | ||
147 | ReleaseObject(pixnNode); | ||
148 | ReleaseStr(scz); | ||
149 | |||
150 | return hr; | ||
151 | } | ||
152 | |||
153 | extern "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 | |||
179 | extern "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 | |||
205 | LExit: | ||
206 | ReleaseStr(sczExecutablePath); | ||
207 | |||
208 | return hr; | ||
209 | } | ||
210 | |||
211 | extern "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 | |||
260 | LExit: | ||
261 | return hr; | ||
262 | } | ||
263 | |||
264 | extern "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 | |||
282 | extern "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 | |||
300 | extern "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 | |||
319 | extern "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 | |||
336 | extern "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 | |||
351 | LExit: | ||
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 | |||
362 | extern "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 | |||
384 | LExit: | ||
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) | ||
6 | extern "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 | |||
38 | enum BURN_CONTAINER_TYPE | ||
39 | { | ||
40 | BURN_CONTAINER_TYPE_NONE, | ||
41 | BURN_CONTAINER_TYPE_CABINET, | ||
42 | BURN_CONTAINER_TYPE_SEVENZIP, | ||
43 | }; | ||
44 | |||
45 | enum 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 | |||
58 | typedef 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 | |||
80 | typedef struct _BURN_CONTAINERS | ||
81 | { | ||
82 | BURN_CONTAINER* rgContainers; | ||
83 | DWORD cContainers; | ||
84 | } BURN_CONTAINERS; | ||
85 | |||
86 | typedef 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 | |||
92 | typedef 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 | |||
114 | typedef 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 | |||
137 | HRESULT ContainersParseFromXml( | ||
138 | __in BURN_SECTION* pSection, | ||
139 | __in BURN_CONTAINERS* pContainers, | ||
140 | __in IXMLDOMNode* pixnBundle | ||
141 | ); | ||
142 | void ContainersUninitialize( | ||
143 | __in BURN_CONTAINERS* pContainers | ||
144 | ); | ||
145 | HRESULT ContainerOpenUX( | ||
146 | __in BURN_SECTION* pSection, | ||
147 | __in BURN_CONTAINER_CONTEXT* pContext | ||
148 | ); | ||
149 | HRESULT ContainerOpen( | ||
150 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
151 | __in BURN_CONTAINER* pContainer, | ||
152 | __in HANDLE hContainerFile, | ||
153 | __in_z LPCWSTR wzFilePath | ||
154 | ); | ||
155 | HRESULT ContainerNextStream( | ||
156 | __inout BURN_CONTAINER_CONTEXT* pContext, | ||
157 | __inout_z LPWSTR* psczStreamName | ||
158 | ); | ||
159 | HRESULT ContainerStreamToFile( | ||
160 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
161 | __in_z LPCWSTR wzFileName | ||
162 | ); | ||
163 | HRESULT ContainerStreamToBuffer( | ||
164 | __in BURN_CONTAINER_CONTEXT* pContext, | ||
165 | __out BYTE** ppbBuffer, | ||
166 | __out SIZE_T* pcbBuffer | ||
167 | ); | ||
168 | HRESULT ContainerSkipStream( | ||
169 | __in BURN_CONTAINER_CONTEXT* pContext | ||
170 | ); | ||
171 | HRESULT ContainerClose( | ||
172 | __in BURN_CONTAINER_CONTEXT* pContext | ||
173 | ); | ||
174 | HRESULT 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 | |||
8 | struct BURN_CACHE_THREAD_CONTEXT | ||
9 | { | ||
10 | BURN_ENGINE_STATE* pEngineState; | ||
11 | DWORD* pcOverallProgressTicks; | ||
12 | BOOL* pfRollback; | ||
13 | }; | ||
14 | |||
15 | |||
16 | // internal function declarations | ||
17 | |||
18 | static 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 | ); | ||
38 | static HRESULT ParsePipeConnection( | ||
39 | __in LPWSTR* rgArgs, | ||
40 | __in BURN_PIPE_CONNECTION* pConnection | ||
41 | ); | ||
42 | static HRESULT DetectPackage( | ||
43 | __in BURN_ENGINE_STATE* pEngineState, | ||
44 | __in BURN_PACKAGE* pPackage | ||
45 | ); | ||
46 | static HRESULT DetectPackagePayloadsCached( | ||
47 | __in BURN_PACKAGE* pPackage | ||
48 | ); | ||
49 | static DWORD WINAPI CacheThreadProc( | ||
50 | __in LPVOID lpThreadParameter | ||
51 | ); | ||
52 | static HRESULT WaitForCacheThread( | ||
53 | __in HANDLE hCacheThread | ||
54 | ); | ||
55 | static 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 | |||
66 | extern "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 | |||
156 | LExit: | ||
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 | |||
168 | extern "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 | |||
179 | LExit: | ||
180 | return hr; | ||
181 | } | ||
182 | |||
183 | extern "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 | |||
219 | LExit: | ||
220 | ReleaseBuffer(pbBuffer); | ||
221 | |||
222 | return hr; | ||
223 | } | ||
224 | |||
225 | extern "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 | |||
355 | LExit: | ||
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 | |||
378 | extern "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 | |||
488 | LExit: | ||
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 | |||
505 | extern "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 | |||
528 | LExit: | ||
529 | return hr; | ||
530 | } | ||
531 | |||
532 | extern "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 | |||
677 | LExit: | ||
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 | |||
717 | extern "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 | |||
741 | LExit: | ||
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 | |||
756 | extern "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 | |||
781 | extern "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 | |||
805 | LExit: | ||
806 | ReleaseBuffer(pbBuffer); | ||
807 | |||
808 | return hr; | ||
809 | } | ||
810 | |||
811 | extern "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 | |||
844 | extern "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 | |||
962 | LExit: | ||
963 | ReleaseStr(scz); | ||
964 | |||
965 | return hr; | ||
966 | } | ||
967 | |||
968 | extern "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 | |||
990 | LExit: | ||
991 | ReleaseFileHandle(hExecutableFile); | ||
992 | |||
993 | return hr; | ||
994 | } | ||
995 | |||
996 | extern "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 | |||
1025 | LExit: | ||
1026 | ReleaseFileHandle(hExecutableFile); | ||
1027 | |||
1028 | return hr; | ||
1029 | } | ||
1030 | |||
1031 | // internal helper functions | ||
1032 | |||
1033 | static 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 | |||
1448 | LExit: | ||
1449 | ReleaseStr(sczVariableName); | ||
1450 | ReleaseStr(sczSanitizedArgument); | ||
1451 | ReleaseStr(sczCommandLine); | ||
1452 | |||
1453 | return hr; | ||
1454 | } | ||
1455 | |||
1456 | static 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 | |||
1472 | LExit: | ||
1473 | return hr; | ||
1474 | } | ||
1475 | |||
1476 | static 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 | |||
1522 | LExit: | ||
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 | |||
1536 | static 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 | |||
1590 | LExit: | ||
1591 | ReleaseStr(sczPayloadCachePath); | ||
1592 | ReleaseStr(sczCachePath); | ||
1593 | return hr; | ||
1594 | } | ||
1595 | |||
1596 | static 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 | |||
1615 | LExit: | ||
1616 | UserExperienceExecutePhaseComplete(&pEngineState->userExperience, hr); // signal that cache completed. | ||
1617 | |||
1618 | if (fComInitialized) | ||
1619 | { | ||
1620 | ::CoUninitialize(); | ||
1621 | } | ||
1622 | |||
1623 | return (DWORD)hr; | ||
1624 | } | ||
1625 | |||
1626 | static 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 | |||
1642 | LExit: | ||
1643 | return hr; | ||
1644 | } | ||
1645 | |||
1646 | static 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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // constants | ||
11 | |||
12 | const LPCWSTR BURN_POLICY_REGISTRY_PATH = L"WiX\\Burn"; | ||
13 | |||
14 | const LPCWSTR BURN_COMMANDLINE_SWITCH_PARENT = L"parent"; | ||
15 | const LPCWSTR BURN_COMMANDLINE_SWITCH_PARENT_NONE = L"parent:none"; | ||
16 | const LPCWSTR BURN_COMMANDLINE_SWITCH_CLEAN_ROOM = L"burn.clean.room"; | ||
17 | const LPCWSTR BURN_COMMANDLINE_SWITCH_ELEVATED = L"burn.elevated"; | ||
18 | const LPCWSTR BURN_COMMANDLINE_SWITCH_EMBEDDED = L"burn.embedded"; | ||
19 | const LPCWSTR BURN_COMMANDLINE_SWITCH_RUNONCE = L"burn.runonce"; | ||
20 | const LPCWSTR BURN_COMMANDLINE_SWITCH_LOG_APPEND = L"burn.log.append"; | ||
21 | const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_DETECT = L"burn.related.detect"; | ||
22 | const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_UPGRADE = L"burn.related.upgrade"; | ||
23 | const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_ADDON = L"burn.related.addon"; | ||
24 | const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_PATCH = L"burn.related.patch"; | ||
25 | const LPCWSTR BURN_COMMANDLINE_SWITCH_RELATED_UPDATE = L"burn.related.update"; | ||
26 | const LPCWSTR BURN_COMMANDLINE_SWITCH_PASSTHROUGH = L"burn.passthrough"; | ||
27 | const LPCWSTR BURN_COMMANDLINE_SWITCH_DISABLE_UNELEVATE = L"burn.disable.unelevate"; | ||
28 | const LPCWSTR BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES = L"burn.ignoredependencies"; | ||
29 | const LPCWSTR BURN_COMMANDLINE_SWITCH_ANCESTORS = L"burn.ancestors"; | ||
30 | const LPCWSTR BURN_COMMANDLINE_SWITCH_FILEHANDLE_ATTACHED = L"burn.filehandle.attached"; | ||
31 | const LPCWSTR BURN_COMMANDLINE_SWITCH_FILEHANDLE_SELF = L"burn.filehandle.self"; | ||
32 | const LPCWSTR BURN_COMMANDLINE_SWITCH_PREFIX = L"burn."; | ||
33 | |||
34 | const LPCWSTR BURN_BUNDLE_LAYOUT_DIRECTORY = L"WixBundleLayoutDirectory"; | ||
35 | const LPCWSTR BURN_BUNDLE_ACTION = L"WixBundleAction"; | ||
36 | const LPCWSTR BURN_BUNDLE_ACTIVE_PARENT = L"WixBundleActiveParent"; | ||
37 | const LPCWSTR BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER = L"WixBundleExecutePackageCacheFolder"; | ||
38 | const LPCWSTR BURN_BUNDLE_EXECUTE_PACKAGE_ACTION = L"WixBundleExecutePackageAction"; | ||
39 | const LPCWSTR BURN_BUNDLE_FORCED_RESTART_PACKAGE = L"WixBundleForcedRestartPackage"; | ||
40 | const LPCWSTR BURN_BUNDLE_INSTALLED = L"WixBundleInstalled"; | ||
41 | const LPCWSTR BURN_BUNDLE_ELEVATED = L"WixBundleElevated"; | ||
42 | const LPCWSTR BURN_BUNDLE_PROVIDER_KEY = L"WixBundleProviderKey"; | ||
43 | const LPCWSTR BURN_BUNDLE_MANUFACTURER = L"WixBundleManufacturer"; | ||
44 | const LPCWSTR BURN_BUNDLE_SOURCE_PROCESS_PATH = L"WixBundleSourceProcessPath"; | ||
45 | const LPCWSTR BURN_BUNDLE_SOURCE_PROCESS_FOLDER = L"WixBundleSourceProcessFolder"; | ||
46 | const LPCWSTR BURN_BUNDLE_TAG = L"WixBundleTag"; | ||
47 | const LPCWSTR BURN_BUNDLE_UILEVEL = L"WixBundleUILevel"; | ||
48 | const LPCWSTR BURN_BUNDLE_VERSION = L"WixBundleVersion"; | ||
49 | |||
50 | // The following constants must stay in sync with src\wix\Binder.cs | ||
51 | const LPCWSTR BURN_BUNDLE_NAME = L"WixBundleName"; | ||
52 | const LPCWSTR BURN_BUNDLE_ORIGINAL_SOURCE = L"WixBundleOriginalSource"; | ||
53 | const LPCWSTR BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER = L"WixBundleOriginalSourceFolder"; | ||
54 | const LPCWSTR BURN_BUNDLE_LAST_USED_SOURCE = L"WixBundleLastUsedSource"; | ||
55 | |||
56 | |||
57 | // enums | ||
58 | |||
59 | enum 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 | |||
68 | enum 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 | |||
78 | typedef 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 | |||
139 | HRESULT CoreInitialize( | ||
140 | __in BURN_ENGINE_STATE* pEngineState | ||
141 | ); | ||
142 | HRESULT CoreSerializeEngineState( | ||
143 | __in BURN_ENGINE_STATE* pEngineState, | ||
144 | __inout BYTE** ppbBuffer, | ||
145 | __inout SIZE_T* piBuffer | ||
146 | ); | ||
147 | HRESULT 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 | // ); | ||
155 | HRESULT CoreDetect( | ||
156 | __in BURN_ENGINE_STATE* pEngineState, | ||
157 | __in_opt HWND hwndParent | ||
158 | ); | ||
159 | HRESULT CorePlan( | ||
160 | __in BURN_ENGINE_STATE* pEngineState, | ||
161 | __in BOOTSTRAPPER_ACTION action | ||
162 | ); | ||
163 | HRESULT CoreElevate( | ||
164 | __in BURN_ENGINE_STATE* pEngineState, | ||
165 | __in_opt HWND hwndParent | ||
166 | ); | ||
167 | HRESULT CoreApply( | ||
168 | __in BURN_ENGINE_STATE* pEngineState, | ||
169 | __in_opt HWND hwndParent | ||
170 | ); | ||
171 | HRESULT CoreLaunchApprovedExe( | ||
172 | __in BURN_ENGINE_STATE* pEngineState, | ||
173 | __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe | ||
174 | ); | ||
175 | HRESULT CoreQuit( | ||
176 | __in BURN_ENGINE_STATE* pEngineState, | ||
177 | __in int nExitCode | ||
178 | ); | ||
179 | HRESULT CoreSaveEngineState( | ||
180 | __in BURN_ENGINE_STATE* pEngineState | ||
181 | ); | ||
182 | LPCWSTR CoreRelationTypeToCommandLineString( | ||
183 | __in BOOTSTRAPPER_RELATION_TYPE relationType | ||
184 | ); | ||
185 | HRESULT 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 | ); | ||
197 | HRESULT CoreAppendFileHandleAttachedToCommandLine( | ||
198 | __in HANDLE hFileWithAttachedContainer, | ||
199 | __out HANDLE* phExecutableFile, | ||
200 | __deref_inout_z LPWSTR* psczCommandLine | ||
201 | ); | ||
202 | HRESULT 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 | ||
8 | const LPCWSTR vcszIgnoreDependenciesDelim = L";"; | ||
9 | |||
10 | |||
11 | // internal function declarations | ||
12 | |||
13 | static HRESULT SplitIgnoreDependencies( | ||
14 | __in_z LPCWSTR wzIgnoreDependencies, | ||
15 | __deref_inout_ecount_opt(*pcDependencies) DEPENDENCY** prgDependencies, | ||
16 | __inout LPUINT pcDependencies | ||
17 | ); | ||
18 | |||
19 | static HRESULT JoinIgnoreDependencies( | ||
20 | __out_z LPWSTR* psczIgnoreDependencies, | ||
21 | __in_ecount(cDependencies) const DEPENDENCY* rgDependencies, | ||
22 | __in UINT cDependencies | ||
23 | ); | ||
24 | |||
25 | static HRESULT GetIgnoredDependents( | ||
26 | __in const BURN_PACKAGE* pPackage, | ||
27 | __in const BURN_PLAN* pPlan, | ||
28 | __deref_inout STRINGDICT_HANDLE* psdIgnoredDependents | ||
29 | ); | ||
30 | |||
31 | static 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 | |||
38 | static 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 | |||
45 | static 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 | |||
53 | static HRESULT RegisterPackageProvider( | ||
54 | __in const BURN_PACKAGE* pPackage | ||
55 | ); | ||
56 | |||
57 | static void UnregisterPackageProvider( | ||
58 | __in const BURN_PACKAGE* pPackage | ||
59 | ); | ||
60 | |||
61 | static HRESULT RegisterPackageDependency( | ||
62 | __in BOOL fPerMachine, | ||
63 | __in const BURN_PACKAGE* pPackage, | ||
64 | __in_z LPCWSTR wzDependentProviderKey | ||
65 | ); | ||
66 | |||
67 | static void UnregisterPackageDependency( | ||
68 | __in BOOL fPerMachine, | ||
69 | __in const BURN_PACKAGE* pPackage, | ||
70 | __in_z LPCWSTR wzDependentProviderKey | ||
71 | ); | ||
72 | |||
73 | static BOOL PackageProviderExists( | ||
74 | __in const BURN_PACKAGE* pPackage | ||
75 | ); | ||
76 | |||
77 | |||
78 | // functions | ||
79 | |||
80 | extern "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 | |||
90 | extern "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 | |||
163 | LExit: | ||
164 | ReleaseObject(pixnNode); | ||
165 | ReleaseObject(pixnNodes); | ||
166 | |||
167 | return hr; | ||
168 | } | ||
169 | |||
170 | extern "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 | |||
211 | LExit: | ||
212 | return hr; | ||
213 | } | ||
214 | |||
215 | extern "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 | |||
235 | LExit: | ||
236 | return hr; | ||
237 | } | ||
238 | |||
239 | extern "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 | |||
258 | LExit: | ||
259 | return hr; | ||
260 | } | ||
261 | |||
262 | extern "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 | |||
276 | LExit: | ||
277 | return hr; | ||
278 | } | ||
279 | |||
280 | extern "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 | |||
303 | LExit: | ||
304 | return hr; | ||
305 | } | ||
306 | |||
307 | extern "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 | |||
318 | extern "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 | |||
437 | LExit: | ||
438 | ReleaseDependencyArray(rgDependents, cDependents); | ||
439 | ReleaseDict(sdIgnoredDependents); | ||
440 | |||
441 | return hr; | ||
442 | } | ||
443 | |||
444 | extern "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 | |||
501 | LExit: | ||
502 | return hr; | ||
503 | } | ||
504 | |||
505 | extern "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 | |||
528 | LExit: | ||
529 | return hr; | ||
530 | } | ||
531 | |||
532 | extern "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 | |||
552 | LExit: | ||
553 | if (!pPackage->fVital) | ||
554 | { | ||
555 | hr = S_OK; | ||
556 | } | ||
557 | |||
558 | return hr; | ||
559 | } | ||
560 | |||
561 | extern "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 | |||
582 | LExit: | ||
583 | if (!pPackage->fVital) | ||
584 | { | ||
585 | hr = S_OK; | ||
586 | } | ||
587 | |||
588 | return hr; | ||
589 | } | ||
590 | |||
591 | extern "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 | |||
607 | LExit: | ||
608 | ReleaseStr(sczVersion); | ||
609 | |||
610 | return hr; | ||
611 | } | ||
612 | |||
613 | extern "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 | |||
637 | LExit: | ||
638 | return hr; | ||
639 | } | ||
640 | |||
641 | extern "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 | *********************************************************************/ | ||
666 | static 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 | |||
698 | LExit: | ||
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 | *********************************************************************/ | ||
709 | static 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 | |||
753 | LExit: | ||
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 | *********************************************************************/ | ||
766 | static 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 | |||
805 | LExit: | ||
806 | ReleaseStr(sczIgnoreDependencies); | ||
807 | |||
808 | return hr; | ||
809 | } | ||
810 | |||
811 | /******************************************************************** | ||
812 | GetProviderId - Gets the ID of the package given the provider key. | ||
813 | |||
814 | *********************************************************************/ | ||
815 | static 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 | |||
854 | LExit: | ||
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 | *********************************************************************/ | ||
865 | static 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 | *********************************************************************/ | ||
968 | static 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 | |||
1025 | LExit: | ||
1026 | return hr; | ||
1027 | } | ||
1028 | |||
1029 | static 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 | |||
1062 | LExit: | ||
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 | *********************************************************************/ | ||
1077 | static 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 | *********************************************************************/ | ||
1111 | static 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 | |||
1148 | LExit: | ||
1149 | return hr; | ||
1150 | } | ||
1151 | |||
1152 | /******************************************************************** | ||
1153 | UnregisterPackageDependency - Unregisters the provider key | ||
1154 | as a dependent of a package. | ||
1155 | |||
1156 | *********************************************************************/ | ||
1157 | static 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 | *********************************************************************/ | ||
1197 | static 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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | // constants | ||
10 | |||
11 | const 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 | *********************************************************************/ | ||
21 | void 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 | *********************************************************************/ | ||
30 | HRESULT 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 | *********************************************************************/ | ||
41 | HRESULT 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 | *********************************************************************/ | ||
53 | HRESULT DependencyDetectProviderKeyBundleId( | ||
54 | __in BURN_REGISTRATION* pRegistration | ||
55 | ); | ||
56 | |||
57 | /******************************************************************** | ||
58 | DependencyPlanInitialize - Initializes the plan. | ||
59 | |||
60 | *********************************************************************/ | ||
61 | HRESULT 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 | *********************************************************************/ | ||
71 | HRESULT 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 | *********************************************************************/ | ||
81 | HRESULT 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 | *********************************************************************/ | ||
91 | BOOL 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 | *********************************************************************/ | ||
101 | HRESULT 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 | *********************************************************************/ | ||
112 | HRESULT 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 | *********************************************************************/ | ||
123 | HRESULT 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 | *********************************************************************/ | ||
133 | HRESULT 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 | *********************************************************************/ | ||
142 | HRESULT DependencyExecutePackageDependencyAction( | ||
143 | __in BOOL fPerMachine, | ||
144 | __in const BURN_EXECUTE_ACTION* pAction | ||
145 | ); | ||
146 | |||
147 | /******************************************************************** | ||
148 | DependencyRegisterBundle - Registers the bundle dependency provider. | ||
149 | |||
150 | *********************************************************************/ | ||
151 | HRESULT 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 | *********************************************************************/ | ||
160 | HRESULT 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 | *********************************************************************/ | ||
170 | void 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 | |||
5 | typedef struct _DETECT_AUTHENTICATION_REQUIRED_DATA | ||
6 | { | ||
7 | BURN_USER_EXPERIENCE* pUX; | ||
8 | LPCWSTR wzPackageOrContainerId; | ||
9 | } DETECT_AUTHENTICATION_REQUIRED_DATA; | ||
10 | |||
11 | // internal function definitions | ||
12 | static HRESULT AuthenticationRequired( | ||
13 | __in LPVOID pData, | ||
14 | __in HINTERNET hUrl, | ||
15 | __in long lHttpCode, | ||
16 | __out BOOL* pfRetrySend, | ||
17 | __out BOOL* pfRetry | ||
18 | ); | ||
19 | |||
20 | static HRESULT DetectAtomFeedUpdate( | ||
21 | __in_z LPCWSTR wzBundleId, | ||
22 | __in BURN_USER_EXPERIENCE* pUX, | ||
23 | __in BURN_UPDATE* pUpdate | ||
24 | ); | ||
25 | |||
26 | static 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 | |||
35 | extern "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 | |||
84 | extern "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 | |||
145 | LExit: | ||
146 | return hr; | ||
147 | } | ||
148 | |||
149 | extern "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 | |||
211 | LExit: | ||
212 | return hr; | ||
213 | } | ||
214 | |||
215 | extern "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 | |||
242 | LExit: | ||
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 | |||
255 | static 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 | |||
307 | LExit: | ||
308 | ReleaseStr(sczError); | ||
309 | |||
310 | return hr; | ||
311 | } | ||
312 | |||
313 | static 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 | |||
350 | LExit: | ||
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 | |||
370 | static 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 | |||
419 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // constants | ||
11 | |||
12 | |||
13 | // structs | ||
14 | |||
15 | |||
16 | // functions | ||
17 | |||
18 | void DetectReset( | ||
19 | __in BURN_REGISTRATION* pRegistration, | ||
20 | __in BURN_PACKAGES* pPackages | ||
21 | ); | ||
22 | |||
23 | HRESULT DetectForwardCompatibleBundle( | ||
24 | __in BURN_USER_EXPERIENCE* pUX, | ||
25 | __in BOOTSTRAPPER_COMMAND* pCommand, | ||
26 | __in BURN_REGISTRATION* pRegistration | ||
27 | ); | ||
28 | |||
29 | HRESULT 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 | |||
36 | HRESULT 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 | |||
6 | const DWORD BURN_TIMEOUT = 5 * 60 * 1000; // TODO: is 5 minutes good? | ||
7 | |||
8 | typedef 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 | |||
47 | typedef struct _BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT | ||
48 | { | ||
49 | PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler; | ||
50 | LPVOID pvContext; | ||
51 | } BURN_ELEVATION_GENERIC_MESSAGE_CONTEXT; | ||
52 | |||
53 | typedef struct _BURN_ELEVATION_MSI_MESSAGE_CONTEXT | ||
54 | { | ||
55 | PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler; | ||
56 | LPVOID pvContext; | ||
57 | } BURN_ELEVATION_MSI_MESSAGE_CONTEXT; | ||
58 | |||
59 | typedef struct _BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT | ||
60 | { | ||
61 | DWORD dwProcessId; | ||
62 | } BURN_ELEVATION_LAUNCH_APPROVED_EXE_MESSAGE_CONTEXT; | ||
63 | |||
64 | typedef 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 | |||
85 | static HRESULT OnMsiBeginTransaction( | ||
86 | __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext | ||
87 | ); | ||
88 | static HRESULT OnMsiCommitTransaction( | ||
89 | __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext | ||
90 | ); | ||
91 | static HRESULT OnMsiRollbackTransaction( | ||
92 | __in BURN_ELEVATION_CHILD_MESSAGE_CONTEXT* pContext | ||
93 | ); | ||
94 | |||
95 | static DWORD WINAPI ElevatedChildCacheThreadProc( | ||
96 | __in LPVOID lpThreadParameter | ||
97 | ); | ||
98 | static HRESULT WaitForElevatedChildCacheThread( | ||
99 | __in HANDLE hCacheThread, | ||
100 | __in DWORD dwExpectedExitCode | ||
101 | ); | ||
102 | static HRESULT OnLoadCompatiblePackage( | ||
103 | __in BURN_PACKAGES* pPackages, | ||
104 | __in BYTE* pbData, | ||
105 | __in DWORD cbData | ||
106 | ); | ||
107 | static HRESULT ProcessGenericExecuteMessages( | ||
108 | __in BURN_PIPE_MESSAGE* pMsg, | ||
109 | __in_opt LPVOID pvContext, | ||
110 | __out DWORD* pdwResult | ||
111 | ); | ||
112 | static HRESULT ProcessMsiPackageMessages( | ||
113 | __in BURN_PIPE_MESSAGE* pMsg, | ||
114 | __in_opt LPVOID pvContext, | ||
115 | __out DWORD* pdwResult | ||
116 | ); | ||
117 | static HRESULT ProcessLaunchApprovedExeMessages( | ||
118 | __in BURN_PIPE_MESSAGE* pMsg, | ||
119 | __in_opt LPVOID pvContext, | ||
120 | __out DWORD* pdwResult | ||
121 | ); | ||
122 | static HRESULT ProcessElevatedChildMessage( | ||
123 | __in BURN_PIPE_MESSAGE* pMsg, | ||
124 | __in_opt LPVOID pvContext, | ||
125 | __out DWORD* pdwResult | ||
126 | ); | ||
127 | static HRESULT ProcessElevatedChildCacheMessage( | ||
128 | __in BURN_PIPE_MESSAGE* pMsg, | ||
129 | __in_opt LPVOID pvContext, | ||
130 | __out DWORD* pdwResult | ||
131 | ); | ||
132 | static HRESULT ProcessResult( | ||
133 | __in DWORD dwResult, | ||
134 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
135 | ); | ||
136 | static 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 | ); | ||
144 | static HRESULT OnApplyUninitialize( | ||
145 | __in HANDLE* phLock | ||
146 | ); | ||
147 | static 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 | ); | ||
154 | static HRESULT OnSessionResume( | ||
155 | __in BURN_REGISTRATION* pRegistration, | ||
156 | __in BURN_VARIABLES* pVariables, | ||
157 | __in BYTE* pbData, | ||
158 | __in DWORD cbData | ||
159 | ); | ||
160 | static HRESULT OnSessionEnd( | ||
161 | __in BURN_REGISTRATION* pRegistration, | ||
162 | __in BYTE* pbData, | ||
163 | __in DWORD cbData | ||
164 | ); | ||
165 | static HRESULT OnSaveState( | ||
166 | __in BURN_REGISTRATION* pRegistration, | ||
167 | __in BYTE* pbData, | ||
168 | __in DWORD cbData | ||
169 | ); | ||
170 | static HRESULT OnLayoutBundle( | ||
171 | __in_z LPCWSTR wzExecutableName, | ||
172 | __in BYTE* pbData, | ||
173 | __in DWORD cbData | ||
174 | ); | ||
175 | static 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 | ); | ||
182 | static void OnCacheCleanup( | ||
183 | __in_z LPCWSTR wzBundleId | ||
184 | ); | ||
185 | static HRESULT OnProcessDependentRegistration( | ||
186 | __in const BURN_REGISTRATION* pRegistration, | ||
187 | __in BYTE* pbData, | ||
188 | __in DWORD cbData | ||
189 | ); | ||
190 | static 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 | ); | ||
198 | static 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 | ); | ||
205 | static 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 | ); | ||
212 | static 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 | ); | ||
219 | static HRESULT OnExecutePackageProviderAction( | ||
220 | __in BURN_PACKAGES* pPackages, | ||
221 | __in BURN_RELATED_BUNDLES* pRelatedBundles, | ||
222 | __in BYTE* pbData, | ||
223 | __in DWORD cbData | ||
224 | ); | ||
225 | static HRESULT OnExecutePackageDependencyAction( | ||
226 | __in BURN_PACKAGES* pPackages, | ||
227 | __in BURN_RELATED_BUNDLES* pRelatedBundles, | ||
228 | __in BYTE* pbData, | ||
229 | __in DWORD cbData | ||
230 | ); | ||
231 | static int GenericExecuteMessageHandler( | ||
232 | __in GENERIC_EXECUTE_MESSAGE* pMessage, | ||
233 | __in LPVOID pvContext | ||
234 | ); | ||
235 | static int MsiExecuteMessageHandler( | ||
236 | __in WIU_MSI_EXECUTE_MESSAGE* pMessage, | ||
237 | __in_opt LPVOID pvContext | ||
238 | ); | ||
239 | static HRESULT OnCleanPackage( | ||
240 | __in BURN_PACKAGES* pPackages, | ||
241 | __in BYTE* pbData, | ||
242 | __in DWORD cbData | ||
243 | ); | ||
244 | static 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 | |||
255 | extern "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 | |||
307 | LExit: | ||
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 | |||
320 | extern "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 | |||
352 | LExit: | ||
353 | ReleaseBuffer(pbData); | ||
354 | |||
355 | return hr; | ||
356 | } | ||
357 | |||
358 | extern "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 | |||
373 | LExit: | ||
374 | ReleaseBuffer(pbData); | ||
375 | |||
376 | return hr; | ||
377 | } | ||
378 | |||
379 | /******************************************************************* | ||
380 | ElevationSessionBegin - | ||
381 | |||
382 | *******************************************************************/ | ||
383 | extern "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 | |||
427 | LExit: | ||
428 | ReleaseBuffer(pbData); | ||
429 | |||
430 | return hr; | ||
431 | } | ||
432 | |||
433 | /******************************************************************* | ||
434 | ElevationSessionResume - | ||
435 | |||
436 | *******************************************************************/ | ||
437 | extern "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 | |||
465 | LExit: | ||
466 | ReleaseBuffer(pbData); | ||
467 | |||
468 | return hr; | ||
469 | } | ||
470 | |||
471 | /******************************************************************* | ||
472 | ElevationSessionEnd - | ||
473 | |||
474 | *******************************************************************/ | ||
475 | extern "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 | |||
503 | LExit: | ||
504 | ReleaseBuffer(pbData); | ||
505 | |||
506 | return hr; | ||
507 | } | ||
508 | |||
509 | /******************************************************************* | ||
510 | ElevationSaveState - | ||
511 | |||
512 | *******************************************************************/ | ||
513 | HRESULT 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 | |||
528 | LExit: | ||
529 | return hr; | ||
530 | } | ||
531 | |||
532 | /******************************************************************* | ||
533 | ElevationLayoutBundle - | ||
534 | |||
535 | *******************************************************************/ | ||
536 | extern "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 | |||
560 | LExit: | ||
561 | ReleaseBuffer(pbData); | ||
562 | |||
563 | return hr; | ||
564 | } | ||
565 | |||
566 | /******************************************************************* | ||
567 | ElevationCacheOrLayoutPayload - | ||
568 | |||
569 | *******************************************************************/ | ||
570 | extern "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 | |||
610 | LExit: | ||
611 | ReleaseBuffer(pbData); | ||
612 | |||
613 | return hr; | ||
614 | } | ||
615 | |||
616 | /******************************************************************* | ||
617 | ElevationCacheCleanup - | ||
618 | |||
619 | *******************************************************************/ | ||
620 | extern "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 | |||
633 | LExit: | ||
634 | return hr; | ||
635 | } | ||
636 | |||
637 | extern "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 | |||
663 | LExit: | ||
664 | ReleaseBuffer(pbData); | ||
665 | |||
666 | return hr; | ||
667 | } | ||
668 | |||
669 | /******************************************************************* | ||
670 | ElevationExecuteExePackage - | ||
671 | |||
672 | *******************************************************************/ | ||
673 | extern "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 | |||
717 | LExit: | ||
718 | ReleaseBuffer(pbData); | ||
719 | |||
720 | return hr; | ||
721 | } | ||
722 | |||
723 | extern "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 | |||
740 | LExit: | ||
741 | return hr; | ||
742 | } | ||
743 | |||
744 | extern "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 | |||
761 | LExit: | ||
762 | return hr; | ||
763 | } | ||
764 | |||
765 | extern "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 | |||
782 | LExit: | ||
783 | return hr; | ||
784 | } | ||
785 | |||
786 | |||
787 | |||
788 | /******************************************************************* | ||
789 | ElevationExecuteMsiPackage - | ||
790 | |||
791 | *******************************************************************/ | ||
792 | extern "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 | |||
856 | LExit: | ||
857 | ReleaseBuffer(pbData); | ||
858 | |||
859 | return hr; | ||
860 | } | ||
861 | |||
862 | /******************************************************************* | ||
863 | ElevationExecuteMspPackage - | ||
864 | |||
865 | *******************************************************************/ | ||
866 | extern "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 | |||
929 | LExit: | ||
930 | ReleaseBuffer(pbData); | ||
931 | |||
932 | return hr; | ||
933 | } | ||
934 | |||
935 | /******************************************************************* | ||
936 | ElevationExecuteMsuPackage - | ||
937 | |||
938 | *******************************************************************/ | ||
939 | extern "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 | |||
980 | LExit: | ||
981 | ReleaseBuffer(pbData); | ||
982 | |||
983 | return hr; | ||
984 | } | ||
985 | |||
986 | extern "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 | |||
1011 | LExit: | ||
1012 | ReleaseBuffer(pbData); | ||
1013 | |||
1014 | return hr; | ||
1015 | } | ||
1016 | |||
1017 | extern "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 | |||
1045 | LExit: | ||
1046 | ReleaseBuffer(pbData); | ||
1047 | |||
1048 | return hr; | ||
1049 | } | ||
1050 | |||
1051 | /******************************************************************* | ||
1052 | ElevationLoadCompatiblePackageAction - Load compatible package | ||
1053 | information from the referenced package. | ||
1054 | |||
1055 | *******************************************************************/ | ||
1056 | extern "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 | |||
1084 | LExit: | ||
1085 | ReleaseBuffer(pbData); | ||
1086 | |||
1087 | return hr; | ||
1088 | } | ||
1089 | |||
1090 | /******************************************************************* | ||
1091 | ElevationCleanPackage - | ||
1092 | |||
1093 | *******************************************************************/ | ||
1094 | extern "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 | |||
1114 | LExit: | ||
1115 | ReleaseBuffer(pbData); | ||
1116 | |||
1117 | return hr; | ||
1118 | } | ||
1119 | |||
1120 | extern "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 | |||
1149 | LExit: | ||
1150 | ReleaseBuffer(pbData); | ||
1151 | |||
1152 | return hr; | ||
1153 | } | ||
1154 | |||
1155 | /******************************************************************* | ||
1156 | ElevationChildPumpMessages - | ||
1157 | |||
1158 | *******************************************************************/ | ||
1159 | extern "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 | |||
1216 | LExit: | ||
1217 | ReleaseHandle(hCacheThread); | ||
1218 | |||
1219 | return hr; | ||
1220 | } | ||
1221 | |||
1222 | extern "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 | |||
1233 | LExit: | ||
1234 | return hr; | ||
1235 | } | ||
1236 | |||
1237 | // internal function definitions | ||
1238 | |||
1239 | static 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 | |||
1263 | LExit: | ||
1264 | if (fComInitialized) | ||
1265 | { | ||
1266 | ::CoUninitialize(); | ||
1267 | } | ||
1268 | |||
1269 | return (DWORD)hr; | ||
1270 | } | ||
1271 | |||
1272 | static 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 | |||
1294 | LExit: | ||
1295 | return hr; | ||
1296 | } | ||
1297 | |||
1298 | static 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 | |||
1368 | LExit: | ||
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 | |||
1382 | static 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 | |||
1468 | LExit: | ||
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 | |||
1484 | static 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 | |||
1513 | LExit: | ||
1514 | return hr; | ||
1515 | } | ||
1516 | |||
1517 | static 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 | |||
1613 | LExit: | ||
1614 | return hr; | ||
1615 | } | ||
1616 | |||
1617 | static 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 | |||
1653 | LExit: | ||
1654 | return hr; | ||
1655 | } | ||
1656 | |||
1657 | static 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 | |||
1677 | static 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 | |||
1689 | LExit: | ||
1690 | return hr; | ||
1691 | } | ||
1692 | |||
1693 | static 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 | |||
1703 | LExit: | ||
1704 | pContext->hMsiTrns = NULL; | ||
1705 | pContext->hMsiTrnsEvent = NULL; | ||
1706 | return hr; | ||
1707 | } | ||
1708 | |||
1709 | static 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 | |||
1718 | LExit: | ||
1719 | pContext->hMsiTrns = NULL; | ||
1720 | pContext->hMsiTrnsEvent = NULL; | ||
1721 | return hr; | ||
1722 | } | ||
1723 | |||
1724 | static 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 | |||
1814 | LExit: | ||
1815 | ReleaseStr(sczBundleName); | ||
1816 | return hr; | ||
1817 | } | ||
1818 | |||
1819 | static 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 | |||
1837 | static 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 | |||
1878 | LExit: | ||
1879 | ReleaseStr(sczEngineWorkingPath); | ||
1880 | |||
1881 | return hr; | ||
1882 | } | ||
1883 | |||
1884 | static 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 | |||
1908 | LExit: | ||
1909 | return hr; | ||
1910 | } | ||
1911 | |||
1912 | static 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 | |||
1938 | LExit: | ||
1939 | return hr; | ||
1940 | } | ||
1941 | |||
1942 | static 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 | |||
1954 | LExit: | ||
1955 | return hr; | ||
1956 | } | ||
1957 | |||
1958 | static 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 | |||
1980 | LExit: | ||
1981 | ReleaseStr(sczUnverifiedPath); | ||
1982 | ReleaseStr(sczLayoutDirectory); | ||
1983 | |||
1984 | return hr; | ||
1985 | } | ||
1986 | |||
1987 | static 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 | |||
2072 | LExit: | ||
2073 | ReleaseStr(sczUnverifiedPath); | ||
2074 | ReleaseStr(sczLayoutDirectory); | ||
2075 | ReleaseStr(scz); | ||
2076 | |||
2077 | return hr; | ||
2078 | } | ||
2079 | |||
2080 | static void OnCacheCleanup( | ||
2081 | __in_z LPCWSTR wzBundleId | ||
2082 | ) | ||
2083 | { | ||
2084 | CacheCleanup(TRUE, wzBundleId); | ||
2085 | } | ||
2086 | |||
2087 | static 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 | |||
2111 | LExit: | ||
2112 | // TODO: do the right thing here. | ||
2113 | //DependencyUninitializeRegistrationAction(&action); | ||
2114 | ReleaseStr(action.sczDependentProviderKey); | ||
2115 | ReleaseStr(action.sczBundleId) | ||
2116 | |||
2117 | return hr; | ||
2118 | } | ||
2119 | |||
2120 | static 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 | |||
2184 | LExit: | ||
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 | |||
2205 | static 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 | |||
2278 | LExit: | ||
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 | |||
2297 | static 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 | |||
2370 | LExit: | ||
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 | |||
2389 | static 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 | |||
2430 | LExit: | ||
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 | |||
2449 | static 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 | |||
2482 | LExit: | ||
2483 | ReleaseStr(sczPackage); | ||
2484 | PlanUninitializeExecuteAction(&executeAction); | ||
2485 | |||
2486 | return hr; | ||
2487 | } | ||
2488 | |||
2489 | static 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 | |||
2525 | LExit: | ||
2526 | ReleaseStr(sczPackage); | ||
2527 | PlanUninitializeExecuteAction(&executeAction); | ||
2528 | |||
2529 | return hr; | ||
2530 | } | ||
2531 | |||
2532 | static 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 | |||
2569 | LExit: | ||
2570 | ReleaseStr(sczPackage); | ||
2571 | PlanUninitializeExecuteAction(&executeAction); | ||
2572 | |||
2573 | return hr; | ||
2574 | } | ||
2575 | |||
2576 | static 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 | |||
2630 | LExit: | ||
2631 | ReleaseBuffer(pbData); | ||
2632 | |||
2633 | return nResult; | ||
2634 | } | ||
2635 | |||
2636 | static 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 | |||
2712 | LExit: | ||
2713 | ReleaseBuffer(pbData); | ||
2714 | |||
2715 | return nResult; | ||
2716 | } | ||
2717 | |||
2718 | static 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 | |||
2740 | LExit: | ||
2741 | ReleaseStr(sczPackage); | ||
2742 | return hr; | ||
2743 | } | ||
2744 | |||
2745 | static 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 | |||
2810 | LExit: | ||
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 | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // Parent (per-user process) side functions. | ||
11 | HRESULT ElevationElevate( | ||
12 | __in BURN_ENGINE_STATE* pEngineState, | ||
13 | __in_opt HWND hwndParent | ||
14 | ); | ||
15 | HRESULT 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 | ); | ||
22 | HRESULT ElevationApplyUninitialize( | ||
23 | __in HANDLE hPipe | ||
24 | ); | ||
25 | HRESULT 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 | ); | ||
35 | HRESULT ElevationSessionResume( | ||
36 | __in HANDLE hPipe, | ||
37 | __in_z LPCWSTR wzResumeCommandLine, | ||
38 | __in BOOL fDisableResume, | ||
39 | __in BURN_VARIABLES* pVariables | ||
40 | ); | ||
41 | HRESULT 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 | ); | ||
47 | HRESULT ElevationSaveState( | ||
48 | __in HANDLE hPipe, | ||
49 | __in_bcount(cbBuffer) BYTE* pbBuffer, | ||
50 | __in SIZE_T cbBuffer | ||
51 | ); | ||
52 | HRESULT ElevationLayoutBundle( | ||
53 | __in HANDLE hPipe, | ||
54 | __in_z LPCWSTR wzLayoutDirectory, | ||
55 | __in_z LPCWSTR wzUnverifiedPath | ||
56 | ); | ||
57 | HRESULT 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 | ); | ||
66 | HRESULT ElevationCacheCleanup( | ||
67 | __in HANDLE hPipe | ||
68 | ); | ||
69 | HRESULT ElevationProcessDependentRegistration( | ||
70 | __in HANDLE hPipe, | ||
71 | __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction | ||
72 | ); | ||
73 | HRESULT 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 | ); | ||
82 | HRESULT 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 | ); | ||
92 | HRESULT 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 | ); | ||
102 | HRESULT 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 | ); | ||
111 | HRESULT ElevationExecutePackageProviderAction( | ||
112 | __in HANDLE hPipe, | ||
113 | __in BURN_EXECUTE_ACTION* pExecuteAction | ||
114 | ); | ||
115 | HRESULT ElevationExecutePackageDependencyAction( | ||
116 | __in HANDLE hPipe, | ||
117 | __in BURN_EXECUTE_ACTION* pExecuteAction | ||
118 | ); | ||
119 | HRESULT ElevationLoadCompatiblePackageAction( | ||
120 | __in HANDLE hPipe, | ||
121 | __in BURN_EXECUTE_ACTION* pExecuteAction | ||
122 | ); | ||
123 | HRESULT ElevationLaunchElevatedChild( | ||
124 | __in HANDLE hPipe, | ||
125 | __in BURN_PACKAGE* pPackage, | ||
126 | __in LPCWSTR wzPipeName, | ||
127 | __in LPCWSTR wzPipeToken, | ||
128 | __out DWORD* pdwChildPid | ||
129 | ); | ||
130 | HRESULT ElevationCleanPackage( | ||
131 | __in HANDLE hPipe, | ||
132 | __in BURN_PACKAGE* pPackage | ||
133 | ); | ||
134 | HRESULT 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. | ||
141 | HRESULT 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 | ); | ||
157 | HRESULT ElevationChildResumeAutomaticUpdates(); | ||
158 | |||
159 | |||
160 | HRESULT ElevationMsiBeginTransaction( | ||
161 | __in HANDLE hPipe, | ||
162 | __in_opt HWND hwndParent, | ||
163 | __in LPVOID pvContext | ||
164 | ); | ||
165 | HRESULT ElevationMsiCommitTransaction( | ||
166 | __in HANDLE hPipe, | ||
167 | __in_opt HWND hwndParent, | ||
168 | __in LPVOID pvContext | ||
169 | ); | ||
170 | HRESULT 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 | |||
8 | struct BURN_EMBEDDED_CALLBACK_CONTEXT | ||
9 | { | ||
10 | PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler; | ||
11 | LPVOID pvContext; | ||
12 | }; | ||
13 | |||
14 | // internal function declarations | ||
15 | |||
16 | static HRESULT ProcessEmbeddedMessages( | ||
17 | __in BURN_PIPE_MESSAGE* pMsg, | ||
18 | __in_opt LPVOID pvContext, | ||
19 | __out DWORD* pdwResult | ||
20 | ); | ||
21 | static 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 | ); | ||
28 | static 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 | *******************************************************************/ | ||
42 | extern "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 | |||
93 | LExit: | ||
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 | |||
107 | static 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 | |||
137 | LExit: | ||
138 | return hr; | ||
139 | } | ||
140 | |||
141 | static 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 | |||
169 | LExit: | ||
170 | ReleaseStr(sczMessage); | ||
171 | |||
172 | return hr; | ||
173 | } | ||
174 | |||
175 | static 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 | |||
195 | LExit: | ||
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 | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | typedef 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 | |||
17 | HRESULT 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 | |||
8 | const DWORD RESTART_RETRIES = 10; | ||
9 | |||
10 | // internal function declarations | ||
11 | |||
12 | static HRESULT InitializeEngineState( | ||
13 | __in BURN_ENGINE_STATE* pEngineState, | ||
14 | __in HANDLE hEngineFile | ||
15 | ); | ||
16 | static void UninitializeEngineState( | ||
17 | __in BURN_ENGINE_STATE* pEngineState | ||
18 | ); | ||
19 | static HRESULT RunUntrusted( | ||
20 | __in LPCWSTR wzCommandLine, | ||
21 | __in BURN_ENGINE_STATE* pEngineState | ||
22 | ); | ||
23 | static HRESULT RunNormal( | ||
24 | __in HINSTANCE hInstance, | ||
25 | __in BURN_ENGINE_STATE* pEngineState | ||
26 | ); | ||
27 | static HRESULT RunElevated( | ||
28 | __in HINSTANCE hInstance, | ||
29 | __in LPCWSTR wzCommandLine, | ||
30 | __in BURN_ENGINE_STATE* pEngineState | ||
31 | ); | ||
32 | static HRESULT RunEmbedded( | ||
33 | __in HINSTANCE hInstance, | ||
34 | __in BURN_ENGINE_STATE* pEngineState | ||
35 | ); | ||
36 | static HRESULT RunRunOnce( | ||
37 | __in const BURN_REGISTRATION* pRegistration, | ||
38 | __in int nCmdShow | ||
39 | ); | ||
40 | static HRESULT RunApplication( | ||
41 | __in BURN_ENGINE_STATE* pEngineState, | ||
42 | __out BOOL* pfReloadApp | ||
43 | ); | ||
44 | static HRESULT ProcessMessage( | ||
45 | __in BURN_ENGINE_STATE* pEngineState, | ||
46 | __in const MSG* pmsg | ||
47 | ); | ||
48 | static HRESULT DAPI RedirectLoggingOverPipe( | ||
49 | __in_z LPCSTR szString, | ||
50 | __in_opt LPVOID pvContext | ||
51 | ); | ||
52 | static HRESULT Restart(); | ||
53 | |||
54 | |||
55 | // function definitions | ||
56 | |||
57 | extern "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 | |||
80 | extern "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 | |||
201 | LExit: | ||
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 | |||
283 | static 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 | |||
332 | LExit: | ||
333 | return hr; | ||
334 | } | ||
335 | |||
336 | static 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 | |||
386 | static 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 | |||
472 | LExit: | ||
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 | |||
485 | static 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 | |||
546 | LExit: | ||
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 | |||
569 | static 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 | |||
609 | LExit: | ||
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 | |||
629 | static 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 | |||
653 | LExit: | ||
654 | return hr; | ||
655 | } | ||
656 | |||
657 | static 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 | |||
677 | LExit: | ||
678 | ReleaseHandle(hProcess); | ||
679 | ReleaseStr(sczNewCommandLine); | ||
680 | ReleaseStr(sczBurnPath); | ||
681 | |||
682 | return hr; | ||
683 | } | ||
684 | |||
685 | static 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 | |||
728 | LExit: | ||
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 | |||
750 | static 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 | |||
787 | static 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 | |||
831 | LExit: | ||
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 | |||
843 | static 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 | |||
886 | LExit: | ||
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 | |||
4 | MessageIdTypedef=DWORD | ||
5 | |||
6 | LanguageNames=(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 | |||
32 | MessageId=1 | ||
33 | Severity=Success | ||
34 | SymbolicName=MSG_BURN_INFO | ||
35 | Language=English | ||
36 | Burn v%1!hs!, Windows v%2!d!.%3!d! (Build %4!d!: Service Pack %5!d!), path: %6!ls! | ||
37 | . | ||
38 | |||
39 | MessageId=2 | ||
40 | Severity=Warning | ||
41 | SymbolicName=MSG_BURN_UNKNOWN_PRIVATE_SWITCH | ||
42 | Language=English | ||
43 | Unknown burn internal command-line switch encountered: '%1!ls!'. | ||
44 | . | ||
45 | |||
46 | MessageId=3 | ||
47 | Severity=Success | ||
48 | SymbolicName=MSG_BURN_RUN_BY_RELATED_BUNDLE | ||
49 | Language=English | ||
50 | This bundle is being run by a related bundle as type '%1!hs!'. | ||
51 | . | ||
52 | |||
53 | MessageId=4 | ||
54 | Severity=Success | ||
55 | SymbolicName=MSG_BA_REQUESTED_RESTART | ||
56 | Language=English | ||
57 | Bootstrapper application requested restart at shutdown. Planned to restart already: %1!hs!. | ||
58 | . | ||
59 | |||
60 | MessageId=5 | ||
61 | Severity=Warning | ||
62 | SymbolicName=MSG_RESTARTING | ||
63 | Language=English | ||
64 | Restarting computer... | ||
65 | ======================================= | ||
66 | . | ||
67 | |||
68 | MessageId=6 | ||
69 | Severity=Success | ||
70 | SymbolicName=MSG_BA_REQUESTED_RELOAD | ||
71 | Language=English | ||
72 | Bootstrapper application requested to be reloaded. | ||
73 | . | ||
74 | |||
75 | MessageId=7 | ||
76 | Severity=Success | ||
77 | SymbolicName=MSG_EXITING | ||
78 | Language=English | ||
79 | Exit code: 0x%1!x!, restarting: %2!hs! | ||
80 | . | ||
81 | |||
82 | MessageId=8 | ||
83 | Severity=Warning | ||
84 | SymbolicName=MSG_RESTART_ABORTED | ||
85 | Language=English | ||
86 | Preventing requested restart because bundle is related: '%1!hs!'. Returning restart requested to parent bundle. | ||
87 | . | ||
88 | |||
89 | MessageId=9 | ||
90 | Severity=Success | ||
91 | SymbolicName=MSG_BURN_COMMAND_LINE | ||
92 | Language=English | ||
93 | Command Line: '%1!ls!' | ||
94 | . | ||
95 | |||
96 | MessageId=10 | ||
97 | Severity=Success | ||
98 | SymbolicName=MSG_LAUNCH_ELEVATED_ENGINE_STARTING | ||
99 | Language=English | ||
100 | Launching elevated engine process. | ||
101 | . | ||
102 | |||
103 | MessageId=11 | ||
104 | Severity=Success | ||
105 | SymbolicName=MSG_LAUNCH_ELEVATED_ENGINE_SUCCESS | ||
106 | Language=English | ||
107 | Launched elevated engine process. | ||
108 | . | ||
109 | |||
110 | MessageId=12 | ||
111 | Severity=Success | ||
112 | SymbolicName=MSG_CONNECT_TO_ELEVATED_ENGINE_SUCCESS | ||
113 | Language=English | ||
114 | Connected to elevated engine. | ||
115 | . | ||
116 | |||
117 | MessageId=51 | ||
118 | Severity=Error | ||
119 | SymbolicName=MSG_FAILED_PARSE_CONDITION | ||
120 | Language=English | ||
121 | Error %1!hs!. Failed to parse condition %2!ls!. Unexpected symbol at position %3!hs! | ||
122 | . | ||
123 | |||
124 | MessageId=52 | ||
125 | Severity=Success | ||
126 | SymbolicName=MSG_CONDITION_RESULT | ||
127 | Language=English | ||
128 | Condition '%1!ls!' evaluates to %2!hs!. | ||
129 | . | ||
130 | |||
131 | MessageId=53 | ||
132 | Severity=Error | ||
133 | SymbolicName=MSG_FAILED_CONDITION_CHECK | ||
134 | Language=English | ||
135 | Bundle global condition check didn't succeed - aborting without loading application. | ||
136 | . | ||
137 | |||
138 | MessageId=54 | ||
139 | Severity=Error | ||
140 | SymbolicName=MSG_PAYLOAD_FILE_NOT_PRESENT | ||
141 | Language=English | ||
142 | Failed to resolve source for file: %2!ls!, error: %1!ls!. | ||
143 | . | ||
144 | |||
145 | MessageId=55 | ||
146 | Severity=Warning | ||
147 | SymbolicName=MSG_CANNOT_LOAD_STATE_FILE | ||
148 | Language=English | ||
149 | Could not load or read state file: %2!ls!, error: 0x%1!x!. | ||
150 | . | ||
151 | |||
152 | MessageId=56 | ||
153 | Severity=Error | ||
154 | SymbolicName=MSG_USER_CANCELED | ||
155 | Language=English | ||
156 | Application canceled operation: %2!ls!, error: %1!ls! | ||
157 | . | ||
158 | |||
159 | MessageId=100 | ||
160 | Severity=Success | ||
161 | SymbolicName=MSG_DETECT_BEGIN | ||
162 | Language=English | ||
163 | Detect begin, %1!u! packages | ||
164 | . | ||
165 | |||
166 | MessageId=101 | ||
167 | Severity=Success | ||
168 | SymbolicName=MSG_DETECTED_PACKAGE | ||
169 | Language=English | ||
170 | Detected package: %1!ls!, state: %2!hs!, cached: %3!hs! | ||
171 | . | ||
172 | |||
173 | MessageId=102 | ||
174 | Severity=Success | ||
175 | SymbolicName=MSG_DETECTED_RELATED_BUNDLE | ||
176 | Language=English | ||
177 | Detected related bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!hs!, operation: %5!hs! | ||
178 | . | ||
179 | |||
180 | MessageId=103 | ||
181 | Severity=Success | ||
182 | SymbolicName=MSG_DETECTED_RELATED_PACKAGE | ||
183 | Language=English | ||
184 | Detected related package: %1!ls!, scope: %2!hs!, version: %3!hs!, language: %4!u! operation: %5!hs! | ||
185 | . | ||
186 | |||
187 | MessageId=104 | ||
188 | Severity=Success | ||
189 | SymbolicName=MSG_DETECTED_MSI_FEATURE | ||
190 | Language=English | ||
191 | Detected package: %1!ls!, feature: %2!ls!, state: %3!hs! | ||
192 | . | ||
193 | |||
194 | MessageId=105 | ||
195 | Severity=Success | ||
196 | SymbolicName=MSG_DETECTED_MSP_TARGET | ||
197 | Language=English | ||
198 | Detected package: %1!ls! target: %2!ls!, state: %3!hs! | ||
199 | . | ||
200 | |||
201 | MessageId=106 | ||
202 | Severity=Success | ||
203 | SymbolicName=MSG_DETECT_CALCULATE_PATCH_APPLICABILITY | ||
204 | Language=English | ||
205 | Calculating patch applicability for target product code: %1!ls!, context: %2!hs! | ||
206 | . | ||
207 | |||
208 | MessageId=107 | ||
209 | Severity=Success | ||
210 | SymbolicName=MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE | ||
211 | Language=English | ||
212 | Detected forward compatible bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!hs!, enabled: %5!hs! | ||
213 | . | ||
214 | |||
215 | MessageId=108 | ||
216 | Severity=Success | ||
217 | SymbolicName=MSG_DETECTED_COMPATIBLE_PACKAGE_FROM_PROVIDER | ||
218 | Language=English | ||
219 | Detected compatible package: %1!ls!, provider: %2!ls!, installed: %3!ls!, version: %4!ls!, chained: %5!ls! | ||
220 | . | ||
221 | |||
222 | MessageId=120 | ||
223 | Severity=Warning | ||
224 | SymbolicName=MSG_DETECT_PACKAGE_NOT_FULLY_CACHED | ||
225 | Language=English | ||
226 | Detected partially cached package: %1!ls!, invalid payload: %2!ls!, reason: 0x%3!x! | ||
227 | . | ||
228 | |||
229 | MessageId=121 | ||
230 | Severity=Warning | ||
231 | SymbolicName=MSG_DETECT_FAILED_CALCULATE_PATCH_APPLICABILITY | ||
232 | Language=English | ||
233 | Could not calculate patch applicability for target product code: %1!ls!, context: %2!hs!, reason: 0x%3!x! | ||
234 | . | ||
235 | |||
236 | MessageId=151 | ||
237 | Severity=Error | ||
238 | SymbolicName=MSG_FAILED_DETECT_PACKAGE | ||
239 | Language=English | ||
240 | Detect failed for package: %2!ls!, error: %1!ls! | ||
241 | . | ||
242 | |||
243 | MessageId=152 | ||
244 | Severity=Error | ||
245 | SymbolicName=MSG_FAILED_READ_RELATED_PACKAGE_LANGUAGE | ||
246 | Language=English | ||
247 | Detected related package: %2!ls!, but failed to read language: %3!hs!, error: 0x%1!x! | ||
248 | . | ||
249 | |||
250 | MessageId=170 | ||
251 | Severity=Warning | ||
252 | SymbolicName=MSG_DETECT_BAD_PRODUCT_CONFIGURATION | ||
253 | Language=English | ||
254 | Detected bad configuration for product: %1!ls! | ||
255 | . | ||
256 | |||
257 | MessageId=199 | ||
258 | Severity=Success | ||
259 | SymbolicName=MSG_DETECT_COMPLETE | ||
260 | Language=English | ||
261 | Detect complete, result: 0x%1!x! | ||
262 | . | ||
263 | |||
264 | MessageId=200 | ||
265 | Severity=Success | ||
266 | SymbolicName=MSG_PLAN_BEGIN | ||
267 | Language=English | ||
268 | Plan begin, %1!u! packages, action: %2!hs! | ||
269 | . | ||
270 | |||
271 | MessageId=201 | ||
272 | Severity=Success | ||
273 | SymbolicName=MSG_PLANNED_PACKAGE | ||
274 | Language=English | ||
275 | Planned 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 | |||
278 | MessageId=202 | ||
279 | Severity=Success | ||
280 | SymbolicName=MSG_PLANNED_BUNDLE_UX_CHANGED_REQUEST | ||
281 | Language=English | ||
282 | Planned bundle: %1!ls!, ba requested state: %2!hs! over default: %3!hs! | ||
283 | . | ||
284 | |||
285 | MessageId=203 | ||
286 | Severity=Success | ||
287 | SymbolicName=MSG_PLANNED_MSI_FEATURE | ||
288 | Language=English | ||
289 | Planned feature: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute action: %5!hs!, rollback action: %6!hs! | ||
290 | . | ||
291 | |||
292 | MessageId=204 | ||
293 | Severity=Success | ||
294 | SymbolicName=MSG_PLAN_MSI_FEATURES | ||
295 | Language=English | ||
296 | Plan %1!u! msi features for package: %2!ls! | ||
297 | . | ||
298 | |||
299 | MessageId=205 | ||
300 | Severity=Warning | ||
301 | SymbolicName=MSG_PLAN_SKIP_PATCH_ACTION | ||
302 | Language=English | ||
303 | Plan %5!hs! skipped patch: %1!ls!, action: %2!hs! because chained target package: %3!ls! being uninstalled | ||
304 | . | ||
305 | |||
306 | MessageId=206 | ||
307 | Severity=Warning | ||
308 | SymbolicName=MSG_PLAN_SKIP_SLIPSTREAM_ACTION | ||
309 | Language=English | ||
310 | Plan %5!hs! skipped patch: %1!ls!, action: %2!hs! because slipstreamed into chained target package: %3!ls!, action: %4!hs! | ||
311 | . | ||
312 | |||
313 | MessageId=207 | ||
314 | Severity=Success | ||
315 | SymbolicName=MSG_PLANNED_RELATED_BUNDLE | ||
316 | Language=English | ||
317 | Planned 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 | |||
320 | MessageId=208 | ||
321 | Severity=Warning | ||
322 | SymbolicName=MSG_PLAN_DISABLING_ROLLBACK_NO_CACHE | ||
323 | Language=English | ||
324 | Plan disabled rollback for package: %1!ls!, due to incomplete cache: %2!hs!, original rollback action: %3!hs! | ||
325 | . | ||
326 | |||
327 | MessageId=209 | ||
328 | Severity=Warning | ||
329 | SymbolicName=MSG_PLAN_SKIPPED_PROVIDER_KEY_REMOVAL | ||
330 | Language=English | ||
331 | Plan skipped removal of provider key: %1!ls! because it is registered to a different bundle: %2!ls! | ||
332 | . | ||
333 | |||
334 | MessageId=210 | ||
335 | Severity=Warning | ||
336 | SymbolicName=MSG_PLAN_SKIPPED_DUE_TO_DEPENDENTS | ||
337 | Language=English | ||
338 | Plan skipped due to %1!u! remaining dependents | ||
339 | . | ||
340 | |||
341 | MessageId=211 | ||
342 | Severity=Success | ||
343 | SymbolicName=MSG_PLANNED_UPGRADE_BUNDLE | ||
344 | Language=English | ||
345 | Planned upgrade bundle: %1!ls!, default requested: %2!hs!, ba requested: %3!hs!, execute: %4!hs!, rollback: %5!hs!, dependency: %6!hs! | ||
346 | . | ||
347 | |||
348 | MessageId=212 | ||
349 | Severity=Success | ||
350 | SymbolicName=MSG_PLANNED_FORWARD_COMPATIBLE_BUNDLE | ||
351 | Language=English | ||
352 | Planned forward compatible bundle: %1!ls!, default requested: %2!hs!, ba requested: %3!hs!, execute: %4!hs!, rollback: %5!hs!, dependency: %6!hs! | ||
353 | . | ||
354 | |||
355 | MessageId=213 | ||
356 | Severity=Success | ||
357 | SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_DEPENDENT | ||
358 | Language=English | ||
359 | Plan 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 | |||
362 | MessageId=214 | ||
363 | Severity=Success | ||
364 | SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_SCHEDULED | ||
365 | Language=English | ||
366 | Plan skipped related bundle: %1!ls!, type: %2!hs!, because it was previously scheduled. | ||
367 | . | ||
368 | |||
369 | MessageId=215 | ||
370 | Severity=Success | ||
371 | SymbolicName=MSG_PLANNED_ORPHAN_PACKAGE_FROM_PROVIDER | ||
372 | Language=English | ||
373 | Will remove orphan package: %1!ls!, installed: %2!ls!, chained: %3!ls! | ||
374 | . | ||
375 | |||
376 | MessageId=216 | ||
377 | Severity=Success | ||
378 | SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_EMBEDDED_BUNDLE_NEWER | ||
379 | Language=English | ||
380 | Plan 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 | |||
383 | MessageId=217 | ||
384 | Severity=Success | ||
385 | SymbolicName=MSG_PLAN_SKIPPED_DEPENDENT_BUNDLE_REPAIR | ||
386 | Language=English | ||
387 | Plan skipped dependent bundle repair: %1!ls!, type: %2!hs!, because no packages are being executed during this uninstall operation. | ||
388 | . | ||
389 | |||
390 | MessageId=299 | ||
391 | Severity=Success | ||
392 | SymbolicName=MSG_PLAN_COMPLETE | ||
393 | Language=English | ||
394 | Plan complete, result: 0x%1!x! | ||
395 | . | ||
396 | |||
397 | MessageId=300 | ||
398 | Severity=Success | ||
399 | SymbolicName=MSG_APPLY_BEGIN | ||
400 | Language=English | ||
401 | Apply begin | ||
402 | . | ||
403 | |||
404 | MessageId=301 | ||
405 | Severity=Success | ||
406 | SymbolicName=MSG_APPLYING_PACKAGE | ||
407 | Language=English | ||
408 | Applying %1!hs! package: %2!ls!, action: %3!hs!, path: %4!ls!, arguments: '%5!ls!' | ||
409 | . | ||
410 | |||
411 | MessageId=302 | ||
412 | Severity=Success | ||
413 | SymbolicName=MSG_ACQUIRED_PAYLOAD | ||
414 | Language=English | ||
415 | Acquired payload: %1!ls! to working path: %2!ls! from: %4!ls!. | ||
416 | . | ||
417 | |||
418 | MessageId=304 | ||
419 | Severity=Success | ||
420 | SymbolicName=MSG_VERIFIED_EXISTING_PAYLOAD | ||
421 | Language=English | ||
422 | Verified existing payload: %1!ls! at path: %2!ls!. | ||
423 | . | ||
424 | |||
425 | MessageId=305 | ||
426 | Severity=Success | ||
427 | SymbolicName=MSG_VERIFIED_ACQUIRED_PAYLOAD | ||
428 | Language=English | ||
429 | Verified acquired payload: %1!ls! at path: %2!ls!, %3!hs! to: %4!ls!. | ||
430 | . | ||
431 | |||
432 | MessageId=306 | ||
433 | Severity=Success | ||
434 | SymbolicName=MSG_APPLYING_PATCH_PACKAGE | ||
435 | Language=English | ||
436 | Applying package: %1!ls!, target: %5!ls!, action: %2!hs!, path: %3!ls!, arguments: '%4!ls!' | ||
437 | . | ||
438 | |||
439 | MessageId=307 | ||
440 | Severity=Warning | ||
441 | SymbolicName=MSG_ATTEMPTED_UNINSTALL_ABSENT_PACKAGE | ||
442 | Language=English | ||
443 | Attempted to uninstall absent package: %1!ls!. Continuing... | ||
444 | . | ||
445 | |||
446 | MessageId=308 | ||
447 | Severity=Warning | ||
448 | SymbolicName=MSG_FAILED_PAUSE_AU | ||
449 | Language=English | ||
450 | Automatic updates could not be paused due to error: 0x%1!x!. Continuing... | ||
451 | . | ||
452 | |||
453 | MessageId=309 | ||
454 | Severity=Warning | ||
455 | SymbolicName=MSG_APPLY_SKIPPED_FAILED_CACHED_PACKAGE | ||
456 | Language=English | ||
457 | Skipping apply of package: %1!ls! due to cache error: 0x%2!x!. Continuing... | ||
458 | . | ||
459 | |||
460 | MessageId=310 | ||
461 | Severity=Error | ||
462 | SymbolicName=MSG_FAILED_VERIFY_PAYLOAD | ||
463 | Language=English | ||
464 | Failed to verify payload: %2!ls! at path: %3!ls!, error: %1!ls!. Deleting file. | ||
465 | . | ||
466 | |||
467 | MessageId=311 | ||
468 | Severity=Error | ||
469 | SymbolicName=MSG_FAILED_ACQUIRE_CONTAINER | ||
470 | Language=English | ||
471 | Failed to acquire container: %2!ls! to working path: %3!ls!, error: %1!ls!. | ||
472 | . | ||
473 | |||
474 | MessageId=312 | ||
475 | Severity=Error | ||
476 | SymbolicName=MSG_FAILED_EXTRACT_CONTAINER | ||
477 | Language=English | ||
478 | Failed to extract payloads from container: %2!ls! to working path: %3!ls!, error: %1!ls!. | ||
479 | . | ||
480 | |||
481 | MessageId=313 | ||
482 | Severity=Error | ||
483 | SymbolicName=MSG_FAILED_ACQUIRE_PAYLOAD | ||
484 | Language=English | ||
485 | Failed to acquire payload: %2!ls! to working path: %3!ls!, error: %1!ls!. | ||
486 | . | ||
487 | |||
488 | MessageId=314 | ||
489 | Severity=Error | ||
490 | SymbolicName=MSG_FAILED_CACHE_PAYLOAD | ||
491 | Language=English | ||
492 | Failed to cache payload: %2!ls! from working path: %3!ls!, error: %1!ls!. | ||
493 | . | ||
494 | |||
495 | MessageId=315 | ||
496 | Severity=Error | ||
497 | SymbolicName=MSG_FAILED_LAYOUT_BUNDLE | ||
498 | Language=English | ||
499 | Failed to layout bundle: %2!ls! to layout directory: %3!ls!, error: %1!ls!. | ||
500 | . | ||
501 | |||
502 | MessageId=316 | ||
503 | Severity=Error | ||
504 | SymbolicName=MSG_FAILED_LAYOUT_CONTAINER | ||
505 | Language=English | ||
506 | Failed to layout container: %2!ls! to layout directory: %3!ls!, error: %1!ls!. | ||
507 | . | ||
508 | |||
509 | |||
510 | MessageId=317 | ||
511 | Severity=Error | ||
512 | SymbolicName=MSG_FAILED_LAYOUT_PAYLOAD | ||
513 | Language=English | ||
514 | Failed to layout payload: %2!ls! to layout directory: %3!ls!, error: %1!ls!. | ||
515 | . | ||
516 | |||
517 | MessageId=318 | ||
518 | Severity=Success | ||
519 | SymbolicName=MSG_ROLLBACK_PACKAGE_SKIPPED | ||
520 | Language=English | ||
521 | Skipped rollback of package: %1!ls!, action: %2!hs!, already: %3!hs! | ||
522 | . | ||
523 | |||
524 | MessageId=319 | ||
525 | Severity=Success | ||
526 | SymbolicName=MSG_APPLY_COMPLETED_PACKAGE | ||
527 | Language=English | ||
528 | Applied %1!hs! package: %2!ls!, result: 0x%3!x!, restart: %4!hs! | ||
529 | . | ||
530 | |||
531 | MessageId=320 | ||
532 | Severity=Success | ||
533 | SymbolicName=MSG_DEPENDENCY_BUNDLE_REGISTER | ||
534 | Language=English | ||
535 | Registering bundle dependency provider: %1!ls!, version: %2!ls! | ||
536 | . | ||
537 | |||
538 | MessageId=321 | ||
539 | Severity=Warning | ||
540 | SymbolicName=MSG_DEPENDENCY_PACKAGE_SKIP_NOPROVIDERS | ||
541 | Language=English | ||
542 | Skipping dependency registration on package with no dependency providers: %1!ls! | ||
543 | . | ||
544 | |||
545 | MessageId=322 | ||
546 | Severity=Warning | ||
547 | SymbolicName=MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE | ||
548 | Language=English | ||
549 | Skipping cross-scope dependency registration on package: %1!ls!, bundle scope: %2!hs!, package scope: %3!hs! | ||
550 | . | ||
551 | |||
552 | MessageId=323 | ||
553 | Severity=Success | ||
554 | SymbolicName=MSG_DEPENDENCY_PACKAGE_REGISTER | ||
555 | Language=English | ||
556 | Registering package dependency provider: %1!ls!, version: %2!ls!, package: %3!ls! | ||
557 | . | ||
558 | |||
559 | MessageId=324 | ||
560 | Severity=Warning | ||
561 | SymbolicName=MSG_DEPENDENCY_PACKAGE_SKIP_MISSING | ||
562 | Language=English | ||
563 | Skipping dependency registration on missing package provider: %1!ls!, package: %2!ls! | ||
564 | . | ||
565 | |||
566 | MessageId=325 | ||
567 | Severity=Success | ||
568 | SymbolicName=MSG_DEPENDENCY_PACKAGE_REGISTER_DEPENDENCY | ||
569 | Language=English | ||
570 | Registering dependency: %1!ls! on package provider: %2!ls!, package: %3!ls! | ||
571 | . | ||
572 | |||
573 | MessageId=326 | ||
574 | Severity=Success | ||
575 | SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY | ||
576 | Language=English | ||
577 | Removed dependency: %1!ls! on package provider: %2!ls!, package %3!ls! | ||
578 | . | ||
579 | |||
580 | MessageId=327 | ||
581 | Severity=Warning | ||
582 | SymbolicName=MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS | ||
583 | Language=English | ||
584 | Will not uninstall package: %1!ls!, found dependents: %2!d! | ||
585 | . | ||
586 | |||
587 | MessageId=328 | ||
588 | Severity=Warning | ||
589 | SymbolicName=MSG_DEPENDENCY_PACKAGE_DEPENDENT | ||
590 | Language=English | ||
591 | Found dependent: %1!ls!, name: %2!ls! | ||
592 | . | ||
593 | |||
594 | MessageId=329 | ||
595 | Severity=Success | ||
596 | SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED | ||
597 | Language=English | ||
598 | Removed package dependency provider: %1!ls!, package: %2!ls! | ||
599 | . | ||
600 | |||
601 | MessageId=330 | ||
602 | Severity=Success | ||
603 | SymbolicName=MSG_DEPENDENCY_BUNDLE_UNREGISTERED | ||
604 | Language=English | ||
605 | Removed bundle dependency provider: %1!ls! | ||
606 | . | ||
607 | |||
608 | MessageId=331 | ||
609 | Severity=Warning | ||
610 | SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED_DEPENDENCY_FAILED | ||
611 | Language=English | ||
612 | Could not remove dependency: %1!ls! on package provider: %2!ls!, package %3!ls!, error: 0x%4!x! | ||
613 | . | ||
614 | |||
615 | MessageId=332 | ||
616 | Severity=Warning | ||
617 | SymbolicName=MSG_DEPENDENCY_PACKAGE_UNREGISTERED_FAILED | ||
618 | Language=English | ||
619 | Could not remove package dependency provider: %1!ls!, package: %2!ls!, error: 0x%3!x! | ||
620 | . | ||
621 | |||
622 | MessageId=333 | ||
623 | Severity=Warning | ||
624 | SymbolicName=MSG_DEPENDENCY_BUNDLE_UNREGISTERED_FAILED | ||
625 | Language=English | ||
626 | Could not remove bundle dependency provider: %1!ls!, error: 0x%2!x! | ||
627 | . | ||
628 | |||
629 | MessageId=335 | ||
630 | Severity=Success | ||
631 | SymbolicName=MSG_ACQUIRE_BUNDLE_PAYLOAD | ||
632 | Language=English | ||
633 | Acquiring bundle payload: %2!ls!, %3!hs! from: %4!ls! | ||
634 | . | ||
635 | |||
636 | MessageId=336 | ||
637 | Severity=Success | ||
638 | SymbolicName=MSG_ACQUIRE_CONTAINER | ||
639 | Language=English | ||
640 | Acquiring container: %1!ls!, %3!hs! from: %4!ls! | ||
641 | . | ||
642 | |||
643 | MessageId=337 | ||
644 | Severity=Success | ||
645 | SymbolicName=MSG_ACQUIRE_CONTAINER_PAYLOAD | ||
646 | Language=English | ||
647 | Acquiring container: %1!ls!, payload: %2!ls!, %3!hs! from: %4!ls! | ||
648 | . | ||
649 | |||
650 | MessageId=338 | ||
651 | Severity=Success | ||
652 | SymbolicName=MSG_ACQUIRE_PACKAGE_PAYLOAD | ||
653 | Language=English | ||
654 | Acquiring package: %1!ls!, payload: %2!ls!, %3!hs! from: %4!ls! | ||
655 | . | ||
656 | |||
657 | MessageId=340 | ||
658 | Severity=Warning | ||
659 | SymbolicName=MSG_PROMPT_BUNDLE_PAYLOAD_SOURCE | ||
660 | Language=English | ||
661 | Prompt for source of bundle payload: %2!ls!, path: %3!ls! | ||
662 | . | ||
663 | |||
664 | MessageId=341 | ||
665 | Severity=Warning | ||
666 | SymbolicName=MSG_PROMPT_CONTAINER_SOURCE | ||
667 | Language=English | ||
668 | Prompt for source of container: %1!ls!, path: %3!ls! | ||
669 | . | ||
670 | |||
671 | MessageId=342 | ||
672 | Severity=Warning | ||
673 | SymbolicName=MSG_PROMPT_CONTAINER_PAYLOAD_SOURCE | ||
674 | Language=English | ||
675 | Prompt for source of container: %1!ls!, payload: %2!ls!, path: %3!ls! | ||
676 | . | ||
677 | |||
678 | MessageId=343 | ||
679 | Severity=Warning | ||
680 | SymbolicName=MSG_PROMPT_PACKAGE_PAYLOAD_SOURCE | ||
681 | Language=English | ||
682 | Prompt for source of package: %1!ls!, payload: %2!ls!, path: %3!ls! | ||
683 | . | ||
684 | |||
685 | MessageId=348 | ||
686 | Severity=Warning | ||
687 | SymbolicName=MSG_APPLY_RETRYING_PACKAGE | ||
688 | Language=English | ||
689 | Application requested retry of package: %1!ls!, encountered error: 0x%2!x!. Retrying... | ||
690 | . | ||
691 | |||
692 | MessageId=349 | ||
693 | Severity=Warning | ||
694 | SymbolicName=MSG_APPLY_RETRYING_PAYLOAD | ||
695 | Language=English | ||
696 | Application requested retry of payload: %2!ls!, encountered error: %1!ls!. Retrying... | ||
697 | . | ||
698 | |||
699 | MessageId=350 | ||
700 | Severity=Warning | ||
701 | SymbolicName=MSG_APPLY_CONTINUING_NONVITAL_PACKAGE | ||
702 | Language=English | ||
703 | Applied non-vital package: %1!ls!, encountered error: 0x%2!x!. Continuing... | ||
704 | . | ||
705 | |||
706 | MessageId=351 | ||
707 | Severity=Success | ||
708 | SymbolicName=MSG_UNCACHE_PACKAGE | ||
709 | Language=English | ||
710 | Removing cached package: %1!ls!, from path: %2!ls! | ||
711 | . | ||
712 | |||
713 | MessageId=352 | ||
714 | Severity=Success | ||
715 | SymbolicName=MSG_UNCACHE_BUNDLE | ||
716 | Language=English | ||
717 | Removing cached bundle: %1!ls!, from path: %2!ls! | ||
718 | . | ||
719 | |||
720 | MessageId=353 | ||
721 | Severity=Warning | ||
722 | SymbolicName=MSG_UNABLE_UNCACHE_PACKAGE | ||
723 | Language=English | ||
724 | Unable to remove cached package: %1!ls!, from path: %2!ls!, reason: 0x%3!x!. Continuing... | ||
725 | . | ||
726 | |||
727 | MessageId=354 | ||
728 | Severity=Warning | ||
729 | SymbolicName=MSG_UNABLE_UNCACHE_BUNDLE | ||
730 | Language=English | ||
731 | Unable to remove cached bundle: %1!ls!, from path: %2!ls!, reason: 0x%3!x!. Continuing... | ||
732 | . | ||
733 | |||
734 | MessageId=355 | ||
735 | Severity=Warning | ||
736 | SymbolicName=MSG_SOURCELIST_REGISTER | ||
737 | Language=English | ||
738 | Unable to register source directory: %1!ls!, product: %2!ls!, reason: 0x%3!x!. Continuing... | ||
739 | . | ||
740 | |||
741 | MessageId=358 | ||
742 | Severity=Success | ||
743 | SymbolicName=MSG_PAUSE_AU_STARTING | ||
744 | Language=English | ||
745 | Pausing automatic updates. | ||
746 | . | ||
747 | |||
748 | MessageId=359 | ||
749 | Severity=Success | ||
750 | SymbolicName=MSG_PAUSE_AU_SUCCEEDED | ||
751 | Language=English | ||
752 | Paused automatic updates. | ||
753 | . | ||
754 | |||
755 | MessageId=360 | ||
756 | Severity=Success | ||
757 | SymbolicName=MSG_SYSTEM_RESTORE_POINT_STARTING | ||
758 | Language=English | ||
759 | Creating a system restore point. | ||
760 | . | ||
761 | |||
762 | MessageId=361 | ||
763 | Severity=Success | ||
764 | SymbolicName=MSG_SYSTEM_RESTORE_POINT_SUCCEEDED | ||
765 | Language=English | ||
766 | Created a system restore point. | ||
767 | . | ||
768 | |||
769 | MessageId=362 | ||
770 | Severity=Success | ||
771 | SymbolicName=MSG_SYSTEM_RESTORE_POINT_DISABLED | ||
772 | Language=English | ||
773 | System restore disabled, system restore point not created. | ||
774 | . | ||
775 | |||
776 | MessageId=363 | ||
777 | Severity=Warning | ||
778 | SymbolicName=MSG_SYSTEM_RESTORE_POINT_FAILED | ||
779 | Language=English | ||
780 | Could not create system restore point, error: 0x%1!x!. Continuing... | ||
781 | . | ||
782 | |||
783 | MessageId=370 | ||
784 | Severity=Success | ||
785 | SymbolicName=MSG_SESSION_BEGIN | ||
786 | Language=English | ||
787 | Session begin, registration key: %1!ls!, options: 0x%2!x!, disable resume: %3!hs! | ||
788 | . | ||
789 | |||
790 | MessageId=371 | ||
791 | Severity=Success | ||
792 | SymbolicName=MSG_SESSION_UPDATE | ||
793 | Language=English | ||
794 | Updating session, registration key: %1!ls!, resume: %2!hs!, restart initiated: %3!hs!, disable resume: %4!hs! | ||
795 | . | ||
796 | |||
797 | MessageId=372 | ||
798 | Severity=Success | ||
799 | SymbolicName=MSG_SESSION_END | ||
800 | Language=English | ||
801 | Session end, registration key: %1!ls!, resume: %2!hs!, restart: %3!hs!, disable resume: %4!hs! | ||
802 | . | ||
803 | |||
804 | MessageId=380 | ||
805 | Severity=Warning | ||
806 | SymbolicName=MSG_APPLY_SKIPPED | ||
807 | Language=English | ||
808 | Apply skipped, no planned actions | ||
809 | . | ||
810 | |||
811 | MessageId=381 | ||
812 | Severity=Warning | ||
813 | SymbolicName=MSG_APPLY_CANCEL_IGNORED_DURING_ROLLBACK | ||
814 | Language=English | ||
815 | Ignoring application request to cancel from %1!ls! during rollback. | ||
816 | . | ||
817 | |||
818 | MessageId=399 | ||
819 | Severity=Success | ||
820 | SymbolicName=MSG_APPLY_COMPLETE | ||
821 | Language=English | ||
822 | Apply complete, result: 0x%1!x!, restart: %2!hs!, ba requested restart: %3!hs! | ||
823 | . | ||
824 | |||
825 | MessageId=400 | ||
826 | Severity=Success | ||
827 | SymbolicName=MSG_SYSTEM_SHUTDOWN | ||
828 | Language=English | ||
829 | Received system request to shut down the process: critical: %1!hs!, elevated: %2!hs!, allowed: %3!hs! | ||
830 | . | ||
831 | |||
832 | MessageId=410 | ||
833 | Severity=Success | ||
834 | SymbolicName=MSG_VARIABLE_DUMP | ||
835 | Language=English | ||
836 | Variable: %1!ls! | ||
837 | . | ||
838 | |||
839 | MessageId=420 | ||
840 | Severity=Success | ||
841 | SymbolicName=MSG_RESUME_AU_STARTING | ||
842 | Language=English | ||
843 | Resuming automatic updates. | ||
844 | . | ||
845 | |||
846 | MessageId=421 | ||
847 | Severity=Success | ||
848 | SymbolicName=MSG_RESUME_AU_SUCCEEDED | ||
849 | Language=English | ||
850 | Resumed automatic updates. | ||
851 | . | ||
852 | |||
853 | MessageId=500 | ||
854 | Severity=Success | ||
855 | SymbolicName=MSG_QUIT | ||
856 | Language=English | ||
857 | Shutting down, exit code: 0x%1!x! | ||
858 | . | ||
859 | |||
860 | MessageId=501 | ||
861 | Severity=Warning | ||
862 | SymbolicName=MSG_STATE_NOT_SAVED | ||
863 | Language=English | ||
864 | The state file could not be saved, error: 0x%1!x!. Continuing... | ||
865 | . | ||
866 | |||
867 | MessageId=600 | ||
868 | Severity=Success | ||
869 | SymbolicName=MSG_LAUNCH_APPROVED_EXE_BEGIN | ||
870 | Language=English | ||
871 | LaunchApprovedExe begin, id: %1!ls! | ||
872 | . | ||
873 | |||
874 | MessageId=601 | ||
875 | Severity=Success | ||
876 | SymbolicName=MSG_LAUNCH_APPROVED_EXE_SEARCH | ||
877 | Language=English | ||
878 | Searching registry for approved exe path, key: %1!ls!, value: '%2!ls!', win64: %3!ls! | ||
879 | . | ||
880 | |||
881 | MessageId=602 | ||
882 | Severity=Success | ||
883 | SymbolicName=MSG_LAUNCHING_APPROVED_EXE | ||
884 | Language=English | ||
885 | Launching approved exe, path: '%1!ls!', 'command: %2!ls!' | ||
886 | . | ||
887 | |||
888 | MessageId=699 | ||
889 | Severity=Success | ||
890 | SymbolicName=MSG_LAUNCH_APPROVED_EXE_COMPLETE | ||
891 | Language=English | ||
892 | LaunchApprovedExe complete, result: 0x%1!x!, processId: %2!lu! | ||
893 | . | ||
894 | |||
895 | MessageId=700 | ||
896 | Severity=Success | ||
897 | SymbolicName=MSG_MSI_PROPERTY_CONDITION_FAILED | ||
898 | Language=English | ||
899 | Skipping 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 | |||
8 | static HRESULT HandleExitCode( | ||
9 | __in BURN_PACKAGE* pPackage, | ||
10 | __in DWORD dwExitCode, | ||
11 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
12 | ); | ||
13 | static HRESULT ParseCommandLineArgumentsFromXml( | ||
14 | __in IXMLDOMNode* pixnExePackage, | ||
15 | __in BURN_PACKAGE* pPackage | ||
16 | ); | ||
17 | static HRESULT ParseExitCodesFromXml( | ||
18 | __in IXMLDOMNode* pixnExePackage, | ||
19 | __in BURN_PACKAGE* pPackage | ||
20 | ); | ||
21 | |||
22 | |||
23 | // function definitions | ||
24 | |||
25 | extern "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 | |||
91 | LExit: | ||
92 | ReleaseObject(pixnNodes); | ||
93 | ReleaseObject(pixnNode); | ||
94 | ReleaseStr(scz); | ||
95 | |||
96 | return hr; | ||
97 | } | ||
98 | |||
99 | extern "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 | |||
130 | extern "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 | |||
148 | LExit: | ||
149 | return hr; | ||
150 | } | ||
151 | |||
152 | // | ||
153 | // PlanCalculate - calculates the execute and rollback state for the requested package state. | ||
154 | // | ||
155 | extern "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 | |||
277 | LExit: | ||
278 | return hr; | ||
279 | } | ||
280 | |||
281 | // | ||
282 | // PlanAdd - adds the calculated execute and rollback actions for the package. | ||
283 | // | ||
284 | extern "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 | |||
366 | LExit: | ||
367 | return hr; | ||
368 | } | ||
369 | |||
370 | extern "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 | |||
575 | LExit: | ||
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 | |||
603 | static 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 | |||
663 | LExit: | ||
664 | ReleaseObject(pixnNodes); | ||
665 | ReleaseObject(pixnNode); | ||
666 | ReleaseStr(scz); | ||
667 | |||
668 | return hr; | ||
669 | } | ||
670 | |||
671 | static 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 | |||
728 | LExit: | ||
729 | ReleaseObject(pixnNodes); | ||
730 | ReleaseObject(pixnNode); | ||
731 | ReleaseStr(scz); | ||
732 | |||
733 | return hr; | ||
734 | } | ||
735 | |||
736 | static 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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // function declarations | ||
11 | |||
12 | HRESULT ExeEngineParsePackageFromXml( | ||
13 | __in IXMLDOMNode* pixnExePackage, | ||
14 | __in BURN_PACKAGE* pPackage | ||
15 | ); | ||
16 | void ExeEnginePackageUninitialize( | ||
17 | __in BURN_PACKAGE* pPackage | ||
18 | ); | ||
19 | HRESULT ExeEngineDetectPackage( | ||
20 | __in BURN_PACKAGE* pPackage, | ||
21 | __in BURN_VARIABLES* pVariables | ||
22 | ); | ||
23 | HRESULT ExeEnginePlanCalculatePackage( | ||
24 | __in BURN_PACKAGE* pPackage, | ||
25 | __out_opt BOOL* pfBARequestedCache | ||
26 | ); | ||
27 | HRESULT 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 | ); | ||
36 | HRESULT 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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // function declarations | ||
11 | |||
12 | BOOL EngineInCleanRoom( | ||
13 | __in_z_opt LPCWSTR wzCommandLine | ||
14 | ); | ||
15 | |||
16 | HRESULT 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 | |||
6 | static DWORD vdwPackageSequence = 0; | ||
7 | static const DWORD LOG_OPEN_RETRY_COUNT = 3; | ||
8 | static const DWORD LOG_OPEN_RETRY_WAIT = 2000; | ||
9 | static 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 | |||
17 | static void CheckLoggingPolicy( | ||
18 | __out DWORD *pdwAttributes | ||
19 | ); | ||
20 | static HRESULT GetNonSessionSpecificTempFolder( | ||
21 | __deref_out_z LPWSTR* psczNonSessionTempFolder | ||
22 | ); | ||
23 | |||
24 | |||
25 | // function definitions | ||
26 | |||
27 | extern "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 | |||
156 | LExit: | ||
157 | ReleaseStr(sczLoggingBaseFolder); | ||
158 | |||
159 | return hr; | ||
160 | } | ||
161 | |||
162 | extern "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 | |||
182 | LExit: | ||
183 | if (hEventLog) | ||
184 | { | ||
185 | ::CloseEventLog(hEventLog); | ||
186 | } | ||
187 | } | ||
188 | |||
189 | extern "C" void LoggingIncrementPackageSequence() | ||
190 | { | ||
191 | ++vdwPackageSequence; | ||
192 | } | ||
193 | |||
194 | extern "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 | |||
233 | LExit: | ||
234 | ReleaseStr(sczLogPath); | ||
235 | |||
236 | return hr; | ||
237 | } | ||
238 | |||
239 | extern "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 | |||
270 | extern "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 | |||
299 | extern "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 | |||
316 | extern "C" LPCSTR LoggingBoolToString( | ||
317 | __in BOOL f | ||
318 | ) | ||
319 | { | ||
320 | if (f) | ||
321 | { | ||
322 | return "Yes"; | ||
323 | } | ||
324 | |||
325 | return "No"; | ||
326 | } | ||
327 | |||
328 | extern "C" LPCSTR LoggingTrueFalseToString( | ||
329 | __in BOOL f | ||
330 | ) | ||
331 | { | ||
332 | if (f) | ||
333 | { | ||
334 | return "true"; | ||
335 | } | ||
336 | |||
337 | return "false"; | ||
338 | } | ||
339 | |||
340 | extern "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 | |||
363 | extern "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 | |||
380 | extern "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 | |||
401 | extern "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 | |||
426 | extern "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 | |||
449 | extern "C" LPCSTR LoggingPerMachineToString( | ||
450 | __in BOOL fPerMachine | ||
451 | ) | ||
452 | { | ||
453 | if (fPerMachine) | ||
454 | { | ||
455 | return "PerMachine"; | ||
456 | } | ||
457 | |||
458 | return "PerUser"; | ||
459 | } | ||
460 | |||
461 | extern "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 | |||
478 | extern "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 | |||
499 | extern "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 | |||
524 | extern "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 | |||
549 | extern "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 | |||
572 | extern "C" LPCSTR LoggingRollbackOrExecute( | ||
573 | __in BOOL fRollback | ||
574 | ) | ||
575 | { | ||
576 | return fRollback ? "rollback" : "execute"; | ||
577 | } | ||
578 | |||
579 | extern "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. | ||
587 | extern "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 | |||
606 | static 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 | |||
641 | static 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 | |||
679 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // constants | ||
11 | |||
12 | enum BURN_LOGGING_STATE | ||
13 | { | ||
14 | BURN_LOGGING_STATE_CLOSED, | ||
15 | BURN_LOGGING_STATE_OPEN, | ||
16 | BURN_LOGGING_STATE_DISABLED, | ||
17 | }; | ||
18 | |||
19 | enum 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 | |||
29 | typedef 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 | |||
44 | HRESULT LoggingOpen( | ||
45 | __in BURN_LOGGING* pLog, | ||
46 | __in BURN_VARIABLES* pVariables, | ||
47 | __in BOOTSTRAPPER_DISPLAY display, | ||
48 | __in_z LPCWSTR wzBundleName | ||
49 | ); | ||
50 | |||
51 | void LoggingOpenFailed(); | ||
52 | |||
53 | void LoggingIncrementPackageSequence(); | ||
54 | |||
55 | HRESULT 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 | |||
64 | LPCSTR LoggingBurnActionToString( | ||
65 | __in BOOTSTRAPPER_ACTION action | ||
66 | ); | ||
67 | |||
68 | LPCSTR LoggingActionStateToString( | ||
69 | __in BOOTSTRAPPER_ACTION_STATE actionState | ||
70 | ); | ||
71 | |||
72 | LPCSTR LoggingDependencyActionToString( | ||
73 | BURN_DEPENDENCY_ACTION action | ||
74 | ); | ||
75 | |||
76 | LPCSTR LoggingBoolToString( | ||
77 | __in BOOL f | ||
78 | ); | ||
79 | |||
80 | LPCSTR LoggingTrueFalseToString( | ||
81 | __in BOOL f | ||
82 | ); | ||
83 | |||
84 | LPCSTR LoggingPackageStateToString( | ||
85 | __in BOOTSTRAPPER_PACKAGE_STATE packageState | ||
86 | ); | ||
87 | |||
88 | LPCSTR LoggingCacheStateToString( | ||
89 | __in BURN_CACHE_STATE cacheState | ||
90 | ); | ||
91 | |||
92 | LPCSTR LoggingMsiFeatureStateToString( | ||
93 | __in BOOTSTRAPPER_FEATURE_STATE featureState | ||
94 | ); | ||
95 | |||
96 | LPCSTR LoggingMsiFeatureActionToString( | ||
97 | __in BOOTSTRAPPER_FEATURE_ACTION featureAction | ||
98 | ); | ||
99 | |||
100 | LPCSTR LoggingMsiInstallContext( | ||
101 | __in MSIINSTALLCONTEXT context | ||
102 | ); | ||
103 | |||
104 | LPCSTR LoggingPerMachineToString( | ||
105 | __in BOOL fPerMachine | ||
106 | ); | ||
107 | |||
108 | LPCSTR LoggingRestartToString( | ||
109 | __in BOOTSTRAPPER_APPLY_RESTART restart | ||
110 | ); | ||
111 | |||
112 | LPCSTR LoggingResumeModeToString( | ||
113 | __in BURN_RESUME_MODE resumeMode | ||
114 | ); | ||
115 | |||
116 | LPCSTR LoggingRelationTypeToString( | ||
117 | __in BOOTSTRAPPER_RELATION_TYPE type | ||
118 | ); | ||
119 | |||
120 | LPCSTR LoggingRelatedOperationToString( | ||
121 | __in BOOTSTRAPPER_RELATED_OPERATION operation | ||
122 | ); | ||
123 | |||
124 | LPCSTR LoggingRequestStateToString( | ||
125 | __in BOOTSTRAPPER_REQUEST_STATE requestState | ||
126 | ); | ||
127 | |||
128 | LPCSTR LoggingRollbackOrExecute( | ||
129 | __in BOOL fRollback | ||
130 | ); | ||
131 | |||
132 | LPWSTR LoggingStringOrUnknownIfNull( | ||
133 | __in LPCWSTR wz | ||
134 | ); | ||
135 | |||
136 | // Note: this function is not thread safe. | ||
137 | LPCSTR 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 | |||
8 | extern "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 | |||
119 | LExit: | ||
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 | |||
5 | interface IBurnPayload; // forward declare. | ||
6 | |||
7 | #if defined(__cplusplus) | ||
8 | extern "C" { | ||
9 | #endif | ||
10 | |||
11 | |||
12 | // function declarations | ||
13 | |||
14 | HRESULT 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 | |||
15 | static HRESULT ParseRelatedMsiFromXml( | ||
16 | __in IXMLDOMNode* pixnRelatedMsi, | ||
17 | __in BURN_RELATED_MSI* pRelatedMsi | ||
18 | ); | ||
19 | static 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 | ); | ||
26 | static 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 | ); | ||
33 | static HRESULT EscapePropertyArgumentString( | ||
34 | __in LPCWSTR wzProperty, | ||
35 | __inout_z LPWSTR* psczEscapedValue, | ||
36 | __in BOOL fZeroOnRealloc | ||
37 | ); | ||
38 | static HRESULT ConcatFeatureActionProperties( | ||
39 | __in BURN_PACKAGE* pPackage, | ||
40 | __in BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions, | ||
41 | __inout_z LPWSTR* psczArguments | ||
42 | ); | ||
43 | static HRESULT ConcatPatchProperty( | ||
44 | __in BURN_PACKAGE* pPackage, | ||
45 | __in_opt BOOTSTRAPPER_ACTION_STATE* rgSlipstreamPatchActions, | ||
46 | __inout_z LPWSTR* psczArguments | ||
47 | ); | ||
48 | static void RegisterSourceDirectory( | ||
49 | __in BURN_PACKAGE* pPackage, | ||
50 | __in_z LPCWSTR wzCacheDirectory | ||
51 | ); | ||
52 | |||
53 | |||
54 | // function definitions | ||
55 | |||
56 | extern "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 | |||
238 | LExit: | ||
239 | ReleaseObject(pixnNodes); | ||
240 | ReleaseObject(pixnNode); | ||
241 | ReleaseStr(scz); | ||
242 | |||
243 | return hr; | ||
244 | } | ||
245 | |||
246 | extern "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 | |||
314 | LExit: | ||
315 | ReleaseNullObject(pixnNodes); | ||
316 | ReleaseMem(pProperties); | ||
317 | |||
318 | return hr; | ||
319 | } | ||
320 | |||
321 | extern "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 | |||
395 | extern "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 | |||
666 | LExit: | ||
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 | // | ||
678 | extern "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 | |||
869 | LExit: | ||
870 | return hr; | ||
871 | } | ||
872 | |||
873 | // | ||
874 | // PlanAdd - adds the calculated execute and rollback actions for the package. | ||
875 | // | ||
876 | extern "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 | |||
969 | LExit: | ||
970 | ReleaseMem(rgFeatureActions); | ||
971 | ReleaseMem(rgRollbackFeatureActions); | ||
972 | |||
973 | return hr; | ||
974 | } | ||
975 | |||
976 | extern "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 | |||
1083 | LExit: | ||
1084 | ReleaseStr(sczInstalledVersion); | ||
1085 | |||
1086 | return hr; | ||
1087 | } | ||
1088 | |||
1089 | extern "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 | |||
1272 | LExit: | ||
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. | ||
1304 | extern "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 | |||
1359 | LExit: | ||
1360 | StrSecureZeroFreeString(sczValue); | ||
1361 | StrSecureZeroFreeString(sczEscapedValue); | ||
1362 | StrSecureZeroFreeString(sczProperty); | ||
1363 | return hr; | ||
1364 | } | ||
1365 | |||
1366 | extern "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 | |||
1396 | static 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 | |||
1486 | LExit: | ||
1487 | ReleaseObject(pixnNodes); | ||
1488 | ReleaseObject(pixnNode); | ||
1489 | ReleaseStr(scz); | ||
1490 | |||
1491 | return hr; | ||
1492 | } | ||
1493 | |||
1494 | static 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 | |||
1551 | LExit: | ||
1552 | return hr; | ||
1553 | } | ||
1554 | |||
1555 | static 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 | |||
1622 | LExit: | ||
1623 | return hr; | ||
1624 | } | ||
1625 | |||
1626 | static 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 | |||
1672 | LExit: | ||
1673 | return hr; | ||
1674 | } | ||
1675 | |||
1676 | static 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 | |||
1814 | LExit: | ||
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 | |||
1826 | static 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 | |||
1880 | LExit: | ||
1881 | ReleaseStr(sczMspPath); | ||
1882 | ReleaseStr(sczCachedDirectory); | ||
1883 | ReleaseStr(sczPatches); | ||
1884 | return hr; | ||
1885 | } | ||
1886 | |||
1887 | static 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 | |||
1906 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // function declarations | ||
11 | |||
12 | HRESULT MsiEngineParsePackageFromXml( | ||
13 | __in IXMLDOMNode* pixnBundle, | ||
14 | __in BURN_PACKAGE* pPackage | ||
15 | ); | ||
16 | HRESULT MsiEngineParsePropertiesFromXml( | ||
17 | __in IXMLDOMNode* pixnPackage, | ||
18 | __out BURN_MSIPROPERTY** prgProperties, | ||
19 | __out DWORD* pcProperties | ||
20 | ); | ||
21 | void MsiEnginePackageUninitialize( | ||
22 | __in BURN_PACKAGE* pPackage | ||
23 | ); | ||
24 | HRESULT MsiEngineDetectPackage( | ||
25 | __in BURN_PACKAGE* pPackage, | ||
26 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
27 | ); | ||
28 | HRESULT MsiEnginePlanCalculatePackage( | ||
29 | __in BURN_PACKAGE* pPackage, | ||
30 | __in BURN_VARIABLES* pVariables, | ||
31 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
32 | __out_opt BOOL* pfBARequestedCache | ||
33 | ); | ||
34 | HRESULT 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 | ); | ||
43 | HRESULT MsiEngineAddCompatiblePackage( | ||
44 | __in BURN_PACKAGES* pPackages, | ||
45 | __in const BURN_PACKAGE* pPackage, | ||
46 | __out_opt BURN_PACKAGE** ppCompatiblePackage | ||
47 | ); | ||
48 | HRESULT 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 | ); | ||
57 | HRESULT 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 | ); | ||
65 | INSTALLUILEVEL 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 | |||
11 | struct POSSIBLE_TARGETPRODUCT | ||
12 | { | ||
13 | WCHAR wzProductCode[39]; | ||
14 | LPWSTR pszLocalPackage; | ||
15 | MSIINSTALLCONTEXT context; | ||
16 | }; | ||
17 | |||
18 | // internal function declarations | ||
19 | |||
20 | static HRESULT GetPossibleTargetProductCodes( | ||
21 | __in BURN_PACKAGES* pPackages, | ||
22 | __deref_inout_ecount_opt(*pcPossibleTargetProductCodes) POSSIBLE_TARGETPRODUCT** prgPossibleTargetProductCodes, | ||
23 | __inout DWORD* pcPossibleTargetProductCodes | ||
24 | ); | ||
25 | static 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 | ); | ||
32 | static 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 | ); | ||
39 | static 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 | ); | ||
46 | static 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 | |||
61 | extern "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 | |||
84 | LExit: | ||
85 | |||
86 | return hr; | ||
87 | } | ||
88 | |||
89 | extern "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 | |||
117 | extern "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 | |||
185 | LExit: | ||
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 | |||
198 | extern "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 | |||
260 | LExit: | ||
261 | ReleaseStr(sczState); | ||
262 | |||
263 | return hr; | ||
264 | } | ||
265 | |||
266 | // | ||
267 | // PlanCalculate - calculates the execute and rollback state for the requested package state. | ||
268 | // | ||
269 | extern "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 | |||
394 | LExit: | ||
395 | |||
396 | return hr; | ||
397 | } | ||
398 | |||
399 | // | ||
400 | // PlanAdd - adds the calculated execute and rollback actions for the package. | ||
401 | // | ||
402 | extern "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 | |||
451 | LExit: | ||
452 | |||
453 | return hr; | ||
454 | } | ||
455 | |||
456 | extern "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 | |||
576 | LExit: | ||
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 | |||
607 | extern "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 | |||
636 | static 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 | |||
736 | LExit: | ||
737 | ReleaseDict(sdUniquePossibleTargetProductCodes); | ||
738 | |||
739 | return hr; | ||
740 | } | ||
741 | |||
742 | static 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 | |||
796 | LExit: | ||
797 | ReleaseStr(pszLocalPackage); | ||
798 | |||
799 | return hr; | ||
800 | } | ||
801 | |||
802 | static 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 | |||
826 | LExit: | ||
827 | return hr; | ||
828 | } | ||
829 | |||
830 | static 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 | |||
870 | static 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 | |||
978 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // constants | ||
11 | |||
12 | |||
13 | // structures | ||
14 | |||
15 | |||
16 | // typedefs | ||
17 | |||
18 | |||
19 | // function declarations | ||
20 | |||
21 | HRESULT MspEngineParsePackageFromXml( | ||
22 | __in IXMLDOMNode* pixnBundle, | ||
23 | __in BURN_PACKAGE* pPackage | ||
24 | ); | ||
25 | void MspEnginePackageUninitialize( | ||
26 | __in BURN_PACKAGE* pPackage | ||
27 | ); | ||
28 | HRESULT MspEngineDetectInitialize( | ||
29 | __in BURN_PACKAGES* pPackages | ||
30 | ); | ||
31 | HRESULT MspEngineDetectPackage( | ||
32 | __in BURN_PACKAGE* pPackage, | ||
33 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
34 | ); | ||
35 | HRESULT MspEnginePlanCalculatePackage( | ||
36 | __in BURN_PACKAGE* pPackage, | ||
37 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
38 | __out_opt BOOL* pfBARequestedCache | ||
39 | ); | ||
40 | HRESULT 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 | ); | ||
49 | HRESULT 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 | ); | ||
58 | void 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 | ||
13 | static HRESULT EnsureWUServiceEnabled( | ||
14 | __in BOOL fStopWusaService, | ||
15 | __out SC_HANDLE* pschWu, | ||
16 | __out BOOL* pfPreviouslyDisabled | ||
17 | ); | ||
18 | static HRESULT SetServiceStartType( | ||
19 | __in SC_HANDLE sch, | ||
20 | __in DWORD stratType | ||
21 | ); | ||
22 | static HRESULT StopWUService( | ||
23 | __in SC_HANDLE schWu | ||
24 | ); | ||
25 | |||
26 | |||
27 | extern "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 | |||
42 | LExit: | ||
43 | return hr; | ||
44 | } | ||
45 | |||
46 | extern "C" void MsuEnginePackageUninitialize( | ||
47 | __in BURN_PACKAGE* pPackage | ||
48 | ) | ||
49 | { | ||
50 | ReleaseNullStr(pPackage->Msu.sczKB); | ||
51 | ReleaseNullStr(pPackage->Msu.sczDetectCondition); | ||
52 | } | ||
53 | |||
54 | extern "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 | |||
72 | LExit: | ||
73 | return hr; | ||
74 | } | ||
75 | |||
76 | // | ||
77 | // PlanCalculate - calculates the execute and rollback state for the requested package state. | ||
78 | // | ||
79 | extern "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 | |||
194 | LExit: | ||
195 | return hr; | ||
196 | } | ||
197 | |||
198 | // | ||
199 | // PlanAdd - adds the calculated execute and rollback actions for the package. | ||
200 | // | ||
201 | extern "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 | |||
249 | LExit: | ||
250 | return hr; | ||
251 | } | ||
252 | |||
253 | extern "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 | |||
405 | LExit: | ||
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 | |||
427 | static 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 | |||
475 | LExit: | ||
476 | ReleaseMem(pConfig); | ||
477 | ReleaseServiceHandle(schWu); | ||
478 | ReleaseServiceHandle(schSCM); | ||
479 | |||
480 | return hr; | ||
481 | } | ||
482 | |||
483 | static 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 | |||
495 | LExit: | ||
496 | return hr; | ||
497 | } | ||
498 | |||
499 | static 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 | |||
511 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // function declarations | ||
11 | |||
12 | HRESULT MsuEngineParsePackageFromXml( | ||
13 | __in IXMLDOMNode* pixnMsiPackage, | ||
14 | __in BURN_PACKAGE* pPackage | ||
15 | ); | ||
16 | void MsuEnginePackageUninitialize( | ||
17 | __in BURN_PACKAGE* pPackage | ||
18 | ); | ||
19 | HRESULT MsuEngineDetectPackage( | ||
20 | __in BURN_PACKAGE* pPackage, | ||
21 | __in BURN_VARIABLES* pVariables | ||
22 | ); | ||
23 | HRESULT MsuEnginePlanCalculatePackage( | ||
24 | __in BURN_PACKAGE* pPackage, | ||
25 | __out_opt BOOL* pfBARequestedCache | ||
26 | ); | ||
27 | HRESULT 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 | ); | ||
35 | HRESULT 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 | |||
5 | static 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 | |||
25 | static 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 | |||
92 | LExit: | ||
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 | |||
110 | static 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 | |||
124 | static 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 | |||
138 | static 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 | |||
161 | LExit: | ||
162 | ::ReleaseMutex(pChainer->hMutex); | ||
163 | |||
164 | return hr; | ||
165 | } | ||
166 | |||
167 | static 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 | |||
187 | static 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 | |||
213 | static 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 | |||
244 | LExit: | ||
245 | ReleaseMem(rgwzFiles); | ||
246 | |||
247 | return hr; | ||
248 | } | ||
249 | |||
250 | static 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 | |||
274 | static 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 | |||
294 | static 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 | |||
325 | LExit: | ||
326 | ReleaseMem(pBuffer); | ||
327 | |||
328 | return hr; | ||
329 | } | ||
330 | |||
331 | extern "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 | |||
409 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | struct 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 | |||
31 | struct 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 | |||
77 | struct NetFxApplication | ||
78 | { | ||
79 | WCHAR szName[MAX_PATH]; | ||
80 | DWORD dwPid; | ||
81 | }; | ||
82 | |||
83 | struct NetFxCloseApplications | ||
84 | { | ||
85 | DWORD dwApplicationsSize; | ||
86 | NetFxApplication applications[1]; | ||
87 | }; | ||
88 | |||
89 | HRESULT 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 | |||
8 | static HRESULT ParsePayloadRefsFromXml( | ||
9 | __in BURN_PACKAGE* pPackage, | ||
10 | __in BURN_PAYLOADS* pPayloads, | ||
11 | __in IXMLDOMNode* pixnPackage | ||
12 | ); | ||
13 | static HRESULT ParsePatchTargetCode( | ||
14 | __in BURN_PACKAGES* pPackages, | ||
15 | __in IXMLDOMNode* pixnBundle | ||
16 | ); | ||
17 | static 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 | |||
26 | extern "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 | |||
302 | LExit: | ||
303 | ReleaseObject(pixnNodes); | ||
304 | ReleaseObject(pixnNode); | ||
305 | ReleaseBSTR(bstrNodeName); | ||
306 | ReleaseStr(scz); | ||
307 | |||
308 | return hr; | ||
309 | } | ||
310 | |||
311 | extern "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 | |||
350 | extern "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 | |||
397 | extern "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 | |||
430 | LExit: | ||
431 | return hr; | ||
432 | } | ||
433 | |||
434 | |||
435 | extern "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 | |||
457 | LExit: | ||
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 | *********************************************************************/ | ||
471 | extern "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 | |||
509 | LExit: | ||
510 | return hr; | ||
511 | } | ||
512 | |||
513 | HRESULT 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 | |||
525 | LExit: | ||
526 | return hr; | ||
527 | } | ||
528 | |||
529 | |||
530 | // internal function declarations | ||
531 | |||
532 | static 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 | |||
585 | LExit: | ||
586 | ReleaseObject(pixnNodes); | ||
587 | ReleaseObject(pixnNode); | ||
588 | ReleaseStr(sczId); | ||
589 | |||
590 | return hr; | ||
591 | } | ||
592 | |||
593 | static 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 | |||
646 | LExit: | ||
647 | ReleaseBSTR(bstrNodeText); | ||
648 | ReleaseObject(pixnNode); | ||
649 | ReleaseObject(pixnNodes); | ||
650 | |||
651 | return hr; | ||
652 | } | ||
653 | |||
654 | static 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 | |||
676 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | struct _BURN_RELATED_BUNDLES; | ||
10 | typedef _BURN_RELATED_BUNDLES BURN_RELATED_BUNDLES; | ||
11 | |||
12 | struct _BURN_PACKAGE; | ||
13 | typedef _BURN_PACKAGE BURN_PACKAGE; | ||
14 | |||
15 | // constants | ||
16 | |||
17 | enum 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 | |||
26 | enum 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 | |||
33 | enum 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 | |||
42 | enum BURN_CACHE_STATE | ||
43 | { | ||
44 | BURN_CACHE_STATE_NONE, | ||
45 | BURN_CACHE_STATE_PARTIAL, | ||
46 | BURN_CACHE_STATE_COMPLETE, | ||
47 | }; | ||
48 | |||
49 | enum BURN_CACHE_TYPE | ||
50 | { | ||
51 | BURN_CACHE_TYPE_NO, | ||
52 | BURN_CACHE_TYPE_YES, | ||
53 | BURN_CACHE_TYPE_ALWAYS, | ||
54 | }; | ||
55 | |||
56 | enum BURN_DEPENDENCY_ACTION | ||
57 | { | ||
58 | BURN_DEPENDENCY_ACTION_NONE, | ||
59 | BURN_DEPENDENCY_ACTION_REGISTER, | ||
60 | BURN_DEPENDENCY_ACTION_UNREGISTER, | ||
61 | }; | ||
62 | |||
63 | enum 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 | |||
72 | typedef 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 | |||
79 | typedef 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 | |||
87 | typedef 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 | |||
100 | typedef 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 | |||
108 | typedef 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 | |||
123 | typedef 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 | |||
139 | typedef struct _BURN_PACKAGE_PAYLOAD | ||
140 | { | ||
141 | BURN_PAYLOAD* pPayload; | ||
142 | BOOL fCached; | ||
143 | } BURN_PACKAGE_PAYLOAD; | ||
144 | |||
145 | typedef struct _BURN_DEPENDENCY_PROVIDER | ||
146 | { | ||
147 | LPWSTR sczKey; | ||
148 | LPWSTR sczVersion; | ||
149 | LPWSTR sczDisplayName; | ||
150 | BOOL fImported; | ||
151 | } BURN_DEPENDENCY_PROVIDER; | ||
152 | |||
153 | typedef struct _BURN_ROLLBACK_BOUNDARY | ||
154 | { | ||
155 | LPWSTR sczId; | ||
156 | BOOL fVital; | ||
157 | BOOL fTransaction; | ||
158 | } BURN_ROLLBACK_BOUNDARY; | ||
159 | |||
160 | typedef struct _BURN_PATCH_TARGETCODE | ||
161 | { | ||
162 | LPWSTR sczTargetCode; | ||
163 | BURN_PATCH_TARGETCODE_TYPE type; | ||
164 | } BURN_PATCH_TARGETCODE; | ||
165 | |||
166 | typedef 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 | |||
280 | typedef 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 | |||
303 | HRESULT PackagesParseFromXml( | ||
304 | __in BURN_PACKAGES* pPackages, | ||
305 | __in BURN_PAYLOADS* pPayloads, | ||
306 | __in IXMLDOMNode* pixnBundle | ||
307 | ); | ||
308 | void PackageUninitialize( | ||
309 | __in BURN_PACKAGE* pPackage | ||
310 | ); | ||
311 | void PackagesUninitialize( | ||
312 | __in BURN_PACKAGES* pPackages | ||
313 | ); | ||
314 | HRESULT PackageFindById( | ||
315 | __in BURN_PACKAGES* pPackages, | ||
316 | __in_z LPCWSTR wzId, | ||
317 | __out BURN_PACKAGE** ppPackage | ||
318 | ); | ||
319 | HRESULT PackageFindRelatedById( | ||
320 | __in BURN_RELATED_BUNDLES* pRelatedBundles, | ||
321 | __in_z LPCWSTR wzId, | ||
322 | __out BURN_PACKAGE** ppPackage | ||
323 | ); | ||
324 | HRESULT PackageGetProperty( | ||
325 | __in const BURN_PACKAGE* pPackage, | ||
326 | __in_z LPCWSTR wzProperty, | ||
327 | __out_z_opt LPWSTR* psczValue | ||
328 | ); | ||
329 | HRESULT 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 | |||
8 | static 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 | |||
18 | extern "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 | |||
176 | LExit: | ||
177 | ReleaseObject(pixnNodes); | ||
178 | ReleaseObject(pixnNode); | ||
179 | ReleaseStr(scz); | ||
180 | |||
181 | return hr; | ||
182 | } | ||
183 | |||
184 | extern "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 | |||
212 | extern "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 | |||
275 | LExit: | ||
276 | ReleaseStr(sczStreamName); | ||
277 | ReleaseStr(sczDirectory); | ||
278 | |||
279 | return hr; | ||
280 | } | ||
281 | |||
282 | extern "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 | |||
304 | LExit: | ||
305 | return hr; | ||
306 | } | ||
307 | |||
308 | extern "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 | |||
333 | LExit: | ||
334 | return hr; | ||
335 | } | ||
336 | |||
337 | |||
338 | // internal function definitions | ||
339 | |||
340 | static 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 | |||
365 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // constants | ||
11 | |||
12 | enum 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 | |||
20 | enum 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 | |||
30 | typedef 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 | |||
55 | typedef struct _BURN_PAYLOADS | ||
56 | { | ||
57 | BURN_PAYLOAD* rgPayloads; | ||
58 | DWORD cPayloads; | ||
59 | } BURN_PAYLOADS; | ||
60 | |||
61 | |||
62 | // functions | ||
63 | |||
64 | HRESULT PayloadsParseFromXml( | ||
65 | __in BURN_PAYLOADS* pPayloads, | ||
66 | __in_opt BURN_CONTAINERS* pContainers, | ||
67 | __in_opt BURN_CATALOGS* pCatalogs, | ||
68 | __in IXMLDOMNode* pixnBundle | ||
69 | ); | ||
70 | void PayloadsUninitialize( | ||
71 | __in BURN_PAYLOADS* pPayloads | ||
72 | ); | ||
73 | HRESULT PayloadExtractFromContainer( | ||
74 | __in BURN_PAYLOADS* pPayloads, | ||
75 | __in_opt BURN_CONTAINER* pContainer, | ||
76 | __in BURN_CONTAINER_CONTEXT* pContainerContext, | ||
77 | __in_z LPCWSTR wzTargetDir | ||
78 | ); | ||
79 | HRESULT PayloadFindById( | ||
80 | __in BURN_PAYLOADS* pPayloads, | ||
81 | __in_z LPCWSTR wzId, | ||
82 | __out BURN_PAYLOAD** ppPayload | ||
83 | ); | ||
84 | HRESULT 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 | |||
5 | static const DWORD PIPE_64KB = 64 * 1024; | ||
6 | static const DWORD PIPE_WAIT_FOR_CONNECTION = 100; // wait a 10th of a second, | ||
7 | static const DWORD PIPE_RETRY_FOR_CONNECTION = 1800; // for up to 3 minutes. | ||
8 | |||
9 | static const LPCWSTR PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls"; | ||
10 | static const LPCWSTR CACHE_PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls.Cache"; | ||
11 | |||
12 | static 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 | ); | ||
19 | static void FreePipeMessage( | ||
20 | __in BURN_PIPE_MESSAGE *pMsg | ||
21 | ); | ||
22 | static HRESULT WritePipeMessage( | ||
23 | __in HANDLE hPipe, | ||
24 | __in DWORD dwMessage, | ||
25 | __in_bcount_opt(cbData) LPVOID pvData, | ||
26 | __in DWORD cbData | ||
27 | ); | ||
28 | static HRESULT GetPipeMessage( | ||
29 | __in HANDLE hPipe, | ||
30 | __in BURN_PIPE_MESSAGE* pMsg | ||
31 | ); | ||
32 | static 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 | *******************************************************************/ | ||
44 | void 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 | *******************************************************************/ | ||
57 | void 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 | *******************************************************************/ | ||
76 | extern "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 | |||
97 | LExit: | ||
98 | return hr; | ||
99 | } | ||
100 | |||
101 | /******************************************************************* | ||
102 | PipePumpMessages - | ||
103 | |||
104 | *******************************************************************/ | ||
105 | extern "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 | |||
185 | LExit: | ||
186 | ReleaseStr(sczMessage); | ||
187 | FreePipeMessage(&msg); | ||
188 | |||
189 | return hr; | ||
190 | } | ||
191 | |||
192 | /******************************************************************* | ||
193 | PipeCreateNameAndSecret - | ||
194 | |||
195 | *******************************************************************/ | ||
196 | extern "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 | |||
225 | LExit: | ||
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 | *******************************************************************/ | ||
236 | extern "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 | |||
303 | LExit: | ||
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 | *******************************************************************/ | ||
322 | const LPCWSTR BURN_COMMANDLINE_SWITCH_UNELEVATED = L"burn.unelevated"; | ||
323 | HRESULT 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 | |||
370 | LExit: | ||
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 | *******************************************************************/ | ||
384 | extern "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 | |||
414 | LExit: | ||
415 | ReleaseHandle(hProcess); | ||
416 | ReleaseStr(sczParameters); | ||
417 | |||
418 | return hr; | ||
419 | } | ||
420 | |||
421 | /******************************************************************* | ||
422 | PipeWaitForChildConnect - | ||
423 | |||
424 | *******************************************************************/ | ||
425 | extern "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 | |||
522 | LExit: | ||
523 | return hr; | ||
524 | } | ||
525 | |||
526 | /******************************************************************* | ||
527 | PipeTerminateChildProcess - | ||
528 | |||
529 | *******************************************************************/ | ||
530 | extern "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 | |||
586 | LExit: | ||
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 | *******************************************************************/ | ||
595 | extern "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 | |||
658 | LExit: | ||
659 | ReleaseStr(sczPipeName); | ||
660 | |||
661 | return hr; | ||
662 | } | ||
663 | |||
664 | |||
665 | static 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 | |||
699 | LExit: | ||
700 | ReleaseMem(pv); | ||
701 | return hr; | ||
702 | } | ||
703 | |||
704 | static 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 | |||
715 | static 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 | |||
742 | LExit: | ||
743 | ReleaseMem(pv); | ||
744 | return hr; | ||
745 | } | ||
746 | |||
747 | static 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 | |||
797 | LExit: | ||
798 | if (!pMsg->fAllocatedData && pMsg->pvData) | ||
799 | { | ||
800 | MemFree(pMsg->pvData); | ||
801 | } | ||
802 | |||
803 | return hr; | ||
804 | } | ||
805 | |||
806 | static 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 | |||
870 | LExit: | ||
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 | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | typedef 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 | |||
20 | typedef 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 | |||
27 | typedef struct _BURN_PIPE_MESSAGE | ||
28 | { | ||
29 | DWORD dwMessage; | ||
30 | DWORD cbData; | ||
31 | |||
32 | BOOL fAllocatedData; | ||
33 | LPVOID pvData; | ||
34 | } BURN_PIPE_MESSAGE; | ||
35 | |||
36 | typedef struct _BURN_PIPE_RESULT | ||
37 | { | ||
38 | DWORD dwResult; | ||
39 | BOOL fRestart; | ||
40 | } BURN_PIPE_RESULT; | ||
41 | |||
42 | |||
43 | typedef 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. | ||
51 | void PipeConnectionInitialize( | ||
52 | __in BURN_PIPE_CONNECTION* pConnection | ||
53 | ); | ||
54 | void PipeConnectionUninitialize( | ||
55 | __in BURN_PIPE_CONNECTION* pConnection | ||
56 | ); | ||
57 | HRESULT 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 | ); | ||
66 | HRESULT 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. | ||
74 | HRESULT PipeCreateNameAndSecret( | ||
75 | __out_z LPWSTR *psczConnectionName, | ||
76 | __out_z LPWSTR *psczSecret | ||
77 | ); | ||
78 | HRESULT PipeCreatePipes( | ||
79 | __in BURN_PIPE_CONNECTION* pConnection, | ||
80 | __in BOOL fCreateCachePipe, | ||
81 | __out HANDLE* phEvent | ||
82 | ); | ||
83 | HRESULT PipeLaunchParentProcess( | ||
84 | __in LPCWSTR wzCommandLine, | ||
85 | __in int nCmdShow, | ||
86 | __in_z LPWSTR sczConnectionName, | ||
87 | __in_z LPWSTR sczSecret, | ||
88 | __in BOOL fDisableUnelevate | ||
89 | ); | ||
90 | HRESULT PipeLaunchChildProcess( | ||
91 | __in_z LPCWSTR wzExecutablePath, | ||
92 | __in BURN_PIPE_CONNECTION* pConnection, | ||
93 | __in BOOL fElevate, | ||
94 | __in_opt HWND hwndParent | ||
95 | ); | ||
96 | HRESULT PipeWaitForChildConnect( | ||
97 | __in BURN_PIPE_CONNECTION* pConnection | ||
98 | ); | ||
99 | HRESULT PipeTerminateChildProcess( | ||
100 | __in BURN_PIPE_CONNECTION* pConnection, | ||
101 | __in DWORD dwParentExitCode, | ||
102 | __in BOOL fRestart | ||
103 | ); | ||
104 | |||
105 | // Child functions. | ||
106 | HRESULT 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 | |||
7 | struct PLAN_NONPERMANENT_PACKAGE_INDICES | ||
8 | { | ||
9 | DWORD iAfterExecuteFirstNonPermanentPackage; | ||
10 | DWORD iBeforeRollbackFirstNonPermanentPackage; | ||
11 | DWORD iAfterExecuteLastNonPermanentPackage; | ||
12 | DWORD iAfterRollbackLastNonPermanentPackage; | ||
13 | }; | ||
14 | |||
15 | // internal function definitions | ||
16 | |||
17 | static void UninitializeRegistrationAction( | ||
18 | __in BURN_DEPENDENT_REGISTRATION_ACTION* pAction | ||
19 | ); | ||
20 | static void UninitializeCacheAction( | ||
21 | __in BURN_CACHE_ACTION* pCacheAction | ||
22 | ); | ||
23 | static void ResetPlannedPackageState( | ||
24 | __in BURN_PACKAGE* pPackage | ||
25 | ); | ||
26 | static 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 | ); | ||
41 | static HRESULT ProcessPackageRollbackBoundary( | ||
42 | __in BURN_PLAN* pPlan, | ||
43 | __in_opt BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary, | ||
44 | __inout BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary | ||
45 | ); | ||
46 | static HRESULT GetActionDefaultRequestState( | ||
47 | __in BOOTSTRAPPER_ACTION action, | ||
48 | __in BOOL fPermanent, | ||
49 | __in BOOTSTRAPPER_PACKAGE_STATE currentState, | ||
50 | __out BOOTSTRAPPER_REQUEST_STATE* pRequestState | ||
51 | ); | ||
52 | static 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 | ); | ||
58 | static HRESULT AddCachePackage( | ||
59 | __in BURN_PLAN* pPlan, | ||
60 | __in BURN_PACKAGE* pPackage, | ||
61 | __out HANDLE* phSyncpointEvent | ||
62 | ); | ||
63 | static HRESULT AddCachePackageHelper( | ||
64 | __in BURN_PLAN* pPlan, | ||
65 | __in BURN_PACKAGE* pPackage, | ||
66 | __out HANDLE* phSyncpointEvent | ||
67 | ); | ||
68 | static HRESULT AddCacheSlipstreamMsps( | ||
69 | __in BURN_PLAN* pPlan, | ||
70 | __in BURN_PACKAGE* pPackage | ||
71 | ); | ||
72 | static BOOL AlreadyPlannedCachePackage( | ||
73 | __in BURN_PLAN* pPlan, | ||
74 | __in_z LPCWSTR wzPackageId, | ||
75 | __out HANDLE* phSyncpointEvent | ||
76 | ); | ||
77 | static DWORD GetNextCheckpointId(); | ||
78 | static HRESULT AppendCacheAction( | ||
79 | __in BURN_PLAN* pPlan, | ||
80 | __out BURN_CACHE_ACTION** ppCacheAction | ||
81 | ); | ||
82 | static HRESULT AppendRollbackCacheAction( | ||
83 | __in BURN_PLAN* pPlan, | ||
84 | __out BURN_CACHE_ACTION** ppCacheAction | ||
85 | ); | ||
86 | static 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 | ); | ||
94 | static 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 | ); | ||
102 | static 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 | ); | ||
111 | static 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 | ); | ||
119 | static 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 | ); | ||
125 | static 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 | ); | ||
131 | static BURN_CACHE_ACTION* ProcessSharedPayload( | ||
132 | __in BURN_PLAN* pPlan, | ||
133 | __in BURN_PAYLOAD* pPayload | ||
134 | ); | ||
135 | static HRESULT RemoveUnnecessaryActions( | ||
136 | __in BOOL fExecute, | ||
137 | __in BURN_EXECUTE_ACTION* rgActions, | ||
138 | __in DWORD cActions | ||
139 | ); | ||
140 | static HRESULT FinalizeSlipstreamPatchActions( | ||
141 | __in BOOL fExecute, | ||
142 | __in BURN_EXECUTE_ACTION* rgActions, | ||
143 | __in DWORD cActions | ||
144 | ); | ||
145 | static HRESULT PlanDependencyActions( | ||
146 | __in BOOL fBundlePerMachine, | ||
147 | __in BURN_PLAN* pPlan, | ||
148 | __in BURN_PACKAGE* pPackage | ||
149 | ); | ||
150 | static HRESULT CalculateExecuteActions( | ||
151 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
152 | __in BURN_PACKAGE* pPackage, | ||
153 | __in BURN_VARIABLES* pVariables, | ||
154 | __out_opt BOOL* pfBARequestedCache | ||
155 | ); | ||
156 | static BOOL NeedsCache( | ||
157 | __in BURN_PLAN* pPlan, | ||
158 | __in BURN_PACKAGE* pPackage | ||
159 | ); | ||
160 | static HRESULT CreateContainerProgress( | ||
161 | __in BURN_PLAN* pPlan, | ||
162 | __in BURN_CONTAINER* pContainer, | ||
163 | __out BURN_CACHE_CONTAINER_PROGRESS** ppContainerProgress | ||
164 | ); | ||
165 | static 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 | |||
173 | extern "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 | |||
266 | extern "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 | |||
309 | extern "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 | |||
319 | LExit: | ||
320 | return hr; | ||
321 | } | ||
322 | |||
323 | extern "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 | |||
387 | LExit: | ||
388 | return hr; | ||
389 | } | ||
390 | |||
391 | extern "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 | |||
461 | LExit: | ||
462 | ReleaseStr(sczLayoutDirectory); | ||
463 | ReleaseStr(sczExecutablePath); | ||
464 | |||
465 | return hr; | ||
466 | } | ||
467 | |||
468 | extern "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 | |||
601 | LExit: | ||
602 | return hr; | ||
603 | } | ||
604 | |||
605 | extern "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 | |||
777 | LExit: | ||
778 | ReleaseDict(sdIgnoreDependents); | ||
779 | ReleaseDependencyArray(rgDependencies, cDependencies); | ||
780 | |||
781 | return hr; | ||
782 | } | ||
783 | |||
784 | extern "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 | |||
814 | LExit: | ||
815 | return hr; | ||
816 | } | ||
817 | |||
818 | extern "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 | |||
848 | LExit: | ||
849 | return hr; | ||
850 | } | ||
851 | |||
852 | static 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 | |||
952 | LExit: | ||
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 | |||
968 | static 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 | |||
993 | LExit: | ||
994 | return hr; | ||
995 | } | ||
996 | |||
997 | extern "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 | |||
1053 | LExit: | ||
1054 | return hr; | ||
1055 | } | ||
1056 | |||
1057 | extern "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 | |||
1088 | LExit: | ||
1089 | return hr; | ||
1090 | } | ||
1091 | |||
1092 | extern "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 | |||
1195 | LExit: | ||
1196 | return hr; | ||
1197 | } | ||
1198 | |||
1199 | extern "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 | |||
1324 | LExit: | ||
1325 | ReleaseDict(sdAncestors); | ||
1326 | ReleaseStrArray(rgsczAncestors, cAncestors); | ||
1327 | |||
1328 | return hr; | ||
1329 | } | ||
1330 | |||
1331 | extern "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 | |||
1490 | LExit: | ||
1491 | ReleaseDict(sdProviderKeys); | ||
1492 | ReleaseStr(sczIgnoreDependencies); | ||
1493 | |||
1494 | return hr; | ||
1495 | } | ||
1496 | |||
1497 | extern "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 | |||
1515 | LExit: | ||
1516 | return hr; | ||
1517 | } | ||
1518 | |||
1519 | extern "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 | |||
1578 | LExit: | ||
1579 | return hr; | ||
1580 | } | ||
1581 | |||
1582 | extern "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 | |||
1610 | LExit: | ||
1611 | return hr; | ||
1612 | } | ||
1613 | |||
1614 | extern "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 | |||
1636 | LExit: | ||
1637 | return hr; | ||
1638 | } | ||
1639 | |||
1640 | extern "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 | |||
1654 | LExit: | ||
1655 | return hr; | ||
1656 | } | ||
1657 | |||
1658 | extern "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 | |||
1672 | LExit: | ||
1673 | return hr; | ||
1674 | } | ||
1675 | |||
1676 | extern "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 | |||
1689 | LExit: | ||
1690 | return hr; | ||
1691 | } | ||
1692 | |||
1693 | extern "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 | |||
1706 | LExit: | ||
1707 | return hr; | ||
1708 | } | ||
1709 | |||
1710 | extern "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 | |||
1737 | LExit: | ||
1738 | return hr; | ||
1739 | } | ||
1740 | |||
1741 | extern "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 | |||
1768 | LExit: | ||
1769 | return hr; | ||
1770 | } | ||
1771 | |||
1772 | extern "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 | |||
1794 | LExit: | ||
1795 | return hr; | ||
1796 | } | ||
1797 | |||
1798 | extern "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 | |||
1821 | LExit: | ||
1822 | return hr; | ||
1823 | } | ||
1824 | |||
1825 | /******************************************************************* | ||
1826 | PlanSetResumeCommand - Initializes resume command string | ||
1827 | |||
1828 | *******************************************************************/ | ||
1829 | extern "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 | |||
1842 | LExit: | ||
1843 | return hr; | ||
1844 | } | ||
1845 | |||
1846 | |||
1847 | // internal function definitions | ||
1848 | |||
1849 | static 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 | |||
1858 | static 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 | |||
1893 | static 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 | |||
1933 | static 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 | |||
2001 | LExit: | ||
2002 | return hr; | ||
2003 | } | ||
2004 | |||
2005 | static 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 | |||
2046 | LExit: | ||
2047 | return hr; | ||
2048 | } | ||
2049 | |||
2050 | static 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 | |||
2068 | LExit: | ||
2069 | return hr; | ||
2070 | } | ||
2071 | |||
2072 | static 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 | |||
2176 | LExit: | ||
2177 | return hr; | ||
2178 | } | ||
2179 | |||
2180 | static 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 | |||
2199 | LExit: | ||
2200 | return hr; | ||
2201 | } | ||
2202 | |||
2203 | static 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 | |||
2233 | static DWORD GetNextCheckpointId() | ||
2234 | { | ||
2235 | static DWORD dwCounter = 0; | ||
2236 | return ++dwCounter; | ||
2237 | } | ||
2238 | |||
2239 | static 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 | |||
2252 | LExit: | ||
2253 | return hr; | ||
2254 | } | ||
2255 | |||
2256 | static 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 | |||
2269 | LExit: | ||
2270 | return hr; | ||
2271 | } | ||
2272 | |||
2273 | static 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 | |||
2328 | LExit: | ||
2329 | ReleaseNullStr(sczContainerWorkingPath); | ||
2330 | |||
2331 | return hr; | ||
2332 | } | ||
2333 | |||
2334 | static 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 | |||
2465 | LExit: | ||
2466 | ReleaseStr(sczPayloadWorkingPath); | ||
2467 | |||
2468 | return hr; | ||
2469 | } | ||
2470 | |||
2471 | static 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 | |||
2513 | static 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 | |||
2604 | LExit: | ||
2605 | ReleaseStr(sczContainerWorkingPath); | ||
2606 | |||
2607 | return hr; | ||
2608 | } | ||
2609 | |||
2610 | static 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 | |||
2647 | LExit: | ||
2648 | ReleaseStr(sczContainerWorkingPath); | ||
2649 | |||
2650 | return hr; | ||
2651 | } | ||
2652 | |||
2653 | static 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 | |||
2674 | LExit: | ||
2675 | return hr; | ||
2676 | } | ||
2677 | |||
2678 | static 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 | |||
2727 | static 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 | |||
2770 | static 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 | |||
2815 | LExit: | ||
2816 | return hr; | ||
2817 | } | ||
2818 | |||
2819 | static 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 | |||
2836 | LExit: | ||
2837 | return hr; | ||
2838 | } | ||
2839 | |||
2840 | static 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 | |||
2873 | LExit: | ||
2874 | return hr; | ||
2875 | } | ||
2876 | |||
2877 | static 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 | |||
2897 | static 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 | |||
2933 | LExit: | ||
2934 | return hr; | ||
2935 | } | ||
2936 | |||
2937 | static 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 | |||
2973 | LExit: | ||
2974 | return hr; | ||
2975 | } | ||
2976 | |||
2977 | |||
2978 | #ifdef DEBUG | ||
2979 | |||
2980 | static 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 | |||
3051 | static 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 | |||
3122 | extern "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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // constants | ||
11 | |||
12 | const DWORD BURN_PLAN_INVALID_ACTION_INDEX = 0x80000000; | ||
13 | |||
14 | enum 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 | |||
22 | enum 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 | |||
29 | enum 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 | |||
36 | enum 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 | |||
54 | enum 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 | |||
73 | enum 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 | |||
83 | typedef struct _BURN_EXTRACT_PAYLOAD | ||
84 | { | ||
85 | BURN_PACKAGE* pPackage; | ||
86 | BURN_PAYLOAD* pPayload; | ||
87 | LPWSTR sczUnverifiedPath; | ||
88 | } BURN_EXTRACT_PAYLOAD; | ||
89 | |||
90 | typedef 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 | |||
97 | typedef 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 | |||
105 | typedef 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 | |||
113 | typedef 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 | |||
211 | typedef struct _BURN_ORDERED_PATCHES | ||
212 | { | ||
213 | DWORD dwOrder; | ||
214 | BURN_PACKAGE* pPackage; | ||
215 | } BURN_ORDERED_PATCHES; | ||
216 | |||
217 | typedef 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 | |||
309 | typedef struct _BURN_CLEAN_ACTION | ||
310 | { | ||
311 | BURN_PACKAGE* pPackage; | ||
312 | } BURN_CLEAN_ACTION; | ||
313 | |||
314 | typedef 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 | |||
370 | void PlanReset( | ||
371 | __in BURN_PLAN* pPlan, | ||
372 | __in BURN_PACKAGES* pPackages | ||
373 | ); | ||
374 | void PlanUninitializeExecuteAction( | ||
375 | __in BURN_EXECUTE_ACTION* pExecuteAction | ||
376 | ); | ||
377 | HRESULT PlanSetVariables( | ||
378 | __in BOOTSTRAPPER_ACTION action, | ||
379 | __in BURN_VARIABLES* pVariables | ||
380 | ); | ||
381 | HRESULT 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 | ); | ||
391 | 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 | HRESULT 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 | ); | ||
412 | HRESULT 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 | ); | ||
420 | HRESULT 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 | ); | ||
430 | HRESULT 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 | ); | ||
440 | HRESULT PlanLayoutPackage( | ||
441 | __in BURN_PLAN* pPlan, | ||
442 | __in BURN_PACKAGE* pPackage, | ||
443 | __in_z_opt LPCWSTR wzLayoutDirectory | ||
444 | ); | ||
445 | HRESULT 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 | ); | ||
453 | HRESULT 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 | ); | ||
463 | HRESULT PlanRelatedBundlesBegin( | ||
464 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
465 | __in BURN_REGISTRATION* pRegistration, | ||
466 | __in BOOTSTRAPPER_RELATION_TYPE relationType, | ||
467 | __in BURN_PLAN* pPlan | ||
468 | ); | ||
469 | HRESULT 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 | ); | ||
477 | HRESULT PlanFinalizeActions( | ||
478 | __in BURN_PLAN* pPlan | ||
479 | ); | ||
480 | HRESULT PlanCleanPackage( | ||
481 | __in BURN_PLAN* pPlan, | ||
482 | __in BURN_PACKAGE* pPackage | ||
483 | ); | ||
484 | HRESULT PlanExecuteCacheSyncAndRollback( | ||
485 | __in BURN_PLAN* pPlan, | ||
486 | __in BURN_PACKAGE* pPackage, | ||
487 | __in HANDLE hCacheEvent, | ||
488 | __in BOOL fPlanPackageCacheRollback | ||
489 | ); | ||
490 | HRESULT PlanExecuteCheckpoint( | ||
491 | __in BURN_PLAN* pPlan | ||
492 | ); | ||
493 | HRESULT PlanInsertExecuteAction( | ||
494 | __in DWORD dwIndex, | ||
495 | __in BURN_PLAN* pPlan, | ||
496 | __out BURN_EXECUTE_ACTION** ppExecuteAction | ||
497 | ); | ||
498 | HRESULT PlanInsertRollbackAction( | ||
499 | __in DWORD dwIndex, | ||
500 | __in BURN_PLAN* pPlan, | ||
501 | __out BURN_EXECUTE_ACTION** ppRollbackAction | ||
502 | ); | ||
503 | HRESULT PlanAppendExecuteAction( | ||
504 | __in BURN_PLAN* pPlan, | ||
505 | __out BURN_EXECUTE_ACTION** ppExecuteAction | ||
506 | ); | ||
507 | HRESULT PlanAppendRollbackAction( | ||
508 | __in BURN_PLAN* pPlan, | ||
509 | __out BURN_EXECUTE_ACTION** ppExecuteAction | ||
510 | ); | ||
511 | HRESULT PlanKeepRegistration( | ||
512 | __in BURN_PLAN* pPlan, | ||
513 | __in DWORD iAfterExecutePackageAction, | ||
514 | __in DWORD iBeforeRollbackPackageAction | ||
515 | ); | ||
516 | HRESULT PlanRemoveRegistration( | ||
517 | __in BURN_PLAN* pPlan, | ||
518 | __in DWORD iAfterExecutePackageAction, | ||
519 | __in DWORD iAfterRollbackPackageAction | ||
520 | ); | ||
521 | HRESULT PlanRollbackBoundaryBegin( | ||
522 | __in BURN_PLAN* pPlan, | ||
523 | __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary | ||
524 | ); | ||
525 | HRESULT PlanRollbackBoundaryComplete( | ||
526 | __in BURN_PLAN* pPlan | ||
527 | ); | ||
528 | HRESULT 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 | ||
536 | void 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 | |||
8 | PFN_INITIATESYSTEMSHUTDOWNEXW vpfnInitiateSystemShutdownExW; | ||
9 | |||
10 | |||
11 | // function definitions | ||
12 | |||
13 | extern "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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // typedefs | ||
11 | |||
12 | typedef 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 | |||
24 | extern PFN_INITIATESYSTEMSHUTDOWNEXW vpfnInitiateSystemShutdownExW; | ||
25 | |||
26 | |||
27 | // function declarations | ||
28 | |||
29 | void 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 | |||
6 | extern "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 | |||
152 | LExit: | ||
153 | ReleaseStr(sczRelationTypeCommandLineSwitch); | ||
154 | |||
155 | return hr; | ||
156 | } | ||
157 | |||
158 | extern "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 | |||
268 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | HRESULT 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 | ); | ||
28 | HRESULT 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 | |||
8 | const LPCWSTR REGISTRY_RUN_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"; | ||
9 | const LPCWSTR REGISTRY_RUN_ONCE_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce"; | ||
10 | const LPCWSTR REGISTRY_REBOOT_PENDING_FORMAT = L"%ls.RebootRequired"; | ||
11 | const LPCWSTR REGISTRY_BUNDLE_INSTALLED = L"Installed"; | ||
12 | const LPCWSTR REGISTRY_BUNDLE_DISPLAY_ICON = L"DisplayIcon"; | ||
13 | const LPCWSTR REGISTRY_BUNDLE_DISPLAY_VERSION = L"DisplayVersion"; | ||
14 | const LPCWSTR REGISTRY_BUNDLE_ESTIMATED_SIZE = L"EstimatedSize"; | ||
15 | const LPCWSTR REGISTRY_BUNDLE_PUBLISHER = L"Publisher"; | ||
16 | const LPCWSTR REGISTRY_BUNDLE_HELP_LINK = L"HelpLink"; | ||
17 | const LPCWSTR REGISTRY_BUNDLE_HELP_TELEPHONE = L"HelpTelephone"; | ||
18 | const LPCWSTR REGISTRY_BUNDLE_URL_INFO_ABOUT = L"URLInfoAbout"; | ||
19 | const LPCWSTR REGISTRY_BUNDLE_URL_UPDATE_INFO = L"URLUpdateInfo"; | ||
20 | const LPCWSTR REGISTRY_BUNDLE_PARENT_DISPLAY_NAME = L"ParentDisplayName"; | ||
21 | const LPCWSTR REGISTRY_BUNDLE_PARENT_KEY_NAME = L"ParentKeyName"; | ||
22 | const LPCWSTR REGISTRY_BUNDLE_COMMENTS = L"Comments"; | ||
23 | const LPCWSTR REGISTRY_BUNDLE_CONTACT = L"Contact"; | ||
24 | const LPCWSTR REGISTRY_BUNDLE_NO_MODIFY = L"NoModify"; | ||
25 | const LPCWSTR REGISTRY_BUNDLE_MODIFY_PATH = L"ModifyPath"; | ||
26 | const LPCWSTR REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY = L"NoElevateOnModify"; | ||
27 | const LPCWSTR REGISTRY_BUNDLE_NO_REMOVE = L"NoRemove"; | ||
28 | const LPCWSTR REGISTRY_BUNDLE_SYSTEM_COMPONENT = L"SystemComponent"; | ||
29 | const LPCWSTR REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING = L"QuietUninstallString"; | ||
30 | const LPCWSTR REGISTRY_BUNDLE_UNINSTALL_STRING = L"UninstallString"; | ||
31 | const LPCWSTR REGISTRY_BUNDLE_RESUME_COMMAND_LINE = L"BundleResumeCommandLine"; | ||
32 | const LPCWSTR REGISTRY_BUNDLE_VERSION_MAJOR = L"VersionMajor"; | ||
33 | const LPCWSTR REGISTRY_BUNDLE_VERSION_MINOR = L"VersionMinor"; | ||
34 | |||
35 | // internal function declarations | ||
36 | |||
37 | static HRESULT ParseSoftwareTagsFromXml( | ||
38 | __in IXMLDOMNode* pixnRegistrationNode, | ||
39 | __out BURN_SOFTWARE_TAG** prgSoftwareTags, | ||
40 | __out DWORD* pcSoftwareTags | ||
41 | ); | ||
42 | static HRESULT SetPaths( | ||
43 | __in BURN_REGISTRATION* pRegistration | ||
44 | ); | ||
45 | static HRESULT GetBundleManufacturer( | ||
46 | __in BURN_REGISTRATION* pRegistration, | ||
47 | __in BURN_VARIABLES* pVariables, | ||
48 | __out LPWSTR* psczBundleManufacturer | ||
49 | ); | ||
50 | static HRESULT GetBundleName( | ||
51 | __in BURN_REGISTRATION* pRegistration, | ||
52 | __in BURN_VARIABLES* pVariables, | ||
53 | __out LPWSTR* psczBundleName | ||
54 | ); | ||
55 | static HRESULT UpdateResumeMode( | ||
56 | __in BURN_REGISTRATION* pRegistration, | ||
57 | __in HKEY hkRegistration, | ||
58 | __in BURN_RESUME_MODE resumeMode, | ||
59 | __in BOOL fRestartInitiated | ||
60 | ); | ||
61 | static HRESULT ParseRelatedCodes( | ||
62 | __in BURN_REGISTRATION* pRegistration, | ||
63 | __in IXMLDOMNode* pixnBundle | ||
64 | ); | ||
65 | static HRESULT FormatUpdateRegistrationKey( | ||
66 | __in BURN_REGISTRATION* pRegistration, | ||
67 | __out_z LPWSTR* psczKey | ||
68 | ); | ||
69 | static HRESULT WriteSoftwareTags( | ||
70 | __in BOOL fPerMachine, | ||
71 | __in BURN_SOFTWARE_TAGS* pSoftwareTags | ||
72 | ); | ||
73 | static HRESULT RemoveSoftwareTags( | ||
74 | __in BOOL fPerMachine, | ||
75 | __in BURN_SOFTWARE_TAGS* pSoftwareTags | ||
76 | ); | ||
77 | static HRESULT WriteUpdateRegistration( | ||
78 | __in BURN_REGISTRATION* pRegistration, | ||
79 | __in BURN_VARIABLES* pVariables | ||
80 | ); | ||
81 | static HRESULT RemoveUpdateRegistration( | ||
82 | __in BURN_REGISTRATION* pRegistration | ||
83 | ); | ||
84 | static HRESULT RegWriteStringVariable( | ||
85 | __in HKEY hkKey, | ||
86 | __in BURN_VARIABLES* pVariables, | ||
87 | __in LPCWSTR wzVariable, | ||
88 | __in LPCWSTR wzName | ||
89 | ); | ||
90 | static 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 | *******************************************************************/ | ||
102 | extern "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 | |||
310 | LExit: | ||
311 | ReleaseObject(pixnRegistrationNode); | ||
312 | ReleaseObject(pixnArpNode); | ||
313 | ReleaseObject(pixnUpdateNode); | ||
314 | ReleaseStr(scz); | ||
315 | |||
316 | return hr; | ||
317 | } | ||
318 | |||
319 | /******************************************************************* | ||
320 | RegistrationUninitialize - | ||
321 | |||
322 | *******************************************************************/ | ||
323 | extern "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 | *******************************************************************/ | ||
405 | extern "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 | |||
442 | LExit: | ||
443 | ReleaseStr(sczBundleManufacturer); | ||
444 | ReleaseStr(sczBundleName); | ||
445 | |||
446 | return hr; | ||
447 | } | ||
448 | |||
449 | extern "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 | *******************************************************************/ | ||
482 | extern "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 | |||
549 | LExit: | ||
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 | *******************************************************************/ | ||
562 | extern "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 | |||
574 | LExit: | ||
575 | return hr; | ||
576 | } | ||
577 | |||
578 | /******************************************************************* | ||
579 | RegistrationSessionBegin - Registers a run session on the system. | ||
580 | |||
581 | *******************************************************************/ | ||
582 | extern "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 | |||
823 | LExit: | ||
824 | ReleaseStr(sczPublisher); | ||
825 | ReleaseRegKey(hkRegistration); | ||
826 | |||
827 | return hr; | ||
828 | } | ||
829 | |||
830 | |||
831 | /******************************************************************* | ||
832 | RegistrationSessionResume - Resumes a previous run session. | ||
833 | |||
834 | *******************************************************************/ | ||
835 | extern "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 | |||
855 | LExit: | ||
856 | ReleaseRegKey(hkRegistration); | ||
857 | |||
858 | return hr; | ||
859 | } | ||
860 | |||
861 | |||
862 | /******************************************************************* | ||
863 | RegistrationSessionEnd - Unregisters a run session from the system. | ||
864 | |||
865 | *******************************************************************/ | ||
866 | extern "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 | |||
942 | LExit: | ||
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 | *******************************************************************/ | ||
954 | extern "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 | |||
971 | LExit: | ||
972 | return hr; | ||
973 | } | ||
974 | |||
975 | /******************************************************************* | ||
976 | RegistrationLoadState - Loads a previously stored engine state BLOB. | ||
977 | |||
978 | *******************************************************************/ | ||
979 | extern "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 | /******************************************************************* | ||
991 | RegistrationGetResumeCommandLine - Gets the resume command line from the registry | ||
992 | |||
993 | *******************************************************************/ | ||
994 | extern "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 | |||
1023 | static 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 | |||
1081 | LExit: | ||
1082 | ReleaseBSTR(bstrTagXml); | ||
1083 | ReleaseObject(pixnNode); | ||
1084 | ReleaseObject(pixnNodes); | ||
1085 | ReleaseMem(pSoftwareTags); | ||
1086 | |||
1087 | return hr; | ||
1088 | } | ||
1089 | |||
1090 | static 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 | |||
1116 | LExit: | ||
1117 | ReleaseStr(sczCacheDirectory); | ||
1118 | return hr; | ||
1119 | } | ||
1120 | |||
1121 | static 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 | |||
1139 | LExit: | ||
1140 | return hr; | ||
1141 | } | ||
1142 | |||
1143 | static 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 | |||
1161 | LExit: | ||
1162 | return hr; | ||
1163 | } | ||
1164 | |||
1165 | static 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 | |||
1262 | LExit: | ||
1263 | ReleaseStr(sczResumeCommandLine); | ||
1264 | ReleaseRegKey(hkRebootRequired); | ||
1265 | ReleaseRegKey(hkRun); | ||
1266 | |||
1267 | return hr; | ||
1268 | } | ||
1269 | |||
1270 | static 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 | |||
1342 | LExit: | ||
1343 | ReleaseObject(pixnNodes); | ||
1344 | ReleaseObject(pixnElement); | ||
1345 | ReleaseStr(sczAction); | ||
1346 | ReleaseStr(sczId); | ||
1347 | |||
1348 | return hr; | ||
1349 | } | ||
1350 | |||
1351 | static 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 | |||
1374 | LExit: | ||
1375 | ReleaseStr(sczKey); | ||
1376 | |||
1377 | return hr; | ||
1378 | } | ||
1379 | |||
1380 | static 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 | |||
1410 | LExit: | ||
1411 | ReleaseStr(sczPath); | ||
1412 | ReleaseStr(sczRegidFolder); | ||
1413 | ReleaseStr(sczRootFolder); | ||
1414 | |||
1415 | return hr; | ||
1416 | } | ||
1417 | |||
1418 | static 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 | |||
1447 | LExit: | ||
1448 | ReleaseStr(sczPath); | ||
1449 | ReleaseStr(sczRegidFolder); | ||
1450 | ReleaseStr(sczRootFolder); | ||
1451 | |||
1452 | return hr; | ||
1453 | } | ||
1454 | |||
1455 | static 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 | |||
1503 | LExit: | ||
1504 | ReleaseRegKey(hkKey); | ||
1505 | ReleaseStr(sczKey); | ||
1506 | |||
1507 | return hr; | ||
1508 | } | ||
1509 | |||
1510 | static 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 | |||
1552 | LExit: | ||
1553 | ReleaseStr(sczPackageVersion); | ||
1554 | ReleaseStr(sczKey); | ||
1555 | |||
1556 | return hr; | ||
1557 | } | ||
1558 | |||
1559 | static 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 | |||
1575 | LExit: | ||
1576 | StrSecureZeroFreeString(sczValue); | ||
1577 | |||
1578 | return hr; | ||
1579 | } | ||
1580 | |||
1581 | static 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 | |||
1595 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | enum BURN_MODE; | ||
11 | enum BURN_DEPENDENCY_REGISTRATION_ACTION; | ||
12 | struct _BURN_LOGGING; | ||
13 | typedef _BURN_LOGGING BURN_LOGGING; | ||
14 | |||
15 | // constants | ||
16 | |||
17 | const LPCWSTR BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; | ||
18 | const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH = L"BundleCachePath"; | ||
19 | const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE = L"BundleAddonCode"; | ||
20 | const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE = L"BundleDetectCode"; | ||
21 | const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE = L"BundlePatchCode"; | ||
22 | const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgradeCode"; | ||
23 | const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME = L"DisplayName"; | ||
24 | const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION = L"BundleVersion"; | ||
25 | const LPCWSTR BURN_REGISTRATION_REGISTRY_ENGINE_VERSION = L"EngineVersion"; | ||
26 | const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey"; | ||
27 | const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_TAG = L"BundleTag"; | ||
28 | |||
29 | enum 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 | |||
38 | enum 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 | |||
48 | typedef 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 | |||
58 | typedef 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 | |||
68 | typedef struct _BURN_RELATED_BUNDLES | ||
69 | { | ||
70 | BURN_RELATED_BUNDLE* rgRelatedBundles; | ||
71 | DWORD cRelatedBundles; | ||
72 | } BURN_RELATED_BUNDLES; | ||
73 | |||
74 | typedef struct _BURN_SOFTWARE_TAG | ||
75 | { | ||
76 | LPWSTR sczFilename; | ||
77 | LPWSTR sczRegid; | ||
78 | LPSTR sczTag; | ||
79 | } BURN_SOFTWARE_TAG; | ||
80 | |||
81 | typedef struct _BURN_SOFTWARE_TAGS | ||
82 | { | ||
83 | BURN_SOFTWARE_TAG* rgSoftwareTags; | ||
84 | DWORD cSoftwareTags; | ||
85 | } BURN_SOFTWARE_TAGS; | ||
86 | |||
87 | typedef 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 | |||
155 | HRESULT RegistrationParseFromXml( | ||
156 | __in BURN_REGISTRATION* pRegistration, | ||
157 | __in IXMLDOMNode* pixnBundle | ||
158 | ); | ||
159 | void RegistrationUninitialize( | ||
160 | __in BURN_REGISTRATION* pRegistration | ||
161 | ); | ||
162 | HRESULT RegistrationSetVariables( | ||
163 | __in BURN_REGISTRATION* pRegistration, | ||
164 | __in BURN_VARIABLES* pVariables | ||
165 | ); | ||
166 | HRESULT RegistrationDetectInstalled( | ||
167 | __in BURN_REGISTRATION* pRegistration, | ||
168 | __out BOOL* pfInstalled | ||
169 | ); | ||
170 | HRESULT RegistrationDetectResumeType( | ||
171 | __in BURN_REGISTRATION* pRegistration, | ||
172 | __out BOOTSTRAPPER_RESUME_TYPE* pResumeType | ||
173 | ); | ||
174 | HRESULT RegistrationDetectRelatedBundles( | ||
175 | __in BURN_REGISTRATION* pRegistration | ||
176 | ); | ||
177 | HRESULT 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 | ); | ||
186 | HRESULT RegistrationSessionResume( | ||
187 | __in BURN_REGISTRATION* pRegistration, | ||
188 | __in BURN_VARIABLES* pVariables | ||
189 | ); | ||
190 | HRESULT 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 | ); | ||
196 | HRESULT RegistrationSaveState( | ||
197 | __in BURN_REGISTRATION* pRegistration, | ||
198 | __in_bcount_opt(cbBuffer) BYTE* pbBuffer, | ||
199 | __in_opt DWORD cbBuffer | ||
200 | ); | ||
201 | HRESULT RegistrationLoadState( | ||
202 | __in BURN_REGISTRATION* pRegistration, | ||
203 | __out_bcount(*pcbBuffer) BYTE** ppbBuffer, | ||
204 | __out DWORD* pcbBuffer | ||
205 | ); | ||
206 | HRESULT 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 | |||
7 | static 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 | ); | ||
14 | static HRESULT DetermineRelationType( | ||
15 | __in HKEY hkBundleId, | ||
16 | __in BURN_REGISTRATION* pRegistration, | ||
17 | __out BOOTSTRAPPER_RELATION_TYPE* pRelationType | ||
18 | ); | ||
19 | static 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 | |||
30 | extern "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 | |||
68 | LExit: | ||
69 | ReleaseStr(sczRelatedBundleId); | ||
70 | ReleaseRegKey(hkUninstallKey); | ||
71 | |||
72 | return hr; | ||
73 | } | ||
74 | |||
75 | extern "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 | |||
96 | static 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 | |||
130 | LExit: | ||
131 | ReleaseRegKey(hkBundleId); | ||
132 | |||
133 | return hr; | ||
134 | } | ||
135 | |||
136 | static 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 | |||
371 | LExit: | ||
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 | |||
389 | static 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 | |||
452 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | HRESULT RelatedBundlesInitializeForScope( | ||
10 | __in BOOL fPerMachine, | ||
11 | __in BURN_REGISTRATION* pRegistration, | ||
12 | __in BURN_RELATED_BUNDLES* pRelatedBundles | ||
13 | ); | ||
14 | void 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 | |||
8 | static HRESULT DirectorySearchExists( | ||
9 | __in BURN_SEARCH* pSearch, | ||
10 | __in BURN_VARIABLES* pVariables | ||
11 | ); | ||
12 | static HRESULT DirectorySearchPath( | ||
13 | __in BURN_SEARCH* pSearch, | ||
14 | __in BURN_VARIABLES* pVariables | ||
15 | ); | ||
16 | static HRESULT FileSearchExists( | ||
17 | __in BURN_SEARCH* pSearch, | ||
18 | __in BURN_VARIABLES* pVariables | ||
19 | ); | ||
20 | static HRESULT FileSearchVersion( | ||
21 | __in BURN_SEARCH* pSearch, | ||
22 | __in BURN_VARIABLES* pVariables | ||
23 | ); | ||
24 | static HRESULT FileSearchPath( | ||
25 | __in BURN_SEARCH* pSearch, | ||
26 | __in BURN_VARIABLES* pVariables | ||
27 | ); | ||
28 | static HRESULT RegistrySearchExists( | ||
29 | __in BURN_SEARCH* pSearch, | ||
30 | __in BURN_VARIABLES* pVariables | ||
31 | ); | ||
32 | static HRESULT RegistrySearchValue( | ||
33 | __in BURN_SEARCH* pSearch, | ||
34 | __in BURN_VARIABLES* pVariables | ||
35 | ); | ||
36 | static HRESULT MsiComponentSearch( | ||
37 | __in BURN_SEARCH* pSearch, | ||
38 | __in BURN_VARIABLES* pVariables | ||
39 | ); | ||
40 | static HRESULT MsiProductSearch( | ||
41 | __in BURN_SEARCH* pSearch, | ||
42 | __in BURN_VARIABLES* pVariables | ||
43 | ); | ||
44 | static HRESULT MsiFeatureSearch( | ||
45 | __in BURN_SEARCH* pSearch, | ||
46 | __in BURN_VARIABLES* pVariables | ||
47 | ); | ||
48 | |||
49 | |||
50 | // function definitions | ||
51 | |||
52 | extern "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 | |||
389 | LExit: | ||
390 | ReleaseObject(pixnNodes); | ||
391 | ReleaseObject(pixnNode); | ||
392 | ReleaseBSTR(bstrNodeName); | ||
393 | ReleaseStr(scz); | ||
394 | return hr; | ||
395 | } | ||
396 | |||
397 | extern "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 | |||
493 | LExit: | ||
494 | return hr; | ||
495 | } | ||
496 | |||
497 | extern "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 | |||
543 | static 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 | |||
578 | LExit: | ||
579 | StrSecureZeroFreeString(sczPath); | ||
580 | |||
581 | return hr; | ||
582 | } | ||
583 | |||
584 | static 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 | |||
619 | LExit: | ||
620 | StrSecureZeroFreeString(sczPath); | ||
621 | |||
622 | return hr; | ||
623 | } | ||
624 | |||
625 | static 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 | |||
663 | LExit: | ||
664 | StrSecureZeroFreeString(sczPath); | ||
665 | return hr; | ||
666 | } | ||
667 | |||
668 | static 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 | |||
695 | LExit: | ||
696 | StrSecureZeroFreeString(sczPath); | ||
697 | return hr; | ||
698 | } | ||
699 | |||
700 | static 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 | |||
735 | LExit: | ||
736 | StrSecureZeroFreeString(sczPath); | ||
737 | |||
738 | return hr; | ||
739 | } | ||
740 | |||
741 | static 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 | |||
810 | LExit: | ||
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 | |||
824 | static 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 | |||
940 | LExit: | ||
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 | |||
956 | static 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 | |||
1029 | LExit: | ||
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 | |||
1041 | static 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 | |||
1168 | LExit: | ||
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 | |||
1181 | static 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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // constants | ||
11 | |||
12 | enum 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 | |||
23 | enum 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 | |||
30 | enum 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 | |||
38 | enum 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 | |||
45 | enum 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 | |||
53 | enum 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 | |||
62 | enum 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 | |||
69 | enum 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 | |||
78 | typedef 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 | |||
128 | typedef struct _BURN_SEARCHES | ||
129 | { | ||
130 | BURN_SEARCH* rgSearches; | ||
131 | DWORD cSearches; | ||
132 | } BURN_SEARCHES; | ||
133 | |||
134 | |||
135 | // function declarations | ||
136 | |||
137 | HRESULT SearchesParseFromXml( | ||
138 | __in BURN_SEARCHES* pSearches, | ||
139 | __in IXMLDOMNode* pixnBundle | ||
140 | ); | ||
141 | HRESULT SearchesExecute( | ||
142 | __in BURN_SEARCHES* pSearches, | ||
143 | __in BURN_VARIABLES* pVariables | ||
144 | ); | ||
145 | void 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 | ||
15 | typedef 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 | |||
32 | static HRESULT VerifySectionMatchesMemoryPEHeader( | ||
33 | __in REFGUID pSection | ||
34 | ); | ||
35 | |||
36 | |||
37 | extern "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, §ionHeader, 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 | |||
247 | LExit: | ||
248 | ReleaseMem(pBurnSectionHeader); | ||
249 | |||
250 | return hr; | ||
251 | } | ||
252 | |||
253 | extern "C" void SectionUninitialize( | ||
254 | __out BURN_SECTION* pSection | ||
255 | ) | ||
256 | { | ||
257 | ReleaseMem(pSection->rgcbContainers); | ||
258 | memset(pSection, 0, sizeof(BURN_SECTION)); | ||
259 | } | ||
260 | |||
261 | extern "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 | |||
303 | LExit: | ||
304 | return hr; | ||
305 | } | ||
306 | |||
307 | HRESULT 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 | |||
397 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // structs | ||
11 | |||
12 | typedef 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 | |||
35 | HRESULT SectionInitialize( | ||
36 | __in BURN_SECTION* pSection, | ||
37 | __in HANDLE hEngineFile, | ||
38 | __in HANDLE hSourceEngineFile | ||
39 | ); | ||
40 | void SectionUninitialize( | ||
41 | __in BURN_SECTION* pSection | ||
42 | ); | ||
43 | HRESULT 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 | |||
5 | using namespace Gdiplus; | ||
6 | |||
7 | #define BURN_SPLASHSCREEN_CLASS_WINDOW L"WixBurnSplashScreen" | ||
8 | #define IDB_SPLASHSCREEN 1 | ||
9 | |||
10 | // struct | ||
11 | |||
12 | struct SPLASHSCREEN_INFO | ||
13 | { | ||
14 | Bitmap* pBitmap; | ||
15 | Point pt; | ||
16 | Size size; | ||
17 | }; | ||
18 | |||
19 | struct SPLASHSCREEN_CONTEXT | ||
20 | { | ||
21 | HANDLE hIntializedEvent; | ||
22 | HINSTANCE hInstance; | ||
23 | LPCWSTR wzCaption; | ||
24 | |||
25 | HWND* pHwnd; | ||
26 | }; | ||
27 | |||
28 | // internal function definitions | ||
29 | |||
30 | static DWORD WINAPI ThreadProc( | ||
31 | __in LPVOID pvContext | ||
32 | ); | ||
33 | static LRESULT CALLBACK WndProc( | ||
34 | __in HWND hWnd, | ||
35 | __in UINT uMsg, | ||
36 | __in WPARAM wParam, | ||
37 | __in LPARAM lParam | ||
38 | ); | ||
39 | static void OnPaint( | ||
40 | __in HDC hdc, | ||
41 | __in SPLASHSCREEN_INFO* pSplashScreen | ||
42 | ); | ||
43 | static HRESULT LoadSplashScreen( | ||
44 | __in HMODULE hInstance, | ||
45 | __in SPLASHSCREEN_INFO* pSplashScreen | ||
46 | ); | ||
47 | |||
48 | |||
49 | // function definitions | ||
50 | |||
51 | extern "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 | |||
79 | LExit: | ||
80 | ReleaseHandle(rgSplashScreenEvents[1]); | ||
81 | ReleaseHandle(rgSplashScreenEvents[0]); | ||
82 | } | ||
83 | |||
84 | extern "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 | |||
106 | LExit: | ||
107 | ReleaseStr(sczDisplayString); | ||
108 | |||
109 | return hr; | ||
110 | } | ||
111 | |||
112 | |||
113 | static 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 | |||
175 | LExit: | ||
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 | |||
194 | static 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 | |||
243 | static 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 | |||
263 | static 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 | |||
314 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // constants | ||
11 | |||
12 | |||
13 | // structs | ||
14 | |||
15 | |||
16 | // functions | ||
17 | |||
18 | void SplashScreenCreate( | ||
19 | __in HINSTANCE hInstance, | ||
20 | __in_z_opt LPCWSTR wzCaption, | ||
21 | __out HWND* pHwnd | ||
22 | ); | ||
23 | HRESULT 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 | |||
10 | struct UITHREAD_CONTEXT | ||
11 | { | ||
12 | HANDLE hInitializedEvent; | ||
13 | HINSTANCE hInstance; | ||
14 | BURN_ENGINE_STATE* pEngineState; | ||
15 | }; | ||
16 | |||
17 | struct UITHREAD_INFO | ||
18 | { | ||
19 | BOOL fElevated; | ||
20 | BURN_USER_EXPERIENCE* pUserExperience; | ||
21 | }; | ||
22 | |||
23 | |||
24 | // internal function declarations | ||
25 | |||
26 | static DWORD WINAPI ThreadProc( | ||
27 | __in LPVOID pvContext | ||
28 | ); | ||
29 | |||
30 | static 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 | |||
40 | HRESULT 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 | |||
68 | LExit: | ||
69 | ReleaseHandle(rgWaitHandles[1]); | ||
70 | ReleaseHandle(rgWaitHandles[0]); | ||
71 | |||
72 | return hr; | ||
73 | } | ||
74 | |||
75 | void 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 | |||
92 | static 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 | |||
159 | LExit: | ||
160 | if (fRegistered) | ||
161 | { | ||
162 | ::UnregisterClassW(BURN_UITHREAD_CLASS_WINDOW, pContext->hInstance); | ||
163 | } | ||
164 | |||
165 | return hr; | ||
166 | } | ||
167 | |||
168 | static 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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // functions | ||
11 | |||
12 | HRESULT UiCreateMessageWindow( | ||
13 | __in HINSTANCE hInstance, | ||
14 | __in BURN_ENGINE_STATE* pEngineState | ||
15 | ); | ||
16 | |||
17 | void 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 | |||
11 | extern "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 | |||
30 | LExit: | ||
31 | ReleaseObject(pixnUpdateNode); | ||
32 | |||
33 | return hr; | ||
34 | } | ||
35 | |||
36 | extern "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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // structs | ||
11 | |||
12 | typedef struct _BURN_UPDATE | ||
13 | { | ||
14 | BOOL fUpdateAvailable; | ||
15 | LPWSTR sczUpdateSource; | ||
16 | |||
17 | BURN_PACKAGE package; | ||
18 | } BURN_UPDATE; | ||
19 | |||
20 | |||
21 | // function declarations | ||
22 | |||
23 | HRESULT UpdateParseFromXml( | ||
24 | __in BURN_UPDATE* pUpdate, | ||
25 | __in IXMLDOMNode* pixnBundle | ||
26 | ); | ||
27 | void 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 | |||
7 | static int FilterResult( | ||
8 | __in DWORD dwAllowedResults, | ||
9 | __in int nResult | ||
10 | ); | ||
11 | |||
12 | static 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 | *******************************************************************/ | ||
27 | extern "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 | |||
61 | LExit: | ||
62 | ReleaseObject(pixnUserExperienceNode); | ||
63 | |||
64 | return hr; | ||
65 | } | ||
66 | |||
67 | /******************************************************************* | ||
68 | UserExperienceUninitialize - | ||
69 | |||
70 | *******************************************************************/ | ||
71 | extern "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 | *******************************************************************/ | ||
86 | extern "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 | |||
119 | LExit: | ||
120 | return hr; | ||
121 | } | ||
122 | |||
123 | /******************************************************************* | ||
124 | UserExperienceUnload - | ||
125 | |||
126 | *******************************************************************/ | ||
127 | extern "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 | |||
155 | extern "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 | |||
172 | LExit: | ||
173 | ReleaseStr(sczWorkingFolder); | ||
174 | |||
175 | return hr; | ||
176 | } | ||
177 | |||
178 | |||
179 | extern "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 | |||
196 | extern "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 | |||
225 | extern "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 | |||
253 | LExit: | ||
254 | return hr; | ||
255 | } | ||
256 | |||
257 | extern "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 | |||
266 | extern "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 | |||
273 | LExit: | ||
274 | return hr; | ||
275 | } | ||
276 | |||
277 | extern "C" void UserExperienceExecuteReset( | ||
278 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
279 | ) | ||
280 | { | ||
281 | pUserExperience->hrApplyError = S_OK; | ||
282 | pUserExperience->hwndApply = NULL; | ||
283 | } | ||
284 | |||
285 | extern "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 | |||
296 | EXTERN_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 | |||
318 | LExit: | ||
319 | return hr; | ||
320 | } | ||
321 | |||
322 | EXTERN_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 | |||
346 | LExit: | ||
347 | return hr; | ||
348 | } | ||
349 | |||
350 | EXTERN_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 | |||
378 | LExit: | ||
379 | return hr; | ||
380 | } | ||
381 | |||
382 | EXTERN_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 | |||
411 | LExit: | ||
412 | return hr; | ||
413 | } | ||
414 | |||
415 | EXTERN_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 | |||
445 | LExit: | ||
446 | return hr; | ||
447 | } | ||
448 | |||
449 | EXTERN_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 | |||
469 | LExit: | ||
470 | return hr; | ||
471 | } | ||
472 | |||
473 | EXTERN_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 | |||
490 | LExit: | ||
491 | return hr; | ||
492 | } | ||
493 | |||
494 | EXTERN_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 | |||
520 | LExit: | ||
521 | return hr; | ||
522 | } | ||
523 | |||
524 | EXTERN_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 | |||
551 | LExit: | ||
552 | return hr; | ||
553 | } | ||
554 | |||
555 | EXTERN_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 | |||
579 | LExit: | ||
580 | return hr; | ||
581 | } | ||
582 | |||
583 | EXTERN_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 | |||
612 | LExit: | ||
613 | return hr; | ||
614 | } | ||
615 | |||
616 | EXTERN_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 | |||
640 | LExit: | ||
641 | return hr; | ||
642 | } | ||
643 | |||
644 | EXTERN_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 | |||
670 | LExit: | ||
671 | return hr; | ||
672 | } | ||
673 | |||
674 | EXTERN_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 | |||
691 | LExit: | ||
692 | return hr; | ||
693 | } | ||
694 | |||
695 | EXTERN_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 | |||
728 | LExit: | ||
729 | return hr; | ||
730 | } | ||
731 | |||
732 | EXTERN_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 | |||
758 | LExit: | ||
759 | return hr; | ||
760 | } | ||
761 | |||
762 | EXTERN_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 | |||
784 | LExit: | ||
785 | return hr; | ||
786 | } | ||
787 | |||
788 | EXTERN_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 | |||
809 | LExit: | ||
810 | return hr; | ||
811 | } | ||
812 | |||
813 | EXTERN_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 | |||
845 | LExit: | ||
846 | return hr; | ||
847 | } | ||
848 | |||
849 | EXTERN_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 | |||
881 | LExit: | ||
882 | return hr; | ||
883 | } | ||
884 | |||
885 | EXTERN_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 | |||
911 | LExit: | ||
912 | return hr; | ||
913 | } | ||
914 | |||
915 | EXTERN_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 | |||
952 | LExit: | ||
953 | return hr; | ||
954 | } | ||
955 | |||
956 | EXTERN_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 | |||
981 | LExit: | ||
982 | return hr; | ||
983 | } | ||
984 | |||
985 | EXTERN_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 | |||
1009 | LExit: | ||
1010 | return hr; | ||
1011 | } | ||
1012 | |||
1013 | EXTERN_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 | |||
1033 | LExit: | ||
1034 | return hr; | ||
1035 | } | ||
1036 | |||
1037 | EXTERN_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 | |||
1054 | LExit: | ||
1055 | return hr; | ||
1056 | } | ||
1057 | |||
1058 | EXTERN_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 | |||
1092 | LExit: | ||
1093 | return hr; | ||
1094 | } | ||
1095 | |||
1096 | EXTERN_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 | |||
1118 | LExit: | ||
1119 | return hr; | ||
1120 | } | ||
1121 | |||
1122 | EXTERN_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 | |||
1139 | LExit: | ||
1140 | return hr; | ||
1141 | } | ||
1142 | |||
1143 | EXTERN_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 | |||
1169 | LExit: | ||
1170 | return hr; | ||
1171 | } | ||
1172 | |||
1173 | EXTERN_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 | |||
1205 | LExit: | ||
1206 | return hr; | ||
1207 | } | ||
1208 | |||
1209 | EXTERN_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 | |||
1233 | LExit: | ||
1234 | return hr; | ||
1235 | } | ||
1236 | |||
1237 | EXTERN_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 | |||
1263 | LExit: | ||
1264 | return hr; | ||
1265 | } | ||
1266 | |||
1267 | EXTERN_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 | |||
1291 | LExit: | ||
1292 | return hr; | ||
1293 | } | ||
1294 | |||
1295 | EXTERN_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 | |||
1317 | LExit: | ||
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 | |||
1333 | EXTERN_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 | |||
1353 | LExit: | ||
1354 | return hr; | ||
1355 | } | ||
1356 | |||
1357 | EXTERN_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 | |||
1376 | LExit: | ||
1377 | return hr; | ||
1378 | } | ||
1379 | |||
1380 | EXTERN_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 | |||
1402 | LExit: | ||
1403 | return hr; | ||
1404 | } | ||
1405 | |||
1406 | EXTERN_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 | |||
1436 | LExit: | ||
1437 | return hr; | ||
1438 | } | ||
1439 | |||
1440 | EXTERN_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 | |||
1469 | LExit: | ||
1470 | return hr; | ||
1471 | } | ||
1472 | |||
1473 | EXTERN_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 | |||
1501 | LExit: | ||
1502 | return hr; | ||
1503 | } | ||
1504 | |||
1505 | EXTERN_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 | |||
1522 | LExit: | ||
1523 | return hr; | ||
1524 | } | ||
1525 | |||
1526 | EXTERN_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 | |||
1552 | LExit: | ||
1553 | return hr; | ||
1554 | } | ||
1555 | |||
1556 | EXTERN_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 | |||
1583 | LExit: | ||
1584 | return hr; | ||
1585 | } | ||
1586 | |||
1587 | EXTERN_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 | |||
1613 | LExit: | ||
1614 | return hr; | ||
1615 | } | ||
1616 | |||
1617 | EXTERN_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 | |||
1645 | LExit: | ||
1646 | return hr; | ||
1647 | } | ||
1648 | |||
1649 | EXTERN_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 | |||
1672 | EXTERN_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 | |||
1692 | LExit: | ||
1693 | return hr; | ||
1694 | } | ||
1695 | |||
1696 | EXTERN_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 | |||
1713 | LExit: | ||
1714 | return hr; | ||
1715 | } | ||
1716 | |||
1717 | EXTERN_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 | |||
1752 | LExit: | ||
1753 | return hr; | ||
1754 | } | ||
1755 | |||
1756 | EXTERN_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 | |||
1775 | LExit: | ||
1776 | return hr; | ||
1777 | } | ||
1778 | |||
1779 | EXTERN_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 | |||
1794 | LExit: | ||
1795 | return hr; | ||
1796 | } | ||
1797 | |||
1798 | EXTERN_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 | |||
1819 | LExit: | ||
1820 | return hr; | ||
1821 | } | ||
1822 | |||
1823 | EXTERN_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 | |||
1843 | LExit: | ||
1844 | return hr; | ||
1845 | } | ||
1846 | |||
1847 | EXTERN_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 | |||
1864 | LExit: | ||
1865 | return hr; | ||
1866 | } | ||
1867 | |||
1868 | extern "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 | |||
1889 | extern "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 | |||
1899 | extern "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 | |||
1925 | static 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. | ||
2085 | static 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 | |||
2120 | LExit: | ||
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) | ||
7 | extern "C" { | ||
8 | #endif | ||
9 | |||
10 | |||
11 | // constants | ||
12 | |||
13 | const DWORD MB_RETRYTRYAGAIN = 0xF; | ||
14 | |||
15 | |||
16 | // structs | ||
17 | |||
18 | struct BOOTSTRAPPER_ENGINE_CONTEXT; | ||
19 | |||
20 | typedef 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 | |||
53 | HRESULT UserExperienceParseFromXml( | ||
54 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
55 | __in IXMLDOMNode* pixnBundle | ||
56 | ); | ||
57 | void UserExperienceUninitialize( | ||
58 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
59 | ); | ||
60 | HRESULT UserExperienceLoad( | ||
61 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
62 | __in BOOTSTRAPPER_ENGINE_CONTEXT* pEngineContext, | ||
63 | __in BOOTSTRAPPER_COMMAND* pCommand | ||
64 | ); | ||
65 | HRESULT UserExperienceUnload( | ||
66 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
67 | ); | ||
68 | HRESULT UserExperienceEnsureWorkingFolder( | ||
69 | __in LPCWSTR wzBundleId, | ||
70 | __deref_out_z LPWSTR* psczUserExperienceWorkingFolder | ||
71 | ); | ||
72 | HRESULT UserExperienceRemove( | ||
73 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
74 | ); | ||
75 | int 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 | ); | ||
84 | HRESULT UserExperienceActivateEngine( | ||
85 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
86 | __out_opt BOOL* pfActivated | ||
87 | ); | ||
88 | void UserExperienceDeactivateEngine( | ||
89 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
90 | ); | ||
91 | HRESULT UserExperienceEnsureEngineInactive( | ||
92 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
93 | ); | ||
94 | void UserExperienceExecuteReset( | ||
95 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
96 | ); | ||
97 | void UserExperienceExecutePhaseComplete( | ||
98 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
99 | __in HRESULT hrResult | ||
100 | ); | ||
101 | BAAPI UserExperienceOnApplyBegin( | ||
102 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
103 | __in DWORD dwPhaseCount | ||
104 | ); | ||
105 | BAAPI UserExperienceOnApplyComplete( | ||
106 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
107 | __in HRESULT hrStatus, | ||
108 | __in BOOTSTRAPPER_APPLY_RESTART restart, | ||
109 | __inout BOOTSTRAPPER_APPLYCOMPLETE_ACTION* pAction | ||
110 | ); | ||
111 | BAAPI 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 | ); | ||
118 | BAAPI 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 | ); | ||
125 | BAAPI 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 | ); | ||
133 | BAAPI UserExperienceOnCacheBegin( | ||
134 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
135 | ); | ||
136 | BAAPI UserExperienceOnCacheComplete( | ||
137 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
138 | __in HRESULT hrStatus | ||
139 | ); | ||
140 | BAAPI UserExperienceOnCachePackageBegin( | ||
141 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
142 | __in_z LPCWSTR wzPackageId, | ||
143 | __in DWORD cCachePayloads, | ||
144 | __in DWORD64 dw64PackageCacheSize | ||
145 | ); | ||
146 | BAAPI UserExperienceOnCachePackageComplete( | ||
147 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
148 | __in_z LPCWSTR wzPackageId, | ||
149 | __in HRESULT hrStatus, | ||
150 | __inout BOOTSTRAPPER_CACHEPACKAGECOMPLETE_ACTION* pAction | ||
151 | ); | ||
152 | BAAPI UserExperienceOnCacheVerifyBegin( | ||
153 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
154 | __in_z_opt LPCWSTR wzPackageOrContainerId, | ||
155 | __in_z_opt LPCWSTR wzPayloadId | ||
156 | ); | ||
157 | BAAPI 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 | ); | ||
164 | BAAPI UserExperienceOnDetectBegin( | ||
165 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
166 | __in BOOL fInstalled, | ||
167 | __in DWORD cPackages | ||
168 | ); | ||
169 | BAAPI UserExperienceOnDetectCompatibleMsiPackage( | ||
170 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
171 | __in_z LPCWSTR wzPackageId, | ||
172 | __in_z LPCWSTR wzCompatiblePackageId, | ||
173 | __in DWORD64 dw64CompatiblePackageVersion | ||
174 | ); | ||
175 | BAAPI UserExperienceOnDetectComplete( | ||
176 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
177 | __in HRESULT hrStatus | ||
178 | ); | ||
179 | BAAPI 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 | ); | ||
188 | BAAPI UserExperienceOnDetectMsiFeature( | ||
189 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
190 | __in_z LPCWSTR wzPackageId, | ||
191 | __in_z LPCWSTR wzFeatureId, | ||
192 | __in BOOTSTRAPPER_FEATURE_STATE state | ||
193 | ); | ||
194 | BAAPI UserExperienceOnDetectPackageBegin( | ||
195 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
196 | __in_z LPCWSTR wzPackageId | ||
197 | ); | ||
198 | BAAPI UserExperienceOnDetectPackageComplete( | ||
199 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
200 | __in_z LPCWSTR wzPackageId, | ||
201 | __in HRESULT hrStatus, | ||
202 | __in BOOTSTRAPPER_PACKAGE_STATE state | ||
203 | ); | ||
204 | BAAPI 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 | ); | ||
213 | BAAPI 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 | ); | ||
222 | BAAPI UserExperienceOnDetectTargetMsiPackage( | ||
223 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
224 | __in_z LPCWSTR wzPackageId, | ||
225 | __in_z LPCWSTR wzProductCode, | ||
226 | __in BOOTSTRAPPER_PACKAGE_STATE patchState | ||
227 | ); | ||
228 | BAAPI 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 | ); | ||
239 | BAAPI UserExperienceOnDetectUpdateBegin( | ||
240 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
241 | __in_z LPCWSTR wzUpdateLocation, | ||
242 | __inout BOOL* pfSkip | ||
243 | ); | ||
244 | BAAPI UserExperienceOnDetectUpdateComplete( | ||
245 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
246 | __in HRESULT hrStatus, | ||
247 | __inout BOOL* pfIgnoreError | ||
248 | ); | ||
249 | BAAPI UserExperienceOnElevateBegin( | ||
250 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
251 | ); | ||
252 | BAAPI UserExperienceOnElevateComplete( | ||
253 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
254 | __in HRESULT hrStatus | ||
255 | ); | ||
256 | BAAPI 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 | ); | ||
267 | BAAPI UserExperienceOnExecuteBegin( | ||
268 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
269 | __in DWORD cExecutingPackages | ||
270 | ); | ||
271 | BAAPI UserExperienceOnExecuteComplete( | ||
272 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
273 | __in HRESULT hrStatus | ||
274 | ); | ||
275 | BAAPI 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 | ); | ||
282 | BAAPI 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 | ); | ||
292 | BAAPI UserExperienceOnExecutePackageBegin( | ||
293 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
294 | __in_z LPCWSTR wzPackageId, | ||
295 | __in BOOL fExecute | ||
296 | ); | ||
297 | BAAPI 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 | ); | ||
304 | BAAPI UserExperienceOnExecutePatchTarget( | ||
305 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
306 | __in_z LPCWSTR wzPackageId, | ||
307 | __in_z LPCWSTR wzTargetProductCode | ||
308 | ); | ||
309 | BAAPI 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 | ); | ||
316 | BAAPI UserExperienceOnLaunchApprovedExeBegin( | ||
317 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
318 | ); | ||
319 | BAAPI UserExperienceOnLaunchApprovedExeComplete( | ||
320 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
321 | __in HRESULT hrStatus, | ||
322 | __in DWORD dwProcessId | ||
323 | ); | ||
324 | BAAPI UserExperienceOnPlanBegin( | ||
325 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
326 | __in DWORD cPackages | ||
327 | ); | ||
328 | BAAPI 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 | ); | ||
335 | BAAPI 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 | ); | ||
345 | BAAPI UserExperienceOnPlanComplete( | ||
346 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
347 | __in HRESULT hrStatus | ||
348 | ); | ||
349 | BAAPI UserExperienceOnPlanMsiFeature( | ||
350 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
351 | __in_z LPCWSTR wzPackageId, | ||
352 | __in_z LPCWSTR wzFeatureId, | ||
353 | __inout BOOTSTRAPPER_FEATURE_STATE* pRequestedState | ||
354 | ); | ||
355 | BAAPI UserExperienceOnPlanPackageBegin( | ||
356 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
357 | __in_z LPCWSTR wzPackageId, | ||
358 | __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState | ||
359 | ); | ||
360 | BAAPI 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 | ); | ||
369 | BAAPI UserExperienceOnPlanRelatedBundle( | ||
370 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
371 | __in_z LPCWSTR wzBundleId, | ||
372 | __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState | ||
373 | ); | ||
374 | BAAPI UserExperienceOnPlanTargetMsiPackage( | ||
375 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
376 | __in_z LPCWSTR wzPackageId, | ||
377 | __in_z LPCWSTR wzProductCode, | ||
378 | __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState | ||
379 | ); | ||
380 | BAAPI UserExperienceOnProgress( | ||
381 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
382 | __in BOOL fRollback, | ||
383 | __in DWORD dwProgressPercentage, | ||
384 | __in DWORD dwOverallPercentage | ||
385 | ); | ||
386 | BAAPI UserExperienceOnRegisterBegin( | ||
387 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
388 | ); | ||
389 | BAAPI UserExperienceOnRegisterComplete( | ||
390 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
391 | __in HRESULT hrStatus | ||
392 | ); | ||
393 | BAAPI 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 | ); | ||
401 | BAAPI UserExperienceOnShutdown( | ||
402 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
403 | __inout BOOTSTRAPPER_SHUTDOWN_ACTION* pAction | ||
404 | ); | ||
405 | BAAPI UserExperienceOnStartup( | ||
406 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
407 | ); | ||
408 | BAAPI UserExperienceOnSystemShutdown( | ||
409 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
410 | __in DWORD dwEndSession, | ||
411 | __inout BOOL* pfCancel | ||
412 | ); | ||
413 | BAAPI UserExperienceOnUnregisterBegin( | ||
414 | __in BURN_USER_EXPERIENCE* pUserExperience | ||
415 | ); | ||
416 | BAAPI UserExperienceOnUnregisterComplete( | ||
417 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
418 | __in HRESULT hrStatus | ||
419 | ); | ||
420 | HRESULT UserExperienceInterpretResult( | ||
421 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
422 | __in DWORD dwAllowedResults, | ||
423 | __in int nResult | ||
424 | ); | ||
425 | int UserExperienceCheckExecuteResult( | ||
426 | __in BURN_USER_EXPERIENCE* pUserExperience, | ||
427 | __in BOOL fRollback, | ||
428 | __in DWORD dwAllowedResults, | ||
429 | __in int nResult | ||
430 | ); | ||
431 | HRESULT 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 | |||
8 | typedef 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 | |||
20 | const DWORD GROW_VARIABLE_ARRAY = 3; | ||
21 | |||
22 | enum 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 | |||
41 | enum 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 | |||
51 | static 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 | ); | ||
58 | static 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 | ); | ||
66 | static HRESULT GetVariable( | ||
67 | __in BURN_VARIABLES* pVariables, | ||
68 | __in_z LPCWSTR wzVariable, | ||
69 | __out BURN_VARIABLE** ppVariable | ||
70 | ); | ||
71 | static HRESULT FindVariableIndexByName( | ||
72 | __in BURN_VARIABLES* pVariables, | ||
73 | __in_z LPCWSTR wzVariable, | ||
74 | __out DWORD* piVariable | ||
75 | ); | ||
76 | static HRESULT InsertVariable( | ||
77 | __in BURN_VARIABLES* pVariables, | ||
78 | __in_z LPCWSTR wzVariable, | ||
79 | __in DWORD iPosition | ||
80 | ); | ||
81 | static 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 | ); | ||
89 | static HRESULT InitializeVariableVersionNT( | ||
90 | __in DWORD_PTR dwpData, | ||
91 | __inout BURN_VARIANT* pValue | ||
92 | ); | ||
93 | static HRESULT InitializeVariableOsInfo( | ||
94 | __in DWORD_PTR dwpData, | ||
95 | __inout BURN_VARIANT* pValue | ||
96 | ); | ||
97 | static HRESULT InitializeVariableSystemInfo( | ||
98 | __in DWORD_PTR dwpData, | ||
99 | __inout BURN_VARIANT* pValue | ||
100 | ); | ||
101 | static HRESULT InitializeVariableComputerName( | ||
102 | __in DWORD_PTR dwpData, | ||
103 | __inout BURN_VARIANT* pValue | ||
104 | ); | ||
105 | static HRESULT InitializeVariableVersionMsi( | ||
106 | __in DWORD_PTR dwpData, | ||
107 | __inout BURN_VARIANT* pValue | ||
108 | ); | ||
109 | static HRESULT InitializeVariableCsidlFolder( | ||
110 | __in DWORD_PTR dwpData, | ||
111 | __inout BURN_VARIANT* pValue | ||
112 | ); | ||
113 | static HRESULT InitializeVariableWindowsVolumeFolder( | ||
114 | __in DWORD_PTR dwpData, | ||
115 | __inout BURN_VARIANT* pValue | ||
116 | ); | ||
117 | static HRESULT InitializeVariableTempFolder( | ||
118 | __in DWORD_PTR dwpData, | ||
119 | __inout BURN_VARIANT* pValue | ||
120 | ); | ||
121 | static HRESULT InitializeVariableSystemFolder( | ||
122 | __in DWORD_PTR dwpData, | ||
123 | __inout BURN_VARIANT* pValue | ||
124 | ); | ||
125 | static HRESULT InitializeVariablePrivileged( | ||
126 | __in DWORD_PTR dwpData, | ||
127 | __inout BURN_VARIANT* pValue | ||
128 | ); | ||
129 | static HRESULT InitializeVariableRebootPending( | ||
130 | __in DWORD_PTR dwpData, | ||
131 | __inout BURN_VARIANT* pValue | ||
132 | ); | ||
133 | static HRESULT InitializeSystemLanguageID( | ||
134 | __in DWORD_PTR dwpData, | ||
135 | __inout BURN_VARIANT* pValue | ||
136 | ); | ||
137 | static HRESULT InitializeUserUILanguageID( | ||
138 | __in DWORD_PTR dwpData, | ||
139 | __inout BURN_VARIANT* pValue | ||
140 | ); | ||
141 | static HRESULT InitializeUserLanguageID( | ||
142 | __in DWORD_PTR dwpData, | ||
143 | __inout BURN_VARIANT* pValue | ||
144 | ); | ||
145 | static HRESULT InitializeVariableString( | ||
146 | __in DWORD_PTR dwpData, | ||
147 | __inout BURN_VARIANT* pValue | ||
148 | ); | ||
149 | static HRESULT InitializeVariableNumeric( | ||
150 | __in DWORD_PTR dwpData, | ||
151 | __inout BURN_VARIANT* pValue | ||
152 | ); | ||
153 | static HRESULT InitializeVariableRegistryFolder( | ||
154 | __in DWORD_PTR dwpData, | ||
155 | __inout BURN_VARIANT* pValue | ||
156 | ); | ||
157 | static HRESULT InitializeVariable6432Folder( | ||
158 | __in DWORD_PTR dwpData, | ||
159 | __inout BURN_VARIANT* pValue | ||
160 | ); | ||
161 | static HRESULT InitializeVariableDate( | ||
162 | __in DWORD_PTR dwpData, | ||
163 | __inout BURN_VARIANT* pValue | ||
164 | ); | ||
165 | static HRESULT InitializeVariableInstallerName( | ||
166 | __in DWORD_PTR dwpData, | ||
167 | __inout BURN_VARIANT* pValue | ||
168 | ); | ||
169 | static HRESULT InitializeVariableInstallerVersion( | ||
170 | __in DWORD_PTR dwpData, | ||
171 | __inout BURN_VARIANT* pValue | ||
172 | ); | ||
173 | static HRESULT InitializeVariableVersion( | ||
174 | __in DWORD_PTR dwpData, | ||
175 | __inout BURN_VARIANT* pValue | ||
176 | ); | ||
177 | static HRESULT InitializeVariableLogonUser( | ||
178 | __in DWORD_PTR dwpData, | ||
179 | __inout BURN_VARIANT* pValue | ||
180 | ); | ||
181 | static HRESULT Get64bitFolderFromRegistry( | ||
182 | __in int nFolder, | ||
183 | __deref_out_z LPWSTR* psczPath | ||
184 | ); | ||
185 | |||
186 | |||
187 | // function definitions | ||
188 | |||
189 | extern "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 | |||
281 | LExit: | ||
282 | return hr; | ||
283 | } | ||
284 | |||
285 | extern "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 | |||
418 | LExit: | ||
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 | |||
430 | extern "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 | |||
451 | extern "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. | ||
492 | extern "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 | |||
517 | LExit: | ||
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. | ||
524 | extern "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 | |||
549 | LExit: | ||
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. | ||
556 | extern "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 | |||
581 | LExit: | ||
582 | ::LeaveCriticalSection(&pVariables->csAccess); | ||
583 | |||
584 | return hr; | ||
585 | } | ||
586 | |||
587 | extern "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 | |||
608 | LExit: | ||
609 | ::LeaveCriticalSection(&pVariables->csAccess); | ||
610 | |||
611 | return hr; | ||
612 | } | ||
613 | |||
614 | // The contents of psczValue may be sensitive, should keep encrypted and SecureZeroFree. | ||
615 | extern "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 | |||
655 | LExit: | ||
656 | ::LeaveCriticalSection(&pVariables->csAccess); | ||
657 | StrSecureZeroFreeString(scz); | ||
658 | |||
659 | return hr; | ||
660 | } | ||
661 | |||
662 | extern "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 | |||
678 | extern "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 | |||
694 | extern "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 | |||
710 | extern "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 | |||
726 | extern "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 | ||
736 | extern "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 | |||
746 | extern "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 | |||
756 | extern "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 | |||
805 | LExit: | ||
806 | ReleaseStr(pwzEscaped); | ||
807 | ReleaseStr(pwz); | ||
808 | return hr; | ||
809 | } | ||
810 | |||
811 | extern "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 | |||
899 | LExit: | ||
900 | ::LeaveCriticalSection(&pVariables->csAccess); | ||
901 | SecureZeroMemory(&ll, sizeof(ll)); | ||
902 | SecureZeroMemory(&qw, sizeof(qw)); | ||
903 | StrSecureZeroFreeString(scz); | ||
904 | |||
905 | return hr; | ||
906 | } | ||
907 | |||
908 | extern "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 | |||
1000 | LExit: | ||
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 | |||
1011 | extern "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 | |||
1031 | extern "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 | |||
1052 | extern "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 | |||
1073 | extern "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 | |||
1097 | extern "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 | |||
1120 | LExit: | ||
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. | ||
1130 | static 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 | |||
1306 | LExit: | ||
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 | |||
1346 | static 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 | |||
1376 | LExit: | ||
1377 | return hr; | ||
1378 | } | ||
1379 | |||
1380 | static 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 | |||
1409 | LExit: | ||
1410 | return hr; | ||
1411 | } | ||
1412 | |||
1413 | static 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 | |||
1452 | LExit: | ||
1453 | return hr; | ||
1454 | } | ||
1455 | |||
1456 | static 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 | |||
1509 | LExit: | ||
1510 | return hr; | ||
1511 | } | ||
1512 | |||
1513 | static 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 | |||
1608 | LExit: | ||
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 | |||
1619 | extern "C" typedef NTSTATUS (NTAPI *RTL_GET_VERSION)(_Out_ PRTL_OSVERSIONINFOEXW lpVersionInformation); | ||
1620 | |||
1621 | static 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 | |||
1682 | LExit: | ||
1683 | if (NULL != ntdll) | ||
1684 | { | ||
1685 | FreeLibrary(ntdll); | ||
1686 | } | ||
1687 | |||
1688 | return hr; | ||
1689 | } | ||
1690 | |||
1691 | static 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 | |||
1764 | LExit: | ||
1765 | return hr; | ||
1766 | } | ||
1767 | |||
1768 | static 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 | |||
1793 | LExit: | ||
1794 | return hr; | ||
1795 | } | ||
1796 | |||
1797 | static 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 | |||
1818 | LExit: | ||
1819 | return hr; | ||
1820 | } | ||
1821 | |||
1822 | static 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 | |||
1845 | LExit: | ||
1846 | return hr; | ||
1847 | } | ||
1848 | |||
1849 | static 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 | |||
1866 | LExit: | ||
1867 | ReleaseStr(sczPath); | ||
1868 | |||
1869 | return hr; | ||
1870 | } | ||
1871 | |||
1872 | static 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 | |||
1892 | LExit: | ||
1893 | return hr; | ||
1894 | } | ||
1895 | |||
1896 | static 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 | |||
1963 | LExit: | ||
1964 | return hr; | ||
1965 | } | ||
1966 | |||
1967 | static 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 | |||
1994 | LExit: | ||
1995 | return hr; | ||
1996 | } | ||
1997 | |||
1998 | static 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 | |||
2016 | LExit: | ||
2017 | return hr; | ||
2018 | } | ||
2019 | |||
2020 | static 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 | |||
2049 | LExit: | ||
2050 | if (fComInitialized) | ||
2051 | { | ||
2052 | ::CoUninitialize(); | ||
2053 | } | ||
2054 | |||
2055 | return hr; | ||
2056 | } | ||
2057 | |||
2058 | static 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 | |||
2071 | LExit: | ||
2072 | return hr; | ||
2073 | } | ||
2074 | |||
2075 | static 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 | |||
2088 | LExit: | ||
2089 | return hr; | ||
2090 | } | ||
2091 | |||
2092 | static 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 | |||
2105 | LExit: | ||
2106 | return hr; | ||
2107 | } | ||
2108 | |||
2109 | static 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 | |||
2121 | LExit: | ||
2122 | return hr; | ||
2123 | } | ||
2124 | |||
2125 | static 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 | |||
2137 | LExit: | ||
2138 | return hr; | ||
2139 | } | ||
2140 | |||
2141 | static 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 | |||
2167 | LExit: | ||
2168 | ReleaseStr(sczPath); | ||
2169 | |||
2170 | return hr; | ||
2171 | } | ||
2172 | |||
2173 | static 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 | |||
2203 | LExit: | ||
2204 | ReleaseStr(sczPath); | ||
2205 | |||
2206 | return hr; | ||
2207 | } | ||
2208 | |||
2209 | // Get the date in the same format as Windows Installer. | ||
2210 | static 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 | |||
2240 | LExit: | ||
2241 | ReleaseStr(sczDate); | ||
2242 | |||
2243 | return hr; | ||
2244 | } | ||
2245 | |||
2246 | static 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 | |||
2257 | LExit: | ||
2258 | return hr; | ||
2259 | } | ||
2260 | |||
2261 | static 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 | |||
2276 | LExit: | ||
2277 | ReleaseStr(sczVersion); | ||
2278 | |||
2279 | return hr; | ||
2280 | } | ||
2281 | |||
2282 | static 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 | |||
2293 | LExit: | ||
2294 | return hr; | ||
2295 | } | ||
2296 | |||
2297 | // Get the current user the same as Windows Installer. | ||
2298 | static 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 | |||
2316 | LExit: | ||
2317 | return hr; | ||
2318 | } | ||
2319 | |||
2320 | static 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 | |||
2340 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // constants | ||
11 | |||
12 | const LPCWSTR VARIABLE_DATE = L"Date"; | ||
13 | const LPCWSTR VARIABLE_LOGONUSER = L"LogonUser"; | ||
14 | const LPCWSTR VARIABLE_INSTALLERNAME = L"InstallerName"; | ||
15 | const LPCWSTR VARIABLE_INSTALLERVERSION = L"InstallerVersion"; | ||
16 | |||
17 | |||
18 | // typedefs | ||
19 | |||
20 | typedef HRESULT (*PFN_INITIALIZEVARIABLE)( | ||
21 | __in DWORD_PTR dwpData, | ||
22 | __inout BURN_VARIANT* pValue | ||
23 | ); | ||
24 | |||
25 | |||
26 | // constants | ||
27 | |||
28 | enum 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 | |||
38 | typedef 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 | |||
52 | typedef 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 | |||
63 | HRESULT VariableInitialize( | ||
64 | __in BURN_VARIABLES* pVariables | ||
65 | ); | ||
66 | HRESULT VariablesParseFromXml( | ||
67 | __in BURN_VARIABLES* pVariables, | ||
68 | __in IXMLDOMNode* pixnBundle | ||
69 | ); | ||
70 | void VariablesUninitialize( | ||
71 | __in BURN_VARIABLES* pVariables | ||
72 | ); | ||
73 | void VariablesDump( | ||
74 | __in BURN_VARIABLES* pVariables | ||
75 | ); | ||
76 | HRESULT VariableGetNumeric( | ||
77 | __in BURN_VARIABLES* pVariables, | ||
78 | __in_z LPCWSTR wzVariable, | ||
79 | __out LONGLONG* pllValue | ||
80 | ); | ||
81 | HRESULT VariableGetString( | ||
82 | __in BURN_VARIABLES* pVariables, | ||
83 | __in_z LPCWSTR wzVariable, | ||
84 | __out_z LPWSTR* psczValue | ||
85 | ); | ||
86 | HRESULT VariableGetVersion( | ||
87 | __in BURN_VARIABLES* pVariables, | ||
88 | __in_z LPCWSTR wzVariable, | ||
89 | __in DWORD64* pqwValue | ||
90 | ); | ||
91 | HRESULT VariableGetVariant( | ||
92 | __in BURN_VARIABLES* pVariables, | ||
93 | __in_z LPCWSTR wzVariable, | ||
94 | __in BURN_VARIANT* pValue | ||
95 | ); | ||
96 | HRESULT VariableGetFormatted( | ||
97 | __in BURN_VARIABLES* pVariables, | ||
98 | __in_z LPCWSTR wzVariable, | ||
99 | __out_z LPWSTR* psczValue | ||
100 | ); | ||
101 | HRESULT VariableSetNumeric( | ||
102 | __in BURN_VARIABLES* pVariables, | ||
103 | __in_z LPCWSTR wzVariable, | ||
104 | __in LONGLONG llValue, | ||
105 | __in BOOL fOverwriteBuiltIn | ||
106 | ); | ||
107 | HRESULT VariableSetLiteralString( | ||
108 | __in BURN_VARIABLES* pVariables, | ||
109 | __in_z LPCWSTR wzVariable, | ||
110 | __in_z_opt LPCWSTR wzValue, | ||
111 | __in BOOL fOverwriteBuiltIn | ||
112 | ); | ||
113 | HRESULT VariableSetString( | ||
114 | __in BURN_VARIABLES* pVariables, | ||
115 | __in_z LPCWSTR wzVariable, | ||
116 | __in_z_opt LPCWSTR wzValue, | ||
117 | __in BOOL fOverwriteBuiltIn | ||
118 | ); | ||
119 | HRESULT VariableSetVersion( | ||
120 | __in BURN_VARIABLES* pVariables, | ||
121 | __in_z LPCWSTR wzVariable, | ||
122 | __in DWORD64 qwValue, | ||
123 | __in BOOL fOverwriteBuiltIn | ||
124 | ); | ||
125 | HRESULT VariableSetLiteralVariant( | ||
126 | __in BURN_VARIABLES* pVariables, | ||
127 | __in_z LPCWSTR wzVariable, | ||
128 | __in BURN_VARIANT* pVariant | ||
129 | ); | ||
130 | HRESULT VariableFormatString( | ||
131 | __in BURN_VARIABLES* pVariables, | ||
132 | __in_z LPCWSTR wzIn, | ||
133 | __out_z_opt LPWSTR* psczOut, | ||
134 | __out_opt DWORD* pcchOut | ||
135 | ); | ||
136 | HRESULT VariableFormatStringObfuscated( | ||
137 | __in BURN_VARIABLES* pVariables, | ||
138 | __in_z LPCWSTR wzIn, | ||
139 | __out_z_opt LPWSTR* psczOut, | ||
140 | __out_opt DWORD* pcchOut | ||
141 | ); | ||
142 | HRESULT VariableEscapeString( | ||
143 | __in_z LPCWSTR wzIn, | ||
144 | __out_z LPWSTR* psczOut | ||
145 | ); | ||
146 | HRESULT VariableSerialize( | ||
147 | __in BURN_VARIABLES* pVariables, | ||
148 | __in BOOL fPersisting, | ||
149 | __inout BYTE** ppbBuffer, | ||
150 | __inout SIZE_T* piBuffer | ||
151 | ); | ||
152 | HRESULT 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 | ); | ||
159 | HRESULT VariableStrAlloc( | ||
160 | __in BOOL fZeroOnRealloc, | ||
161 | __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, | ||
162 | __in DWORD_PTR cch | ||
163 | ); | ||
164 | HRESULT 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 | ); | ||
170 | HRESULT VariableStrAllocConcat( | ||
171 | __in BOOL fZeroOnRealloc, | ||
172 | __deref_out_z LPWSTR* ppwz, | ||
173 | __in_z LPCWSTR wzSource, | ||
174 | __in DWORD_PTR cchSource | ||
175 | ); | ||
176 | HRESULT __cdecl VariableStrAllocFormatted( | ||
177 | __in BOOL fZeroOnRealloc, | ||
178 | __deref_out_z LPWSTR* ppwz, | ||
179 | __in __format_string LPCWSTR wzFormat, | ||
180 | ... | ||
181 | ); | ||
182 | HRESULT 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 | |||
9 | static HRESULT BVariantEncryptNumeric( | ||
10 | __in BURN_VARIANT* pVariant, | ||
11 | __in BOOL fEncrypt | ||
12 | ); | ||
13 | |||
14 | static HRESULT BVariantEncryptString( | ||
15 | __in BURN_VARIANT* pVariant, | ||
16 | __in BOOL fEncrypt | ||
17 | ); | ||
18 | |||
19 | static HRESULT BVariantEncryptVersion( | ||
20 | __in BURN_VARIANT* pVariant, | ||
21 | __in BOOL fEncrypt | ||
22 | ); | ||
23 | |||
24 | static HRESULT BVariantRetrieveDecryptedNumeric( | ||
25 | __in BURN_VARIANT* pVariant, | ||
26 | __out LONGLONG* pllValue | ||
27 | ); | ||
28 | |||
29 | static HRESULT BVariantRetrieveDecryptedString( | ||
30 | __in BURN_VARIANT* pVariant, | ||
31 | __out LPWSTR* psczValue | ||
32 | ); | ||
33 | |||
34 | static HRESULT BVariantRetrieveDecryptedVersion( | ||
35 | __in BURN_VARIANT* pVariant, | ||
36 | __out DWORD64* pqwValue | ||
37 | ); | ||
38 | |||
39 | // function definitions | ||
40 | |||
41 | extern "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. | ||
53 | extern "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. | ||
90 | extern "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 | |||
131 | LExit: | ||
132 | return hr; | ||
133 | } | ||
134 | |||
135 | // The contents of pqwValue may be sensitive, should keep encrypted and SecureZeroMemory. | ||
136 | extern "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 | |||
172 | extern "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 | |||
192 | extern "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 | |||
223 | LExit: | ||
224 | BVariantSetEncryption(pVariant, fEncryptValue); | ||
225 | return hr; | ||
226 | } | ||
227 | |||
228 | extern "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 | |||
248 | extern "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 | |||
295 | LExit: | ||
296 | return hr; | ||
297 | } | ||
298 | |||
299 | extern "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 | |||
346 | LExit: | ||
347 | return hr; | ||
348 | } | ||
349 | |||
350 | extern "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 | |||
389 | LExit: | ||
390 | return hr; | ||
391 | } | ||
392 | |||
393 | extern "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 | |||
426 | LExit: | ||
427 | return hr; | ||
428 | } | ||
429 | |||
430 | static 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 | |||
450 | static 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 | |||
495 | LExit: | ||
496 | return hr; | ||
497 | } | ||
498 | |||
499 | static 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. | ||
520 | static 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 | |||
541 | LExit: | ||
542 | return hr; | ||
543 | } | ||
544 | |||
545 | // The contents of psczValue may be sensitive, should keep encrypted and SecureZeroFree. | ||
546 | static 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 | |||
573 | LExit: | ||
574 | return hr; | ||
575 | } | ||
576 | |||
577 | // The contents of pqwValue may be sensitive, should keep encrypted and SecureZeroMemory. | ||
578 | static 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 | |||
599 | LExit: | ||
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) | ||
6 | extern "C" { | ||
7 | #endif | ||
8 | |||
9 | |||
10 | // constants | ||
11 | |||
12 | enum 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 | |||
23 | typedef 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 | |||
39 | void BVariantUninitialize( | ||
40 | __in BURN_VARIANT* pVariant | ||
41 | ); | ||
42 | HRESULT BVariantGetNumeric( | ||
43 | __in BURN_VARIANT* pVariant, | ||
44 | __out LONGLONG* pllValue | ||
45 | ); | ||
46 | HRESULT BVariantGetString( | ||
47 | __in BURN_VARIANT* pVariant, | ||
48 | __out_z LPWSTR* psczValue | ||
49 | ); | ||
50 | HRESULT BVariantGetVersion( | ||
51 | __in BURN_VARIANT* pVariant, | ||
52 | __out DWORD64* pqwValue | ||
53 | ); | ||
54 | HRESULT BVariantSetNumeric( | ||
55 | __in BURN_VARIANT* pVariant, | ||
56 | __in LONGLONG llValue | ||
57 | ); | ||
58 | HRESULT BVariantSetString( | ||
59 | __in BURN_VARIANT* pVariant, | ||
60 | __in_z_opt LPCWSTR wzValue, | ||
61 | __in DWORD_PTR cchValue | ||
62 | ); | ||
63 | HRESULT BVariantSetVersion( | ||
64 | __in BURN_VARIANT* pVariant, | ||
65 | __in DWORD64 qwValue | ||
66 | ); | ||
67 | /******************************************************************** | ||
68 | BVariantSetValue - 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 | ********************************************************************/ | ||
73 | HRESULT BVariantSetValue( | ||
74 | __in BURN_VARIANT* pVariant, | ||
75 | __in BURN_VARIANT* pValue | ||
76 | ); | ||
77 | /******************************************************************** | ||
78 | BVariantCopy - creates a copy of pSource. | ||
79 | The encryption state of pTarget is set to | ||
80 | the encryption state of pSource. | ||
81 | ********************************************************************/ | ||
82 | HRESULT BVariantCopy( | ||
83 | __in BURN_VARIANT* pSource, | ||
84 | __out BURN_VARIANT* pTarget | ||
85 | ); | ||
86 | HRESULT BVariantChangeType( | ||
87 | __in BURN_VARIANT* pVariant, | ||
88 | __in BURN_VARIANT_TYPE type | ||
89 | ); | ||
90 | /******************************************************************** | ||
91 | BVariantSetEncryption - sets the encryption state of pVariant. | ||
92 | If the encryption state matches the requested | ||
93 | state, this function does nothing. | ||
94 | ********************************************************************/ | ||
95 | HRESULT 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") | ||
9 | static DWORD dwMagic = 0x00f14300; | ||
10 | static DWORD dwVersion = 0x00000002; | ||
11 | |||
12 | static GUID guidBundleId = { }; | ||
13 | |||
14 | static DWORD dwStubSize = 0; | ||
15 | static DWORD dwOriginalChecksum = 0; | ||
16 | static DWORD dwOriginalSignatureOffset = 0; | ||
17 | static DWORD dwOriginalSignatureSize = 0; | ||
18 | |||
19 | static DWORD dwContainerFormat = 1; | ||
20 | static DWORD dwContainerCount = 0; | ||
21 | static DWORD qwBootstrapperApplicationContainerSize = 0; | ||
22 | static 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 | |||
6 | int 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 | |||
59 | LExit: | ||
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 | |||
9 | 1 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 | ||