Skip to content
This repository has been archived by the owner on Oct 1, 2020. It is now read-only.

Commit

Permalink
Added patch to implement support for SetFileCompletionNotificationModes.
Browse files Browse the repository at this point in the history
  • Loading branch information
slackner committed Oct 15, 2016
1 parent f176092 commit dd324a1
Show file tree
Hide file tree
Showing 6 changed files with 338 additions and 41 deletions.

This file was deleted.

@@ -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

0 comments on commit dd324a1

Please sign in to comment.