[USETUP][SETUPLIB] Split the registry helper code.
[reactos.git] / base / setup / lib / filesup.c
1 /*
2 * PROJECT: ReactOS Setup Library
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: File support functions.
5 * COPYRIGHT: Copyright 2017-2018 Hermes Belusca-Maito
6 */
7
8 /* INCLUDES *****************************************************************/
9
10 #include "precomp.h"
11
12 #define NDEBUG
13 #include <debug.h>
14
15 /* FUNCTIONS ****************************************************************/
16
17 NTSTATUS
18 ConcatPathsV(
19 IN OUT PWSTR PathBuffer,
20 IN SIZE_T cchPathSize,
21 IN ULONG NumberOfPathComponents,
22 IN va_list PathComponentsList)
23 {
24 NTSTATUS Status = STATUS_SUCCESS;
25 SIZE_T cchPathLen;
26 PCWSTR PathComponent;
27
28 if (cchPathSize < 1)
29 return STATUS_SUCCESS;
30
31 while (NumberOfPathComponents--)
32 {
33 PathComponent = va_arg(PathComponentsList, PCWSTR);
34 if (!PathComponent)
35 continue;
36
37 cchPathLen = min(cchPathSize, wcslen(PathBuffer));
38 if (cchPathLen >= cchPathSize)
39 return STATUS_BUFFER_OVERFLOW;
40
41 if (PathComponent[0] != OBJ_NAME_PATH_SEPARATOR &&
42 cchPathLen > 0 && PathBuffer[cchPathLen-1] != OBJ_NAME_PATH_SEPARATOR)
43 {
44 /* PathComponent does not start with '\' and PathBuffer does not end with '\' */
45 Status = RtlStringCchCatW(PathBuffer, cchPathSize, L"\\");
46 if (!NT_SUCCESS(Status))
47 return Status;
48 }
49 else if (PathComponent[0] == OBJ_NAME_PATH_SEPARATOR &&
50 cchPathLen > 0 && PathBuffer[cchPathLen-1] == OBJ_NAME_PATH_SEPARATOR)
51 {
52 /* PathComponent starts with '\' and PathBuffer ends with '\' */
53 while (*PathComponent == OBJ_NAME_PATH_SEPARATOR)
54 ++PathComponent; // Skip any backslash
55 }
56 Status = RtlStringCchCatW(PathBuffer, cchPathSize, PathComponent);
57 if (!NT_SUCCESS(Status))
58 return Status;
59 }
60
61 return Status;
62 }
63
64 NTSTATUS
65 CombinePathsV(
66 OUT PWSTR PathBuffer,
67 IN SIZE_T cchPathSize,
68 IN ULONG NumberOfPathComponents,
69 IN va_list PathComponentsList)
70 {
71 if (cchPathSize < 1)
72 return STATUS_SUCCESS;
73
74 *PathBuffer = UNICODE_NULL;
75 return ConcatPathsV(PathBuffer, cchPathSize,
76 NumberOfPathComponents,
77 PathComponentsList);
78 }
79
80 NTSTATUS
81 ConcatPaths(
82 IN OUT PWSTR PathBuffer,
83 IN SIZE_T cchPathSize,
84 IN ULONG NumberOfPathComponents,
85 IN /* PCWSTR */ ...)
86 {
87 NTSTATUS Status;
88 va_list PathComponentsList;
89
90 if (cchPathSize < 1)
91 return STATUS_SUCCESS;
92
93 va_start(PathComponentsList, NumberOfPathComponents);
94 Status = ConcatPathsV(PathBuffer, cchPathSize,
95 NumberOfPathComponents,
96 PathComponentsList);
97 va_end(PathComponentsList);
98
99 return Status;
100 }
101
102 NTSTATUS
103 CombinePaths(
104 OUT PWSTR PathBuffer,
105 IN SIZE_T cchPathSize,
106 IN ULONG NumberOfPathComponents,
107 IN /* PCWSTR */ ...)
108 {
109 NTSTATUS Status;
110 va_list PathComponentsList;
111
112 if (cchPathSize < 1)
113 return STATUS_SUCCESS;
114
115 *PathBuffer = UNICODE_NULL;
116
117 va_start(PathComponentsList, NumberOfPathComponents);
118 Status = CombinePathsV(PathBuffer, cchPathSize,
119 NumberOfPathComponents,
120 PathComponentsList);
121 va_end(PathComponentsList);
122
123 return Status;
124 }
125
126 //
127 // NOTE: It may be possible to merge both DoesPathExist and DoesFileExist...
128 //
129 BOOLEAN
130 DoesPathExist(
131 IN HANDLE RootDirectory OPTIONAL,
132 IN PCWSTR PathName)
133 {
134 NTSTATUS Status;
135 HANDLE FileHandle;
136 OBJECT_ATTRIBUTES ObjectAttributes;
137 IO_STATUS_BLOCK IoStatusBlock;
138 UNICODE_STRING Name;
139
140 RtlInitUnicodeString(&Name, PathName);
141
142 InitializeObjectAttributes(&ObjectAttributes,
143 &Name,
144 OBJ_CASE_INSENSITIVE,
145 RootDirectory,
146 NULL);
147
148 Status = NtOpenFile(&FileHandle,
149 FILE_LIST_DIRECTORY | SYNCHRONIZE,
150 &ObjectAttributes,
151 &IoStatusBlock,
152 FILE_SHARE_READ | FILE_SHARE_WRITE,
153 FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
154 if (NT_SUCCESS(Status))
155 NtClose(FileHandle);
156 else
157 DPRINT("Failed to open directory '%wZ', Status 0x%08lx\n", &Name, Status);
158
159 return NT_SUCCESS(Status);
160 }
161
162 BOOLEAN
163 DoesFileExist(
164 IN HANDLE RootDirectory OPTIONAL,
165 IN PCWSTR PathNameToFile)
166 {
167 NTSTATUS Status;
168 UNICODE_STRING FileName;
169 HANDLE FileHandle;
170 OBJECT_ATTRIBUTES ObjectAttributes;
171 IO_STATUS_BLOCK IoStatusBlock;
172
173 RtlInitUnicodeString(&FileName, PathNameToFile);
174
175 InitializeObjectAttributes(&ObjectAttributes,
176 &FileName,
177 OBJ_CASE_INSENSITIVE,
178 RootDirectory,
179 NULL);
180
181 Status = NtOpenFile(&FileHandle,
182 FILE_GENERIC_READ, // Contains SYNCHRONIZE
183 &ObjectAttributes,
184 &IoStatusBlock,
185 FILE_SHARE_READ | FILE_SHARE_WRITE,
186 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
187 if (NT_SUCCESS(Status))
188 NtClose(FileHandle);
189 else
190 DPRINT("Failed to open file '%wZ', Status 0x%08lx\n", &FileName, Status);
191
192 return NT_SUCCESS(Status);
193 }
194
195 // FIXME: DEPRECATED! HACKish function that needs to be deprecated!
196 BOOLEAN
197 DoesFileExist_2(
198 IN PCWSTR PathName OPTIONAL,
199 IN PCWSTR FileName)
200 {
201 WCHAR FullName[MAX_PATH];
202 CombinePaths(FullName, ARRAYSIZE(FullName), 2, PathName, FileName);
203 return DoesFileExist(NULL, FullName);
204 }
205
206 /*
207 * The format of NtPath should be:
208 * \Device\HarddiskXXX\PartitionYYY[\path] ,
209 * where XXX and YYY respectively represent the hard disk and partition numbers,
210 * and [\path] represent an optional path (separated by '\\').
211 *
212 * If a NT path of such a form is correctly parsed, the function returns respectively:
213 * - in pDiskNumber: the hard disk number XXX,
214 * - in pPartNumber: the partition number YYY,
215 * - in PathComponent: pointer value (inside NtPath) to the beginning of \path.
216 *
217 * NOTE: The function does not accept leading whitespace.
218 */
219 BOOLEAN
220 NtPathToDiskPartComponents(
221 IN PCWSTR NtPath,
222 OUT PULONG pDiskNumber,
223 OUT PULONG pPartNumber,
224 OUT PCWSTR* PathComponent OPTIONAL)
225 {
226 ULONG DiskNumber, PartNumber;
227 PCWSTR Path;
228
229 *pDiskNumber = 0;
230 *pPartNumber = 0;
231 if (PathComponent) *PathComponent = NULL;
232
233 Path = NtPath;
234
235 if (_wcsnicmp(Path, L"\\Device\\Harddisk", 16) != 0)
236 {
237 /* The NT path doesn't start with the prefix string, thus it cannot be a hard disk device path */
238 DPRINT1("'%S' : Not a possible hard disk device.\n", NtPath);
239 return FALSE;
240 }
241
242 Path += 16;
243
244 /* A number must be present now */
245 if (!iswdigit(*Path))
246 {
247 DPRINT1("'%S' : expected a number! Not a regular hard disk device.\n", Path);
248 return FALSE;
249 }
250 DiskNumber = wcstoul(Path, (PWSTR*)&Path, 10);
251
252 /* Either NULL termination, or a path separator must be present now */
253 if (*Path && *Path != OBJ_NAME_PATH_SEPARATOR)
254 {
255 DPRINT1("'%S' : expected a path separator!\n", Path);
256 return FALSE;
257 }
258
259 if (!*Path)
260 {
261 DPRINT1("The path only specified a hard disk (and nothing else, like a partition...), so we stop there.\n");
262 goto Quit;
263 }
264
265 /* Here, *Path == L'\\' */
266
267 if (_wcsnicmp(Path, L"\\Partition", 10) != 0)
268 {
269 /* Actually, \Partition is optional so, if we don't have it, we still return success. Or should we? */
270 DPRINT1("'%S' : unexpected format!\n", NtPath);
271 goto Quit;
272 }
273
274 Path += 10;
275
276 /* A number must be present now */
277 if (!iswdigit(*Path))
278 {
279 /* If we don't have a number it means this part of path is actually not a partition specifier, so we shouldn't fail either. Or should we? */
280 DPRINT1("'%S' : expected a number!\n", Path);
281 goto Quit;
282 }
283 PartNumber = wcstoul(Path, (PWSTR*)&Path, 10);
284
285 /* Either NULL termination, or a path separator must be present now */
286 if (*Path && *Path != OBJ_NAME_PATH_SEPARATOR)
287 {
288 /* We shouldn't fail here because it just means this part of path is actually not a partition specifier. Or should we? */
289 DPRINT1("'%S' : expected a path separator!\n", Path);
290 goto Quit;
291 }
292
293 /* OK, here we really have a partition specifier: return its number */
294 *pPartNumber = PartNumber;
295
296 Quit:
297 /* Return the disk number */
298 *pDiskNumber = DiskNumber;
299
300 /* Return the path component also, if the user wants it */
301 if (PathComponent) *PathComponent = Path;
302
303 return TRUE;
304 }
305
306 NTSTATUS
307 OpenAndMapFile(
308 IN HANDLE RootDirectory OPTIONAL,
309 IN PCWSTR PathNameToFile,
310 OUT PHANDLE FileHandle, // IN OUT PHANDLE OPTIONAL
311 OUT PHANDLE SectionHandle,
312 OUT PVOID* BaseAddress,
313 OUT PULONG FileSize OPTIONAL,
314 IN BOOLEAN ReadWriteAccess)
315 {
316 NTSTATUS Status;
317 UNICODE_STRING FileName;
318 OBJECT_ATTRIBUTES ObjectAttributes;
319 IO_STATUS_BLOCK IoStatusBlock;
320 ULONG SectionPageProtection;
321 SIZE_T ViewSize;
322 PVOID ViewBase;
323
324 /* Open the file */
325
326 RtlInitUnicodeString(&FileName, PathNameToFile);
327
328 InitializeObjectAttributes(&ObjectAttributes,
329 &FileName,
330 OBJ_CASE_INSENSITIVE,
331 RootDirectory,
332 NULL);
333
334 *FileHandle = NULL;
335 *SectionHandle = NULL;
336
337 Status = NtOpenFile(FileHandle,
338 FILE_GENERIC_READ | // Contains SYNCHRONIZE
339 (ReadWriteAccess ? FILE_GENERIC_WRITE : 0),
340 &ObjectAttributes,
341 &IoStatusBlock,
342 FILE_SHARE_READ,
343 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
344 if (!NT_SUCCESS(Status))
345 {
346 DPRINT1("Failed to open file '%wZ', Status 0x%08lx\n", &FileName, Status);
347 return Status;
348 }
349
350 if (FileSize)
351 {
352 /* Query the file size */
353 FILE_STANDARD_INFORMATION FileInfo;
354 Status = NtQueryInformationFile(*FileHandle,
355 &IoStatusBlock,
356 &FileInfo,
357 sizeof(FileInfo),
358 FileStandardInformation);
359 if (!NT_SUCCESS(Status))
360 {
361 DPRINT("NtQueryInformationFile() failed (Status %lx)\n", Status);
362 NtClose(*FileHandle);
363 *FileHandle = NULL;
364 return Status;
365 }
366
367 if (FileInfo.EndOfFile.HighPart != 0)
368 DPRINT1("WARNING!! The file '%wZ' is too large!\n", &FileName);
369
370 *FileSize = FileInfo.EndOfFile.LowPart;
371
372 DPRINT("File size: %lu\n", *FileSize);
373 }
374
375 /* Map the file in memory */
376
377 SectionPageProtection = (ReadWriteAccess ? PAGE_READWRITE : PAGE_READONLY);
378
379 /* Create the section */
380 Status = NtCreateSection(SectionHandle,
381 STANDARD_RIGHTS_REQUIRED | SECTION_QUERY |
382 SECTION_MAP_READ |
383 (ReadWriteAccess ? SECTION_MAP_WRITE : 0),
384 NULL,
385 NULL,
386 SectionPageProtection,
387 SEC_COMMIT /* | SEC_IMAGE (_NO_EXECUTE) */,
388 *FileHandle);
389 if (!NT_SUCCESS(Status))
390 {
391 DPRINT1("Failed to create a memory section for file '%wZ', Status 0x%08lx\n", &FileName, Status);
392 NtClose(*FileHandle);
393 *FileHandle = NULL;
394 return Status;
395 }
396
397 /* Map the section */
398 ViewSize = 0;
399 ViewBase = NULL;
400 Status = NtMapViewOfSection(*SectionHandle,
401 NtCurrentProcess(),
402 &ViewBase,
403 0, 0,
404 NULL,
405 &ViewSize,
406 ViewShare,
407 0,
408 SectionPageProtection);
409 if (!NT_SUCCESS(Status))
410 {
411 DPRINT1("Failed to map a view for file '%wZ', Status 0x%08lx\n", &FileName, Status);
412 NtClose(*SectionHandle);
413 *SectionHandle = NULL;
414 NtClose(*FileHandle);
415 *FileHandle = NULL;
416 return Status;
417 }
418
419 *BaseAddress = ViewBase;
420 return STATUS_SUCCESS;
421 }
422
423 BOOLEAN
424 UnMapFile(
425 IN HANDLE SectionHandle,
426 IN PVOID BaseAddress)
427 {
428 NTSTATUS Status;
429 BOOLEAN Success = TRUE;
430
431 Status = NtUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
432 if (!NT_SUCCESS(Status))
433 {
434 DPRINT1("UnMapFile: NtUnmapViewOfSection(0x%p) failed with Status 0x%08lx\n",
435 BaseAddress, Status);
436 Success = FALSE;
437 }
438 Status = NtClose(SectionHandle);
439 if (!NT_SUCCESS(Status))
440 {
441 DPRINT1("UnMapFile: NtClose(0x%p) failed with Status 0x%08lx\n",
442 SectionHandle, Status);
443 Success = FALSE;
444 }
445
446 return Success;
447 }
448
449 /* EOF */