[KERNEL32]
authorPierre Schweitzer <pierre@reactos.org>
Tue, 21 Apr 2015 10:55:26 +0000 (10:55 +0000)
committerPierre Schweitzer <pierre@reactos.org>
Tue, 21 Apr 2015 10:55:26 +0000 (10:55 +0000)
- Implement BasepOpenFileForMove() used by PrivMoveFileIdentityW(), with its Windows bugs
- Implement PrivMoveFileIdentityW()

Not sure yet why Windows implementation exposes a sharing violation during our API test. Our absence of sharing violation looks more legit...
To be investigated.

svn path=/trunk/; revision=67335

reactos/dll/win32/kernel32/client/file/move.c
reactos/dll/win32/kernel32/include/kernel32.h

index be12927..9d67a89 100644 (file)
@@ -886,14 +886,379 @@ Cleanup:
 }
 
 /*
- * @unimplemented
+ * @implemented
+ */
+NTSTATUS
+WINAPI
+BasepOpenFileForMove(IN LPCWSTR File,
+                     OUT PUNICODE_STRING RelativeNtName,
+                     OUT LPWSTR * NtName,
+                     OUT PHANDLE FileHandle,
+                     OUT POBJECT_ATTRIBUTES ObjectAttributes,
+                     IN ACCESS_MASK DesiredAccess,
+                     IN ULONG ShareAccess,
+                     IN ULONG OpenOptions)
+{
+    RTL_RELATIVE_NAME_U RelativeName;
+    NTSTATUS Status;
+    IO_STATUS_BLOCK IoStatusBlock;
+    FILE_ATTRIBUTE_TAG_INFORMATION TagInfo;
+    ULONG IntShareAccess;
+    BOOLEAN HasRelative = FALSE;
+
+    _SEH2_TRY
+    {
+        /* Zero output */
+        RelativeNtName->Length = 
+        RelativeNtName->MaximumLength = 0;
+        RelativeNtName->Buffer = NULL;
+        *NtName = NULL;
+
+        if (!RtlDosPathNameToRelativeNtPathName_U(File, RelativeNtName, NULL, &RelativeName))
+        {
+            Status = STATUS_OBJECT_PATH_NOT_FOUND;
+            _SEH2_LEAVE;
+        }
+
+        HasRelative = TRUE;
+        *NtName = RelativeNtName->Buffer;
+
+        if (RelativeName.RelativeName.Length)
+        {
+            RelativeNtName->Length = RelativeName.RelativeName.Length;
+            RelativeNtName->MaximumLength = RelativeName.RelativeName.MaximumLength;
+            RelativeNtName->Buffer = RelativeName.RelativeName.Buffer;
+        }
+        else
+        {
+            RelativeName.ContainingDirectory = 0;
+        }
+
+        InitializeObjectAttributes(ObjectAttributes,
+                                   RelativeNtName,
+                                   OBJ_CASE_INSENSITIVE,
+                                   RelativeName.ContainingDirectory,
+                                   NULL);
+        /* Force certain flags here, given ops we'll do */
+        IntShareAccess = ShareAccess | FILE_SHARE_READ | FILE_SHARE_WRITE;
+        OpenOptions |= FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT;
+
+        /* We'll try to read reparse tag */
+        Status = NtOpenFile(FileHandle,
+                            DesiredAccess | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
+                            ObjectAttributes,
+                            &IoStatusBlock,
+                            IntShareAccess,
+                            OpenOptions | FILE_OPEN_REPARSE_POINT);
+        if (NT_SUCCESS(Status))
+        {
+            /* Attempt the read */
+            Status = NtQueryInformationFile(*FileHandle,
+                                            &IoStatusBlock,
+                                            &TagInfo,
+                                            sizeof(FILE_ATTRIBUTE_TAG_INFORMATION),
+                                            FileAttributeTagInformation);
+
+            /* Return if failure with a status that wouldn't mean the FSD cannot support reparse points */
+            if (!NT_SUCCESS(Status) &&
+                (Status != STATUS_NOT_IMPLEMENTED && Status != STATUS_INVALID_PARAMETER))
+            {
+                _SEH2_LEAVE;
+            }
+
+            if (NT_SUCCESS(Status))
+            {
+                /* This cannot happen on mount points */
+                if (TagInfo.FileAttributes & FILE_ATTRIBUTE_DEVICE ||
+                    TagInfo.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
+                {
+                    _SEH2_LEAVE;
+                }
+            }
+
+            NtClose(*FileHandle);
+            *FileHandle = INVALID_HANDLE_VALUE;
+
+            IntShareAccess = ShareAccess | FILE_SHARE_READ | FILE_SHARE_DELETE;
+        }
+        else if (Status == STATUS_INVALID_PARAMETER)
+        {
+            IntShareAccess = ShareAccess | FILE_SHARE_READ | FILE_SHARE_WRITE;
+        }
+        else
+        {
+            _SEH2_LEAVE;
+        }
+
+        /* Reattempt to open normally, following reparse point if needed */
+        Status = NtOpenFile(FileHandle,
+                            DesiredAccess | SYNCHRONIZE,
+                            ObjectAttributes,
+                            &IoStatusBlock,
+                            IntShareAccess,
+                            OpenOptions);
+    }
+    _SEH2_FINALLY
+    {
+        if (HasRelative)
+        {
+            RtlReleaseRelativeName(&RelativeName);
+        }
+    }
+    _SEH2_END;
+
+    return Status;
+}
+
+/*
+ * @implemented
  */
 BOOL
 WINAPI
-PrivMoveFileIdentityW(DWORD Unknown1, DWORD Unknown2, DWORD Unknown3)
+PrivMoveFileIdentityW(IN LPCWSTR lpSource, IN LPCWSTR lpDestination, IN DWORD dwFlags)
 {
-    STUB;
-    return FALSE;
+    ACCESS_MASK SourceAccess;
+    UNICODE_STRING NtSource, NtDestination;
+    LPWSTR RelativeSource, RelativeDestination;
+    HANDLE SourceHandle, DestinationHandle;
+    OBJECT_ATTRIBUTES ObjectAttributesSource, ObjectAttributesDestination;
+    NTSTATUS Status, OldStatus = STATUS_SUCCESS;
+    ACCESS_MASK DestAccess;
+    IO_STATUS_BLOCK IoStatusBlock;
+    FILE_BASIC_INFORMATION SourceInformation, DestinationInformation;
+    FILE_DISPOSITION_INFORMATION FileDispositionInfo;
+
+    DPRINT("PrivMoveFileIdentityW(%S, %S, %x)\n", lpSource, lpDestination, dwFlags);
+
+    SourceHandle = INVALID_HANDLE_VALUE;
+    NtSource.Length =
+    NtSource.MaximumLength = 0;
+    NtSource.Buffer = NULL;
+    RelativeSource = NULL;
+    DestinationHandle = INVALID_HANDLE_VALUE;
+    NtDestination.Length =
+    NtDestination.MaximumLength = 0;
+    NtDestination.Buffer = NULL;
+    RelativeDestination = NULL;
+
+    /* FILE_WRITE_DATA is required for later on notification */
+    SourceAccess = FILE_READ_ATTRIBUTES | FILE_WRITE_DATA;
+    if (dwFlags & PRIV_DELETE_ON_SUCCESS)
+    {
+        SourceAccess |= DELETE;
+    }
+
+    _SEH2_TRY
+    {
+        /* We will loop twice:
+         * First we attempt to open with FILE_WRITE_DATA for notification
+         * If it fails and we have flag for non-trackable files, we retry
+         * without FILE_WRITE_DATA.
+         * If that one fails, then, we quit for real
+         */
+        while (TRUE)
+        {
+            Status = BasepOpenFileForMove(lpSource,
+                                          &NtSource,
+                                          &RelativeSource,
+                                          &SourceHandle,
+                                          &ObjectAttributesSource,
+                                          SourceAccess,
+                                          FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                                          FILE_OPEN_NO_RECALL);
+            if (NT_SUCCESS(Status))
+            {
+                break;
+            }
+
+            /* If we already attempted the opening without FILE_WRITE_DATA
+             * or if we cannot move on non-trackable files, fail.
+             */
+            if (!(SourceAccess & FILE_WRITE_DATA) || !(dwFlags & PRIV_ALLOW_NON_TRACKABLE))
+            {
+                _SEH2_LEAVE;
+            }
+
+            if (RelativeSource)
+            {
+                RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeSource);
+                RelativeSource = NULL;
+            }
+
+            if (SourceHandle != INVALID_HANDLE_VALUE)
+            {
+                NtClose(SourceHandle);
+                SourceHandle = INVALID_HANDLE_VALUE;
+            }
+
+            SourceAccess &= ~FILE_WRITE_DATA;
+
+            /* Remember fist failure in the path */
+            if (NT_SUCCESS(OldStatus))
+            {
+                OldStatus = Status;
+            }
+        }
+
+        DestAccess = FILE_WRITE_ATTRIBUTES;
+        /* If we could preserve FILE_WRITE_DATA for source, attempt to get it for destination
+         * Still for notification purposes
+         */
+        if (SourceAccess & FILE_WRITE_DATA)
+        {
+            DestAccess |= FILE_WRITE_DATA;
+        }
+
+        /* cf comment for first loop */
+        while (TRUE)
+        {
+            Status = BasepOpenFileForMove(lpDestination,
+                                          &NtDestination,
+                                          &RelativeDestination,
+                                          &DestinationHandle,
+                                          &ObjectAttributesDestination,
+                                          DestAccess,
+                                          FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                                          FILE_OPEN_NO_RECALL);
+            if (NT_SUCCESS(Status))
+            {
+                break;
+            }
+
+            /* If we already attempted the opening without FILE_WRITE_DATA
+             * or if we cannot move on non-trackable files, fail.
+             */
+            if (!(DestAccess & FILE_WRITE_DATA) || !(dwFlags & PRIV_ALLOW_NON_TRACKABLE))
+            {
+                _SEH2_LEAVE;
+            }
+
+            if (RelativeDestination)
+            {
+                RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeDestination);
+                RelativeDestination = NULL;
+            }
+
+            if (DestinationHandle != INVALID_HANDLE_VALUE)
+            {
+                NtClose(DestinationHandle);
+                DestinationHandle = INVALID_HANDLE_VALUE;
+            }
+
+            DestAccess &= ~FILE_WRITE_DATA;
+
+            /* Remember fist failure in the path */
+            if (NT_SUCCESS(OldStatus))
+            {
+                OldStatus = Status;
+            }
+        }
+
+        /* Get the creation time from source */
+        Status = NtQueryInformationFile(SourceHandle,
+                                        &IoStatusBlock,
+                                        &SourceInformation,
+                                        sizeof(SourceInformation),
+                                        FileBasicInformation);
+        if (NT_SUCCESS(Status))
+        {
+            /* Then, prepare to set it for destination */
+            RtlZeroMemory(&DestinationInformation, sizeof(DestinationInformation));
+            DestinationInformation.CreationTime.QuadPart = SourceInformation.CreationTime.QuadPart;
+
+            /* And set it, that's all folks! */
+            Status = NtSetInformationFile(DestinationHandle,
+                                          &IoStatusBlock,
+                                          &DestinationInformation,
+                                          sizeof(DestinationInformation),
+                                          FileBasicInformation);
+        }
+
+        if (!NT_SUCCESS(Status))
+        {
+            if (!(dwFlags & PRIV_ALLOW_NON_TRACKABLE))
+            {
+                _SEH2_LEAVE;
+            }
+
+            /* Remember the failure for later notification */
+            if (NT_SUCCESS(OldStatus))
+            {
+                OldStatus = Status;
+            }
+        }
+
+        /* If we could open with FILE_WRITE_DATA both source and destination,
+         * then, notify
+         */
+        if (DestAccess & FILE_WRITE_DATA && SourceAccess & FILE_WRITE_DATA)
+        {
+            Status = BasepNotifyTrackingService(&SourceHandle,
+                                                &ObjectAttributesSource,
+                                                DestinationHandle,
+                                                &NtDestination);
+#if 1 // ReactOS HACK
+            /* FIXME: To be removed once BasepNotifyTrackingService is implemented */
+            if (Status == STATUS_NOT_IMPLEMENTED)
+                Status = STATUS_SUCCESS;
+#endif
+            if (!NT_SUCCESS(Status))
+            {
+                if (dwFlags & PRIV_ALLOW_NON_TRACKABLE)
+                {
+                    if (NT_SUCCESS(OldStatus))
+                        OldStatus = Status;
+                }
+            }
+        }
+    }
+    _SEH2_FINALLY
+    {
+        if (RelativeSource)
+            RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeSource);
+
+        if (RelativeDestination)
+            RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeDestination);
+    }
+    _SEH2_END;
+
+    /* If caller asked for source deletion, if everything succeed, proceed */
+    if (NT_SUCCESS(Status) && dwFlags & PRIV_DELETE_ON_SUCCESS)
+    {
+        FileDispositionInfo.DeleteFile = TRUE;
+
+        Status = NtSetInformationFile(SourceHandle,
+                                      &IoStatusBlock,
+                                      &FileDispositionInfo,
+                                      sizeof(FileDispositionInfo),
+                                      FileDispositionInformation);
+    }
+
+    /* Cleanup/close portion */
+    if (DestinationHandle != INVALID_HANDLE_VALUE)
+    {
+        NtClose(DestinationHandle);
+    }
+
+    if (SourceHandle != INVALID_HANDLE_VALUE)
+    {
+        NtClose(SourceHandle);
+    }
+
+    /* Set last error if any, and quit */
+    if (NT_SUCCESS(Status))
+    {
+        if (!NT_SUCCESS(OldStatus))
+        {
+            BaseSetLastNTError(OldStatus);
+        }
+    }
+    else
+    {
+        BaseSetLastNTError(Status);
+    }
+
+    return NT_SUCCESS(Status);
 }
 
 /* EOF */
index 2cca331..f264480 100644 (file)
@@ -142,6 +142,10 @@ extern WaitForInputIdleType UserWaitForInputIdleRoutine;
 #define BASEP_COPY_PUBLIC_MASK      0xF
 #define BASEP_COPY_BASEP_MASK       0xFFFFFFF0
 
+/* Flags for PrivMoveFileIdentityW */
+#define PRIV_DELETE_ON_SUCCESS      0x1
+#define PRIV_ALLOW_NON_TRACKABLE    0x2
+
 /* GLOBAL VARIABLES **********************************************************/
 
 extern BOOL bIsFileApiAnsi;