You've already forked wine-staging
							
							
				mirror of
				https://gitlab.winehq.org/wine/wine-staging.git
				synced 2025-09-12 18:50:20 -07:00 
			
		
		
		
	Added patch to implement support for SetFileCompletionNotificationModes.
This commit is contained in:
		| @@ -1,26 +0,0 @@ | ||||
| From 479993311ae3467ba359b76057744dec56fa2fea Mon Sep 17 00:00:00 2001 | ||||
| From: Sebastian Lackner <sebastian@fds-team.de> | ||||
| Date: Sun, 26 Jul 2015 20:54:23 +0200 | ||||
| Subject: kernel32: Fake success in SetFileCompletionNotificationModes. | ||||
|  | ||||
| --- | ||||
|  dlls/kernel32/file.c | 3 +-- | ||||
|  1 file changed, 1 insertion(+), 2 deletions(-) | ||||
|  | ||||
| diff --git a/dlls/kernel32/file.c b/dlls/kernel32/file.c | ||||
| index e43829e..569ed22 100644 | ||||
| --- a/dlls/kernel32/file.c | ||||
| +++ b/dlls/kernel32/file.c | ||||
| @@ -1046,8 +1046,7 @@ BOOL WINAPI SetEndOfFile( HANDLE hFile ) | ||||
|  BOOL WINAPI SetFileCompletionNotificationModes( HANDLE handle, UCHAR flags ) | ||||
|  { | ||||
|      FIXME("%p %x - stub\n", handle, flags); | ||||
| -    SetLastError(ERROR_CALL_NOT_IMPLEMENTED); | ||||
| -    return FALSE; | ||||
| +    return TRUE; | ||||
|  } | ||||
|   | ||||
|   | ||||
| --  | ||||
| 2.4.5 | ||||
|  | ||||
| @@ -0,0 +1,312 @@ | ||||
| From 542040171f936b56bc90f663cf6118a5998179af Mon Sep 17 00:00:00 2001 | ||||
| From: Sebastian Lackner <sebastian@fds-team.de> | ||||
| Date: Sat, 15 Oct 2016 19:50:46 +0200 | ||||
| Subject: ntdll: Implement FileIoCompletionNotificationInformation info class. | ||||
|  | ||||
| FIXME: The tests do not seem to work on all testbots yet. | ||||
| FIXME: Could we use the existing wineserver call instead? | ||||
| --- | ||||
|  dlls/kernel32/file.c    |  13 +++-- | ||||
|  dlls/ntdll/file.c       |  17 +++++++ | ||||
|  dlls/ntdll/tests/file.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++++ | ||||
|  include/winternl.h      |   8 +++ | ||||
|  server/fd.c             |  21 +++++++- | ||||
|  server/protocol.def     |   8 +++ | ||||
|  6 files changed, 193 insertions(+), 4 deletions(-) | ||||
|  | ||||
| diff --git a/dlls/kernel32/file.c b/dlls/kernel32/file.c | ||||
| index cc7ead1..5fe2268 100644 | ||||
| --- a/dlls/kernel32/file.c | ||||
| +++ b/dlls/kernel32/file.c | ||||
| @@ -1061,13 +1061,20 @@ BOOL WINAPI SetEndOfFile( HANDLE hFile ) | ||||
|      return FALSE; | ||||
|  } | ||||
|   | ||||
| + | ||||
|  /************************************************************************** | ||||
|   *           SetFileCompletionNotificationModes   (KERNEL32.@) | ||||
|   */ | ||||
| -BOOL WINAPI SetFileCompletionNotificationModes( HANDLE handle, UCHAR flags ) | ||||
| +BOOL WINAPI SetFileCompletionNotificationModes( HANDLE file, UCHAR flags ) | ||||
|  { | ||||
| -    FIXME("%p %x - stub\n", handle, flags); | ||||
| -    SetLastError(ERROR_CALL_NOT_IMPLEMENTED); | ||||
| +    FILE_IO_COMPLETION_NOTIFICATION_INFORMATION info; | ||||
| +    IO_STATUS_BLOCK io; | ||||
| +    NTSTATUS status; | ||||
| + | ||||
| +    info.Flags = flags; | ||||
| +    status = NtSetInformationFile( file, &io, &info, sizeof(info), FileIoCompletionNotificationInformation ); | ||||
| +    if (status == STATUS_SUCCESS) return TRUE; | ||||
| +    SetLastError( RtlNtStatusToDosError(status) ); | ||||
|      return FALSE; | ||||
|  } | ||||
|   | ||||
| diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c | ||||
| index 7fbde50..14715b1 100644 | ||||
| --- a/dlls/ntdll/file.c | ||||
| +++ b/dlls/ntdll/file.c | ||||
| @@ -2788,6 +2788,23 @@ NTSTATUS WINAPI NtSetInformationFile(HANDLE handle, PIO_STATUS_BLOCK io, | ||||
|              io->u.Status = STATUS_INVALID_PARAMETER_3; | ||||
|          break; | ||||
|   | ||||
| +    case FileIoCompletionNotificationInformation: | ||||
| +        if (len >= sizeof(FILE_IO_COMPLETION_NOTIFICATION_INFORMATION)) | ||||
| +        { | ||||
| +            FILE_IO_COMPLETION_NOTIFICATION_INFORMATION *info = ptr; | ||||
| + | ||||
| +            SERVER_START_REQ( set_fd_compl_info ) | ||||
| +            { | ||||
| +                req->handle   = wine_server_obj_handle( handle ); | ||||
| +                req->flags    = (info->Flags & FILE_SKIP_COMPLETION_PORT_ON_SUCCESS) ? | ||||
| +                                COMPLETION_SKIP_ON_SUCCESS : 0; | ||||
| +                io->u.Status  = wine_server_call( req ); | ||||
| +            } | ||||
| +            SERVER_END_REQ; | ||||
| +        } else | ||||
| +            io->u.Status = STATUS_INFO_LENGTH_MISMATCH; | ||||
| +        break; | ||||
| + | ||||
|      case FileAllInformation: | ||||
|          io->u.Status = STATUS_INVALID_INFO_CLASS; | ||||
|          break; | ||||
| diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c | ||||
| index 586b2c9..dfaf4a5 100644 | ||||
| --- a/dlls/ntdll/tests/file.c | ||||
| +++ b/dlls/ntdll/tests/file.c | ||||
| @@ -3261,6 +3261,135 @@ static void test_file_all_name_information(void) | ||||
|      HeapFree( GetProcessHeap(), 0, file_name ); | ||||
|  } | ||||
|   | ||||
| +static void test_file_completion_information(void) | ||||
| +{ | ||||
| +    static const char buf[] = "testdata"; | ||||
| +    FILE_IO_COMPLETION_NOTIFICATION_INFORMATION info; | ||||
| +    OVERLAPPED ov, *pov; | ||||
| +    IO_STATUS_BLOCK io; | ||||
| +    NTSTATUS status; | ||||
| +    DWORD num_bytes; | ||||
| +    HANDLE port, h; | ||||
| +    ULONG_PTR key; | ||||
| +    BOOL ret; | ||||
| +    int i; | ||||
| + | ||||
| +    if (!(h = create_temp_file(0))) return; | ||||
| + | ||||
| +    status = pNtSetInformationFile(h, &io, &info, sizeof(info) - 1, FileIoCompletionNotificationInformation); | ||||
| +    ok(status == STATUS_INFO_LENGTH_MISMATCH || status == STATUS_INVALID_INFO_CLASS /* XP */, | ||||
| +       "expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); | ||||
| +    if (status == STATUS_INVALID_INFO_CLASS) | ||||
| +    { | ||||
| +        CloseHandle(h); | ||||
| +        return; | ||||
| +    } | ||||
| + | ||||
| +    info.Flags = FILE_SKIP_COMPLETION_PORT_ON_SUCCESS; | ||||
| +    status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); | ||||
| +    ok(status == STATUS_INVALID_PARAMETER, "expected STATUS_INVALID_PARAMETER, got %08x\n", status); | ||||
| + | ||||
| +    CloseHandle(h); | ||||
| +    if (!(h = create_temp_file(FILE_FLAG_OVERLAPPED))) return; | ||||
| + | ||||
| +    info.Flags = FILE_SKIP_SET_EVENT_ON_HANDLE; | ||||
| +    status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); | ||||
| +    ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); | ||||
| + | ||||
| +    info.Flags = FILE_SKIP_SET_USER_EVENT_ON_FAST_IO; | ||||
| +    status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); | ||||
| +    ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); | ||||
| + | ||||
| +    CloseHandle(h); | ||||
| +    if (!(h = create_temp_file(FILE_FLAG_OVERLAPPED))) return; | ||||
| + | ||||
| +    memset(&ov, 0, sizeof(ov)); | ||||
| +    ov.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL); | ||||
| +    port = CreateIoCompletionPort(h, NULL, 0xdeadbeef, 0); | ||||
| +    ok(port != NULL, "CreateIoCompletionPort failed, error %u\n", GetLastError()); | ||||
| + | ||||
| +    for (i = 0; i < 10; i++) | ||||
| +    { | ||||
| +        SetLastError(0xdeadbeef); | ||||
| +        ret = WriteFile(h, buf, sizeof(buf), &num_bytes, &ov); | ||||
| +        if (ret || GetLastError() != ERROR_IO_PENDING) break; | ||||
| +        ret = GetOverlappedResult(h, &ov, &num_bytes, TRUE); | ||||
| +        ok(ret, "GetOverlappedResult failed, error %u\n", GetLastError()); | ||||
| +        ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000); | ||||
| +        ok(ret, "GetQueuedCompletionStatus failed, error %u\n", GetLastError()); | ||||
| +        ret = FALSE; | ||||
| +    } | ||||
| +    if (ret) | ||||
| +    { | ||||
| +        ok(num_bytes == sizeof(buf), "expected sizeof(buf), got %u\n", num_bytes); | ||||
| + | ||||
| +        key = 0; | ||||
| +        pov = NULL; | ||||
| +        ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000); | ||||
| +        ok(ret, "GetQueuedCompletionStatus failed, error %u\n", GetLastError()); | ||||
| +        ok(key == 0xdeadbeef, "expected 0xdeadbeef, got %lx\n", key); | ||||
| +        ok(pov == &ov, "expected %p, got %p\n", &ov, pov); | ||||
| +    } | ||||
| +    else | ||||
| +        win_skip("WriteFile never returned TRUE\n"); | ||||
| + | ||||
| +    info.Flags = FILE_SKIP_COMPLETION_PORT_ON_SUCCESS; | ||||
| +    status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); | ||||
| +    ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); | ||||
| + | ||||
| +    for (i = 0; i < 10; i++) | ||||
| +    { | ||||
| +        SetLastError(0xdeadbeef); | ||||
| +        ret = WriteFile(h, buf, sizeof(buf), &num_bytes, &ov); | ||||
| +        if (ret || GetLastError() != ERROR_IO_PENDING) break; | ||||
| +        ret = GetOverlappedResult(h, &ov, &num_bytes, TRUE); | ||||
| +        ok(ret, "GetOverlappedResult failed, error %u\n", GetLastError()); | ||||
| +        ret = FALSE; | ||||
| +    } | ||||
| +    if (ret) | ||||
| +    { | ||||
| +        ok(num_bytes == sizeof(buf), "expected sizeof(buf), got %u\n", num_bytes); | ||||
| + | ||||
| +        pov = (void *)0xdeadbeef; | ||||
| +        ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 500); | ||||
| +        ok(!ret, "GetQueuedCompletionStatus succeeded\n"); | ||||
| +        ok(pov == NULL, "expected NULL, got %p\n", pov); | ||||
| +    } | ||||
| +    else | ||||
| +        win_skip("WriteFile never returned TRUE\n"); | ||||
| + | ||||
| +    info.Flags = 0; | ||||
| +    status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); | ||||
| +    ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); | ||||
| + | ||||
| +    for (i = 0; i < 10; i++) | ||||
| +    { | ||||
| +        SetLastError(0xdeadbeef); | ||||
| +        ret = WriteFile(h, buf, sizeof(buf), &num_bytes, &ov); | ||||
| +        if (ret || GetLastError() != ERROR_IO_PENDING) break; | ||||
| +        ret = GetOverlappedResult(h, &ov, &num_bytes, TRUE); | ||||
| +        ok(ret, "GetOverlappedResult failed, error %u\n", GetLastError()); | ||||
| +        ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000); | ||||
| +        ok(ret, "GetQueuedCompletionStatus failed, error %u\n", GetLastError()); | ||||
| +        ret = FALSE; | ||||
| +    } | ||||
| +    if (ret) | ||||
| +    { | ||||
| +        ok(num_bytes == sizeof(buf), "expected sizeof(buf), got %u\n", num_bytes); | ||||
| + | ||||
| +        pov = (void *)0xdeadbeef; | ||||
| +        ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000); | ||||
| +        ok(!ret, "GetQueuedCompletionStatus succeeded\n"); | ||||
| +        ok(pov == NULL, "expected NULL, got %p\n", pov); | ||||
| +    } | ||||
| +    else | ||||
| +        win_skip("WriteFile never returned TRUE\n"); | ||||
| + | ||||
| +    CloseHandle(ov.hEvent); | ||||
| +    CloseHandle(port); | ||||
| +    CloseHandle(h); | ||||
| +} | ||||
| + | ||||
|  static void test_query_volume_information_file(void) | ||||
|  { | ||||
|      NTSTATUS status; | ||||
| @@ -4214,6 +4343,7 @@ START_TEST(file) | ||||
|      test_file_rename_information(); | ||||
|      test_file_link_information(); | ||||
|      test_file_disposition_information(); | ||||
| +    test_file_completion_information(); | ||||
|      test_query_volume_information_file(); | ||||
|      test_query_attribute_information_file(); | ||||
|  } | ||||
| diff --git a/include/winternl.h b/include/winternl.h | ||||
| index f35091c..7613d8b 100644 | ||||
| --- a/include/winternl.h | ||||
| +++ b/include/winternl.h | ||||
| @@ -725,6 +725,14 @@ typedef struct _FILE_ALL_INFORMATION { | ||||
|      FILE_NAME_INFORMATION      NameInformation; | ||||
|  } FILE_ALL_INFORMATION, *PFILE_ALL_INFORMATION; | ||||
|   | ||||
| +typedef struct _FILE_IO_COMPLETION_NOTIFICATION_INFORMATION { | ||||
| +    ULONG Flags; | ||||
| +} FILE_IO_COMPLETION_NOTIFICATION_INFORMATION, *PFILE_IO_COMPLETION_NOTIFICATION_INFORMATION; | ||||
| + | ||||
| +#define FILE_SKIP_COMPLETION_PORT_ON_SUCCESS 0x1 | ||||
| +#define FILE_SKIP_SET_EVENT_ON_HANDLE        0x2 | ||||
| +#define FILE_SKIP_SET_USER_EVENT_ON_FAST_IO  0x4 | ||||
| + | ||||
|  typedef enum _FSINFOCLASS { | ||||
|      FileFsVolumeInformation = 1, | ||||
|      FileFsLabelInformation, | ||||
| diff --git a/server/fd.c b/server/fd.c | ||||
| index 17b1b66..0d5c7a2 100644 | ||||
| --- a/server/fd.c | ||||
| +++ b/server/fd.c | ||||
| @@ -194,6 +194,7 @@ struct fd | ||||
|      struct async_queue  *wait_q;      /* other async waiters of this fd */ | ||||
|      struct completion   *completion;  /* completion object attached to this fd */ | ||||
|      apc_param_t          comp_key;    /* completion key to set in completion events */ | ||||
| +    unsigned int         comp_flags;  /* completion flags */ | ||||
|  }; | ||||
|   | ||||
|  static void fd_dump( struct object *obj, int verbose ); | ||||
| @@ -1607,6 +1608,7 @@ static struct fd *alloc_fd_object(void) | ||||
|      fd->write_q    = NULL; | ||||
|      fd->wait_q     = NULL; | ||||
|      fd->completion = NULL; | ||||
| +    fd->comp_flags = 0; | ||||
|      list_init( &fd->inode_entry ); | ||||
|      list_init( &fd->locks ); | ||||
|   | ||||
| @@ -2556,12 +2558,29 @@ DECL_HANDLER(add_fd_completion) | ||||
|      struct fd *fd = get_handle_fd_obj( current->process, req->handle, 0 ); | ||||
|      if (fd) | ||||
|      { | ||||
| -        if (fd->completion) | ||||
| +        if (fd->completion && (!(fd->comp_flags & COMPLETION_SKIP_ON_SUCCESS) || req->status)) | ||||
|              add_completion( fd->completion, fd->comp_key, req->cvalue, req->status, req->information ); | ||||
|          release_object( fd ); | ||||
|      } | ||||
|  } | ||||
|   | ||||
| +/* set fd completion information */ | ||||
| +DECL_HANDLER(set_fd_compl_info) | ||||
| +{ | ||||
| +    struct fd *fd = get_handle_fd_obj( current->process, req->handle, 0 ); | ||||
| +    if (fd) | ||||
| +    { | ||||
| +        if (!(fd->options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT))) | ||||
| +        { | ||||
| +            /* removing COMPLETION_SKIP_ON_SUCCESS is not allowed */ | ||||
| +            fd->comp_flags |= req->flags; | ||||
| +        } | ||||
| +        else | ||||
| +            set_error( STATUS_INVALID_PARAMETER ); | ||||
| +        release_object( fd ); | ||||
| +    } | ||||
| +} | ||||
| + | ||||
|  /* set fd disposition information */ | ||||
|  DECL_HANDLER(set_fd_disp_info) | ||||
|  { | ||||
| diff --git a/server/protocol.def b/server/protocol.def | ||||
| index 8d86737..229596a 100644 | ||||
| --- a/server/protocol.def | ||||
| +++ b/server/protocol.def | ||||
| @@ -3678,6 +3678,14 @@ struct handle_info | ||||
|  @END | ||||
|   | ||||
|   | ||||
| +/* set fd completion information */ | ||||
| +@REQ(set_fd_compl_info) | ||||
| +    obj_handle_t handle;          /* handle to a file or directory */ | ||||
| +    int          flags;           /* completion flags (see below) */ | ||||
| +@END | ||||
| +#define COMPLETION_SKIP_ON_SUCCESS 0x01 | ||||
| + | ||||
| + | ||||
|  /* set fd disposition information */ | ||||
|  @REQ(set_fd_disp_info) | ||||
|      obj_handle_t handle;          /* handle to a file or directory */ | ||||
| --  | ||||
| 2.9.0 | ||||
|  | ||||
| @@ -1 +1 @@ | ||||
| Fixes: [38960] Fake success in kernel32.SetFileCompletionNotificationModes | ||||
| Fixes: [38960] Add support for kernel32.SetFileCompletionNotificationModes | ||||
|   | ||||
		Reference in New Issue
	
	Block a user