Signed-off-by: Jinoh Kang jinoh.kang.kr@gmail.com ---
Notes: v2 -> v3: move some tests from om.c to pipe.c v3 -> v4: fix and simplify STATUS_PENDING testing
dlls/ntdll/tests/om.c | 9 +++++ dlls/ntdll/tests/pipe.c | 90 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 98 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/tests/om.c b/dlls/ntdll/tests/om.c index f872d6709a1..04acf5b2241 100644 --- a/dlls/ntdll/tests/om.c +++ b/dlls/ntdll/tests/om.c @@ -1871,6 +1871,15 @@ static void test_query_object(void)
pNtClose( handle );
+ handle = CreateFileA( "\\.\pipe\", 0, 0, NULL, OPEN_EXISTING, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFile failed (%d)\n", GetLastError() ); + + test_object_name( handle, L"\Device\NamedPipe\", TRUE ); + test_object_type( handle, L"File" ); + test_file_info( handle ); + + pNtClose( handle ); + RtlInitUnicodeString( &path, L"\REGISTRY\Machine" ); status = pNtCreateKey( &handle, KEY_READ, &attr, 0, 0, 0, 0 ); ok( status == STATUS_SUCCESS, "NtCreateKey failed status %x\n", status ); diff --git a/dlls/ntdll/tests/pipe.c b/dlls/ntdll/tests/pipe.c index 6940e0020b7..aab19ba5aa6 100644 --- a/dlls/ntdll/tests/pipe.c +++ b/dlls/ntdll/tests/pipe.c @@ -51,6 +51,13 @@ typedef struct { ULONG NamedPipeEnd; } FILE_PIPE_LOCAL_INFORMATION;
+typedef struct _FILE_PIPE_WAIT_FOR_BUFFER { + LARGE_INTEGER Timeout; + ULONG NameLength; + BOOLEAN TimeoutSpecified; + WCHAR Name[1]; +} FILE_PIPE_WAIT_FOR_BUFFER, *PFILE_PIPE_WAIT_FOR_BUFFER; + #ifndef FILE_SYNCHRONOUS_IO_ALERT #define FILE_SYNCHRONOUS_IO_ALERT 0x10 #endif @@ -62,6 +69,10 @@ typedef struct { #ifndef FSCTL_PIPE_LISTEN #define FSCTL_PIPE_LISTEN CTL_CODE(FILE_DEVICE_NAMED_PIPE, 2, METHOD_BUFFERED, FILE_ANY_ACCESS) #endif + +#ifndef FSCTL_PIPE_WAIT +#define FSCTL_PIPE_WAIT CTL_CODE(FILE_DEVICE_NAMED_PIPE, 6, METHOD_BUFFERED, FILE_ANY_ACCESS) +#endif #endif
static NTSTATUS (WINAPI *pNtFsControlFile) (HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, PVOID apc_context, PIO_STATUS_BLOCK io, ULONG code, PVOID in_buffer, ULONG in_size, PVOID out_buffer, ULONG out_size); @@ -177,6 +188,48 @@ static NTSTATUS listen_pipe(HANDLE hPipe, HANDLE hEvent, PIO_STATUS_BLOCK iosb, return pNtFsControlFile(hPipe, hEvent, use_apc ? &ioapc: NULL, use_apc ? &dummy: NULL, iosb, FSCTL_PIPE_LISTEN, 0, 0, 0, 0); }
+static NTSTATUS wait_pipe(HANDLE handle, PUNICODE_STRING name, const LARGE_INTEGER* timeout) +{ + HANDLE event; + NTSTATUS status; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK iosb; + FILE_PIPE_WAIT_FOR_BUFFER *pipe_wait; + ULONG pipe_wait_size; + + pipe_wait_size = offsetof(FILE_PIPE_WAIT_FOR_BUFFER, Name[0]) + name->Length; + pipe_wait = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pipe_wait_size); + if (!pipe_wait) return STATUS_NO_MEMORY; + + pipe_wait->TimeoutSpecified = !!timeout; + pipe_wait->NameLength = name->Length; + if (timeout) pipe_wait->Timeout = *timeout; + memcpy(pipe_wait->Name, name->Buffer, name->Length); + + InitializeObjectAttributes(&attr, NULL, 0, 0, NULL); + status = NtCreateEvent(&event, GENERIC_ALL, &attr, NotificationEvent, FALSE); + if (status != STATUS_SUCCESS) + { + ok(0, "NtCreateEvent failure: %08x\n", status); + HeapFree(GetProcessHeap(), 0, pipe_wait); + return status; + } + + memset(&iosb, 0, sizeof(iosb)); + iosb.Status = STATUS_PENDING; + status = pNtFsControlFile(handle, event, NULL, NULL, &iosb, FSCTL_PIPE_WAIT, + pipe_wait, pipe_wait_size, NULL, 0); + if (status == STATUS_PENDING) + { + WaitForSingleObject(event, INFINITE); + status = iosb.Status; + } + + NtClose(event); + HeapFree(GetProcessHeap(), 0, pipe_wait); + return status; +} + static void test_create_invalid(void) { IO_STATUS_BLOCK iosb; @@ -2420,6 +2473,7 @@ static void test_security_info(void)
static void test_empty_name(void) { + static const LARGE_INTEGER zero_timeout = {{ 0 }}; HANDLE hdirectory, hpipe, hpipe2, hwrite, hwrite2, handle; OBJECT_TYPE_INFORMATION *type_info; OBJECT_NAME_INFORMATION *name_info; @@ -2450,6 +2504,10 @@ static void test_empty_name(void) FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, 0, NULL, 0 ); ok(!status, "Got unexpected status %#x.\n", status);
+ pRtlInitUnicodeString(&name, L"nonexistent_pipe"); + status = wait_pipe(hdirectory, &name, &zero_timeout); + todo_wine ok(status == STATUS_ILLEGAL_FUNCTION, "unexpected status for FSCTL_PIPE_WAIT on \Device\NamedPipe: %08x\n", status); + name.Buffer = NULL; name.Length = 0; name.MaximumLength = 0; @@ -2462,6 +2520,17 @@ static void test_empty_name(void) todo_wine ok(status == STATUS_OBJECT_NAME_INVALID, "Got unexpected status %#x.\n", status); if (!status) CloseHandle(hpipe); + + pRtlInitUnicodeString(&name, L"test3\pipe"); + attr.RootDirectory = hdirectory; + attr.ObjectName = &name; + timeout.QuadPart = -(LONG64)10000000; + status = pNtCreateNamedPipeFile(&hpipe, GENERIC_READ|GENERIC_WRITE, &attr, &io, FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout); + ok(status == STATUS_OBJECT_NAME_INVALID, "unexpected status from NtCreateNamedPipeFile: %08x\n", status); + if (!status) + CloseHandle(hpipe); + CloseHandle(hdirectory);
pRtlInitUnicodeString(&name, L"\Device\NamedPipe\"); @@ -2475,6 +2544,10 @@ static void test_empty_name(void) FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, 0, NULL, 0 ); ok(!status, "Got unexpected status %#x.\n", status);
+ pRtlInitUnicodeString(&name, L"nonexistent_pipe"); + status = wait_pipe(hdirectory, &name, &zero_timeout); + ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "unexpected status for FSCTL_PIPE_WAIT on \Device\NamedPipe\: %08x\n", status); + name.Buffer = NULL; name.Length = 0; name.MaximumLength = 0; @@ -2565,9 +2638,24 @@ static void test_empty_name(void)
CloseHandle(hwrite); CloseHandle(hpipe); - CloseHandle(hdirectory); CloseHandle(hpipe2); CloseHandle(hwrite2); + + pRtlInitUnicodeString(&name, L"test3\pipe"); + attr.RootDirectory = hdirectory; + attr.ObjectName = &name; + timeout.QuadPart = -(LONG64)10000000; + status = pNtCreateNamedPipeFile(&hpipe, GENERIC_READ|GENERIC_WRITE, &attr, &io, FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout); + todo_wine ok(!status, "unexpected failure from NtCreateNamedPipeFile: %08x\n", status); + + handle = CreateFileA("\\.\pipe\test3\pipe", GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, 0, 0 ); + todo_wine ok(handle != INVALID_HANDLE_VALUE, "Failed to open NamedPipe (%u)\n", GetLastError()); + + CloseHandle(handle); + CloseHandle(hpipe); + CloseHandle(hdirectory); }
START_TEST(pipe)
Separate the named pipe root directory from the named pipe device file. Open the root directory instead of the device file if the path ends with backslash.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52105 Signed-off-by: Jinoh Kang jinoh.kang.kr@gmail.com ---
Notes: v3 -> v4: - don't memcpy backslash - formatting changes
dlls/ntdll/tests/om.c | 2 +- dlls/ntdll/tests/pipe.c | 6 +- server/named_pipe.c | 225 +++++++++++++++++++++++++++++++++------- 3 files changed, 189 insertions(+), 44 deletions(-)
diff --git a/dlls/ntdll/tests/om.c b/dlls/ntdll/tests/om.c index 04acf5b2241..b5a32ec1081 100644 --- a/dlls/ntdll/tests/om.c +++ b/dlls/ntdll/tests/om.c @@ -1874,7 +1874,7 @@ static void test_query_object(void) handle = CreateFileA( "\\.\pipe\", 0, 0, NULL, OPEN_EXISTING, 0, 0 ); ok( handle != INVALID_HANDLE_VALUE, "CreateFile failed (%d)\n", GetLastError() );
- test_object_name( handle, L"\Device\NamedPipe\", TRUE ); + test_object_name( handle, L"\Device\NamedPipe\", FALSE ); test_object_type( handle, L"File" ); test_file_info( handle );
diff --git a/dlls/ntdll/tests/pipe.c b/dlls/ntdll/tests/pipe.c index aab19ba5aa6..90312459175 100644 --- a/dlls/ntdll/tests/pipe.c +++ b/dlls/ntdll/tests/pipe.c @@ -2506,7 +2506,7 @@ static void test_empty_name(void)
pRtlInitUnicodeString(&name, L"nonexistent_pipe"); status = wait_pipe(hdirectory, &name, &zero_timeout); - todo_wine ok(status == STATUS_ILLEGAL_FUNCTION, "unexpected status for FSCTL_PIPE_WAIT on \Device\NamedPipe: %08x\n", status); + ok(status == STATUS_ILLEGAL_FUNCTION, "unexpected status for FSCTL_PIPE_WAIT on \Device\NamedPipe: %08x\n", status);
name.Buffer = NULL; name.Length = 0; @@ -2647,11 +2647,11 @@ static void test_empty_name(void) timeout.QuadPart = -(LONG64)10000000; status = pNtCreateNamedPipeFile(&hpipe, GENERIC_READ|GENERIC_WRITE, &attr, &io, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout); - todo_wine ok(!status, "unexpected failure from NtCreateNamedPipeFile: %08x\n", status); + ok(!status, "unexpected failure from NtCreateNamedPipeFile: %08x\n", status);
handle = CreateFileA("\\.\pipe\test3\pipe", GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 ); - todo_wine ok(handle != INVALID_HANDLE_VALUE, "Failed to open NamedPipe (%u)\n", GetLastError()); + ok(handle != INVALID_HANDLE_VALUE, "Failed to open NamedPipe (%u)\n", GetLastError());
CloseHandle(handle); CloseHandle(hpipe); diff --git a/server/named_pipe.c b/server/named_pipe.c index 3e6cf09d4f2..e6b025af663 100644 --- a/server/named_pipe.c +++ b/server/named_pipe.c @@ -103,6 +103,14 @@ struct named_pipe_device_file struct named_pipe_device *device; /* named pipe device */ };
+/* mostly identical to named_pipe_device_file (declared separately for type checking) */ +struct named_pipe_dir +{ + struct object obj; /* object header */ + struct fd *fd; /* pseudo-fd for ioctls */ + struct named_pipe_device *device; /* named pipe device */ +}; + static void named_pipe_dump( struct object *obj, int verbose ); static unsigned int named_pipe_map_access( struct object *obj, unsigned int access ); static WCHAR *named_pipe_get_full_name( struct object *obj, data_size_t *ret_len ); @@ -321,6 +329,57 @@ static const struct fd_ops named_pipe_device_fd_ops = default_fd_reselect_async /* reselect_async */ };
+static void named_pipe_dir_dump( struct object *obj, int verbose ); +static struct fd *named_pipe_dir_get_fd( struct object *obj ); +static enum server_fd_type named_pipe_dir_get_fd_type( struct fd *fd ); +static void named_pipe_dir_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ); +static WCHAR *named_pipe_dir_get_full_name( struct object *obj, data_size_t *ret_len ); +static struct object *named_pipe_dir_lookup_name( struct object *obj, struct unicode_str *name, + unsigned int attr, struct object *root ); +static struct object *named_pipe_dir_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ); +static void named_pipe_dir_destroy( struct object *obj ); + +static const struct object_ops named_pipe_dir_ops = +{ + sizeof(struct named_pipe_dir), /* size */ + &file_type, /* type */ + named_pipe_dir_dump, /* dump */ + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + named_pipe_dir_get_fd, /* get_fd */ + default_map_access, /* map_access */ + default_get_sd, /* get_sd */ + default_set_sd, /* set_sd */ + named_pipe_dir_get_full_name, /* get_full_name */ + named_pipe_dir_lookup_name, /* lookup_name */ + no_link_name, /* link_name */ + NULL, /* unlink_name */ + named_pipe_dir_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ + no_close_handle, /* close_handle */ + named_pipe_dir_destroy /* destroy */ +}; + +static const struct fd_ops named_pipe_dir_fd_ops = +{ + default_fd_get_poll_events, /* get_poll_events */ + default_poll_event, /* poll_event */ + named_pipe_dir_get_fd_type, /* get_fd_type */ + no_fd_read, /* read */ + no_fd_write, /* write */ + no_fd_flush, /* flush */ + default_fd_get_file_info, /* get_file_info */ + no_fd_get_volume_info, /* get_volume_info */ + named_pipe_dir_ioctl, /* ioctl */ + default_fd_cancel_async, /* cancel_async */ + default_fd_queue_async, /* queue_async */ + default_fd_reselect_async /* reselect_async */ +}; + static void named_pipe_dump( struct object *obj, int verbose ) { fputs( "Named pipe\n", stderr ); @@ -501,6 +560,18 @@ static struct object *named_pipe_device_lookup_name( struct object *obj, struct assert( device->pipes );
if (!name) return NULL; /* open the device itself */ + if (!name->len && name->str) + { + /* open the root directory */ + struct named_pipe_dir *dir = alloc_object( &named_pipe_dir_ops ); + + if (!dir) return NULL; + + dir->fd = NULL; /* defer alloc_pseudo_fd() until after we have options */ + dir->device = (struct named_pipe_device *)grab_object( obj ); + + return &dir->obj; + }
if ((found = find_object( device->pipes, name, attr | OBJ_CASE_INSENSITIVE ))) name->len = 0; @@ -581,6 +652,117 @@ static void named_pipe_device_file_destroy( struct object *obj ) release_object( file->device ); }
+static void named_pipe_dir_dump( struct object *obj, int verbose ) +{ + struct named_pipe_dir *dir = (struct named_pipe_dir *)obj; + + fprintf( stderr, "Root directory of named pipe device %p\n", dir->device ); +} + +static struct fd *named_pipe_dir_get_fd( struct object *obj ) +{ + struct named_pipe_dir *dir = (struct named_pipe_dir *)obj; + return (struct fd *)grab_object( dir->fd ); +} + +static enum server_fd_type named_pipe_dir_get_fd_type( struct fd *fd ) +{ + /* TODO actually implement NtQueryDirectoryFile */ + return FD_TYPE_DIR; +} + +static void named_pipe_dir_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) +{ + struct named_pipe_dir *dir = get_fd_user( fd ); + + switch(code) + { + case FSCTL_PIPE_WAIT: + { + const FILE_PIPE_WAIT_FOR_BUFFER *buffer = get_req_data(); + data_size_t size = get_req_data_size(); + struct named_pipe *pipe; + struct unicode_str name; + timeout_t when; + + if (size < sizeof(*buffer) || + size < FIELD_OFFSET(FILE_PIPE_WAIT_FOR_BUFFER, Name[buffer->NameLength/sizeof(WCHAR)])) + { + set_error( STATUS_INVALID_PARAMETER ); + return; + } + name.str = buffer->Name; + name.len = (buffer->NameLength / sizeof(WCHAR)) * sizeof(WCHAR); + if (!(pipe = open_named_object( &dir->obj, &named_pipe_ops, &name, 0 ))) return; + + if (list_empty( &pipe->listeners )) + { + queue_async( &pipe->waiters, async ); + when = buffer->TimeoutSpecified ? buffer->Timeout.QuadPart : pipe->timeout; + async_set_timeout( async, when, STATUS_IO_TIMEOUT ); + set_error( STATUS_PENDING ); + } + + release_object( pipe ); + return; + } + + default: + default_fd_ioctl( fd, code, async ); + } +} + +static WCHAR *named_pipe_dir_get_full_name( struct object *obj, data_size_t *ret_len ) +{ + struct named_pipe_dir *dir = (struct named_pipe_dir *)obj; + data_size_t len; + char *device_name, *ret; + + device_name = (char *)dir->device->obj.ops->get_full_name( &dir->device->obj, &len ); + if (!device_name) return NULL; + + len += sizeof(WCHAR); + ret = realloc(device_name, len); + if (!ret) + { + free(device_name); + return NULL; + } + *(WCHAR *)(ret + len - sizeof(WCHAR)) = '\'; + + *ret_len = len; + return (WCHAR *)ret; +} + +static struct object *named_pipe_dir_lookup_name( struct object *obj, struct unicode_str *name, + unsigned int attr, struct object *root ) +{ + struct named_pipe_dir *dir = (struct named_pipe_dir *)obj; + if (!name || !name->len) return NULL; /* open the directory itself */ + return dir->device->obj.ops->lookup_name( &dir->device->obj, name, attr, root ); +} + +static struct object *named_pipe_dir_open_file( struct object *obj, unsigned int access, + unsigned int sharing, unsigned int options ) +{ + struct named_pipe_dir *dir = (struct named_pipe_dir *)obj; + if (!dir->fd) + { + if (!(dir->fd = alloc_pseudo_fd( &named_pipe_dir_fd_ops, obj, options ))) + return NULL; + allow_fd_caching( dir->fd ); + } + return grab_object( obj ); +} + +static void named_pipe_dir_destroy( struct object *obj ) +{ + struct named_pipe_dir *file = (struct named_pipe_dir*)obj; + assert( obj->ops == &named_pipe_dir_ops ); + if (file->fd) release_object( file->fd ); + if (file->device) release_object( file->device ); +} + static void pipe_end_flush( struct fd *fd, struct async *async ) { struct pipe_end *pipe_end = get_fd_user( fd ); @@ -1284,14 +1466,13 @@ static struct pipe_end *create_pipe_client( struct named_pipe *pipe, data_size_t
static int named_pipe_link_name( struct object *obj, struct object_name *name, struct object *parent ) { - struct named_pipe_device *dev = (struct named_pipe_device *)parent; - + if (parent->ops == &named_pipe_dir_ops) parent = &((struct named_pipe_dir *)parent)->device->obj; if (parent->ops != &named_pipe_device_ops) { set_error( STATUS_OBJECT_NAME_INVALID ); return 0; } - namespace_add( dev->pipes, name ); + namespace_add( ((struct named_pipe_device *)parent)->pipes, name ); name->parent = grab_object( parent ); return 1; } @@ -1334,43 +1515,7 @@ static struct object *named_pipe_open_file( struct object *obj, unsigned int acc
static void named_pipe_device_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) { - struct named_pipe_device *device = get_fd_user( fd ); - - switch(code) - { - case FSCTL_PIPE_WAIT: - { - const FILE_PIPE_WAIT_FOR_BUFFER *buffer = get_req_data(); - data_size_t size = get_req_data_size(); - struct named_pipe *pipe; - struct unicode_str name; - timeout_t when; - - if (size < sizeof(*buffer) || - size < FIELD_OFFSET(FILE_PIPE_WAIT_FOR_BUFFER, Name[buffer->NameLength/sizeof(WCHAR)])) - { - set_error( STATUS_INVALID_PARAMETER ); - return; - } - name.str = buffer->Name; - name.len = (buffer->NameLength / sizeof(WCHAR)) * sizeof(WCHAR); - if (!(pipe = open_named_object( &device->obj, &named_pipe_ops, &name, 0 ))) return; - - if (list_empty( &pipe->listeners )) - { - queue_async( &pipe->waiters, async ); - when = buffer->TimeoutSpecified ? buffer->Timeout.QuadPart : pipe->timeout; - async_set_timeout( async, when, STATUS_IO_TIMEOUT ); - set_error( STATUS_PENDING ); - } - - release_object( pipe ); - return; - } - - default: - default_fd_ioctl( fd, code, async ); - } + set_error( STATUS_ILLEGAL_FUNCTION ); }