X-Git-Url: https://git.reactos.org/?p=reactos.git;a=blobdiff_plain;f=base%2Fsetup%2Flib%2Ffilesup.c;h=03d1ad800310e77025550fd96738d263842a8e01;hp=c6314c222e23367e58cba6bfd226e88b962ca6ae;hb=2268b3d64512f93fdeb0a8c5e1591f9e85db879e;hpb=60d9ea76d1252c80b85e8424e4a84081c20855b1 diff --git a/base/setup/lib/filesup.c b/base/setup/lib/filesup.c index c6314c222e2..03d1ad80031 100644 --- a/base/setup/lib/filesup.c +++ b/base/setup/lib/filesup.c @@ -12,8 +12,465 @@ #define NDEBUG #include + +// ACHTUNG! HAXX FIXME!! +#define _SEH2_TRY +#define _SEH2_LEAVE goto __SEH2_FINALLY__label; +#define _SEH2_FINALLY __SEH2_FINALLY__label: +#define _SEH2_END + + /* FUNCTIONS ****************************************************************/ +// TODO: Move SetupCreateDirectory later... + +NTSTATUS +SetupDeleteFile( + IN PCWSTR FileName, + IN BOOLEAN ForceDelete) // ForceDelete can be used to delete read-only files +{ + NTSTATUS Status; + UNICODE_STRING NtPathU; + OBJECT_ATTRIBUTES ObjectAttributes; + IO_STATUS_BLOCK IoStatusBlock; + HANDLE FileHandle; + FILE_DISPOSITION_INFORMATION FileDispInfo; + BOOLEAN RetryOnce = FALSE; + + /* Open the directory name that was passed in */ + RtlInitUnicodeString(&NtPathU, FileName); + InitializeObjectAttributes(&ObjectAttributes, + &NtPathU, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + +Retry: // We go back there once if RetryOnce == TRUE + Status = NtOpenFile(&FileHandle, + DELETE | FILE_READ_ATTRIBUTES | + (RetryOnce ? FILE_WRITE_ATTRIBUTES : 0), + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT); + if (!NT_SUCCESS(Status)) + { + DPRINT1("NtOpenFile failed with Status 0x%08lx\n", Status); + return Status; + } + + if (RetryOnce) + { + FILE_BASIC_INFORMATION FileInformation; + + Status = NtQueryInformationFile(FileHandle, + &IoStatusBlock, + &FileInformation, + sizeof(FILE_BASIC_INFORMATION), + FileBasicInformation); + if (!NT_SUCCESS(Status)) + { + DPRINT1("NtQueryInformationFile failed with Status 0x%08lx\n", Status); + NtClose(FileHandle); + return Status; + } + + FileInformation.FileAttributes = FILE_ATTRIBUTE_NORMAL; + Status = NtSetInformationFile(FileHandle, + &IoStatusBlock, + &FileInformation, + sizeof(FILE_BASIC_INFORMATION), + FileBasicInformation); + NtClose(FileHandle); + if (!NT_SUCCESS(Status)) + { + DPRINT1("NtSetInformationFile failed with Status 0x%08lx\n", Status); + return Status; + } + } + + /* Ask for the file to be deleted */ + FileDispInfo.DeleteFile = TRUE; + Status = NtSetInformationFile(FileHandle, + &IoStatusBlock, + &FileDispInfo, + sizeof(FILE_DISPOSITION_INFORMATION), + FileDispositionInformation); + NtClose(FileHandle); + + if (!NT_SUCCESS(Status)) + DPRINT1("Deletion of file '%S' failed, Status 0x%08lx\n", FileName, Status); + + // FIXME: Check the precise value of Status! + if (!NT_SUCCESS(Status) && ForceDelete && !RetryOnce) + { + /* Retry once */ + RetryOnce = TRUE; + goto Retry; + } + + /* Return result to the caller */ + return Status; +} + +NTSTATUS +SetupCopyFile( + IN PCWSTR SourceFileName, + IN PCWSTR DestinationFileName, + IN BOOLEAN FailIfExists) +{ + NTSTATUS Status; + UNICODE_STRING FileName; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE FileHandleSource; + HANDLE FileHandleDest; + IO_STATUS_BLOCK IoStatusBlock; + FILE_STANDARD_INFORMATION FileStandard; + FILE_BASIC_INFORMATION FileBasic; + ULONG RegionSize; + HANDLE SourceFileSection; + PVOID SourceFileMap = NULL; + SIZE_T SourceSectionSize = 0; + LARGE_INTEGER ByteOffset; + + RtlInitUnicodeString(&FileName, SourceFileName); + + InitializeObjectAttributes(&ObjectAttributes, + &FileName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = NtOpenFile(&FileHandleSource, + GENERIC_READ, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ, + FILE_SEQUENTIAL_ONLY); + if (!NT_SUCCESS(Status)) + { + DPRINT1("NtOpenFile failed: %x, %wZ\n", Status, &FileName); + goto done; + } + + Status = NtQueryInformationFile(FileHandleSource, + &IoStatusBlock, + &FileStandard, + sizeof(FILE_STANDARD_INFORMATION), + FileStandardInformation); + if (!NT_SUCCESS(Status)) + { + DPRINT1("NtQueryInformationFile failed: %x\n", Status); + goto closesrc; + } + + Status = NtQueryInformationFile(FileHandleSource, + &IoStatusBlock, + &FileBasic, + sizeof(FILE_BASIC_INFORMATION), + FileBasicInformation); + if (!NT_SUCCESS(Status)) + { + DPRINT1("NtQueryInformationFile failed: %x\n", Status); + goto closesrc; + } + + Status = NtCreateSection(&SourceFileSection, + SECTION_MAP_READ, + NULL, + NULL, + PAGE_READONLY, + SEC_COMMIT, + FileHandleSource); + if (!NT_SUCCESS(Status)) + { + DPRINT1("NtCreateSection failed: %x, %S\n", Status, SourceFileName); + goto closesrc; + } + + Status = NtMapViewOfSection(SourceFileSection, + NtCurrentProcess(), + &SourceFileMap, + 0, + 0, + NULL, + &SourceSectionSize, + ViewUnmap, + 0, + PAGE_READONLY); + if (!NT_SUCCESS(Status)) + { + DPRINT1("NtMapViewOfSection failed: %x, %S\n", Status, SourceFileName); + goto closesrcsec; + } + + RtlInitUnicodeString(&FileName, DestinationFileName); + + InitializeObjectAttributes(&ObjectAttributes, + &FileName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = NtCreateFile(&FileHandleDest, + GENERIC_WRITE | SYNCHRONIZE, + &ObjectAttributes, + &IoStatusBlock, + NULL, + FileBasic.FileAttributes, // FILE_ATTRIBUTE_NORMAL, + 0, + FailIfExists ? FILE_CREATE : FILE_OVERWRITE_IF, + FILE_NO_INTERMEDIATE_BUFFERING | + FILE_SEQUENTIAL_ONLY | + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0); + if (!NT_SUCCESS(Status)) + { + /* + * Open may have failed because the file to overwrite + * is in readonly mode. + */ + if (Status == STATUS_ACCESS_DENIED) + { + FILE_BASIC_INFORMATION FileBasicInfo; + + /* Reattempt to open it with limited access */ + Status = NtCreateFile(&FileHandleDest, + FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, + &ObjectAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + 0, + FILE_OPEN, + FILE_NO_INTERMEDIATE_BUFFERING | + FILE_SEQUENTIAL_ONLY | + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0); + /* Fail for real if we cannot open it that way */ + if (!NT_SUCCESS(Status)) + { + DPRINT1("NtCreateFile failed: %x, %wZ\n", Status, &FileName); + goto unmapsrcsec; + } + + /* Zero our basic info, just to set attributes */ + RtlZeroMemory(&FileBasicInfo, sizeof(FileBasicInfo)); + /* Reset attributes to normal, no read-only */ + FileBasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL; + /* + * We basically don't care about whether it succeed: + * if it didn't, later open will fail. + */ + NtSetInformationFile(FileHandleDest, &IoStatusBlock, &FileBasicInfo, + sizeof(FileBasicInfo), FileBasicInformation); + + /* Close file */ + NtClose(FileHandleDest); + + /* And re-attempt overwrite */ + Status = NtCreateFile(&FileHandleDest, + GENERIC_WRITE | SYNCHRONIZE, + &ObjectAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + 0, + FILE_OVERWRITE_IF, + FILE_NO_INTERMEDIATE_BUFFERING | + FILE_SEQUENTIAL_ONLY | + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0); + } + + /* We failed */ + if (!NT_SUCCESS(Status)) + { + DPRINT1("NtCreateFile failed: %x, %wZ\n", Status, &FileName); + goto unmapsrcsec; + } + } + + RegionSize = (ULONG)PAGE_ROUND_UP(FileStandard.EndOfFile.u.LowPart); + IoStatusBlock.Status = 0; + ByteOffset.QuadPart = 0ULL; + Status = NtWriteFile(FileHandleDest, + NULL, + NULL, + NULL, + &IoStatusBlock, + SourceFileMap, + RegionSize, + &ByteOffset, + NULL); + if (!NT_SUCCESS(Status)) + { + DPRINT1("NtWriteFile failed: %x:%x, iosb: %p src: %p, size: %x\n", + Status, IoStatusBlock.Status, &IoStatusBlock, SourceFileMap, RegionSize); + goto closedest; + } + + /* Copy file date/time from source file */ + Status = NtSetInformationFile(FileHandleDest, + &IoStatusBlock, + &FileBasic, + sizeof(FILE_BASIC_INFORMATION), + FileBasicInformation); + if (!NT_SUCCESS(Status)) + { + DPRINT1("NtSetInformationFile failed: %x\n", Status); + goto closedest; + } + + /* Shorten the file back to its real size after completing the write */ + Status = NtSetInformationFile(FileHandleDest, + &IoStatusBlock, + &FileStandard.EndOfFile, + sizeof(FILE_END_OF_FILE_INFORMATION), + FileEndOfFileInformation); + if (!NT_SUCCESS(Status)) + { + DPRINT1("NtSetInformationFile failed: %x\n", Status); + } + +closedest: + NtClose(FileHandleDest); + +unmapsrcsec: + NtUnmapViewOfSection(NtCurrentProcess(), SourceFileMap); + +closesrcsec: + NtClose(SourceFileSection); + +closesrc: + NtClose(FileHandleSource); + +done: + return Status; +} + +/* + * Synchronized with its kernel32 counterpart, but we don't manage reparse points here. + */ +NTSTATUS +SetupMoveFile( + IN PCWSTR ExistingFileName, + IN PCWSTR NewFileName, + IN ULONG Flags) +{ + NTSTATUS Status; + IO_STATUS_BLOCK IoStatusBlock; + OBJECT_ATTRIBUTES ObjectAttributes; + PFILE_RENAME_INFORMATION RenameInfo; + UNICODE_STRING NewPathU, ExistingPathU; + HANDLE SourceHandle = NULL; + BOOLEAN ReplaceIfExists; + + RtlInitUnicodeString(&ExistingPathU, ExistingFileName); + RtlInitUnicodeString(&NewPathU, NewFileName); + + _SEH2_TRY + { + ReplaceIfExists = !!(Flags & MOVEFILE_REPLACE_EXISTING); + + /* Unless we manage a proper opening, we'll attempt to reopen without reparse support */ + InitializeObjectAttributes(&ObjectAttributes, + &ExistingPathU, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + /* Attempt to open source file */ + Status = NtOpenFile(&SourceHandle, + FILE_READ_ATTRIBUTES | DELETE | SYNCHRONIZE, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN_FOR_BACKUP_INTENT | ((Flags & MOVEFILE_WRITE_THROUGH) ? FILE_WRITE_THROUGH : 0)); + if (!NT_SUCCESS(Status)) + { + if (Status != STATUS_INVALID_PARAMETER) + { + _SEH2_LEAVE; + } + } + + /* At that point, we MUST have a source handle */ + ASSERT(SourceHandle); + + /* Allocate renaming buffer and fill it */ + RenameInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, NewPathU.Length + sizeof(FILE_RENAME_INFORMATION)); + if (RenameInfo == NULL) + { + Status = STATUS_NO_MEMORY; + _SEH2_LEAVE; + } + + RtlCopyMemory(&RenameInfo->FileName, NewPathU.Buffer, NewPathU.Length); + RenameInfo->ReplaceIfExists = ReplaceIfExists; + RenameInfo->RootDirectory = NULL; + RenameInfo->FileNameLength = NewPathU.Length; + + /* Attempt to rename the file */ + Status = NtSetInformationFile(SourceHandle, + &IoStatusBlock, + RenameInfo, + NewPathU.Length + sizeof(FILE_RENAME_INFORMATION), + FileRenameInformation); + RtlFreeHeap(RtlGetProcessHeap(), 0, RenameInfo); + if (NT_SUCCESS(Status)) + { + /* If it succeeded, all fine, quit */ + _SEH2_LEAVE; + } + /* + * If we failed for any other reason than not the same device, fail. + * If we failed because of different devices, only allow renaming + * if user allowed copy. + */ + if (Status != STATUS_NOT_SAME_DEVICE || !(Flags & MOVEFILE_COPY_ALLOWED)) + { + /* ReactOS hack! To be removed once all FSD have proper renaming support + * Just leave status to error and leave + */ + if (Status == STATUS_NOT_IMPLEMENTED) + { + DPRINT1("Forcing copy, renaming not supported by FSD\n"); + } + else + { + _SEH2_LEAVE; + } + } + + /* Close the source file */ + NtClose(SourceHandle); + SourceHandle = NULL; + + /* Perform the file copy */ + Status = SetupCopyFile(ExistingFileName, + NewFileName, + !ReplaceIfExists); + + /* If it succeeded, delete the source file */ + if (NT_SUCCESS(Status)) + { + /* Force-delete files even if read-only */ + SetupDeleteFile(ExistingFileName, TRUE); + } + } + _SEH2_FINALLY + { + if (SourceHandle) + NtClose(SourceHandle); + } + _SEH2_END; + + return Status; +} + NTSTATUS ConcatPathsV( IN OUT PWSTR PathBuffer,