[PATCH] kernelbase: CopyFileExA support COPY_FILE_OPEN_SOURCE_FOR_WRITE flag

Alistair Leslie-Hughes leslie_alistair at hotmail.com
Sun Mar 6 01:27:20 CST 2022


Signed-off-by: Alistair Leslie-Hughes <leslie_alistair at hotmail.com>
---
 dlls/kernel32/tests/file.c | 65 ++++++++++++++++++++++++++++++++++++++
 dlls/kernelbase/file.c     | 22 ++++++++++---
 2 files changed, 83 insertions(+), 4 deletions(-)

diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c
index 77174d43d5b..eb5bc5ceb36 100644
--- a/dlls/kernel32/tests/file.c
+++ b/dlls/kernel32/tests/file.c
@@ -1202,6 +1202,70 @@ static void test_CopyFileEx(void)
     ok(!ret, "DeleteFileA unexpectedly succeeded\n");
 }
 
+static void test_CopyFileEx_CopyWrite(void)
+{
+    char temp_path[MAX_PATH];
+    char source[MAX_PATH], dest[MAX_PATH];
+    static const char prefix[] = "pfx";
+    HANDLE hfile;
+    DWORD ret;
+    BOOL retok;
+
+    ret = GetTempPathA(MAX_PATH, temp_path);
+    ok(ret != 0, "GetTempPathA error %ld\n", GetLastError());
+    ok(ret < MAX_PATH, "temp path should fit into MAX_PATH\n");
+
+    ret = GetTempFileNameA(temp_path, prefix, 0, source);
+    ok(ret != 0, "GetTempFileNameA error %ld\n", GetLastError());
+
+    ret = GetTempFileNameA(temp_path, prefix, 0, dest);
+    ok(ret != 0, "GetTempFileNameA error %ld\n", GetLastError());
+
+    ret = DeleteFileA(dest);
+    ok(ret, "DeleteFileA Failed\n");
+
+    /* Test with Read access */
+    hfile = CreateFileA(source, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
+    ok(hfile != INVALID_HANDLE_VALUE, "failed to open source file, error %ld\n", GetLastError());
+
+    retok = CopyFileExA(source, dest, NULL, NULL, NULL, COPY_FILE_OPEN_SOURCE_FOR_WRITE);
+    ok(retok, "CopyFileExA failed\n");
+    ok(GetLastError() == 0, "expected 0, got %ld\n", GetLastError());
+    ok(GetFileAttributesA(dest) != INVALID_FILE_ATTRIBUTES, "file wasn't created\n");
+    CloseHandle(hfile);
+
+    ret = DeleteFileA(dest);
+    ok(ret, "DeleteFileA Failed\n");
+
+    /* Test with Shared Read access */
+    hfile = CreateFileA(source, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
+    ok(hfile != INVALID_HANDLE_VALUE, "failed to open source file, error %ld\n", GetLastError());
+
+    retok = CopyFileExA(source, dest, NULL, NULL, NULL, COPY_FILE_OPEN_SOURCE_FOR_WRITE);
+    ok(retok, "CopyFileExA failed\n");
+    ok(GetLastError() == 0, "expected 0, got %ld\n", GetLastError());
+    ok(GetFileAttributesA(dest) != INVALID_FILE_ATTRIBUTES, "file wasn't created\n");
+    CloseHandle(hfile);
+
+    ret = DeleteFileA(dest);
+    ok(ret, "DeleteFileA Failed\n");
+
+    /* Test with Shared R/W access */
+    hfile = CreateFileA(source, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
+    ok(hfile != INVALID_HANDLE_VALUE, "failed to open source file, error %ld\n", GetLastError());
+
+    retok = CopyFileExA(source, dest, NULL, NULL, NULL, COPY_FILE_OPEN_SOURCE_FOR_WRITE);
+    ok(retok, "CopyFileExA failed\n");
+    ok(GetLastError() == 0, "got %ld\n", GetLastError());
+    ok(GetFileAttributesA(dest) != INVALID_FILE_ATTRIBUTES, "file wasn't created\n");
+    CloseHandle(hfile);
+
+    ret = DeleteFileA(source);
+    ok(ret, "DeleteFileA failed with error %ld\n", GetLastError());
+    ret = DeleteFileA(dest);
+    ok(ret, "DeleteFileA unexpectedly succeeded\n");
+}
+
 /*
  *   Debugging routine to dump a buffer in a hexdump-like fashion.
  */
@@ -6158,6 +6222,7 @@ START_TEST(file)
     test_CopyFileW();
     test_CopyFile2();
     test_CopyFileEx();
+    test_CopyFileEx_CopyWrite();
     test_CreateFile();
     test_CreateFileA();
     test_CreateFileW();
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c
index c6dc904044a..3ea81e112a7 100644
--- a/dlls/kernelbase/file.c
+++ b/dlls/kernelbase/file.c
@@ -504,6 +504,7 @@ BOOL WINAPI CopyFileExW( const WCHAR *source, const WCHAR *dest, LPPROGRESS_ROUT
     DWORD count;
     BOOL ret = FALSE;
     char *buffer;
+    DWORD source_access = GENERIC_READ;
 
     if (!source || !dest)
     {
@@ -518,12 +519,25 @@ BOOL WINAPI CopyFileExW( const WCHAR *source, const WCHAR *dest, LPPROGRESS_ROUT
 
     TRACE("%s -> %s, %lx\n", debugstr_w(source), debugstr_w(dest), flags);
 
-    if ((h1 = CreateFileW( source, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+    if (flags & COPY_FILE_OPEN_SOURCE_FOR_WRITE)
+        source_access |= GENERIC_WRITE;
+
+    if ((h1 = CreateFileW( source, source_access, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                            NULL, OPEN_EXISTING, 0, 0 )) == INVALID_HANDLE_VALUE)
     {
-        WARN("Unable to open source %s\n", debugstr_w(source));
-        HeapFree( GetProcessHeap(), 0, buffer );
-        return FALSE;
+        /* Retry with Read mode, if we cannot open R/W */
+        if (GetLastError() == ERROR_SHARING_VIOLATION && flags & COPY_FILE_OPEN_SOURCE_FOR_WRITE)
+        {
+            h1 = CreateFileW( source, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                           NULL, OPEN_EXISTING, 0, 0 );
+        }
+
+        if(h1 == INVALID_HANDLE_VALUE)
+        {
+            WARN("Unable to open source %s\n", debugstr_w(source));
+            HeapFree( GetProcessHeap(), 0, buffer );
+            return FALSE;
+        }
     }
 
     if (!set_ntstatus( NtQueryInformationFile( h1, &io, &info, sizeof(info), FileBasicInformation )))
-- 
2.34.1




More information about the wine-devel mailing list