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