Crysis 3 Remastered (and 2 probably) in RT mode rely on GetFileSize() returning available read size (more exactly, correctly return 0 when no data is available) on pipe it uses to read the output from child dxc.exe process it creates (and hangs forever trying to read the pipe when we return INVALID_FILE_SIZE). While MSDN explicitly says that "You cannot use the GetFileSize function with a handle of a nonseeking device such as a pipe or a communications device.", if application dares GetFileSize (or NtQueryInformationFile(FileStandardInformation)) actually works for named and unnamed pipes.
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/tests/pipe.c | 21 ++++++++++++++++++ server/named_pipe.c | 47 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 63 insertions(+), 5 deletions(-)
diff --git a/dlls/ntdll/tests/pipe.c b/dlls/ntdll/tests/pipe.c index 072c3444c65..be2f30dfc7d 100644 --- a/dlls/ntdll/tests/pipe.c +++ b/dlls/ntdll/tests/pipe.c @@ -2081,6 +2081,7 @@ static void test_pipe_with_data_state(HANDLE pipe, BOOL is_server, DWORD state) IO_STATUS_BLOCK io; char buf[256] = "test"; NTSTATUS status, expected_status; + FILE_STANDARD_INFORMATION std_info;
memset(&io, 0xcc, sizeof(io)); status = pNtQueryInformationFile(pipe, &io, &local_info, sizeof(local_info), FilePipeLocalInformation); @@ -2104,6 +2105,26 @@ static void test_pipe_with_data_state(HANDLE pipe, BOOL is_server, DWORD state) is_server ? "server" : "client", state); }
+ status = pNtQueryInformationFile(pipe, &io, &std_info, sizeof(std_info), FileStandardInformation); + if (!is_server && state == FILE_PIPE_DISCONNECTED_STATE) + ok(status == STATUS_PIPE_DISCONNECTED, + "NtQueryInformationFile(FileStandardInformation) failed in %s state %lu: %lx\n", + is_server ? "server" : "client", state, status); + else + ok(status == STATUS_SUCCESS, + "NtQueryInformationFile(FileStandardInformation) failed in %s state %lu: %lx\n", + is_server ? "server" : "client", state, status); + if (!status) + { + ok(std_info.AllocationSize.QuadPart == local_info.InboundQuota + local_info.OutboundQuota, + "got %I64u, expected %lu.\n", + std_info.AllocationSize.QuadPart, local_info.InboundQuota + local_info.OutboundQuota); + ok(std_info.EndOfFile.QuadPart == local_info.ReadDataAvailable, "got %I64u.\n", std_info.EndOfFile.QuadPart); + ok(std_info.NumberOfLinks == 1, "got %lu.\n", std_info.NumberOfLinks); + todo_wine ok(std_info.DeletePending, "got %d.\n", std_info.DeletePending); + ok(!std_info.Directory, "got %d.\n", std_info.Directory); + } + status = pNtQueryInformationFile(pipe, &io, &pipe_info, sizeof(pipe_info), FilePipeInformation); if (!is_server && state == FILE_PIPE_DISCONNECTED_STATE) ok(status == STATUS_PIPE_DISCONNECTED, diff --git a/server/named_pipe.c b/server/named_pipe.c index 3adc79068ab..f3404a33c3b 100644 --- a/server/named_pipe.c +++ b/server/named_pipe.c @@ -598,6 +598,17 @@ static void pipe_end_flush( struct fd *fd, struct async *async ) } }
+static data_size_t pipe_end_get_avail( struct pipe_end *pipe_end ) +{ + struct pipe_message *message; + data_size_t avail = 0; + + LIST_FOR_EACH_ENTRY( message, &pipe_end->message_queue, struct pipe_message, entry ) + avail += message->iosb->in_size - message->read_pos; + + return avail; +} + static void pipe_end_get_file_info( struct fd *fd, obj_handle_t handle, unsigned int info_class ) { struct pipe_end *pipe_end = get_fd_user( fd ); @@ -670,8 +681,6 @@ static void pipe_end_get_file_info( struct fd *fd, obj_handle_t handle, unsigned case FilePipeLocalInformation: { FILE_PIPE_LOCAL_INFORMATION *pipe_info; - struct pipe_message *message; - data_size_t avail = 0;
if (!(get_handle_access( current->process, handle) & FILE_READ_ATTRIBUTES)) { @@ -709,9 +718,7 @@ static void pipe_end_get_file_info( struct fd *fd, obj_handle_t handle, unsigned pipe_info->CurrentInstances = pipe->instances; pipe_info->InboundQuota = pipe->insize;
- LIST_FOR_EACH_ENTRY( message, &pipe_end->message_queue, struct pipe_message, entry ) - avail += message->iosb->in_size - message->read_pos; - pipe_info->ReadDataAvailable = avail; + pipe_info->ReadDataAvailable = pipe_end_get_avail( pipe_end );
pipe_info->OutboundQuota = pipe->outsize; pipe_info->WriteQuotaAvailable = 0; /* FIXME */ @@ -720,6 +727,36 @@ static void pipe_end_get_file_info( struct fd *fd, obj_handle_t handle, unsigned ? FILE_PIPE_SERVER_END : FILE_PIPE_CLIENT_END; break; } + case FileStandardInformation: + { + FILE_STANDARD_INFORMATION *std_info; + + if (!(get_handle_access( current->process, handle) & FILE_READ_ATTRIBUTES)) + { + set_error( STATUS_ACCESS_DENIED ); + return; + } + + if (get_reply_max_size() < sizeof(*std_info)) + { + set_error( STATUS_INFO_LENGTH_MISMATCH ); + return; + } + + if (!pipe) + { + set_error( STATUS_PIPE_DISCONNECTED ); + return; + } + + if (!(std_info = set_reply_data_size( sizeof(*std_info) ))) return; + std_info->AllocationSize.QuadPart = pipe->outsize + pipe->insize; + std_info->EndOfFile.QuadPart = pipe_end_get_avail( pipe_end ); + std_info->NumberOfLinks = 1; /* FIXME */ + std_info->DeletePending = 0; /* FIXME */ + std_info->Directory = 0; + break; + } default: default_fd_get_file_info( fd, handle, info_class ); }