[USETUP] When opening the target file for dropping readonly mode only use required...
[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 ULONG Length)
138 {
139 UINT i;
140
141 // TODO: Add check for 8.3 too.
142
143 /* Check for whitespaces */
144 for (i = 0; i < Length; i++)
145 {
146 if (isspace(InstallDir[i]))
147 return FALSE;
148 }
149
150 return TRUE;
151 }
152
153
154 NTSTATUS
155 SetupCreateDirectory(
156 PWCHAR PathName)
157 {
158 PWCHAR PathBuffer = NULL;
159 PWCHAR Ptr, EndPtr;
160 ULONG BackslashCount;
161 ULONG Size;
162 NTSTATUS Status = STATUS_SUCCESS;
163
164 Size = (wcslen(PathName) + 1) * sizeof(WCHAR);
165 PathBuffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, Size);
166 if (PathBuffer == NULL)
167 return STATUS_INSUFFICIENT_RESOURCES;
168
169 wcscpy(PathBuffer, PathName);
170 EndPtr = PathBuffer + wcslen(PathName);
171
172 Ptr = PathBuffer;
173
174 /* Skip the '\Device\HarddiskX\PartitionY\ part */
175 BackslashCount = 0;
176 while (Ptr < EndPtr && BackslashCount < 4)
177 {
178 if (*Ptr == L'\\')
179 BackslashCount++;
180
181 Ptr++;
182 }
183
184 while (Ptr < EndPtr)
185 {
186 if (*Ptr == L'\\')
187 {
188 *Ptr = 0;
189
190 DPRINT("PathBuffer: %S\n", PathBuffer);
191 if (!DoesPathExist(PathBuffer))
192 {
193 DPRINT("Create: %S\n", PathBuffer);
194 Status = SetupCreateSingleDirectory(PathBuffer);
195 if (!NT_SUCCESS(Status))
196 goto done;
197 }
198
199 *Ptr = L'\\';
200 }
201
202 Ptr++;
203 }
204
205 if (!DoesPathExist(PathBuffer))
206 {
207 DPRINT("Create: %S\n", PathBuffer);
208 Status = SetupCreateSingleDirectory(PathBuffer);
209 if (!NT_SUCCESS(Status))
210 goto done;
211 }
212
213 done:
214 DPRINT("Done.\n");
215 if (PathBuffer != NULL)
216 RtlFreeHeap(RtlGetProcessHeap(), 0, PathBuffer);
217
218 return Status;
219 }
220
221
222 NTSTATUS
223 SetupCopyFile(
224 PWCHAR SourceFileName,
225 PWCHAR DestinationFileName)
226 {
227 OBJECT_ATTRIBUTES ObjectAttributes;
228 HANDLE FileHandleSource;
229 HANDLE FileHandleDest;
230 static IO_STATUS_BLOCK IoStatusBlock;
231 FILE_STANDARD_INFORMATION FileStandard;
232 FILE_BASIC_INFORMATION FileBasic;
233 ULONG RegionSize;
234 UNICODE_STRING FileName;
235 NTSTATUS Status;
236 PVOID SourceFileMap = 0;
237 HANDLE SourceFileSection;
238 SIZE_T SourceSectionSize = 0;
239 LARGE_INTEGER ByteOffset;
240
241 #ifdef __REACTOS__
242 RtlInitUnicodeString(&FileName,
243 SourceFileName);
244
245 InitializeObjectAttributes(&ObjectAttributes,
246 &FileName,
247 OBJ_CASE_INSENSITIVE,
248 NULL,
249 NULL);
250
251 Status = NtOpenFile(&FileHandleSource,
252 GENERIC_READ,
253 &ObjectAttributes,
254 &IoStatusBlock,
255 FILE_SHARE_READ,
256 FILE_SEQUENTIAL_ONLY);
257 if (!NT_SUCCESS(Status))
258 {
259 DPRINT1("NtOpenFile failed: %x, %wZ\n", Status, &FileName);
260 goto done;
261 }
262 #else
263 FileHandleSource = CreateFileW(SourceFileName,
264 GENERIC_READ,
265 FILE_SHARE_READ,
266 NULL,
267 OPEN_EXISTING,
268 0,
269 NULL);
270 if (FileHandleSource == INVALID_HANDLE_VALUE)
271 {
272 Status = STATUS_UNSUCCESSFUL;
273 goto done;
274 }
275 #endif
276
277 Status = NtQueryInformationFile(FileHandleSource,
278 &IoStatusBlock,
279 &FileStandard,
280 sizeof(FILE_STANDARD_INFORMATION),
281 FileStandardInformation);
282 if (!NT_SUCCESS(Status))
283 {
284 DPRINT1("NtQueryInformationFile failed: %x\n", Status);
285 goto closesrc;
286 }
287
288 Status = NtQueryInformationFile(FileHandleSource,
289 &IoStatusBlock,&FileBasic,
290 sizeof(FILE_BASIC_INFORMATION),
291 FileBasicInformation);
292 if (!NT_SUCCESS(Status))
293 {
294 DPRINT1("NtQueryInformationFile failed: %x\n", Status);
295 goto closesrc;
296 }
297
298 Status = NtCreateSection(&SourceFileSection,
299 SECTION_MAP_READ,
300 NULL,
301 NULL,
302 PAGE_READONLY,
303 SEC_COMMIT,
304 FileHandleSource);
305 if (!NT_SUCCESS(Status))
306 {
307 DPRINT1("NtCreateSection failed: %x, %S\n", Status, SourceFileName);
308 goto closesrc;
309 }
310
311 Status = NtMapViewOfSection(SourceFileSection,
312 NtCurrentProcess(),
313 &SourceFileMap,
314 0,
315 0,
316 NULL,
317 &SourceSectionSize,
318 ViewUnmap,
319 0,
320 PAGE_READONLY );
321 if (!NT_SUCCESS(Status))
322 {
323 DPRINT1("NtMapViewOfSection failed: %x, %S\n", Status, SourceFileName);
324 goto closesrcsec;
325 }
326
327 RtlInitUnicodeString(&FileName,
328 DestinationFileName);
329
330 InitializeObjectAttributes(&ObjectAttributes,
331 &FileName,
332 OBJ_CASE_INSENSITIVE,
333 NULL,
334 NULL);
335
336 Status = NtCreateFile(&FileHandleDest,
337 GENERIC_WRITE | SYNCHRONIZE,
338 &ObjectAttributes,
339 &IoStatusBlock,
340 NULL,
341 FILE_ATTRIBUTE_NORMAL,
342 0,
343 FILE_OVERWRITE_IF,
344 FILE_NO_INTERMEDIATE_BUFFERING |
345 FILE_SEQUENTIAL_ONLY |
346 FILE_SYNCHRONOUS_IO_NONALERT,
347 NULL,
348 0);
349 if (!NT_SUCCESS(Status))
350 {
351 /* Open may have failed because the file to overwrite
352 * is in readonly mode
353 */
354 if (Status == STATUS_ACCESS_DENIED)
355 {
356 FILE_BASIC_INFORMATION FileBasicInfo;
357
358 /* Reattempt to open it with limited access */
359 Status = NtCreateFile(&FileHandleDest,
360 FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
361 &ObjectAttributes,
362 &IoStatusBlock,
363 NULL,
364 FILE_ATTRIBUTE_NORMAL,
365 0,
366 FILE_OPEN,
367 FILE_NO_INTERMEDIATE_BUFFERING |
368 FILE_SEQUENTIAL_ONLY |
369 FILE_SYNCHRONOUS_IO_NONALERT,
370 NULL,
371 0);
372 /* Fail for real if we cannot open it that way */
373 if (!NT_SUCCESS(Status))
374 {
375 DPRINT1("NtCreateFile failed: %x, %wZ\n", Status, &FileName);
376 goto unmapsrcsec;
377 }
378
379 /* Zero our basic info, just to set attributes */
380 RtlZeroMemory(&FileBasicInfo, sizeof(FileBasicInfo));
381 /* Reset attributes to normal, no read-only */
382 FileBasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
383 /* We basically don't care about whether it succeed:
384 * if it didn't, later open will fail
385 */
386 NtSetInformationFile(FileHandleDest, &IoStatusBlock, &FileBasicInfo,
387 sizeof(FileBasicInfo), FileBasicInformation);
388
389 /* Close file */
390 NtClose(FileHandleDest);
391
392 /* And re-attempt overwrite */
393 Status = NtCreateFile(&FileHandleDest,
394 GENERIC_WRITE | SYNCHRONIZE,
395 &ObjectAttributes,
396 &IoStatusBlock,
397 NULL,
398 FILE_ATTRIBUTE_NORMAL,
399 0,
400 FILE_OVERWRITE_IF,
401 FILE_NO_INTERMEDIATE_BUFFERING |
402 FILE_SEQUENTIAL_ONLY |
403 FILE_SYNCHRONOUS_IO_NONALERT,
404 NULL,
405 0);
406 }
407
408 /* We failed */
409 if (!NT_SUCCESS(Status))
410 {
411 DPRINT1("NtCreateFile failed: %x, %wZ\n", Status, &FileName);
412 goto unmapsrcsec;
413 }
414 }
415
416 RegionSize = (ULONG)PAGE_ROUND_UP(FileStandard.EndOfFile.u.LowPart);
417 IoStatusBlock.Status = 0;
418 ByteOffset.QuadPart = 0ULL;
419 Status = NtWriteFile(FileHandleDest,
420 NULL,
421 NULL,
422 NULL,
423 &IoStatusBlock,
424 SourceFileMap,
425 RegionSize,
426 &ByteOffset,
427 NULL);
428 if (!NT_SUCCESS(Status))
429 {
430 DPRINT1("NtWriteFile failed: %x:%x, iosb: %p src: %p, size: %x\n", Status, IoStatusBlock.Status, &IoStatusBlock, SourceFileMap, RegionSize);
431 goto closedest;
432 }
433
434 /* Copy file date/time from source file */
435 Status = NtSetInformationFile(FileHandleDest,
436 &IoStatusBlock,
437 &FileBasic,
438 sizeof(FILE_BASIC_INFORMATION),
439 FileBasicInformation);
440 if (!NT_SUCCESS(Status))
441 {
442 DPRINT1("NtSetInformationFile failed: %x\n", Status);
443 goto closedest;
444 }
445
446 /* shorten the file back to it's real size after completing the write */
447 Status = NtSetInformationFile(FileHandleDest,
448 &IoStatusBlock,
449 &FileStandard.EndOfFile,
450 sizeof(FILE_END_OF_FILE_INFORMATION),
451 FileEndOfFileInformation);
452 if (!NT_SUCCESS(Status))
453 {
454 DPRINT1("NtSetInformationFile failed: %x\n", Status);
455 }
456
457 closedest:
458 NtClose(FileHandleDest);
459
460 unmapsrcsec:
461 NtUnmapViewOfSection(NtCurrentProcess(), SourceFileMap);
462
463 closesrcsec:
464 NtClose(SourceFileSection);
465
466 closesrc:
467 NtClose(FileHandleSource);
468
469 done:
470 return Status;
471 }
472
473 #ifdef __REACTOS__
474 NTSTATUS
475 SetupExtractFile(
476 PWCHAR CabinetFileName,
477 PWCHAR SourceFileName,
478 PWCHAR DestinationPathName)
479 {
480 ULONG CabStatus;
481
482 DPRINT("SetupExtractFile(CabinetFileName %S, SourceFileName %S, DestinationPathName %S)\n",
483 CabinetFileName, SourceFileName, DestinationPathName);
484
485 if (HasCurrentCabinet)
486 {
487 DPRINT("CurrentCabinetName: %S\n", CurrentCabinetName);
488 }
489
490 if ((HasCurrentCabinet) && (wcscmp(CabinetFileName, CurrentCabinetName) == 0))
491 {
492 DPRINT("Using same cabinet as last time\n");
493
494 /* Use our last location because the files should be sequential */
495 CabStatus = CabinetFindNextFileSequential(SourceFileName, &Search);
496 if (CabStatus != CAB_STATUS_SUCCESS)
497 {
498 DPRINT("Sequential miss on file: %S\n", SourceFileName);
499
500 /* Looks like we got unlucky */
501 CabStatus = CabinetFindFirst(SourceFileName, &Search);
502 }
503 }
504 else
505 {
506 DPRINT("Using new cabinet\n");
507
508 if (HasCurrentCabinet)
509 {
510 CabinetCleanup();
511 }
512
513 wcscpy(CurrentCabinetName, CabinetFileName);
514
515 CabinetInitialize();
516 CabinetSetEventHandlers(NULL, NULL, NULL);
517 CabinetSetCabinetName(CabinetFileName);
518
519 CabStatus = CabinetOpen();
520 if (CabStatus == CAB_STATUS_SUCCESS)
521 {
522 DPRINT("Opened cabinet %S\n", CabinetGetCabinetName());
523 HasCurrentCabinet = TRUE;
524 }
525 else
526 {
527 DPRINT("Cannot open cabinet (%d)\n", CabStatus);
528 return STATUS_UNSUCCESSFUL;
529 }
530
531 /* We have to start at the beginning here */
532 CabStatus = CabinetFindFirst(SourceFileName, &Search);
533 }
534
535 if (CabStatus != CAB_STATUS_SUCCESS)
536 {
537 DPRINT1("Unable to find '%S' in cabinet '%S'\n", SourceFileName, CabinetGetCabinetName());
538 return STATUS_UNSUCCESSFUL;
539 }
540
541 CabinetSetDestinationPath(DestinationPathName);
542 CabStatus = CabinetExtractFile(&Search);
543 if (CabStatus != CAB_STATUS_SUCCESS)
544 {
545 DPRINT("Cannot extract file %S (%d)\n", SourceFileName, CabStatus);
546 return STATUS_UNSUCCESSFUL;
547 }
548
549 return STATUS_SUCCESS;
550 }
551 #endif
552
553 BOOLEAN
554 DoesFileExist(
555 PWSTR PathName,
556 PWSTR FileName)
557 {
558 OBJECT_ATTRIBUTES ObjectAttributes;
559 IO_STATUS_BLOCK IoStatusBlock;
560 UNICODE_STRING Name;
561 WCHAR FullName[MAX_PATH];
562 HANDLE FileHandle;
563 NTSTATUS Status;
564
565 wcscpy(FullName, PathName);
566 if (FileName != NULL)
567 {
568 if (FileName[0] != L'\\')
569 wcscat(FullName, L"\\");
570 wcscat(FullName, FileName);
571 }
572
573 RtlInitUnicodeString(&Name,
574 FullName);
575
576 InitializeObjectAttributes(&ObjectAttributes,
577 &Name,
578 OBJ_CASE_INSENSITIVE,
579 NULL,
580 NULL);
581
582 Status = NtOpenFile(&FileHandle,
583 GENERIC_READ | SYNCHRONIZE,
584 &ObjectAttributes,
585 &IoStatusBlock,
586 0,
587 FILE_SYNCHRONOUS_IO_NONALERT);
588 if (!NT_SUCCESS(Status))
589 {
590 return FALSE;
591 }
592
593 NtClose(FileHandle);
594
595 return TRUE;
596 }
597
598 /* EOF */