diff options
author | Rob Mensching <rob@firegiant.com> | 2022-03-31 11:56:14 -0700 |
---|---|---|
committer | Rob Mensching <rob@firegiant.com> | 2022-03-31 18:01:06 -0700 |
commit | 47582b162368e8edf7a3b11c13b8e9dabc5f0a26 (patch) | |
tree | 2c4063eff325684bed39de0edacd7866a257ae02 /src/dtf/SfxCA/Extract.cpp | |
parent | 167296c42497c4e95f0d5d71168542d747655981 (diff) | |
download | wix-47582b162368e8edf7a3b11c13b8e9dabc5f0a26.tar.gz wix-47582b162368e8edf7a3b11c13b8e9dabc5f0a26.tar.bz2 wix-47582b162368e8edf7a3b11c13b8e9dabc5f0a26.zip |
Provide managed CA and Embedded UI DTF libraries via NuGet
Lots of refactoring to bring the SFX tooling back into the 'dtf'
layer since they are (in the end) tightly coupled to some DTF
assemblies. Also refactored the DTF tests into their own folder
and added a couple integration tests to build using the new CA/UI
NuGet package.
Closes wixtoolset/issues#6080
Diffstat (limited to 'src/dtf/SfxCA/Extract.cpp')
-rw-r--r-- | src/dtf/SfxCA/Extract.cpp | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/src/dtf/SfxCA/Extract.cpp b/src/dtf/SfxCA/Extract.cpp new file mode 100644 index 00000000..171cf52f --- /dev/null +++ b/src/dtf/SfxCA/Extract.cpp | |||
@@ -0,0 +1,282 @@ | |||
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 | // CABINET EXTRACTION | ||
7 | //--------------------------------------------------------------------- | ||
8 | |||
9 | // Globals make this code unsuited for multhreaded use, | ||
10 | // but FDI doesn't provide any other way to pass context. | ||
11 | |||
12 | // Handle to the FDI (cab extraction) engine. Need access to this in a callback. | ||
13 | static HFDI g_hfdi; | ||
14 | |||
15 | // FDI is not unicode-aware, so avoid passing these paths through the callbacks. | ||
16 | static const wchar_t* g_szExtractDir; | ||
17 | static const wchar_t* g_szCabFile; | ||
18 | |||
19 | // Offset into the source file where the cabinet really starts. | ||
20 | // Used to trick FDI into extracting from a concatenated cabinet. | ||
21 | static int g_lCabOffset; | ||
22 | |||
23 | // Use the secure CRT version of _wsopen if available. | ||
24 | #ifdef __GOT_SECURE_LIB__ | ||
25 | #define _wsopen__s(hf,file,oflag,shflag,pmode) _wsopen_s(&hf,file,oflag,shflag,pmode) | ||
26 | #else | ||
27 | #define _wsopen__s(hf,file,oflag,shflag,pmode) hf = _wsopen(file,oflag,shflag,pmode) | ||
28 | #endif | ||
29 | |||
30 | /// <summary> | ||
31 | /// FDI callback to open a cabinet file. | ||
32 | /// </summary> | ||
33 | /// <param name="pszFile">Name of the file to be opened. This parameter | ||
34 | /// is ignored since with our limited use this method is only ever called | ||
35 | /// to open the main cabinet file.</param> | ||
36 | /// <param name="oflag">Type of operations allowed.</param> | ||
37 | /// <param name="pmode">Permission setting.</param> | ||
38 | /// <returns>Integer file handle, or -1 if the file could not be opened.</returns> | ||
39 | /// <remarks> | ||
40 | /// To support reading from a cabinet that is concatenated onto | ||
41 | /// another file, this function first searches for the offset of the cabinet, | ||
42 | /// then saves that offset for use in recalculating later seeks. | ||
43 | /// </remarks> | ||
44 | static FNOPEN(CabOpen) | ||
45 | { | ||
46 | UNREFERENCED_PARAMETER(pszFile); | ||
47 | int hf; | ||
48 | _wsopen__s(hf, g_szCabFile, oflag, _SH_DENYWR, pmode); | ||
49 | if (hf != -1) | ||
50 | { | ||
51 | FDICABINETINFO cabInfo; | ||
52 | int length = _lseek(hf, 0, SEEK_END); | ||
53 | for(int offset = 0; offset < length; offset += 256) | ||
54 | { | ||
55 | if (_lseek(hf, offset, SEEK_SET) != offset) break; | ||
56 | if (FDIIsCabinet(g_hfdi, hf, &cabInfo)) | ||
57 | { | ||
58 | g_lCabOffset = offset; | ||
59 | _lseek(hf, offset, SEEK_SET); | ||
60 | return hf; | ||
61 | } | ||
62 | } | ||
63 | _close(hf); | ||
64 | } | ||
65 | return -1; | ||
66 | } | ||
67 | |||
68 | /// <summary> | ||
69 | /// FDI callback to seek within a file. | ||
70 | /// </summary> | ||
71 | /// <param name="hf">File handle.</param> | ||
72 | /// <param name="dist">Seek distance</param> | ||
73 | /// <param name="seektype">Whether to seek relative to the | ||
74 | /// beginning, current position, or end of the file.</param> | ||
75 | /// <returns>Resultant position within the cabinet.</returns> | ||
76 | /// <remarks> | ||
77 | /// To support reading from a cabinet that is concatenated onto | ||
78 | /// another file, this function recalculates seeks based on the | ||
79 | /// offset that was determined when the cabinet was opened. | ||
80 | /// </remarks> | ||
81 | static FNSEEK(CabSeek) | ||
82 | { | ||
83 | if (seektype == SEEK_SET) dist += g_lCabOffset; | ||
84 | int pos = _lseek((int) hf, dist, seektype); | ||
85 | pos -= g_lCabOffset; | ||
86 | return pos; | ||
87 | } | ||
88 | |||
89 | /// <summary> | ||
90 | /// Ensures a directory and its parent directory path exists. | ||
91 | /// </summary> | ||
92 | /// <param name="szDirPath">Directory path, not including file name.</param> | ||
93 | /// <returns>0 if the directory exists or was successfully created, else nonzero.</returns> | ||
94 | /// <remarks> | ||
95 | /// This function modifies characters in szDirPath, but always restores them | ||
96 | /// regardless of error condition. | ||
97 | /// </remarks> | ||
98 | static int EnsureDirectoryExists(__inout_z wchar_t* szDirPath) | ||
99 | { | ||
100 | int ret = 0; | ||
101 | if (!::CreateDirectoryW(szDirPath, NULL)) | ||
102 | { | ||
103 | UINT err = ::GetLastError(); | ||
104 | if (err != ERROR_ALREADY_EXISTS) | ||
105 | { | ||
106 | // Directory creation failed for some reason other than already existing. | ||
107 | // Try to create the parent directory first. | ||
108 | wchar_t* szLastSlash = NULL; | ||
109 | for (wchar_t* sz = szDirPath; *sz; sz++) | ||
110 | { | ||
111 | if (*sz == L'\\') | ||
112 | { | ||
113 | szLastSlash = sz; | ||
114 | } | ||
115 | } | ||
116 | if (szLastSlash) | ||
117 | { | ||
118 | // Temporarily take one directory off the path and recurse. | ||
119 | *szLastSlash = L'\0'; | ||
120 | ret = EnsureDirectoryExists(szDirPath); | ||
121 | *szLastSlash = L'\\'; | ||
122 | |||
123 | // Try to create the directory if all parents are created. | ||
124 | if (ret == 0 && !::CreateDirectoryW(szDirPath, NULL)) | ||
125 | { | ||
126 | err = ::GetLastError(); | ||
127 | if (err != ERROR_ALREADY_EXISTS) | ||
128 | { | ||
129 | ret = -1; | ||
130 | } | ||
131 | } | ||
132 | } | ||
133 | else | ||
134 | { | ||
135 | ret = -1; | ||
136 | } | ||
137 | } | ||
138 | } | ||
139 | return ret; | ||
140 | } | ||
141 | |||
142 | /// <summary> | ||
143 | /// Ensures a file's directory and its parent directory path exists. | ||
144 | /// </summary> | ||
145 | /// <param name="szDirPath">Path including file name.</param> | ||
146 | /// <returns>0 if the file's directory exists or was successfully created, else nonzero.</returns> | ||
147 | /// <remarks> | ||
148 | /// This function modifies characters in szFilePath, but always restores them | ||
149 | /// regardless of error condition. | ||
150 | /// </remarks> | ||
151 | static int EnsureFileDirectoryExists(__inout_z wchar_t* szFilePath) | ||
152 | { | ||
153 | int ret = 0; | ||
154 | wchar_t* szLastSlash = NULL; | ||
155 | for (wchar_t* sz = szFilePath; *sz; sz++) | ||
156 | { | ||
157 | if (*sz == L'\\') | ||
158 | { | ||
159 | szLastSlash = sz; | ||
160 | } | ||
161 | } | ||
162 | if (szLastSlash) | ||
163 | { | ||
164 | *szLastSlash = L'\0'; | ||
165 | ret = EnsureDirectoryExists(szFilePath); | ||
166 | *szLastSlash = L'\\'; | ||
167 | } | ||
168 | return ret; | ||
169 | } | ||
170 | |||
171 | /// <summary> | ||
172 | /// FDI callback for handling files in the cabinet. | ||
173 | /// </summary> | ||
174 | /// <param name="fdint">Type of notification.</param> | ||
175 | /// <param name="pfdin">Structure containing data about the notification.</param> | ||
176 | /// <remarks> | ||
177 | /// Refer to fdi.h for more comments on this notification callback. | ||
178 | /// </remarks> | ||
179 | static FNFDINOTIFY(CabNotification) | ||
180 | { | ||
181 | // fdintCOPY_FILE: | ||
182 | // Called for each file that *starts* in the current cabinet, giving | ||
183 | // the client the opportunity to request that the file be copied or | ||
184 | // skipped. | ||
185 | // Entry: | ||
186 | // pfdin->psz1 = file name in cabinet | ||
187 | // pfdin->cb = uncompressed size of file | ||
188 | // pfdin->date = file date | ||
189 | // pfdin->time = file time | ||
190 | // pfdin->attribs = file attributes | ||
191 | // pfdin->iFolder = file's folder index | ||
192 | // Exit-Success: | ||
193 | // Return non-zero file handle for destination file; FDI writes | ||
194 | // data to this file use the PFNWRITE function supplied to FDICreate, | ||
195 | // and then calls fdintCLOSE_FILE_INFO to close the file and set | ||
196 | // the date, time, and attributes. | ||
197 | // Exit-Failure: | ||
198 | // Returns 0 => Skip file, do not copy | ||
199 | // Returns -1 => Abort FDICopy() call | ||
200 | if (fdint == fdintCOPY_FILE) | ||
201 | { | ||
202 | size_t cchFile = MultiByteToWideChar(CP_UTF8, 0, pfdin->psz1, -1, NULL, 0); | ||
203 | size_t cchFilePath = wcslen(g_szExtractDir) + 1 + cchFile; | ||
204 | wchar_t* szFilePath = (wchar_t*) _alloca((cchFilePath + 1) * sizeof(wchar_t)); | ||
205 | if (szFilePath == NULL) return -1; | ||
206 | StringCchCopyW(szFilePath, cchFilePath + 1, g_szExtractDir); | ||
207 | StringCchCatW(szFilePath, cchFilePath + 1, L"\\"); | ||
208 | MultiByteToWideChar(CP_UTF8, 0, pfdin->psz1, -1, | ||
209 | szFilePath + cchFilePath - cchFile, (int) cchFile + 1); | ||
210 | int hf = -1; | ||
211 | if (EnsureFileDirectoryExists(szFilePath) == 0) | ||
212 | { | ||
213 | _wsopen__s(hf, szFilePath, | ||
214 | _O_BINARY | _O_CREAT | _O_WRONLY | _O_SEQUENTIAL, | ||
215 | _SH_DENYWR, _S_IREAD | _S_IWRITE); | ||
216 | } | ||
217 | return hf; | ||
218 | } | ||
219 | |||
220 | // fdintCLOSE_FILE_INFO: | ||
221 | // Called after all of the data has been written to a target file. | ||
222 | // This function must close the file and set the file date, time, | ||
223 | // and attributes. | ||
224 | // Entry: | ||
225 | // pfdin->psz1 = file name in cabinet | ||
226 | // pfdin->hf = file handle | ||
227 | // pfdin->date = file date | ||
228 | // pfdin->time = file time | ||
229 | // pfdin->attribs = file attributes | ||
230 | // pfdin->iFolder = file's folder index | ||
231 | // pfdin->cb = Run After Extract (0 - don't run, 1 Run) | ||
232 | // Exit-Success: | ||
233 | // Returns TRUE | ||
234 | // Exit-Failure: | ||
235 | // Returns FALSE, or -1 to abort | ||
236 | else if (fdint == fdintCLOSE_FILE_INFO) | ||
237 | { | ||
238 | _close((int) pfdin->hf); | ||
239 | return TRUE; | ||
240 | } | ||
241 | return 0; | ||
242 | } | ||
243 | |||
244 | /// <summary> | ||
245 | /// Extracts all contents of a cabinet file to a directory. | ||
246 | /// </summary> | ||
247 | /// <param name="szCabFile">Path to the cabinet file to be extracted. | ||
248 | /// The cabinet may actually start at some offset within the file, | ||
249 | /// as long as that offset is a multiple of 256.</param> | ||
250 | /// <param name="szExtractDir">Directory where files are to be extracted. | ||
251 | /// This directory must already exist, but should be empty.</param> | ||
252 | /// <returns>0 if the cabinet was extracted successfully, | ||
253 | /// or an error code if any error occurred.</returns> | ||
254 | /// <remarks> | ||
255 | /// The extraction will not overwrite any files in the destination | ||
256 | /// directory; extraction will be interrupted and fail if any files | ||
257 | /// with the same name already exist. | ||
258 | /// </remarks> | ||
259 | int ExtractCabinet(const wchar_t* szCabFile, const wchar_t* szExtractDir) | ||
260 | { | ||
261 | ERF erf; | ||
262 | // Most of the FDI callbacks can be handled by existing CRT I/O functions. | ||
263 | // For our functionality we only need to handle the open and seek callbacks. | ||
264 | HFDI hfdi = FDICreate((PFNALLOC) malloc, (PFNFREE) free, CabOpen, | ||
265 | (PFNREAD) _read, (PFNWRITE) _write, (PFNCLOSE) _close, | ||
266 | CabSeek, cpu80386, &erf); | ||
267 | if (hfdi != NULL) | ||
268 | { | ||
269 | g_hfdi = hfdi; | ||
270 | g_szCabFile = szCabFile; | ||
271 | g_szExtractDir = szExtractDir; | ||
272 | char szEmpty[1] = {0}; | ||
273 | if (FDICopy(hfdi, szEmpty, szEmpty, 0, CabNotification, NULL, NULL)) | ||
274 | { | ||
275 | FDIDestroy(hfdi); | ||
276 | return 0; | ||
277 | } | ||
278 | FDIDestroy(hfdi); | ||
279 | } | ||
280 | |||
281 | return erf.erfOper; | ||
282 | } | ||