[USETUP] Move Setup[Delete|Copy|Move]File() from usetup code into the setuplib's...
[reactos.git] / base / setup / lib / filesup.c
index c6314c2..03d1ad8 100644 (file)
 #define NDEBUG
 #include <debug.h>
 
+
+// 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,