From babfa79436e1d745886ead360bee402bfd46074a Mon Sep 17 00:00:00 2001
From: Vitaliy Margolen <wine-patch@kievinfo.com>
Date: Mon, 5 Dec 2005 13:09:35 +0100
Subject: [PATCH] Move named pipe objects into directory name space. Change
 tests accordingly. Add small test for WaitNamedPipe.

---
 dlls/kernel/sync.c             |   3 +-
 dlls/kernel/tests/pipe.c       |   4 +-
 dlls/ntdll/file.c              |  21 +++--
 dlls/ntdll/tests/om.c          |   4 +-
 include/wine/server_protocol.h |   4 +-
 server/directory.c             |  14 ++-
 server/named_pipe.c            | 151 +++++++++++++++++++++++++++------
 server/object.h                |   2 +
 server/protocol.def            |   2 +
 server/trace.c                 |   2 +
 10 files changed, 165 insertions(+), 42 deletions(-)

diff --git a/dlls/kernel/sync.c b/dlls/kernel/sync.c
index 0c340984b4d..6158a990084 100644
--- a/dlls/kernel/sync.c
+++ b/dlls/kernel/sync.c
@@ -1340,7 +1340,8 @@ BOOL WINAPI WaitNamedPipeW (LPCWSTR name, DWORD nTimeOut)
         req->timeout = nTimeOut;
         req->overlapped = &ov;
         req->func = PIPE_CompletionWait;
-        wine_server_add_data( req, nt_name.Buffer + 4, nt_name.Length - 4*sizeof(WCHAR) );
+        wine_server_add_data( req, nt_name.Buffer + sizeof(leadin)/sizeof(WCHAR),
+                              nt_name.Length - sizeof(leadin) );
         ret = !wine_server_call_err( req );
     }
     SERVER_END_REQ;
diff --git a/dlls/kernel/tests/pipe.c b/dlls/kernel/tests/pipe.c
index aaad8d6781a..a4c15fe192e 100644
--- a/dlls/kernel/tests/pipe.c
+++ b/dlls/kernel/tests/pipe.c
@@ -91,8 +91,10 @@ static void test_CreateNamedPipe(int pipemode)
         /* lpSecurityAttrib */ NULL);
     ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n");
 
+    todo_wine ok(WaitNamedPipeA(PIPENAME, 2000), "WaitNamedPipe failed (%08lx)\n", GetLastError());
+
     hFile = CreateFileA(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
-    ok(hFile != INVALID_HANDLE_VALUE, "CreateFile failed\n");
+    ok(hFile != INVALID_HANDLE_VALUE, "CreateFile failed (%08lx)\n", GetLastError());
 
     /* don't try to do i/o if one side couldn't be opened, as it hangs */
     if (hFile != INVALID_HANDLE_VALUE) {
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c
index c52e16b3b08..606b39f9c55 100644
--- a/dlls/ntdll/file.c
+++ b/dlls/ntdll/file.c
@@ -151,11 +151,6 @@ NTSTATUS WINAPI NtCreateFile( PHANDLE handle, ACCESS_MASK access, POBJECT_ATTRIB
 
     if (!attr || !attr->ObjectName) return STATUS_INVALID_PARAMETER;
 
-    if (attr->RootDirectory)
-    {
-        FIXME( "RootDirectory %p not supported\n", attr->RootDirectory );
-        return STATUS_OBJECT_NAME_NOT_FOUND;
-    }
     if (alloc_size) FIXME( "alloc_size not supported\n" );
 
     /* check for named pipe */
@@ -167,9 +162,10 @@ NTSTATUS WINAPI NtCreateFile( PHANDLE handle, ACCESS_MASK access, POBJECT_ATTRIB
         {
             req->access = access;
             req->attributes = (attr) ? attr->Attributes : 0;
+            req->rootdir = attr ? attr->RootDirectory : 0;
             req->flags = options;
-            wine_server_add_data( req, attr->ObjectName->Buffer + 4,
-                                  attr->ObjectName->Length - 4*sizeof(WCHAR) );
+            wine_server_add_data( req, attr->ObjectName->Buffer,
+                                  attr->ObjectName->Length );
             io->u.Status = wine_server_call( req );
             *handle = reply->handle;
         }
@@ -177,6 +173,12 @@ NTSTATUS WINAPI NtCreateFile( PHANDLE handle, ACCESS_MASK access, POBJECT_ATTRIB
         return io->u.Status;
     }
 
+    if (attr->RootDirectory)
+    {
+        FIXME( "RootDirectory %p not supported\n", attr->RootDirectory );
+        return STATUS_OBJECT_NAME_NOT_FOUND;
+    }
+
     /* check for mailslot */
 
     if (attr->ObjectName->Length > sizeof(mailslotW) &&
@@ -1957,6 +1959,7 @@ NTSTATUS WINAPI NtCreateNamedPipeFile( PHANDLE handle, ULONG access,
     {
         req->access  = access;
         req->attributes = (attr) ? attr->Attributes : 0;
+        req->rootdir = attr ? attr->RootDirectory : 0;
         req->options = options;
         req->flags = 
             (pipe_type) ? NAMED_PIPE_MESSAGE_STREAM_WRITE : 0 |
@@ -1966,8 +1969,8 @@ NTSTATUS WINAPI NtCreateNamedPipeFile( PHANDLE handle, ULONG access,
         req->outsize = outbound_quota;
         req->insize  = inbound_quota;
         req->timeout = timeout->QuadPart / -10000;
-        wine_server_add_data( req, attr->ObjectName->Buffer + 4, 
-                              attr->ObjectName->Length - 4 * sizeof(WCHAR) );
+        wine_server_add_data( req, attr->ObjectName->Buffer,
+                              attr->ObjectName->Length );
         status = wine_server_call( req );
         if (!status) *handle = reply->handle;
     }
diff --git a/dlls/ntdll/tests/om.c b/dlls/ntdll/tests/om.c
index 492033d31c0..96a68a51d1d 100644
--- a/dlls/ntdll/tests/om.c
+++ b/dlls/ntdll/tests/om.c
@@ -126,7 +126,7 @@ void test_namespace_pipe(void)
     InitializeObjectAttributes(&attr, &str, 0, 0, NULL);
     status = pNtCreateNamedPipeFile(&pipe, GENERIC_READ|GENERIC_WRITE, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE,
                                     FILE_CREATE, FILE_PIPE_FULL_DUPLEX, FALSE, FALSE, FALSE, 1, 256, 256, &timeout);
-    todo_wine ok(status == STATUS_INSTANCE_NOT_AVAILABLE,
+    ok(status == STATUS_INSTANCE_NOT_AVAILABLE,
         "NtCreateNamedPipeFile should have failed with STATUS_INSTANCE_NOT_AVAILABLE got(%08lx)\n", status);
 
     attr.Attributes = OBJ_CASE_INSENSITIVE;
@@ -137,7 +137,7 @@ void test_namespace_pipe(void)
     pRtlInitUnicodeString(&str, buffer3);
     InitializeObjectAttributes(&attr, &str, 0, 0, NULL);
     status = pNtOpenFile(&h, GENERIC_READ, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN);
-    todo_wine ok(status == STATUS_OBJECT_PATH_NOT_FOUND || status == STATUS_PIPE_NOT_AVAILABLE,
+    ok(status == STATUS_OBJECT_PATH_NOT_FOUND || status == STATUS_PIPE_NOT_AVAILABLE,
         "pNtOpenFile should have failed with STATUS_OBJECT_PATH_NOT_FOUND got(%08lx)\n", status);
 
     pRtlInitUnicodeString(&str, buffer4);
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 4ab0136c824..429fcdf1ab4 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -2378,6 +2378,7 @@ struct create_named_pipe_request
     struct request_header __header;
     unsigned int   access;
     unsigned int   attributes;
+    obj_handle_t   rootdir;
     unsigned int   options;
     unsigned int   flags;
     unsigned int   maxinstances;
@@ -2404,6 +2405,7 @@ struct open_named_pipe_request
     struct request_header __header;
     unsigned int   access;
     unsigned int   attributes;
+    obj_handle_t   rootdir;
     unsigned int   flags;
     /* VARARG(name,unicode_str); */
 };
@@ -4312,6 +4314,6 @@ union generic_reply
     struct query_symlink_reply query_symlink_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 205
+#define SERVER_PROTOCOL_VERSION 206
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/directory.c b/server/directory.c
index c5de9863ea1..e8088a42454 100644
--- a/server/directory.c
+++ b/server/directory.c
@@ -283,8 +283,9 @@ void *open_object_dir( struct directory *root, const struct unicode_str *name,
 
 /* Global initialization */
 
-static struct directory *dir_driver, *dir_device;
+static struct directory *dir_driver;
 static struct symlink *link_dosdev, *link_global1, *link_global2, *link_local;
+static struct named_pipe_device *dev_named_pipe;
 
 void init_directories(void)
 {
@@ -306,7 +307,7 @@ void init_directories(void)
     static const struct unicode_str link_global_str = {link_globalW, sizeof(link_globalW)};
     static const struct unicode_str link_local_str  = {link_localW, sizeof(link_localW)};
 
-    struct directory *dir_global, *dir_basenamed;
+    struct directory *dir_global, *dir_basenamed, *dir_device;
 
     root_directory = create_directory( NULL, NULL, 0, HASH_SIZE );
     dir_driver     = create_directory( root_directory, &dir_driver_str, 0, HASH_SIZE );
@@ -322,20 +323,25 @@ void init_directories(void)
     link_global2   = create_symlink( dir_basenamed, &link_global_str, 0, &dir_basenamed_str );
     link_local     = create_symlink( dir_basenamed, &link_local_str, 0, &dir_basenamed_str );
 
-    /* the symlinks hold references so we can release these */
+    /* devices */
+    dev_named_pipe = create_named_pipe_device();
+
+    /* the symlinks or devices hold references so we can release these */
+    release_object( dir_device );
     release_object( dir_global );
     release_object( dir_basenamed );
 }
 
 void close_directories(void)
 {
+    release_object( dev_named_pipe );
+
     release_object( link_dosdev );
     release_object( link_global1 );
     release_object( link_global2 );
     release_object( link_local );
 
     release_object( dir_driver );
-    release_object( dir_device );
     release_object( root_directory );
 }
 
diff --git a/server/named_pipe.c b/server/named_pipe.c
index 0aa57336c3f..ac6aaabf9d2 100644
--- a/server/named_pipe.c
+++ b/server/named_pipe.c
@@ -99,6 +99,12 @@ struct named_pipe
     struct list         waiters;     /* list of clients waiting to connect */
 };
 
+struct named_pipe_device
+{
+    struct object       obj;         /* object header */
+    struct namespace   *pipes;       /* named pipe namespace */
+};
+
 static void named_pipe_dump( struct object *obj, int verbose );
 static void named_pipe_destroy( struct object *obj );
 
@@ -181,6 +187,26 @@ static const struct fd_ops pipe_client_fd_ops =
     default_fd_cancel_async       /* cancel_async */
 };
 
+static void named_pipe_device_dump( struct object *obj, int verbose );
+static struct object *named_pipe_device_lookup_name( struct object *obj,
+    struct unicode_str *name, unsigned int attr );
+static void named_pipe_device_destroy( struct object *obj );
+
+static const struct object_ops named_pipe_device_ops =
+{
+    sizeof(struct named_pipe_device), /* size */
+    named_pipe_device_dump,           /* dump */
+    no_add_queue,                     /* add_queue */
+    NULL,                             /* remove_queue */
+    NULL,                             /* signaled */
+    no_satisfied,                     /* satisfied */
+    no_signal,                        /* signal */
+    no_get_fd,                        /* get_fd */
+    named_pipe_device_lookup_name,    /* lookup_name */
+    no_close_handle,                  /* close_handle */
+    named_pipe_device_destroy         /* destroy */
+};
+
 static void named_pipe_dump( struct object *obj, int verbose )
 {
     struct named_pipe *pipe = (struct named_pipe *) obj;
@@ -338,6 +364,58 @@ static void pipe_client_destroy( struct object *obj)
     assert( !client->fd );
 }
 
+static void named_pipe_device_dump( struct object *obj, int verbose )
+{
+    assert( obj->ops == &named_pipe_device_ops );
+    fprintf( stderr, "Named pipe device\n" );
+}
+
+static struct object *named_pipe_device_lookup_name( struct object *obj, struct unicode_str *name,
+                                                     unsigned int attr )
+{
+    struct named_pipe_device *device = (struct named_pipe_device*)obj;
+    struct object *found;
+
+    assert( obj->ops == &named_pipe_device_ops );
+    assert( device->pipes );
+
+    if (!name->len) return NULL;
+
+    if ((found = find_object( device->pipes, name, attr | OBJ_CASE_INSENSITIVE )))
+        name->len = 0;
+
+    return found;
+}
+
+static void named_pipe_device_destroy( struct object *obj )
+{
+    struct named_pipe_device *device = (struct named_pipe_device*)obj;
+    assert( obj->ops == &named_pipe_device_ops );
+    free( device->pipes );
+}
+
+/* this will be deleted as soon an we fix wait_named_pipe */
+static struct named_pipe_device *named_pipe_device;
+
+struct named_pipe_device *create_named_pipe_device( void )
+{
+    static const WCHAR pipeW[] = {'\\','?','?','\\','P','I','P','E'};
+    static struct unicode_str pipe = {pipeW, sizeof(pipeW)};
+    struct named_pipe_device *dev;
+
+    if ((dev = create_named_object_dir( NULL, &pipe, 0, &named_pipe_device_ops )) &&
+        get_error() != STATUS_OBJECT_NAME_EXISTS)
+    {
+        if (!(dev->pipes = create_namespace( 7 )))
+        {
+            release_object( dev );
+            dev = NULL;
+        }
+    }
+    named_pipe_device = dev;
+    return dev;
+}
+
 static int pipe_data_remaining( struct pipe_server *server )
 {
     struct pollfd pfd;
@@ -445,37 +523,43 @@ static int pipe_client_get_info( struct fd *fd )
     return flags;
 }
 
-static struct named_pipe *create_named_pipe( const struct unicode_str *name, unsigned int attr )
+static struct named_pipe *create_named_pipe( struct directory *root, const struct unicode_str *name,
+                                             unsigned int attr )
 {
-    struct named_pipe *pipe;
+    struct object *obj;
+    struct named_pipe *pipe = NULL;
+    struct unicode_str new_name;
 
-    pipe = create_named_object( sync_namespace, &named_pipe_ops, name, attr | OBJ_OPENIF );
-    if (pipe)
+    if (!name || !name->len) return alloc_object( &named_pipe_ops );
+
+    if (!(obj = find_object_dir( root, name, attr, &new_name ))) return NULL;
+    if (!new_name.len)
     {
-        if (get_error() != STATUS_OBJECT_NAME_EXISTS)
+        if (attr & OBJ_OPENIF && obj->ops == &named_pipe_ops)
+            set_error( STATUS_OBJECT_NAME_EXISTS );
+        else
         {
-            /* initialize it if it didn't already exist */
-            pipe->instances = 0;
-            list_init( &pipe->servers );
-            list_init( &pipe->waiters );
+            release_object( obj );
+            obj = NULL;
+            if (attr & OBJ_OPENIF)
+                set_error( STATUS_OBJECT_TYPE_MISMATCH );
+            else
+                set_error( STATUS_OBJECT_NAME_COLLISION );
         }
+        return (struct named_pipe *)obj;
     }
-    return pipe;
-}
-
-static struct named_pipe *open_named_pipe( const struct unicode_str *name, unsigned int attr )
-{
-    struct object *obj;
 
-    if ((obj = find_object( sync_namespace, name, attr )))
-    {
-        if (obj->ops == &named_pipe_ops) return (struct named_pipe *)obj;
-        release_object( obj );
+    if (obj->ops != &named_pipe_device_ops)
         set_error( STATUS_OBJECT_TYPE_MISMATCH );
+    else
+    {
+        struct named_pipe_device *dev = (struct named_pipe_device *)obj;
+        if ((pipe = create_object( dev->pipes, &named_pipe_ops, &new_name, NULL )))
+            clear_error();
     }
-    else set_error( STATUS_OBJECT_NAME_NOT_FOUND );
 
-    return NULL;
+    release_object( obj );
+    return pipe;
 }
 
 static struct pipe_server *get_pipe_server_obj( struct process *process,
@@ -552,13 +636,24 @@ DECL_HANDLER(create_named_pipe)
     struct named_pipe *pipe;
     struct pipe_server *server;
     struct unicode_str name;
+    struct directory *root = NULL;
 
     reply->handle = 0;
     get_req_unicode_str( &name );
-    if (!(pipe = create_named_pipe( &name, req->attributes ))) return;
+    if (req->rootdir && !(root = get_directory_obj( current->process, req->rootdir, 0 )))
+        return;
+
+    pipe = create_named_pipe( root, &name, req->attributes | OBJ_OPENIF );
+
+    if (root) release_object( root );
+    if (!pipe) return;
 
     if (get_error() != STATUS_OBJECT_NAME_EXISTS)
     {
+        /* initialize it if it didn't already exist */
+        pipe->instances = 0;
+        list_init( &pipe->servers );
+        list_init( &pipe->waiters );
         pipe->insize = req->insize;
         pipe->outsize = req->outsize;
         pipe->maxinstances = req->maxinstances;
@@ -601,11 +696,18 @@ DECL_HANDLER(open_named_pipe)
     struct pipe_server *server;
     struct pipe_client *client;
     struct unicode_str name;
+    struct directory *root = NULL;
     struct named_pipe *pipe;
     int fds[2];
 
     get_req_unicode_str( &name );
-    if (!(pipe = open_named_pipe( &name, req->attributes ))) return;
+    if (req->rootdir && !(root = get_directory_obj( current->process, req->rootdir, 0 )))
+        return;
+
+    pipe = open_object_dir( root, &name, req->attributes, &named_pipe_ops );
+
+    if (root) release_object( root );
+    if (!pipe) return;
 
     server = find_server2( pipe, ps_idle_server, ps_wait_open );
     release_object( pipe );
@@ -701,7 +803,8 @@ DECL_HANDLER(wait_named_pipe)
     struct unicode_str name;
 
     get_req_unicode_str( &name );
-    if (!(pipe = open_named_pipe( &name, OBJ_CASE_INSENSITIVE )))
+    pipe = (struct named_pipe *)find_object( named_pipe_device->pipes, &name, OBJ_CASE_INSENSITIVE );
+    if (!pipe)
     {
         set_error( STATUS_PIPE_NOT_AVAILABLE );
         return;
diff --git a/server/object.h b/server/object.h
index 2beb148f257..66cd357f57c 100644
--- a/server/object.h
+++ b/server/object.h
@@ -197,6 +197,8 @@ extern void close_directories(void);
 extern struct symlink *create_symlink( struct directory *root, const struct unicode_str *name,
                                        unsigned int attr, const struct unicode_str *target );
 
+/* devices */
+extern struct named_pipe_device *create_named_pipe_device( void );
 
 /* global variables */
 
diff --git a/server/protocol.def b/server/protocol.def
index 297313c92fe..905e0ff82ec 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1692,6 +1692,7 @@ enum message_type
 @REQ(create_named_pipe)
     unsigned int   access;
     unsigned int   attributes;   /* object attributes */
+    obj_handle_t   rootdir;      /* root directory */
     unsigned int   options;
     unsigned int   flags;
     unsigned int   maxinstances;
@@ -1713,6 +1714,7 @@ enum message_type
 @REQ(open_named_pipe)
     unsigned int   access;
     unsigned int   attributes;   /* object attributes */
+    obj_handle_t   rootdir;      /* root directory */
     unsigned int   flags;        /* file flags */
     VARARG(name,unicode_str);    /* pipe name */
 @REPLY
diff --git a/server/trace.c b/server/trace.c
index 5d5159ab133..3618f6ed7dc 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -2154,6 +2154,7 @@ static void dump_create_named_pipe_request( const struct create_named_pipe_reque
 {
     fprintf( stderr, " access=%08x,", req->access );
     fprintf( stderr, " attributes=%08x,", req->attributes );
+    fprintf( stderr, " rootdir=%p,", req->rootdir );
     fprintf( stderr, " options=%08x,", req->options );
     fprintf( stderr, " flags=%08x,", req->flags );
     fprintf( stderr, " maxinstances=%08x,", req->maxinstances );
@@ -2173,6 +2174,7 @@ static void dump_open_named_pipe_request( const struct open_named_pipe_request *
 {
     fprintf( stderr, " access=%08x,", req->access );
     fprintf( stderr, " attributes=%08x,", req->attributes );
+    fprintf( stderr, " rootdir=%p,", req->rootdir );
     fprintf( stderr, " flags=%08x,", req->flags );
     fprintf( stderr, " name=" );
     dump_varargs_unicode_str( cur_size );
-- 
GitLab