e0bd0b110e0d7dbc349b115a6a3ac1f30a26a812
[reactos.git] / base / setup / usetup / filesup.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS text-mode setup
4 * FILE: base/setup/usetup/filesup.c
5 * PURPOSE: File support functions
6 * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 */
8
9 /* INCLUDES *****************************************************************/
10
11 #include "usetup.h"
12
13 #define NDEBUG
14 #include <debug.h>
15
16 /* FUNCTIONS ****************************************************************/
17
18 static BOOLEAN HasCurrentCabinet = FALSE;
19 static WCHAR CurrentCabinetName[MAX_PATH];
20 static CAB_SEARCH Search;
21
22 static
23 NTSTATUS
24 SetupCreateSingleDirectory(
25 PWCHAR DirectoryName)
26 {
27 OBJECT_ATTRIBUTES ObjectAttributes;
28 IO_STATUS_BLOCK IoStatusBlock;
29 UNICODE_STRING PathName;
30 HANDLE DirectoryHandle;
31 NTSTATUS Status;
32
33 if (!RtlCreateUnicodeString(&PathName, DirectoryName))
34 return STATUS_NO_MEMORY;
35
36 if (PathName.Length > sizeof(WCHAR) &&
37 PathName.Buffer[PathName.Length / sizeof(WCHAR) - 2] == L'\\' &&
38 PathName.Buffer[PathName.Length / sizeof(WCHAR) - 1] == L'.')
39 {
40 PathName.Length -= sizeof(WCHAR);
41 PathName.Buffer[PathName.Length / sizeof(WCHAR)] = 0;
42 }
43
44 if (PathName.Length > sizeof(WCHAR) &&
45 PathName.Buffer[PathName.Length / sizeof(WCHAR) - 1] == L'\\')
46 {
47 PathName.Length -= sizeof(WCHAR);
48 PathName.Buffer[PathName.Length / sizeof(WCHAR)] = 0;
49 }
50
51 InitializeObjectAttributes(&ObjectAttributes,
52 &PathName,
53 OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
54 NULL,
55 NULL);
56
57 Status = NtCreateFile(&DirectoryHandle,
58 FILE_LIST_DIRECTORY | SYNCHRONIZE,
59 &ObjectAttributes,
60 &IoStatusBlock,
61 NULL,
62 FILE_ATTRIBUTE_DIRECTORY,
63 FILE_SHARE_READ | FILE_SHARE_WRITE,
64 FILE_OPEN_IF,
65 FILE_OPEN_FOR_BACKUP_INTENT | FILE_DIRECTORY_FILE,
66 NULL,
67 0);
68 if (NT_SUCCESS(Status))
69 {
70 NtClose(DirectoryHandle);
71 }
72
73 RtlFreeUnicodeString(&PathName);
74
75 return Status;
76 }
77
78 NTSTATUS
79 SetupCreateDirectory(
80 PWCHAR PathName)
81 {
82 PWCHAR PathBuffer = NULL;
83 PWCHAR Ptr, EndPtr;
84 ULONG BackslashCount;
85 ULONG Size;
86 NTSTATUS Status = STATUS_SUCCESS;
87
88 Size = (wcslen(PathName) + 1) * sizeof(WCHAR);
89 PathBuffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, Size);
90 if (PathBuffer == NULL)
91 return STATUS_INSUFFICIENT_RESOURCES;
92
93 wcscpy(PathBuffer, PathName);
94 EndPtr = PathBuffer + wcslen(PathName);
95
96 Ptr = PathBuffer;
97
98 /* Skip the '\Device\HarddiskX\PartitionY\ part */
99 BackslashCount = 0;
100 while (Ptr < EndPtr && BackslashCount < 4)
101 {
102 if (*Ptr == L'\\')
103 BackslashCount++;
104
105 Ptr++;
106 }
107
108 while (Ptr < EndPtr)
109 {
110 if (*Ptr == L'\\')
111 {
112 *Ptr = 0;
113
114 DPRINT("PathBuffer: %S\n", PathBuffer);
115 if (!DoesPathExist(NULL, PathBuffer))
116 {
117 DPRINT("Create: %S\n", PathBuffer);
118 Status = SetupCreateSingleDirectory(PathBuffer);
119 if (!NT_SUCCESS(Status))
120 goto done;
121 }
122
123 *Ptr = L'\\';
124 }
125
126 Ptr++;
127 }
128
129 if (!DoesPathExist(NULL, PathBuffer))
130 {
131 DPRINT("Create: %S\n", PathBuffer);
132 Status = SetupCreateSingleDirectory(PathBuffer);
133 if (!NT_SUCCESS(Status))
134 goto done;
135 }
136
137 done:
138 DPRINT("Done.\n");
139 if (PathBuffer != NULL)
140 RtlFreeHeap(RtlGetProcessHeap(), 0, PathBuffer);
141
142 return Status;
143 }
144
145 NTSTATUS
146 SetupCopyFile(
147 PWCHAR SourceFileName,
148 PWCHAR DestinationFileName)
149 {
150 OBJECT_ATTRIBUTES ObjectAttributes;
151 HANDLE FileHandleSource;
152 HANDLE FileHandleDest;
153 static IO_STATUS_BLOCK IoStatusBlock;
154 FILE_STANDARD_INFORMATION FileStandard;
155 FILE_BASIC_INFORMATION FileBasic;
156 ULONG RegionSize;
157 UNICODE_STRING FileName;
158 NTSTATUS Status;
159 PVOID SourceFileMap = 0;
160 HANDLE SourceFileSection;
161 SIZE_T SourceSectionSize = 0;
162 LARGE_INTEGER ByteOffset;
163
164 RtlInitUnicodeString(&FileName,
165 SourceFileName);
166
167 InitializeObjectAttributes(&ObjectAttributes,
168 &FileName,
169 OBJ_CASE_INSENSITIVE,
170 NULL,
171 NULL);
172
173 Status = NtOpenFile(&FileHandleSource,
174 GENERIC_READ,
175 &ObjectAttributes,
176 &IoStatusBlock,
177 FILE_SHARE_READ,
178 FILE_SEQUENTIAL_ONLY);
179 if (!NT_SUCCESS(Status))
180 {
181 DPRINT1("NtOpenFile failed: %x, %wZ\n", Status, &FileName);
182 goto done;
183 }
184
185 Status = NtQueryInformationFile(FileHandleSource,
186 &IoStatusBlock,
187 &FileStandard,
188 sizeof(FILE_STANDARD_INFORMATION),
189 FileStandardInformation);
190 if (!NT_SUCCESS(Status))
191 {
192 DPRINT1("NtQueryInformationFile failed: %x\n", Status);
193 goto closesrc;
194 }
195
196 Status = NtQueryInformationFile(FileHandleSource,
197 &IoStatusBlock,&FileBasic,
198 sizeof(FILE_BASIC_INFORMATION),
199 FileBasicInformation);
200 if (!NT_SUCCESS(Status))
201 {
202 DPRINT1("NtQueryInformationFile failed: %x\n", Status);
203 goto closesrc;
204 }
205
206 Status = NtCreateSection(&SourceFileSection,
207 SECTION_MAP_READ,
208 NULL,
209 NULL,
210 PAGE_READONLY,
211 SEC_COMMIT,
212 FileHandleSource);
213 if (!NT_SUCCESS(Status))
214 {
215 DPRINT1("NtCreateSection failed: %x, %S\n", Status, SourceFileName);
216 goto closesrc;
217 }
218
219 Status = NtMapViewOfSection(SourceFileSection,
220 NtCurrentProcess(),
221 &SourceFileMap,
222 0,
223 0,
224 NULL,
225 &SourceSectionSize,
226 ViewUnmap,
227 0,
228 PAGE_READONLY );
229 if (!NT_SUCCESS(Status))
230 {
231 DPRINT1("NtMapViewOfSection failed: %x, %S\n", Status, SourceFileName);
232 goto closesrcsec;
233 }
234
235 RtlInitUnicodeString(&FileName,
236 DestinationFileName);
237
238 InitializeObjectAttributes(&ObjectAttributes,
239 &FileName,
240 OBJ_CASE_INSENSITIVE,
241 NULL,
242 NULL);
243
244 Status = NtCreateFile(&FileHandleDest,
245 GENERIC_WRITE | SYNCHRONIZE,
246 &ObjectAttributes,
247 &IoStatusBlock,
248 NULL,
249 FILE_ATTRIBUTE_NORMAL,
250 0,
251 FILE_OVERWRITE_IF,
252 FILE_NO_INTERMEDIATE_BUFFERING |
253 FILE_SEQUENTIAL_ONLY |
254 FILE_SYNCHRONOUS_IO_NONALERT,
255 NULL,
256 0);
257 if (!NT_SUCCESS(Status))
258 {
259 /* Open may have failed because the file to overwrite
260 * is in readonly mode
261 */
262 if (Status == STATUS_ACCESS_DENIED)
263 {
264 FILE_BASIC_INFORMATION FileBasicInfo;
265
266 /* Reattempt to open it with limited access */
267 Status = NtCreateFile(&FileHandleDest,
268 FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
269 &ObjectAttributes,
270 &IoStatusBlock,
271 NULL,
272 FILE_ATTRIBUTE_NORMAL,
273 0,
274 FILE_OPEN,
275 FILE_NO_INTERMEDIATE_BUFFERING |
276 FILE_SEQUENTIAL_ONLY |
277 FILE_SYNCHRONOUS_IO_NONALERT,
278 NULL,
279 0);
280 /* Fail for real if we cannot open it that way */
281 if (!NT_SUCCESS(Status))
282 {
283 DPRINT1("NtCreateFile failed: %x, %wZ\n", Status, &FileName);
284 goto unmapsrcsec;
285 }
286
287 /* Zero our basic info, just to set attributes */
288 RtlZeroMemory(&FileBasicInfo, sizeof(FileBasicInfo));
289 /* Reset attributes to normal, no read-only */
290 FileBasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
291 /* We basically don't care about whether it succeed:
292 * if it didn't, later open will fail
293 */
294 NtSetInformationFile(FileHandleDest, &IoStatusBlock, &FileBasicInfo,
295 sizeof(FileBasicInfo), FileBasicInformation);
296
297 /* Close file */
298 NtClose(FileHandleDest);
299
300 /* And re-attempt overwrite */
301 Status = NtCreateFile(&FileHandleDest,
302 GENERIC_WRITE | SYNCHRONIZE,
303 &ObjectAttributes,
304 &IoStatusBlock,
305 NULL,
306 FILE_ATTRIBUTE_NORMAL,
307 0,
308 FILE_OVERWRITE_IF,
309 FILE_NO_INTERMEDIATE_BUFFERING |
310 FILE_SEQUENTIAL_ONLY |
311 FILE_SYNCHRONOUS_IO_NONALERT,
312 NULL,
313 0);
314 }
315
316 /* We failed */
317 if (!NT_SUCCESS(Status))
318 {
319 DPRINT1("NtCreateFile failed: %x, %wZ\n", Status, &FileName);
320 goto unmapsrcsec;
321 }
322 }
323
324 RegionSize = (ULONG)PAGE_ROUND_UP(FileStandard.EndOfFile.u.LowPart);
325 IoStatusBlock.Status = 0;
326 ByteOffset.QuadPart = 0ULL;
327 Status = NtWriteFile(FileHandleDest,
328 NULL,
329 NULL,
330 NULL,
331 &IoStatusBlock,
332 SourceFileMap,
333 RegionSize,
334 &ByteOffset,
335 NULL);
336 if (!NT_SUCCESS(Status))
337 {
338 DPRINT1("NtWriteFile failed: %x:%x, iosb: %p src: %p, size: %x\n", Status, IoStatusBlock.Status, &IoStatusBlock, SourceFileMap, RegionSize);
339 goto closedest;
340 }
341
342 /* Copy file date/time from source file */
343 Status = NtSetInformationFile(FileHandleDest,
344 &IoStatusBlock,
345 &FileBasic,
346 sizeof(FILE_BASIC_INFORMATION),
347 FileBasicInformation);
348 if (!NT_SUCCESS(Status))
349 {
350 DPRINT1("NtSetInformationFile failed: %x\n", Status);
351 goto closedest;
352 }
353
354 /* shorten the file back to it's real size after completing the write */
355 Status = NtSetInformationFile(FileHandleDest,
356 &IoStatusBlock,
357 &FileStandard.EndOfFile,
358 sizeof(FILE_END_OF_FILE_INFORMATION),
359 FileEndOfFileInformation);
360 if (!NT_SUCCESS(Status))
361 {
362 DPRINT1("NtSetInformationFile failed: %x\n", Status);
363 }
364
365 closedest:
366 NtClose(FileHandleDest);
367
368 unmapsrcsec:
369 NtUnmapViewOfSection(NtCurrentProcess(), SourceFileMap);
370
371 closesrcsec:
372 NtClose(SourceFileSection);
373
374 closesrc:
375 NtClose(FileHandleSource);
376
377 done:
378 return Status;
379 }
380
381
382 NTSTATUS
383 SetupExtractFile(
384 PWCHAR CabinetFileName,
385 PWCHAR SourceFileName,
386 PWCHAR DestinationPathName)
387 {
388 ULONG CabStatus;
389
390 DPRINT("SetupExtractFile(CabinetFileName %S, SourceFileName %S, DestinationPathName %S)\n",
391 CabinetFileName, SourceFileName, DestinationPathName);
392
393 if (HasCurrentCabinet)
394 {
395 DPRINT("CurrentCabinetName: %S\n", CurrentCabinetName);
396 }
397
398 if ((HasCurrentCabinet) && (wcscmp(CabinetFileName, CurrentCabinetName) == 0))
399 {
400 DPRINT("Using same cabinet as last time\n");
401
402 /* Use our last location because the files should be sequential */
403 CabStatus = CabinetFindNextFileSequential(SourceFileName, &Search);
404 if (CabStatus != CAB_STATUS_SUCCESS)
405 {
406 DPRINT("Sequential miss on file: %S\n", SourceFileName);
407
408 /* Looks like we got unlucky */
409 CabStatus = CabinetFindFirst(SourceFileName, &Search);
410 }
411 }
412 else
413 {
414 DPRINT("Using new cabinet\n");
415
416 if (HasCurrentCabinet)
417 {
418 CabinetCleanup();
419 }
420
421 wcscpy(CurrentCabinetName, CabinetFileName);
422
423 CabinetInitialize();
424 CabinetSetEventHandlers(NULL, NULL, NULL);
425 CabinetSetCabinetName(CabinetFileName);
426
427 CabStatus = CabinetOpen();
428 if (CabStatus == CAB_STATUS_SUCCESS)
429 {
430 DPRINT("Opened cabinet %S\n", CabinetGetCabinetName());
431 HasCurrentCabinet = TRUE;
432 }
433 else
434 {
435 DPRINT("Cannot open cabinet (%d)\n", CabStatus);
436 return STATUS_UNSUCCESSFUL;
437 }
438
439 /* We have to start at the beginning here */
440 CabStatus = CabinetFindFirst(SourceFileName, &Search);
441 }
442
443 if (CabStatus != CAB_STATUS_SUCCESS)
444 {
445 DPRINT1("Unable to find '%S' in cabinet '%S'\n", SourceFileName, CabinetGetCabinetName());
446 return STATUS_UNSUCCESSFUL;
447 }
448
449 CabinetSetDestinationPath(DestinationPathName);
450 CabStatus = CabinetExtractFile(&Search);
451 if (CabStatus != CAB_STATUS_SUCCESS)
452 {
453 DPRINT("Cannot extract file %S (%d)\n", SourceFileName, CabStatus);
454 return STATUS_UNSUCCESSFUL;
455 }
456
457 return STATUS_SUCCESS;
458 }
459
460
461 BOOLEAN
462 IsValidPath(
463 IN PCWSTR InstallDir)
464 {
465 UINT i, Length;
466
467 Length = wcslen(InstallDir);
468
469 // TODO: Add check for 8.3 too.
470
471 /* Path must be at least 2 characters long */
472 // if (Length < 2)
473 // return FALSE;
474
475 /* Path must start with a backslash */
476 // if (InstallDir[0] != L'\\')
477 // return FALSE;
478
479 /* Path must not end with a backslash */
480 if (InstallDir[Length - 1] == L'\\')
481 return FALSE;
482
483 /* Path must not contain whitespace characters */
484 for (i = 0; i < Length; i++)
485 {
486 if (iswspace(InstallDir[i]))
487 return FALSE;
488 }
489
490 /* Path component must not end with a dot */
491 for (i = 0; i < Length; i++)
492 {
493 if (InstallDir[i] == L'\\' && i > 0)
494 {
495 if (InstallDir[i - 1] == L'.')
496 return FALSE;
497 }
498 }
499
500 if (InstallDir[Length - 1] == L'.')
501 return FALSE;
502
503 return TRUE;
504 }
505
506 /* EOF */