[SETUPLIB][USETUP] Refactor the DoesFileExist() function so that it now looks closer...
[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 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 GENERIC_READ | 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 DPRINT1("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 {
315 NTSTATUS Status;
316 UNICODE_STRING FileName;
317 OBJECT_ATTRIBUTES ObjectAttributes;
318 IO_STATUS_BLOCK IoStatusBlock;
319 SIZE_T ViewSize;
320 PVOID ViewBase;
321
322 RtlInitUnicodeString(&FileName, PathNameToFile);
323
324 InitializeObjectAttributes(&ObjectAttributes,
325 &FileName,
326 OBJ_CASE_INSENSITIVE,
327 RootDirectory,
328 NULL);
329
330 *FileHandle = NULL;
331 *SectionHandle = NULL;
332
333 Status = NtOpenFile(FileHandle,
334 GENERIC_READ | SYNCHRONIZE,
335 &ObjectAttributes,
336 &IoStatusBlock,
337 FILE_SHARE_READ,
338 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
339 if (!NT_SUCCESS(Status))
340 {
341 DPRINT1("Failed to open file '%wZ', Status 0x%08lx\n", &FileName, Status);
342 return Status;
343 }
344
345 if (FileSize)
346 {
347 /* Query the file size */
348 FILE_STANDARD_INFORMATION FileInfo;
349 Status = NtQueryInformationFile(*FileHandle,
350 &IoStatusBlock,
351 &FileInfo,
352 sizeof(FileInfo),
353 FileStandardInformation);
354 if (!NT_SUCCESS(Status))
355 {
356 DPRINT("NtQueryInformationFile() failed (Status %lx)\n", Status);
357 NtClose(*FileHandle);
358 *FileHandle = NULL;
359 return Status;
360 }
361
362 if (FileInfo.EndOfFile.HighPart != 0)
363 DPRINT1("WARNING!! The file '%wZ' is too large!\n", &FileName);
364
365 *FileSize = FileInfo.EndOfFile.LowPart;
366
367 DPRINT("File size: %lu\n", *FileSize);
368 }
369
370 /* Map the file in memory */
371
372 /* Create the section */
373 Status = NtCreateSection(SectionHandle,
374 SECTION_MAP_READ,
375 NULL,
376 NULL,
377 PAGE_READONLY,
378 SEC_COMMIT /* | SEC_IMAGE (_NO_EXECUTE) */,
379 *FileHandle);
380 if (!NT_SUCCESS(Status))
381 {
382 DPRINT1("Failed to create a memory section for file '%wZ', Status 0x%08lx\n", &FileName, Status);
383 NtClose(*FileHandle);
384 *FileHandle = NULL;
385 return Status;
386 }
387
388 /* Map the section */
389 ViewSize = 0;
390 ViewBase = NULL;
391 Status = NtMapViewOfSection(*SectionHandle,
392 NtCurrentProcess(),
393 &ViewBase,
394 0, 0,
395 NULL,
396 &ViewSize,
397 ViewShare,
398 0,
399 PAGE_READONLY);
400 if (!NT_SUCCESS(Status))
401 {
402 DPRINT1("Failed to map a view for file '%wZ', Status 0x%08lx\n", &FileName, Status);
403 NtClose(*SectionHandle);
404 *SectionHandle = NULL;
405 NtClose(*FileHandle);
406 *FileHandle = NULL;
407 return Status;
408 }
409
410 *BaseAddress = ViewBase;
411 return STATUS_SUCCESS;
412 }
413
414 BOOLEAN
415 UnMapFile(
416 IN HANDLE SectionHandle,
417 IN PVOID BaseAddress)
418 {
419 NTSTATUS Status;
420 BOOLEAN Success = TRUE;
421
422 Status = NtUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
423 if (!NT_SUCCESS(Status))
424 {
425 DPRINT1("UnMapFile: NtUnmapViewOfSection(0x%p) failed with Status 0x%08lx\n",
426 BaseAddress, Status);
427 Success = FALSE;
428 }
429 Status = NtClose(SectionHandle);
430 if (!NT_SUCCESS(Status))
431 {
432 DPRINT1("UnMapFile: NtClose(0x%p) failed with Status 0x%08lx\n",
433 SectionHandle, Status);
434 Success = FALSE;
435 }
436
437 return Success;
438 }
439
440 /* EOF */