From 9149fde0ff5e1aa291cd5b9883a46ccb318da609 Mon Sep 17 00:00:00 2001 From: Sebastian Lackner Date: Sat, 6 Jun 2015 01:56:11 +0200 Subject: [PATCH] Added patches to fix error code for ReadFile/WriteFile on closed pipe (fixes Wine Staging Bug #348). --- debian/changelog | 2 + ...dd-tests-for-behaviour-of-WriteFile-.patch | 68 +++++++ ...rrect-error-codes-for-NtWriteFile-wh.patch | 166 ++++++++++++++++++ ...file-descriptors-after-opening-a-fil.patch | 53 ++++++ patches/ntdll-AT_ROUND_TO_PAGE/definition | 1 + patches/patchinstall.sh | 6 + 6 files changed, 296 insertions(+) create mode 100644 patches/kernel32-Named_Pipe/0020-kernel32-tests-Add-tests-for-behaviour-of-WriteFile-.patch create mode 100644 patches/kernel32-Named_Pipe/0021-server-Return-correct-error-codes-for-NtWriteFile-wh.patch create mode 100644 patches/kernel32-Named_Pipe/0022-ntdll-Pre-cache-file-descriptors-after-opening-a-fil.patch create mode 100644 patches/ntdll-AT_ROUND_TO_PAGE/definition diff --git a/debian/changelog b/debian/changelog index 49bfd174..30f700ce 100644 --- a/debian/changelog +++ b/debian/changelog @@ -26,6 +26,8 @@ wine-staging (1.7.45) UNRELEASED; urgency=low Smudde). * Added patch to support AT_ROUND_TO_PAGE flag in NtMapViewOfSection (fixes Wine Staging Bug 347). + * Added patches to fix error code for ReadFile/WriteFile on closed pipe (fixes + Wine Staging Bug #348). * Removed patch to handle '\r' as whitespace in wbemprox queries (accepted upstream). * Removed patch to make sure OpenClipboard with current owner doesn't fail diff --git a/patches/kernel32-Named_Pipe/0020-kernel32-tests-Add-tests-for-behaviour-of-WriteFile-.patch b/patches/kernel32-Named_Pipe/0020-kernel32-tests-Add-tests-for-behaviour-of-WriteFile-.patch new file mode 100644 index 00000000..c975776b --- /dev/null +++ b/patches/kernel32-Named_Pipe/0020-kernel32-tests-Add-tests-for-behaviour-of-WriteFile-.patch @@ -0,0 +1,68 @@ +From 7939dafa125d74e832436adf2443a19f2689425e Mon Sep 17 00:00:00 2001 +From: Sebastian Lackner +Date: Fri, 5 Jun 2015 23:06:29 +0200 +Subject: kernel32/tests: Add tests for behaviour of WriteFile on closed pipe. + +--- + dlls/kernel32/tests/pipe.c | 24 ++++++++++++++++++++---- + 1 file changed, 20 insertions(+), 4 deletions(-) + +diff --git a/dlls/kernel32/tests/pipe.c b/dlls/kernel32/tests/pipe.c +index 654d508..0fca432 100644 +--- a/dlls/kernel32/tests/pipe.c ++++ b/dlls/kernel32/tests/pipe.c +@@ -1189,7 +1189,11 @@ static void test_CloseNamedPipe(void) + SetLastError(0xdeadbeef); + ok(!ReadFile(hFile, ibuf, 0, &readden, NULL), "ReadFile() succeeded\n"); + ok(GetLastError() == ERROR_BROKEN_PIPE, "GetLastError() returned %08x, expected ERROR_BROKEN_PIPE\n", GetLastError()); +- SetLastError(0); ++ ++ SetLastError(0xdeadbeef); ++ ok(!WriteFile(hFile, obuf, sizeof(obuf), &written, NULL), "WriteFile() succeeded\n"); ++ todo_wine ++ ok(GetLastError() == ERROR_NO_DATA, "GetLastError() returned %08x, expected ERROR_NO_DATA\n", GetLastError()); + + CloseHandle(hFile); + } +@@ -1224,7 +1228,11 @@ static void test_CloseNamedPipe(void) + ok(!ReadFile(hFile, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile() succeeded\n"); + todo_wine + ok(GetLastError() == ERROR_BROKEN_PIPE, "GetLastError() returned %08x, expected ERROR_BROKEN_PIPE\n", GetLastError()); +- SetLastError(0); ++ ++ SetLastError(0xdeadbeef); ++ ok(!WriteFile(hFile, obuf, sizeof(obuf), &written, NULL), "WriteFile() succeeded\n"); ++ todo_wine ++ ok(GetLastError() == ERROR_NO_DATA, "GetLastError() returned %08x, expected ERROR_NO_DATA\n", GetLastError()); + + CloseHandle(hFile); + } +@@ -1265,7 +1273,11 @@ static void test_CloseNamedPipe(void) + SetLastError(0xdeadbeef); + ok(!ReadFile(hnp, ibuf, 0, &readden, NULL), "ReadFile() succeeded\n"); + ok(GetLastError() == ERROR_BROKEN_PIPE, "GetLastError() returned %08x, expected ERROR_BROKEN_PIPE\n", GetLastError()); +- SetLastError(0); ++ ++ SetLastError(0xdeadbeef); ++ ok(!WriteFile(hnp, obuf, sizeof(obuf), &written, NULL), "WriteFile() succeeded\n"); ++ todo_wine ++ ok(GetLastError() == ERROR_NO_DATA, "GetLastError() returned %08x, expected ERROR_NO_DATA\n", GetLastError()); + + CloseHandle(hnp); + } +@@ -1299,7 +1311,11 @@ static void test_CloseNamedPipe(void) + SetLastError(0xdeadbeef); + ok(!ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile() succeeded\n"); + ok(GetLastError() == ERROR_BROKEN_PIPE, "GetLastError() returned %08x, expected ERROR_BROKEN_PIPE\n", GetLastError()); +- SetLastError(0); ++ ++ SetLastError(0xdeadbeef); ++ ok(!WriteFile(hnp, obuf, sizeof(obuf), &written, NULL), "WriteFile() succeeded\n"); ++ todo_wine ++ ok(GetLastError() == ERROR_NO_DATA, "GetLastError() returned %08x, expected ERROR_NO_DATA\n", GetLastError()); + + CloseHandle(hnp); + } +-- +2.4.2 + diff --git a/patches/kernel32-Named_Pipe/0021-server-Return-correct-error-codes-for-NtWriteFile-wh.patch b/patches/kernel32-Named_Pipe/0021-server-Return-correct-error-codes-for-NtWriteFile-wh.patch new file mode 100644 index 00000000..46cfbd05 --- /dev/null +++ b/patches/kernel32-Named_Pipe/0021-server-Return-correct-error-codes-for-NtWriteFile-wh.patch @@ -0,0 +1,166 @@ +From 4b593f0dd46be8b51cb4e1d31efc5e16bc61cdc9 Mon Sep 17 00:00:00 2001 +From: Sebastian Lackner +Date: Sat, 6 Jun 2015 01:21:05 +0200 +Subject: server: Return correct error codes for NtWriteFile when pipes are + closed without disconnecting. + +--- + dlls/kernel32/tests/pipe.c | 3 --- + dlls/ntdll/file.c | 3 +++ + server/named_pipe.c | 43 +++++++++++++++++++++++++++++++++++++++++-- + server/protocol.def | 1 + + 4 files changed, 45 insertions(+), 5 deletions(-) + +diff --git a/dlls/kernel32/tests/pipe.c b/dlls/kernel32/tests/pipe.c +index 0fca432..4590d3c 100644 +--- a/dlls/kernel32/tests/pipe.c ++++ b/dlls/kernel32/tests/pipe.c +@@ -1192,7 +1192,6 @@ static void test_CloseNamedPipe(void) + + SetLastError(0xdeadbeef); + ok(!WriteFile(hFile, obuf, sizeof(obuf), &written, NULL), "WriteFile() succeeded\n"); +- todo_wine + ok(GetLastError() == ERROR_NO_DATA, "GetLastError() returned %08x, expected ERROR_NO_DATA\n", GetLastError()); + + CloseHandle(hFile); +@@ -1276,7 +1275,6 @@ static void test_CloseNamedPipe(void) + + SetLastError(0xdeadbeef); + ok(!WriteFile(hnp, obuf, sizeof(obuf), &written, NULL), "WriteFile() succeeded\n"); +- todo_wine + ok(GetLastError() == ERROR_NO_DATA, "GetLastError() returned %08x, expected ERROR_NO_DATA\n", GetLastError()); + + CloseHandle(hnp); +@@ -1314,7 +1312,6 @@ static void test_CloseNamedPipe(void) + + SetLastError(0xdeadbeef); + ok(!WriteFile(hnp, obuf, sizeof(obuf), &written, NULL), "WriteFile() succeeded\n"); +- todo_wine + ok(GetLastError() == ERROR_NO_DATA, "GetLastError() returned %08x, expected ERROR_NO_DATA\n", GetLastError()); + + CloseHandle(hnp); +diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c +index f0d816f..26707e2 100644 +--- a/dlls/ntdll/file.c ++++ b/dlls/ntdll/file.c +@@ -1198,6 +1198,9 @@ static NTSTATUS write_unix_fd(int fd, const char *buf, ULONG *total, ULONG lengt + return STATUS_SUCCESS; + else if (errno == EFAULT) + return STATUS_INVALID_USER_BUFFER; ++ else if (type == FD_TYPE_PIPE && (errno == EPIPE || errno == ECONNRESET)) ++ return (get_pipe_flags( fd ) & NAMED_PIPE_CLOSED_HANDLE) ? ++ STATUS_PIPE_EMPTY : STATUS_PIPE_DISCONNECTED; + return FILE_GetNtStatus(); + } + } +diff --git a/server/named_pipe.c b/server/named_pipe.c +index cd647c4..4718d1b 100644 +--- a/server/named_pipe.c ++++ b/server/named_pipe.c +@@ -144,6 +144,7 @@ static const struct object_ops named_pipe_ops = + /* server end functions */ + static void pipe_server_dump( struct object *obj, int verbose ); + static struct fd *pipe_server_get_fd( struct object *obj ); ++static int pipe_server_close_handle( struct object *obj, struct process *process, obj_handle_t handle ); + static void pipe_server_destroy( struct object *obj); + static obj_handle_t pipe_server_flush( struct fd *fd, const async_data_t *async, int blocking ); + static enum server_fd_type pipe_server_get_fd_type( struct fd *fd ); +@@ -166,7 +167,7 @@ static const struct object_ops pipe_server_ops = + default_set_sd, /* set_sd */ + no_lookup_name, /* lookup_name */ + no_open_file, /* open_file */ +- fd_close_handle, /* close_handle */ ++ pipe_server_close_handle, /* close_handle */ + pipe_server_destroy /* destroy */ + }; + +@@ -188,6 +189,7 @@ static const struct fd_ops pipe_server_fd_ops = + static void pipe_client_dump( struct object *obj, int verbose ); + static int pipe_client_signaled( struct object *obj, struct wait_queue_entry *entry ); + static struct fd *pipe_client_get_fd( struct object *obj ); ++static int pipe_client_close_handle( struct object *obj, struct process *process, obj_handle_t handle ); + static void pipe_client_destroy( struct object *obj ); + static obj_handle_t pipe_client_flush( struct fd *fd, const async_data_t *async, int blocking ); + static enum server_fd_type pipe_client_get_fd_type( struct fd *fd ); +@@ -208,7 +210,7 @@ static const struct object_ops pipe_client_ops = + default_set_sd, /* set_sd */ + no_lookup_name, /* lookup_name */ + no_open_file, /* open_file */ +- fd_close_handle, /* close_handle */ ++ pipe_client_close_handle, /* close_handle */ + pipe_client_destroy /* destroy */ + }; + +@@ -272,6 +274,8 @@ static const struct fd_ops named_pipe_device_fd_ops = + default_fd_cancel_async /* cancel_async */ + }; + ++static inline int messagemode_flags( int flags ); ++ + static void named_pipe_dump( struct object *obj, int verbose ) + { + struct named_pipe *pipe = (struct named_pipe *) obj; +@@ -386,6 +390,23 @@ static void do_disconnect( struct pipe_server *server ) + server->fd = NULL; + } + ++static int pipe_server_close_handle( struct object *obj, struct process *process, obj_handle_t handle ) ++{ ++#ifdef __linux__ ++ struct pipe_server *server = (struct pipe_server *)obj; ++ struct pipe_client *client = server->client; ++ int unix_fd; ++ ++ assert( obj->ops == &pipe_server_ops ); ++ if (obj->handle_count == 1 && client && client->fd && (unix_fd = get_unix_fd( client->fd )) != -1) ++ { ++ /* set the NAMED_PIPE_CLOSED_HANDLE flag, to distinguish disconnect / closing pipe */ ++ fcntl( unix_fd, F_SETSIG, messagemode_flags( client->pipe_flags ) | NAMED_PIPE_CLOSED_HANDLE ); ++ } ++#endif ++ return 1; ++} ++ + static void pipe_server_destroy( struct object *obj) + { + struct pipe_server *server = (struct pipe_server *)obj; +@@ -412,6 +433,24 @@ static void pipe_server_destroy( struct object *obj) + release_object( server->pipe ); + } + ++static int pipe_client_close_handle( struct object *obj, struct process *process, obj_handle_t handle ) ++{ ++#ifdef __linux__ ++ struct pipe_client *client = (struct pipe_client *)obj; ++ struct pipe_server *server = client->server; ++ int unix_fd; ++ ++ assert( obj->ops == &pipe_client_ops ); ++ if (obj->handle_count == 1 && server && server->fd && server->state != ps_disconnected_server && ++ server->state != ps_wait_connect && (unix_fd = get_unix_fd( server->fd )) != -1) ++ { ++ /* set the NAMED_PIPE_CLOSED_HANDLE flag, to distinguish disconnect / closing pipe */ ++ fcntl( unix_fd, F_SETSIG, messagemode_flags( server->pipe_flags ) | NAMED_PIPE_CLOSED_HANDLE ); ++ } ++#endif ++ return 1; ++} ++ + static void pipe_client_destroy( struct object *obj) + { + struct pipe_client *client = (struct pipe_client *)obj; +diff --git a/server/protocol.def b/server/protocol.def +index 428f2e0..4efda91 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -2351,6 +2351,7 @@ enum message_type + #define NAMED_PIPE_MESSAGE_STREAM_WRITE 0x0001 + #define NAMED_PIPE_MESSAGE_STREAM_READ 0x0002 + #define NAMED_PIPE_NONBLOCKING_MODE 0x0004 ++#define NAMED_PIPE_CLOSED_HANDLE 0x0008 + #define NAMED_PIPE_SERVER_END 0x8000 + + /* Get named pipe information by handle */ +-- +2.4.2 + diff --git a/patches/kernel32-Named_Pipe/0022-ntdll-Pre-cache-file-descriptors-after-opening-a-fil.patch b/patches/kernel32-Named_Pipe/0022-ntdll-Pre-cache-file-descriptors-after-opening-a-fil.patch new file mode 100644 index 00000000..cc59da7d --- /dev/null +++ b/patches/kernel32-Named_Pipe/0022-ntdll-Pre-cache-file-descriptors-after-opening-a-fil.patch @@ -0,0 +1,53 @@ +From 2fc1f5ba4668fb0a33ee55118da34a43668fe169 Mon Sep 17 00:00:00 2001 +From: Sebastian Lackner +Date: Sat, 6 Jun 2015 01:44:20 +0200 +Subject: ntdll: Pre-cache file descriptors after opening a file. + +--- + dlls/kernel32/tests/pipe.c | 2 -- + dlls/ntdll/file.c | 12 +++++++++++- + 2 files changed, 11 insertions(+), 3 deletions(-) + +diff --git a/dlls/kernel32/tests/pipe.c b/dlls/kernel32/tests/pipe.c +index 146b7f7..ddad351 100644 +--- a/dlls/kernel32/tests/pipe.c ++++ b/dlls/kernel32/tests/pipe.c +@@ -1235,12 +1235,10 @@ static void test_CloseNamedPipe(void) + + SetLastError(0xdeadbeef); + ok(!ReadFile(hFile, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile() succeeded\n"); +- todo_wine + ok(GetLastError() == ERROR_BROKEN_PIPE, "GetLastError() returned %08x, expected ERROR_BROKEN_PIPE\n", GetLastError()); + + SetLastError(0xdeadbeef); + ok(!WriteFile(hFile, obuf, sizeof(obuf), &written, NULL), "WriteFile() succeeded\n"); +- todo_wine + ok(GetLastError() == ERROR_NO_DATA, "GetLastError() returned %08x, expected ERROR_NO_DATA\n", GetLastError()); + + CloseHandle(hFile); +diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c +index ff0d306..25e9826 100644 +--- a/dlls/ntdll/file.c ++++ b/dlls/ntdll/file.c +@@ -274,7 +274,17 @@ static NTSTATUS FILE_CreateFile( PHANDLE handle, ACCESS_MASK access, POBJECT_ATT + *handle = wine_server_ptr_handle( reply->handle ); + } + SERVER_END_REQ; +- if (io->u.Status == STATUS_SUCCESS) io->Information = FILE_OPENED; ++ if (io->u.Status == STATUS_SUCCESS) ++ { ++ /* pre-cache the file descriptor. this is necessary because the fd cannot be ++ * acquired anymore after one end of the pipe has been closed - see kernel32/pipe ++ * tests. */ ++ int unix_fd, needs_close; ++ int ret = server_get_unix_fd( *handle, 0, &unix_fd, &needs_close, NULL, NULL ); ++ if (!ret && needs_close) close( unix_fd ); ++ io->Information = FILE_OPENED; ++ } ++ + return io->u.Status; + } + +-- +2.4.2 + diff --git a/patches/ntdll-AT_ROUND_TO_PAGE/definition b/patches/ntdll-AT_ROUND_TO_PAGE/definition new file mode 100644 index 00000000..5bf1ee56 --- /dev/null +++ b/patches/ntdll-AT_ROUND_TO_PAGE/definition @@ -0,0 +1 @@ +Fixes: Support for AT_ROUND_TO_PAGE flag in NtMapViewOfSection diff --git a/patches/patchinstall.sh b/patches/patchinstall.sh index edc6593d..e06b523b 100755 --- a/patches/patchinstall.sh +++ b/patches/patchinstall.sh @@ -3178,6 +3178,9 @@ if test "$enable_kernel32_Named_Pipe" -eq 1; then patch_apply kernel32-Named_Pipe/0017-kernel32-tests-Add-additional-tests-for-PIPE_NOWAIT-.patch patch_apply kernel32-Named_Pipe/0018-ntdll-Improve-ReadDataAvailable-handling-in-FilePipe.patch patch_apply kernel32-Named_Pipe/0019-ntdll-Set-NamedPipeState-to-FILE_PIPE_CLOSING_STATE-.patch + patch_apply kernel32-Named_Pipe/0020-kernel32-tests-Add-tests-for-behaviour-of-WriteFile-.patch + patch_apply kernel32-Named_Pipe/0021-server-Return-correct-error-codes-for-NtWriteFile-wh.patch + patch_apply kernel32-Named_Pipe/0022-ntdll-Pre-cache-file-descriptors-after-opening-a-fil.patch ( echo '+ { "Dan Kegel", "kernel32: ConnectNamedPort should return FALSE and set ERROR_PIPE_CONNECTED on success in overlapped mode.", 1 },'; echo '+ { "Sebastian Lackner", "kernel32/tests: Add tests for PeekNamedPipe with partial received messages.", 1 },'; @@ -3198,6 +3201,9 @@ if test "$enable_kernel32_Named_Pipe" -eq 1; then echo '+ { "Sebastian Lackner", "kernel32/tests: Add additional tests for PIPE_NOWAIT in overlapped mode.", 1 },'; echo '+ { "Qian Hong", "ntdll: Improve ReadDataAvailable handling in FilePipeLocalInformation class support.", 1 },'; echo '+ { "Sebastian Lackner", "ntdll: Set NamedPipeState to FILE_PIPE_CLOSING_STATE on broken pipe in NtQueryInformationFile.", 1 },'; + echo '+ { "Sebastian Lackner", "kernel32/tests: Add tests for behaviour of WriteFile on closed pipe.", 1 },'; + echo '+ { "Sebastian Lackner", "server: Return correct error codes for NtWriteFile when pipes are closed without disconnecting.", 1 },'; + echo '+ { "Sebastian Lackner", "ntdll: Pre-cache file descriptors after opening a file.", 1 },'; ) >> "$patchlist" fi