Signed-off-by: Alistair Leslie-Hughes leslie_alistair@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 )))
Alistair Leslie-Hughes leslie_alistair@hotmail.com writes:
Signed-off-by: Alistair Leslie-Hughes leslie_alistair@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");
+}
This doesn't demonstrate any difference in behavior when using the flag. The tests succeed even without the code changes.