290c4cb41429779e4e8a2c8544c2291b1a9f70bd
[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 DPRINT1("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 PathName OPTIONAL,
166 IN PCWSTR FileName)
167 {
168 NTSTATUS Status;
169 HANDLE FileHandle;
170 OBJECT_ATTRIBUTES ObjectAttributes;
171 IO_STATUS_BLOCK IoStatusBlock;
172 UNICODE_STRING Name;
173 WCHAR FullName[MAX_PATH];
174
175 CombinePaths(FullName, ARRAYSIZE(FullName), 2, PathName, FileName);
176 RtlInitUnicodeString(&Name, FullName);
177
178 InitializeObjectAttributes(&ObjectAttributes,
179 &Name,
180 OBJ_CASE_INSENSITIVE,
181 RootDirectory,
182 NULL);
183
184 Status = NtOpenFile(&FileHandle,
185 GENERIC_READ | SYNCHRONIZE,
186 &ObjectAttributes,
187 &IoStatusBlock,
188 FILE_SHARE_READ | FILE_SHARE_WRITE,
189 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
190 if (NT_SUCCESS(Status))
191 NtClose(FileHandle);
192 else
193 DPRINT1("Failed to open file %wZ, Status 0x%08lx\n", &Name, Status);
194
195 return NT_SUCCESS(Status);
196 }
197
198 /*
199 * The format of NtPath should be:
200 * \Device\HarddiskXXX\PartitionYYY[\path] ,
201 * where XXX and YYY respectively represent the hard disk and partition numbers,
202 * and [\path] represent an optional path (separated by '\\').
203 *
204 * If a NT path of such a form is correctly parsed, the function returns respectively:
205 * - in pDiskNumber: the hard disk number XXX,
206 * - in pPartNumber: the partition number YYY,
207 * - in PathComponent: pointer value (inside NtPath) to the beginning of \path.
208 *
209 * NOTE: The function does not accept leading whitespace.
210 */
211 BOOLEAN
212 NtPathToDiskPartComponents(
213 IN PCWSTR NtPath,
214 OUT PULONG pDiskNumber,
215 OUT PULONG pPartNumber,
216 OUT PCWSTR* PathComponent OPTIONAL)
217 {
218 ULONG DiskNumber, PartNumber;
219 PCWSTR Path;
220
221 *pDiskNumber = 0;
222 *pPartNumber = 0;
223 if (PathComponent) *PathComponent = NULL;
224
225 Path = NtPath;
226
227 if (_wcsnicmp(Path, L"\\Device\\Harddisk", 16) != 0)
228 {
229 /* The NT path doesn't start with the prefix string, thus it cannot be a hard disk device path */
230 DPRINT1("'%S' : Not a possible hard disk device.\n", NtPath);
231 return FALSE;
232 }
233
234 Path += 16;
235
236 /* A number must be present now */
237 if (!iswdigit(*Path))
238 {
239 DPRINT1("'%S' : expected a number! Not a regular hard disk device.\n", Path);
240 return FALSE;
241 }
242 DiskNumber = wcstoul(Path, (PWSTR*)&Path, 10);
243
244 /* Either NULL termination, or a path separator must be present now */
245 if (*Path && *Path != OBJ_NAME_PATH_SEPARATOR)
246 {
247 DPRINT1("'%S' : expected a path separator!\n", Path);
248 return FALSE;
249 }
250
251 if (!*Path)
252 {
253 DPRINT1("The path only specified a hard disk (and nothing else, like a partition...), so we stop there.\n");
254 goto Quit;
255 }
256
257 /* Here, *Path == L'\\' */
258
259 if (_wcsnicmp(Path, L"\\Partition", 10) != 0)
260 {
261 /* Actually, \Partition is optional so, if we don't have it, we still return success. Or should we? */
262 DPRINT1("'%S' : unexpected format!\n", NtPath);
263 goto Quit;
264 }
265
266 Path += 10;
267
268 /* A number must be present now */
269 if (!iswdigit(*Path))
270 {
271 /* 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? */
272 DPRINT1("'%S' : expected a number!\n", Path);
273 goto Quit;
274 }
275 PartNumber = wcstoul(Path, (PWSTR*)&Path, 10);
276
277 /* Either NULL termination, or a path separator must be present now */
278 if (*Path && *Path != OBJ_NAME_PATH_SEPARATOR)
279 {
280 /* We shouldn't fail here because it just means this part of path is actually not a partition specifier. Or should we? */
281 DPRINT1("'%S' : expected a path separator!\n", Path);
282 goto Quit;
283 }
284
285 /* OK, here we really have a partition specifier: return its number */
286 *pPartNumber = PartNumber;
287
288 Quit:
289 /* Return the disk number */
290 *pDiskNumber = DiskNumber;
291
292 /* Return the path component also, if the user wants it */
293 if (PathComponent) *PathComponent = Path;
294
295 return TRUE;
296 }
297
298 NTSTATUS
299 OpenAndMapFile(
300 IN HANDLE RootDirectory OPTIONAL,
301 IN PCWSTR PathName OPTIONAL,
302 IN PCWSTR FileName, // OPTIONAL
303 OUT PHANDLE FileHandle, // IN OUT PHANDLE OPTIONAL
304 OUT PHANDLE SectionHandle,
305 OUT PVOID* BaseAddress,
306 OUT PULONG FileSize OPTIONAL)
307 {
308 NTSTATUS Status;
309 OBJECT_ATTRIBUTES ObjectAttributes;
310 IO_STATUS_BLOCK IoStatusBlock;
311 SIZE_T ViewSize;
312 PVOID ViewBase;
313 UNICODE_STRING Name;
314 WCHAR FullName[MAX_PATH];
315
316 CombinePaths(FullName, ARRAYSIZE(FullName), 2, PathName, FileName);
317 RtlInitUnicodeString(&Name, FullName);
318
319 InitializeObjectAttributes(&ObjectAttributes,
320 &Name,
321 OBJ_CASE_INSENSITIVE,
322 RootDirectory,
323 NULL);
324
325 *FileHandle = NULL;
326 *SectionHandle = NULL;
327
328 Status = NtOpenFile(FileHandle,
329 GENERIC_READ | SYNCHRONIZE,
330 &ObjectAttributes,
331 &IoStatusBlock,
332 FILE_SHARE_READ,
333 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
334 if (!NT_SUCCESS(Status))
335 {
336 DPRINT1("Failed to open file '%wZ', Status 0x%08lx\n", &Name, Status);
337 return Status;
338 }
339
340 if (FileSize)
341 {
342 /* Query the file size */
343 FILE_STANDARD_INFORMATION FileInfo;
344 Status = NtQueryInformationFile(*FileHandle,
345 &IoStatusBlock,
346 &FileInfo,
347 sizeof(FileInfo),
348 FileStandardInformation);
349 if (!NT_SUCCESS(Status))
350 {
351 DPRINT("NtQueryInformationFile() failed (Status %lx)\n", Status);
352 NtClose(*FileHandle);
353 *FileHandle = NULL;
354 return Status;
355 }
356
357 if (FileInfo.EndOfFile.HighPart != 0)
358 DPRINT1("WARNING!! The file '%wZ' is too large!\n", &Name);
359
360 *FileSize = FileInfo.EndOfFile.LowPart;
361
362 DPRINT("File size: %lu\n", *FileSize);
363 }
364
365 /* Map the file in memory */
366
367 /* Create the section */
368 Status = NtCreateSection(SectionHandle,
369 SECTION_MAP_READ,
370 NULL,
371 NULL,
372 PAGE_READONLY,
373 SEC_COMMIT /* | SEC_IMAGE (_NO_EXECUTE) */,
374 *FileHandle);
375 if (!NT_SUCCESS(Status))
376 {
377 DPRINT1("Failed to create a memory section for file '%wZ', Status 0x%08lx\n", &Name, Status);
378 NtClose(*FileHandle);
379 *FileHandle = NULL;
380 return Status;
381 }
382
383 /* Map the section */
384 ViewSize = 0;
385 ViewBase = NULL;
386 Status = NtMapViewOfSection(*SectionHandle,
387 NtCurrentProcess(),
388 &ViewBase,
389 0, 0,
390 NULL,
391 &ViewSize,
392 ViewShare,
393 0,
394 PAGE_READONLY);
395 if (!NT_SUCCESS(Status))
396 {
397 DPRINT1("Failed to map a view for file %wZ, Status 0x%08lx\n", &Name, Status);
398 NtClose(*SectionHandle);
399 *SectionHandle = NULL;
400 NtClose(*FileHandle);
401 *FileHandle = NULL;
402 return Status;
403 }
404
405 *BaseAddress = ViewBase;
406 return STATUS_SUCCESS;
407 }
408
409 BOOLEAN
410 UnMapFile(
411 IN HANDLE SectionHandle,
412 IN PVOID BaseAddress)
413 {
414 NTSTATUS Status;
415 BOOLEAN Success = TRUE;
416
417 Status = NtUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
418 if (!NT_SUCCESS(Status))
419 {
420 DPRINT1("UnMapFile: NtUnmapViewOfSection(0x%p) failed with Status 0x%08lx\n",
421 BaseAddress, Status);
422 Success = FALSE;
423 }
424 Status = NtClose(SectionHandle);
425 if (!NT_SUCCESS(Status))
426 {
427 DPRINT1("UnMapFile: NtClose(0x%p) failed with Status 0x%08lx\n",
428 SectionHandle, Status);
429 Success = FALSE;
430 }
431
432 return Success;
433 }
434
435 /* EOF */