From f31780d39178e9e4aa98962d04a51e4f7440757d Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
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 |  2 +-
 dlls/ntdll/file.c          |  3 +++
 server/named_pipe.c        | 43 +++++++++++++++++++++++++++++++++++++++++--
 server/protocol.def        |  1 +
 4 files changed, 46 insertions(+), 3 deletions(-)

diff --git a/dlls/kernel32/tests/pipe.c b/dlls/kernel32/tests/pipe.c
index 3035dd4..9986337 100644
--- a/dlls/kernel32/tests/pipe.c
+++ b/dlls/kernel32/tests/pipe.c
@@ -1909,7 +1909,7 @@ static void test_CloseHandle(void)
     SetLastError(0xdeadbeef);
     ret = WriteFile(hfile, testdata, sizeof(testdata), &numbytes, NULL);
     ok(!ret, "WriteFile unexpectedly succeeded\n");
-    todo_wine ok(GetLastError() == ERROR_NO_DATA, "expected ERROR_NO_DATA, got %u\n", GetLastError());
+    ok(GetLastError() == ERROR_NO_DATA, "expected ERROR_NO_DATA, got %u\n", GetLastError());
 
     CloseHandle(hfile);
 
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c
index a925899..af4b64e 100644
--- a/dlls/ntdll/file.c
+++ b/dlls/ntdll/file.c
@@ -1211,6 +1211,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 55c0db7..e4dbf26 100644
--- a/server/named_pipe.c
+++ b/server/named_pipe.c
@@ -147,6 +147,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 );
@@ -172,7 +173,7 @@ static const struct object_ops pipe_server_ops =
     NULL,                         /* unlink_name */
     no_open_file,                 /* open_file */
     no_alloc_handle,              /* alloc_handle */
-    fd_close_handle,              /* close_handle */
+    pipe_server_close_handle,     /* close_handle */
     pipe_server_destroy           /* destroy */
 };
 
@@ -194,6 +195,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 );
@@ -217,7 +219,7 @@ static const struct object_ops pipe_client_ops =
     NULL,                         /* unlink_name */
     no_open_file,                 /* open_file */
     no_alloc_handle,              /* alloc_handle */
-    fd_close_handle,              /* close_handle */
+    pipe_client_close_handle,     /* close_handle */
     pipe_client_destroy           /* destroy */
 };
 
@@ -284,6 +286,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 )
 {
     fputs( "Named pipe\n", stderr );
@@ -394,6 +398,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;
@@ -420,6 +441,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 2d87f03..1dc70d4 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -2378,6 +2378,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.7.0

