From bb9a6f2fb4431cb18e2a835808c02156bbd9d709 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 ba365ed666..23f1a5f5f5 100644
--- a/dlls/kernel32/tests/pipe.c
+++ b/dlls/kernel32/tests/pipe.c
@@ -1917,7 +1917,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 98a699e48f..25f59761cf 100644
--- a/dlls/ntdll/file.c
+++ b/dlls/ntdll/file.c
@@ -1235,6 +1235,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 d310740165..2775fcab51 100644
--- a/server/named_pipe.c
+++ b/server/named_pipe.c
@@ -129,6 +129,8 @@ struct named_pipe_device
     struct namespace   *pipes;       /* named pipe namespace */
 };
 
+static inline int messagemode_flags( int flags );
+
 static void named_pipe_dump( struct object *obj, int verbose );
 static unsigned int named_pipe_map_access( struct object *obj, unsigned int access );
 static int named_pipe_link_name( struct object *obj, struct object_name *name, struct object *parent );
@@ -167,6 +169,7 @@ static void pipe_end_reselect_async( struct fd *fd, struct async_queue *queue );
 /* 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, struct async *async, int blocking );
 static enum server_fd_type pipe_server_get_fd_type( struct fd *fd );
@@ -192,7 +195,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 */
 };
 
@@ -213,6 +216,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, struct async *async, int blocking );
 static obj_handle_t pipe_client_ioctl( struct fd *fd, ioctl_code_t code, struct async *async,
@@ -238,7 +242,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 */
 };
 
@@ -482,6 +486,23 @@ static void pipe_end_destroy( struct pipe_end *pipe_end )
     free_async_queue( pipe_end->write_q );
 }
 
+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->pipe_end.fd && (unix_fd = get_unix_fd( client->pipe_end.fd )) != -1)
+    {
+        /* set the NAMED_PIPE_CLOSED_HANDLE flag, to distinguish disconnect / closing pipe */
+        fcntl( unix_fd, F_SETSIG, messagemode_flags( client->pipe_end.flags ) | NAMED_PIPE_CLOSED_HANDLE );
+    }
+#endif
+    return 1;
+}
+
 static void pipe_server_destroy( struct object *obj)
 {
     struct pipe_server *server = (struct pipe_server *)obj;
@@ -511,6 +532,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->pipe_end.fd &&
+        server->state != ps_wait_connect && (unix_fd = get_unix_fd( server->pipe_end.fd )) != -1)
+    {
+        /* set the NAMED_PIPE_CLOSED_HANDLE flag, to distinguish disconnect / closing pipe */
+        fcntl( unix_fd, F_SETSIG, messagemode_flags( server->pipe_end.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 341a6f6085..9769bb9e7b 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -2431,6 +2431,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.11.0

