diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c index d19831ffe9e..b3981e8ebed 100644 --- a/dlls/ntdll/file.c +++ b/dlls/ntdll/file.c @@ -339,296 +339,6 @@ NTSTATUS FILE_GetNtStatus(void) } } -/*********************************************************************** - * FILE_AsyncReadService (INTERNAL) - */ -static NTSTATUS FILE_AsyncReadService( void *user, IO_STATUS_BLOCK *iosb, NTSTATUS status ) -{ - struct async_fileio_read *fileio = user; - int fd, needs_close, result; - - switch (status) - { - case STATUS_ALERTED: /* got some new data */ - /* check to see if the data is ready (non-blocking) */ - if ((status = unix_funcs->server_get_unix_fd( fileio->io.handle, FILE_READ_DATA, &fd, - &needs_close, NULL, NULL ))) - break; - - result = unix_funcs->virtual_locked_read(fd, &fileio->buffer[fileio->already], fileio->count-fileio->already); - if (needs_close) close( fd ); - - if (result < 0) - { - if (errno == EAGAIN || errno == EINTR) - status = STATUS_PENDING; - else /* check to see if the transfer is complete */ - status = FILE_GetNtStatus(); - } - else if (result == 0) - { - status = fileio->already ? STATUS_SUCCESS : STATUS_PIPE_BROKEN; - } - else - { - fileio->already += result; - if (fileio->already >= fileio->count || fileio->avail_mode) - status = STATUS_SUCCESS; - else - status = STATUS_PENDING; - } - break; - - case STATUS_TIMEOUT: - case STATUS_IO_TIMEOUT: - if (fileio->already) status = STATUS_SUCCESS; - break; - } - if (status != STATUS_PENDING) - { - iosb->u.Status = status; - iosb->Information = fileio->already; - release_fileio( &fileio->io ); - } - return status; -} - -/* do a read call through the server */ -static NTSTATUS server_read_file( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_context, - IO_STATUS_BLOCK *io, void *buffer, ULONG size, - LARGE_INTEGER *offset, ULONG *key ) -{ - struct async_irp *async; - NTSTATUS status; - HANDLE wait_handle; - ULONG options; - - if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle ))) - return STATUS_NO_MEMORY; - - async->buffer = buffer; - async->size = size; - - SERVER_START_REQ( read ) - { - req->async = server_async( handle, &async->io, event, apc, apc_context, io ); - req->pos = offset ? offset->QuadPart : 0; - wine_server_set_reply( req, buffer, size ); - status = unix_funcs->virtual_locked_server_call( req ); - wait_handle = wine_server_ptr_handle( reply->wait ); - options = reply->options; - if (wait_handle && status != STATUS_PENDING) - { - io->u.Status = status; - io->Information = wine_server_reply_size( reply ); - } - } - SERVER_END_REQ; - - if (status != STATUS_PENDING) RtlFreeHeap( GetProcessHeap(), 0, async ); - - if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT), io ); - return status; -} - -/* do a write call through the server */ -static NTSTATUS server_write_file( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_context, - IO_STATUS_BLOCK *io, const void *buffer, ULONG size, - LARGE_INTEGER *offset, ULONG *key ) -{ - struct async_irp *async; - NTSTATUS status; - HANDLE wait_handle; - ULONG options; - - if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle ))) - return STATUS_NO_MEMORY; - - async->buffer = NULL; - async->size = 0; - - SERVER_START_REQ( write ) - { - req->async = server_async( handle, &async->io, event, apc, apc_context, io ); - req->pos = offset ? offset->QuadPart : 0; - wine_server_add_data( req, buffer, size ); - status = wine_server_call( req ); - wait_handle = wine_server_ptr_handle( reply->wait ); - options = reply->options; - if (wait_handle && status != STATUS_PENDING) - { - io->u.Status = status; - io->Information = reply->size; - } - } - SERVER_END_REQ; - - if (status != STATUS_PENDING) RtlFreeHeap( GetProcessHeap(), 0, async ); - - if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT), io ); - return status; -} - -struct io_timeouts -{ - int interval; /* max interval between two bytes */ - int total; /* total timeout for the whole operation */ - int end_time; /* absolute time of end of operation */ -}; - -/* retrieve the I/O timeouts to use for a given handle */ -static NTSTATUS get_io_timeouts( HANDLE handle, enum server_fd_type type, ULONG count, BOOL is_read, - struct io_timeouts *timeouts ) -{ - NTSTATUS status = STATUS_SUCCESS; - - timeouts->interval = timeouts->total = -1; - - switch(type) - { - case FD_TYPE_SERIAL: - { - /* GetCommTimeouts */ - SERIAL_TIMEOUTS st; - IO_STATUS_BLOCK io; - - status = NtDeviceIoControlFile( handle, NULL, NULL, NULL, &io, - IOCTL_SERIAL_GET_TIMEOUTS, NULL, 0, &st, sizeof(st) ); - if (status) break; - - if (is_read) - { - if (st.ReadIntervalTimeout) - timeouts->interval = st.ReadIntervalTimeout; - - if (st.ReadTotalTimeoutMultiplier || st.ReadTotalTimeoutConstant) - { - timeouts->total = st.ReadTotalTimeoutConstant; - if (st.ReadTotalTimeoutMultiplier != MAXDWORD) - timeouts->total += count * st.ReadTotalTimeoutMultiplier; - } - else if (st.ReadIntervalTimeout == MAXDWORD) - timeouts->interval = timeouts->total = 0; - } - else /* write */ - { - if (st.WriteTotalTimeoutMultiplier || st.WriteTotalTimeoutConstant) - { - timeouts->total = st.WriteTotalTimeoutConstant; - if (st.WriteTotalTimeoutMultiplier != MAXDWORD) - timeouts->total += count * st.WriteTotalTimeoutMultiplier; - } - } - } - break; - case FD_TYPE_MAILSLOT: - if (is_read) - { - timeouts->interval = 0; /* return as soon as we got something */ - SERVER_START_REQ( set_mailslot_info ) - { - req->handle = wine_server_obj_handle( handle ); - req->flags = 0; - if (!(status = wine_server_call( req )) && - reply->read_timeout != TIMEOUT_INFINITE) - timeouts->total = reply->read_timeout / -10000; - } - SERVER_END_REQ; - } - break; - case FD_TYPE_SOCKET: - case FD_TYPE_CHAR: - if (is_read) timeouts->interval = 0; /* return as soon as we got something */ - break; - default: - break; - } - if (timeouts->total != -1) timeouts->end_time = NtGetTickCount() + timeouts->total; - return STATUS_SUCCESS; -} - - -/* retrieve the timeout for the next wait, in milliseconds */ -static inline int get_next_io_timeout( const struct io_timeouts *timeouts, ULONG already ) -{ - int ret = -1; - - if (timeouts->total != -1) - { - ret = timeouts->end_time - NtGetTickCount(); - if (ret < 0) ret = 0; - } - if (already && timeouts->interval != -1) - { - if (ret == -1 || ret > timeouts->interval) ret = timeouts->interval; - } - return ret; -} - - -/* retrieve the avail_mode flag for async reads */ -static NTSTATUS get_io_avail_mode( HANDLE handle, enum server_fd_type type, BOOL *avail_mode ) -{ - NTSTATUS status = STATUS_SUCCESS; - - switch(type) - { - case FD_TYPE_SERIAL: - { - /* GetCommTimeouts */ - SERIAL_TIMEOUTS st; - IO_STATUS_BLOCK io; - - status = NtDeviceIoControlFile( handle, NULL, NULL, NULL, &io, - IOCTL_SERIAL_GET_TIMEOUTS, NULL, 0, &st, sizeof(st) ); - if (status) break; - *avail_mode = (!st.ReadTotalTimeoutMultiplier && - !st.ReadTotalTimeoutConstant && - st.ReadIntervalTimeout == MAXDWORD); - } - break; - case FD_TYPE_MAILSLOT: - case FD_TYPE_SOCKET: - case FD_TYPE_CHAR: - *avail_mode = TRUE; - break; - default: - *avail_mode = FALSE; - break; - } - return status; -} - -/* register an async I/O for a file read; helper for NtReadFile */ -static NTSTATUS register_async_file_read( HANDLE handle, HANDLE event, - PIO_APC_ROUTINE apc, void *apc_user, - IO_STATUS_BLOCK *iosb, void *buffer, - ULONG already, ULONG length, BOOL avail_mode ) -{ - struct async_fileio_read *fileio; - NTSTATUS status; - - if (!(fileio = (struct async_fileio_read *)alloc_fileio( sizeof(*fileio), FILE_AsyncReadService, handle ))) - return STATUS_NO_MEMORY; - - fileio->already = already; - fileio->count = length; - fileio->buffer = buffer; - fileio->avail_mode = avail_mode; - - SERVER_START_REQ( register_async ) - { - req->type = ASYNC_TYPE_READ; - req->count = length; - req->async = server_async( handle, &fileio->io, event, apc, apc_user, iosb ); - status = wine_server_call( req ); - } - SERVER_END_REQ; - - if (status != STATUS_PENDING) RtlFreeHeap( GetProcessHeap(), 0, fileio ); - return status; -} - /****************************************************************************** * NtReadFile [NTDLL.@] @@ -657,196 +367,7 @@ NTSTATUS WINAPI NtReadFile(HANDLE hFile, HANDLE hEvent, PIO_STATUS_BLOCK io_status, void* buffer, ULONG length, PLARGE_INTEGER offset, PULONG key) { - int result, unix_handle, needs_close; - unsigned int options; - struct io_timeouts timeouts; - NTSTATUS status, ret_status; - ULONG total = 0; - enum server_fd_type type; - ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user; - BOOL send_completion = FALSE, async_read, timeout_init_done = FALSE; - - TRACE("(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p)\n", - hFile,hEvent,apc,apc_user,io_status,buffer,length,offset,key); - - if (!io_status) return STATUS_ACCESS_VIOLATION; - - status = unix_funcs->server_get_unix_fd( hFile, FILE_READ_DATA, &unix_handle, - &needs_close, &type, &options ); - if (status && status != STATUS_BAD_DEVICE_TYPE) return status; - - if (!unix_funcs->virtual_check_buffer_for_write( buffer, length )) return STATUS_ACCESS_VIOLATION; - - if (status == STATUS_BAD_DEVICE_TYPE) - return server_read_file( hFile, hEvent, apc, apc_user, io_status, buffer, length, offset, key ); - - async_read = !(options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)); - - if (type == FD_TYPE_FILE) - { - if (async_read && (!offset || offset->QuadPart < 0)) - { - status = STATUS_INVALID_PARAMETER; - goto done; - } - - if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION) - { - /* async I/O doesn't make sense on regular files */ - while ((result = unix_funcs->virtual_locked_pread( unix_handle, buffer, length, offset->QuadPart )) == -1) - { - if (errno != EINTR) - { - status = FILE_GetNtStatus(); - goto done; - } - } - if (!async_read) - /* update file pointer position */ - lseek( unix_handle, offset->QuadPart + result, SEEK_SET ); - - total = result; - status = (total || !length) ? STATUS_SUCCESS : STATUS_END_OF_FILE; - goto done; - } - } - else if (type == FD_TYPE_SERIAL || type == FD_TYPE_DEVICE) - { - if (async_read && (!offset || offset->QuadPart < 0)) - { - status = STATUS_INVALID_PARAMETER; - goto done; - } - } - - if (type == FD_TYPE_SERIAL && async_read && length) - { - /* an asynchronous serial port read with a read interval timeout needs to - skip the synchronous read to make sure that the server starts the read - interval timer after the first read */ - if ((status = get_io_timeouts( hFile, type, length, TRUE, &timeouts ))) goto err; - if (timeouts.interval) - { - status = register_async_file_read( hFile, hEvent, apc, apc_user, io_status, - buffer, total, length, FALSE ); - goto err; - } - } - - for (;;) - { - if ((result = unix_funcs->virtual_locked_read( unix_handle, (char *)buffer + total, length - total )) >= 0) - { - total += result; - if (!result || total == length) - { - if (total) - { - status = STATUS_SUCCESS; - goto done; - } - switch (type) - { - case FD_TYPE_FILE: - case FD_TYPE_CHAR: - case FD_TYPE_DEVICE: - status = length ? STATUS_END_OF_FILE : STATUS_SUCCESS; - goto done; - case FD_TYPE_SERIAL: - if (!length) - { - status = STATUS_SUCCESS; - goto done; - } - break; - default: - status = STATUS_PIPE_BROKEN; - goto err; - } - } - else if (type == FD_TYPE_FILE) continue; /* no async I/O on regular files */ - } - else if (errno != EAGAIN) - { - if (errno == EINTR) continue; - if (!total) status = FILE_GetNtStatus(); - goto err; - } - - if (async_read) - { - BOOL avail_mode; - - if ((status = get_io_avail_mode( hFile, type, &avail_mode ))) - goto err; - if (total && avail_mode) - { - status = STATUS_SUCCESS; - goto done; - } - status = register_async_file_read( hFile, hEvent, apc, apc_user, io_status, - buffer, total, length, avail_mode ); - goto err; - } - else /* synchronous read, wait for the fd to become ready */ - { - struct pollfd pfd; - int ret, timeout; - - if (!timeout_init_done) - { - timeout_init_done = TRUE; - if ((status = get_io_timeouts( hFile, type, length, TRUE, &timeouts ))) - goto err; - if (hEvent) NtResetEvent( hEvent, NULL ); - } - timeout = get_next_io_timeout( &timeouts, total ); - - pfd.fd = unix_handle; - pfd.events = POLLIN; - - if (!timeout || !(ret = poll( &pfd, 1, timeout ))) - { - if (total) /* return with what we got so far */ - status = STATUS_SUCCESS; - else - status = (type == FD_TYPE_MAILSLOT) ? STATUS_IO_TIMEOUT : STATUS_TIMEOUT; - goto done; - } - if (ret == -1 && errno != EINTR) - { - status = FILE_GetNtStatus(); - goto done; - } - /* will now restart the read */ - } - } - -done: - send_completion = cvalue != 0; - -err: - if (needs_close) close( unix_handle ); - if (status == STATUS_SUCCESS || (status == STATUS_END_OF_FILE && (!async_read || type == FD_TYPE_FILE))) - { - io_status->u.Status = status; - io_status->Information = total; - TRACE("= SUCCESS (%u)\n", total); - if (hEvent) NtSetEvent( hEvent, NULL ); - if (apc && (!status || async_read)) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc, - (ULONG_PTR)apc_user, (ULONG_PTR)io_status, 0 ); - } - else - { - TRACE("= 0x%08x\n", status); - if (status != STATUS_PENDING && hEvent) NtResetEvent( hEvent, NULL ); - } - - ret_status = async_read && type == FD_TYPE_FILE && (status == STATUS_SUCCESS || status == STATUS_END_OF_FILE) - ? STATUS_PENDING : status; - - if (send_completion) NTDLL_AddCompletion( hFile, cvalue, status, total, ret_status == STATUS_PENDING ); - return ret_status; + return unix_funcs->NtReadFile( hFile, hEvent, apc, apc_user, io_status, buffer, length, offset, key ); } @@ -858,144 +379,11 @@ NTSTATUS WINAPI NtReadFileScatter( HANDLE file, HANDLE event, PIO_APC_ROUTINE ap PIO_STATUS_BLOCK io_status, FILE_SEGMENT_ELEMENT *segments, ULONG length, PLARGE_INTEGER offset, PULONG key ) { - int result, unix_handle, needs_close; - unsigned int options; - NTSTATUS status; - ULONG pos = 0, total = 0; - enum server_fd_type type; - ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user; - BOOL send_completion = FALSE; - - TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p),partial stub!\n", - file, event, apc, apc_user, io_status, segments, length, offset, key); - - if (!io_status) return STATUS_ACCESS_VIOLATION; - - status = unix_funcs->server_get_unix_fd( file, FILE_READ_DATA, &unix_handle, - &needs_close, &type, &options ); - if (status) return status; - - if ((type != FD_TYPE_FILE) || - (options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)) || - !(options & FILE_NO_INTERMEDIATE_BUFFERING)) - { - status = STATUS_INVALID_PARAMETER; - goto error; - } - - while (length) - { - if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION) - result = pread( unix_handle, (char *)segments->Buffer + pos, - min( length - pos, page_size - pos ), offset->QuadPart + total ); - else - result = read( unix_handle, (char *)segments->Buffer + pos, min( length - pos, page_size - pos ) ); - - if (result == -1) - { - if (errno == EINTR) continue; - status = FILE_GetNtStatus(); - break; - } - if (!result) break; - total += result; - length -= result; - if ((pos += result) == page_size) - { - pos = 0; - segments++; - } - } - - if (total == 0) status = STATUS_END_OF_FILE; - - send_completion = cvalue != 0; - - if (needs_close) close( unix_handle ); - - io_status->u.Status = status; - io_status->Information = total; - TRACE("= 0x%08x (%u)\n", status, total); - if (event) NtSetEvent( event, NULL ); - if (apc) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc, - (ULONG_PTR)apc_user, (ULONG_PTR)io_status, 0 ); - if (send_completion) NTDLL_AddCompletion( file, cvalue, status, total, TRUE ); - - return STATUS_PENDING; - -error: - if (needs_close) close( unix_handle ); - - TRACE("= 0x%08x\n", status); - if (event) NtResetEvent( event, NULL ); - - return status; + return unix_funcs->NtReadFileScatter( file, event, apc, apc_user, io_status, + segments, length, offset, key ); } -/*********************************************************************** - * FILE_AsyncWriteService (INTERNAL) - */ -static NTSTATUS FILE_AsyncWriteService( void *user, IO_STATUS_BLOCK *iosb, NTSTATUS status ) -{ - struct async_fileio_write *fileio = user; - int result, fd, needs_close; - enum server_fd_type type; - - switch (status) - { - case STATUS_ALERTED: - /* write some data (non-blocking) */ - if ((status = unix_funcs->server_get_unix_fd( fileio->io.handle, FILE_WRITE_DATA, &fd, - &needs_close, &type, NULL ))) - break; - - if (!fileio->count && (type == FD_TYPE_MAILSLOT || type == FD_TYPE_SOCKET)) - result = send( fd, fileio->buffer, 0, 0 ); - else - result = write( fd, &fileio->buffer[fileio->already], fileio->count - fileio->already ); - - if (needs_close) close( fd ); - - if (result < 0) - { - if (errno == EAGAIN || errno == EINTR) status = STATUS_PENDING; - else status = FILE_GetNtStatus(); - } - else - { - fileio->already += result; - status = (fileio->already < fileio->count) ? STATUS_PENDING : STATUS_SUCCESS; - } - break; - - case STATUS_TIMEOUT: - case STATUS_IO_TIMEOUT: - if (fileio->already) status = STATUS_SUCCESS; - break; - } - if (status != STATUS_PENDING) - { - iosb->u.Status = status; - iosb->Information = fileio->already; - release_fileio( &fileio->io ); - } - return status; -} - -static NTSTATUS set_pending_write( HANDLE device ) -{ - NTSTATUS status; - - SERVER_START_REQ( set_serial_info ) - { - req->handle = wine_server_obj_handle( device ); - req->flags = SERIALINFO_PENDING_WRITE; - status = wine_server_call( req ); - } - SERVER_END_REQ; - return status; -} /****************************************************************************** * NtWriteFile [NTDLL.@] @@ -1025,223 +413,7 @@ NTSTATUS WINAPI NtWriteFile(HANDLE hFile, HANDLE hEvent, const void* buffer, ULONG length, PLARGE_INTEGER offset, PULONG key) { - int result, unix_handle, needs_close; - unsigned int options; - struct io_timeouts timeouts; - NTSTATUS status, ret_status; - ULONG total = 0; - enum server_fd_type type; - ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user; - BOOL send_completion = FALSE, async_write, append_write = FALSE, timeout_init_done = FALSE; - LARGE_INTEGER offset_eof; - - TRACE("(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p)\n", - hFile,hEvent,apc,apc_user,io_status,buffer,length,offset,key); - - if (!io_status) return STATUS_ACCESS_VIOLATION; - - status = unix_funcs->server_get_unix_fd( hFile, FILE_WRITE_DATA, &unix_handle, - &needs_close, &type, &options ); - if (status == STATUS_ACCESS_DENIED) - { - status = unix_funcs->server_get_unix_fd( hFile, FILE_APPEND_DATA, &unix_handle, - &needs_close, &type, &options ); - append_write = TRUE; - } - if (status && status != STATUS_BAD_DEVICE_TYPE) return status; - - async_write = !(options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)); - - if (!unix_funcs->virtual_check_buffer_for_read( buffer, length )) - { - status = STATUS_INVALID_USER_BUFFER; - goto done; - } - - if (status == STATUS_BAD_DEVICE_TYPE) - return server_write_file( hFile, hEvent, apc, apc_user, io_status, buffer, length, offset, key ); - - if (type == FD_TYPE_FILE) - { - if (async_write && - (!offset || (offset->QuadPart < 0 && offset->QuadPart != FILE_WRITE_TO_END_OF_FILE))) - { - status = STATUS_INVALID_PARAMETER; - goto done; - } - - if (append_write) - { - offset_eof.QuadPart = FILE_WRITE_TO_END_OF_FILE; - offset = &offset_eof; - } - - if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION) - { - off_t off = offset->QuadPart; - - if (offset->QuadPart == FILE_WRITE_TO_END_OF_FILE) - { - struct stat st; - - if (fstat( unix_handle, &st ) == -1) - { - status = FILE_GetNtStatus(); - goto done; - } - off = st.st_size; - } - else if (offset->QuadPart < 0) - { - status = STATUS_INVALID_PARAMETER; - goto done; - } - - /* async I/O doesn't make sense on regular files */ - while ((result = pwrite( unix_handle, buffer, length, off )) == -1) - { - if (errno != EINTR) - { - if (errno == EFAULT) status = STATUS_INVALID_USER_BUFFER; - else status = FILE_GetNtStatus(); - goto done; - } - } - - if (!async_write) - /* update file pointer position */ - lseek( unix_handle, off + result, SEEK_SET ); - - total = result; - status = STATUS_SUCCESS; - goto done; - } - } - else if (type == FD_TYPE_SERIAL || type == FD_TYPE_DEVICE) - { - if (async_write && - (!offset || (offset->QuadPart < 0 && offset->QuadPart != FILE_WRITE_TO_END_OF_FILE))) - { - status = STATUS_INVALID_PARAMETER; - goto done; - } - } - - for (;;) - { - /* zero-length writes on sockets may not work with plain write(2) */ - if (!length && (type == FD_TYPE_MAILSLOT || type == FD_TYPE_SOCKET)) - result = send( unix_handle, buffer, 0, 0 ); - else - result = write( unix_handle, (const char *)buffer + total, length - total ); - - if (result >= 0) - { - total += result; - if (total == length) - { - status = STATUS_SUCCESS; - goto done; - } - if (type == FD_TYPE_FILE) continue; /* no async I/O on regular files */ - } - else if (errno != EAGAIN) - { - if (errno == EINTR) continue; - if (!total) - { - if (errno == EFAULT) status = STATUS_INVALID_USER_BUFFER; - else status = FILE_GetNtStatus(); - } - goto err; - } - - if (async_write) - { - struct async_fileio_write *fileio; - - fileio = (struct async_fileio_write *)alloc_fileio( sizeof(*fileio), FILE_AsyncWriteService, hFile ); - if (!fileio) - { - status = STATUS_NO_MEMORY; - goto err; - } - fileio->already = total; - fileio->count = length; - fileio->buffer = buffer; - - SERVER_START_REQ( register_async ) - { - req->type = ASYNC_TYPE_WRITE; - req->count = length; - req->async = server_async( hFile, &fileio->io, hEvent, apc, apc_user, io_status ); - status = wine_server_call( req ); - } - SERVER_END_REQ; - - if (status != STATUS_PENDING) RtlFreeHeap( GetProcessHeap(), 0, fileio ); - goto err; - } - else /* synchronous write, wait for the fd to become ready */ - { - struct pollfd pfd; - int ret, timeout; - - if (!timeout_init_done) - { - timeout_init_done = TRUE; - if ((status = get_io_timeouts( hFile, type, length, FALSE, &timeouts ))) - goto err; - if (hEvent) NtResetEvent( hEvent, NULL ); - } - timeout = get_next_io_timeout( &timeouts, total ); - - pfd.fd = unix_handle; - pfd.events = POLLOUT; - - if (!timeout || !(ret = poll( &pfd, 1, timeout ))) - { - /* return with what we got so far */ - status = total ? STATUS_SUCCESS : STATUS_TIMEOUT; - goto done; - } - if (ret == -1 && errno != EINTR) - { - status = FILE_GetNtStatus(); - goto done; - } - /* will now restart the write */ - } - } - -done: - send_completion = cvalue != 0; - -err: - if (needs_close) close( unix_handle ); - - if (type == FD_TYPE_SERIAL && (status == STATUS_SUCCESS || status == STATUS_PENDING)) - set_pending_write( hFile ); - - if (status == STATUS_SUCCESS) - { - io_status->u.Status = status; - io_status->Information = total; - TRACE("= SUCCESS (%u)\n", total); - if (hEvent) NtSetEvent( hEvent, NULL ); - if (apc) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc, - (ULONG_PTR)apc_user, (ULONG_PTR)io_status, 0 ); - } - else - { - TRACE("= 0x%08x\n", status); - if (status != STATUS_PENDING && hEvent) NtResetEvent( hEvent, NULL ); - } - - ret_status = async_write && type == FD_TYPE_FILE && status == STATUS_SUCCESS ? STATUS_PENDING : status; - if (send_completion) NTDLL_AddCompletion( hFile, cvalue, status, total, ret_status == STATUS_PENDING ); - - return ret_status; + return unix_funcs->NtWriteFile( hFile, hEvent, apc, apc_user, io_status, buffer, length, offset, key ); } @@ -1253,87 +425,8 @@ NTSTATUS WINAPI NtWriteFileGather( HANDLE file, HANDLE event, PIO_APC_ROUTINE ap PIO_STATUS_BLOCK io_status, FILE_SEGMENT_ELEMENT *segments, ULONG length, PLARGE_INTEGER offset, PULONG key ) { - int result, unix_handle, needs_close; - unsigned int options; - NTSTATUS status; - ULONG pos = 0, total = 0; - enum server_fd_type type; - ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user; - BOOL send_completion = FALSE; - - TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p),partial stub!\n", - file, event, apc, apc_user, io_status, segments, length, offset, key); - - if (length % page_size) return STATUS_INVALID_PARAMETER; - if (!io_status) return STATUS_ACCESS_VIOLATION; - - status = unix_funcs->server_get_unix_fd( file, FILE_WRITE_DATA, &unix_handle, - &needs_close, &type, &options ); - if (status) return status; - - if ((type != FD_TYPE_FILE) || - (options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)) || - !(options & FILE_NO_INTERMEDIATE_BUFFERING)) - { - status = STATUS_INVALID_PARAMETER; - goto error; - } - - while (length) - { - if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION) - result = pwrite( unix_handle, (char *)segments->Buffer + pos, - page_size - pos, offset->QuadPart + total ); - else - result = write( unix_handle, (char *)segments->Buffer + pos, page_size - pos ); - - if (result == -1) - { - if (errno == EINTR) continue; - if (errno == EFAULT) - { - status = STATUS_INVALID_USER_BUFFER; - goto error; - } - status = FILE_GetNtStatus(); - break; - } - if (!result) - { - status = STATUS_DISK_FULL; - break; - } - total += result; - length -= result; - if ((pos += result) == page_size) - { - pos = 0; - segments++; - } - } - - send_completion = cvalue != 0; - - error: - if (needs_close) close( unix_handle ); - if (status == STATUS_SUCCESS) - { - io_status->u.Status = status; - io_status->Information = total; - TRACE("= SUCCESS (%u)\n", total); - if (event) NtSetEvent( event, NULL ); - if (apc) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc, - (ULONG_PTR)apc_user, (ULONG_PTR)io_status, 0 ); - } - else - { - TRACE("= 0x%08x\n", status); - if (status != STATUS_PENDING && event) NtResetEvent( event, NULL ); - } - - if (send_completion) NTDLL_AddCompletion( file, cvalue, status, total, FALSE ); - - return status; + return unix_funcs->NtWriteFileGather( file, event, apc, apc_user, io_status, + segments, length, offset, key ); } diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h index bfb291374d0..0f6851f7df0 100644 --- a/dlls/ntdll/ntdll_misc.h +++ b/dlls/ntdll/ntdll_misc.h @@ -150,10 +150,6 @@ extern void virtual_fill_image_information( const pe_image_info_t *pe_info, SECTION_IMAGE_INFORMATION *info ) DECLSPEC_HIDDEN; extern struct _KUSER_SHARED_DATA *user_shared_data DECLSPEC_HIDDEN; -/* completion */ -extern NTSTATUS NTDLL_AddCompletion( HANDLE hFile, ULONG_PTR CompletionValue, - NTSTATUS CompletionStatus, ULONG Information, BOOL async) DECLSPEC_HIDDEN; - /* locale */ extern LCID user_lcid, system_lcid; extern DWORD ntdll_umbstowcs( const char* src, DWORD srclen, WCHAR* dst, DWORD dstlen ) DECLSPEC_HIDDEN; diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c index 8d7292f8da8..e133f032cee 100644 --- a/dlls/ntdll/sync.c +++ b/dlls/ntdll/sync.c @@ -582,24 +582,6 @@ NTSTATUS WINAPI NtQueryIoCompletion( HANDLE handle, IO_COMPLETION_INFORMATION_CL return unix_funcs->NtQueryIoCompletion( handle, class, buffer, len, ret_len ); } -NTSTATUS NTDLL_AddCompletion( HANDLE hFile, ULONG_PTR CompletionValue, - NTSTATUS CompletionStatus, ULONG Information, BOOL async ) -{ - NTSTATUS status; - - SERVER_START_REQ( add_fd_completion ) - { - req->handle = wine_server_obj_handle( hFile ); - req->cvalue = CompletionValue; - req->status = CompletionStatus; - req->information = Information; - req->async = async; - status = wine_server_call( req ); - } - SERVER_END_REQ; - return status; -} - /****************************************************************** * RtlRunOnceInitialize (NTDLL.@) */ diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index d39ca8cd635..a01747e1f2c 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -40,6 +40,9 @@ #ifdef HAVE_MNTENT_H #include #endif +#ifdef HAVE_POLL_H +#include +#endif #ifdef HAVE_SYS_STAT_H # include #endif @@ -112,6 +115,7 @@ #include "winioctl.h" #include "winternl.h" #include "ddk/ntddk.h" +#include "ddk/ntddser.h" #include "ddk/wdm.h" #define WINE_MOUNTMGR_EXTENSIONS #include "ddk/mountmgr.h" @@ -125,6 +129,9 @@ WINE_DECLARE_DEBUG_CHANNEL(winediag); #define MAX_DOS_DRIVES 26 +#define FILE_WRITE_TO_END_OF_FILE ((LONGLONG)-1) +#define FILE_USE_FILE_POINTER_POSITION ((LONGLONG)-2) + /* just in case... */ #undef VFAT_IOCTL_READDIR_BOTH #undef EXT2_IOC_GETFLAGS @@ -4282,3 +4289,1067 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, io->Information = 0; return io->u.Status; } + + +/*********************************************************************** + * Asynchronous file I/O * + */ + +typedef NTSTATUS async_callback_t( void *user, IO_STATUS_BLOCK *io, NTSTATUS status ); + +struct async_fileio +{ + async_callback_t *callback; /* must be the first field */ + struct async_fileio *next; + HANDLE handle; +}; + +struct async_fileio_read +{ + struct async_fileio io; + char *buffer; + unsigned int already; + unsigned int count; + BOOL avail_mode; +}; + +struct async_fileio_write +{ + struct async_fileio io; + const char *buffer; + unsigned int already; + unsigned int count; +}; + +struct async_irp +{ + struct async_fileio io; + void *buffer; /* buffer for output */ + ULONG size; /* size of buffer */ +}; + +static struct async_fileio *fileio_freelist; + +static void release_fileio( struct async_fileio *io ) +{ + for (;;) + { + struct async_fileio *next = fileio_freelist; + io->next = next; + if (InterlockedCompareExchangePointer( (void **)&fileio_freelist, io, next ) == next) return; + } +} + +static struct async_fileio *alloc_fileio( DWORD size, async_callback_t callback, HANDLE handle ) +{ + /* first free remaining previous fileinfos */ + struct async_fileio *io = InterlockedExchangePointer( (void **)&fileio_freelist, NULL ); + + while (io) + { + struct async_fileio *next = io->next; + RtlFreeHeap( GetProcessHeap(), 0, io ); + io = next; + } + + if ((io = RtlAllocateHeap( GetProcessHeap(), 0, size ))) + { + io->callback = callback; + io->handle = handle; + } + return io; +} + +static async_data_t server_async( HANDLE handle, struct async_fileio *user, HANDLE event, + PIO_APC_ROUTINE apc, void *apc_context, IO_STATUS_BLOCK *io ) +{ + async_data_t async; + async.handle = wine_server_obj_handle( handle ); + async.user = wine_server_client_ptr( user ); + async.iosb = wine_server_client_ptr( io ); + async.event = wine_server_obj_handle( event ); + async.apc = wine_server_client_ptr( apc ); + async.apc_context = wine_server_client_ptr( apc_context ); + return async; +} + +static NTSTATUS wait_async( HANDLE handle, BOOL alertable, IO_STATUS_BLOCK *io ) +{ + if (NtWaitForSingleObject( handle, alertable, NULL )) return STATUS_PENDING; + return io->u.Status; +} + +/* callback for irp async I/O completion */ +static NTSTATUS irp_completion( void *user, IO_STATUS_BLOCK *io, NTSTATUS status ) +{ + struct async_irp *async = user; + ULONG information = 0; + + if (status == STATUS_ALERTED) + { + SERVER_START_REQ( get_async_result ) + { + req->user_arg = wine_server_client_ptr( async ); + wine_server_set_reply( req, async->buffer, async->size ); + status = virtual_locked_server_call( req ); + information = reply->size; + } + SERVER_END_REQ; + } + if (status != STATUS_PENDING) + { + io->u.Status = status; + io->Information = information; + release_fileio( &async->io ); + } + return status; +} + +static NTSTATUS async_read_proc( void *user, IO_STATUS_BLOCK *iosb, NTSTATUS status ) +{ + struct async_fileio_read *fileio = user; + int fd, needs_close, result; + + switch (status) + { + case STATUS_ALERTED: /* got some new data */ + /* check to see if the data is ready (non-blocking) */ + if ((status = server_get_unix_fd( fileio->io.handle, FILE_READ_DATA, &fd, + &needs_close, NULL, NULL ))) + break; + + result = virtual_locked_read(fd, &fileio->buffer[fileio->already], fileio->count-fileio->already); + if (needs_close) close( fd ); + + if (result < 0) + { + if (errno == EAGAIN || errno == EINTR) + status = STATUS_PENDING; + else /* check to see if the transfer is complete */ + status = errno_to_status( errno ); + } + else if (result == 0) + { + status = fileio->already ? STATUS_SUCCESS : STATUS_PIPE_BROKEN; + } + else + { + fileio->already += result; + if (fileio->already >= fileio->count || fileio->avail_mode) + status = STATUS_SUCCESS; + else + status = STATUS_PENDING; + } + break; + + case STATUS_TIMEOUT: + case STATUS_IO_TIMEOUT: + if (fileio->already) status = STATUS_SUCCESS; + break; + } + if (status != STATUS_PENDING) + { + iosb->u.Status = status; + iosb->Information = fileio->already; + release_fileio( &fileio->io ); + } + return status; +} + +static NTSTATUS async_write_proc( void *user, IO_STATUS_BLOCK *iosb, NTSTATUS status ) +{ + struct async_fileio_write *fileio = user; + int result, fd, needs_close; + enum server_fd_type type; + + switch (status) + { + case STATUS_ALERTED: + /* write some data (non-blocking) */ + if ((status = server_get_unix_fd( fileio->io.handle, FILE_WRITE_DATA, &fd, + &needs_close, &type, NULL ))) + break; + + if (!fileio->count && (type == FD_TYPE_MAILSLOT || type == FD_TYPE_SOCKET)) + result = send( fd, fileio->buffer, 0, 0 ); + else + result = write( fd, &fileio->buffer[fileio->already], fileio->count - fileio->already ); + + if (needs_close) close( fd ); + + if (result < 0) + { + if (errno == EAGAIN || errno == EINTR) status = STATUS_PENDING; + else status = errno_to_status( errno ); + } + else + { + fileio->already += result; + status = (fileio->already < fileio->count) ? STATUS_PENDING : STATUS_SUCCESS; + } + break; + + case STATUS_TIMEOUT: + case STATUS_IO_TIMEOUT: + if (fileio->already) status = STATUS_SUCCESS; + break; + } + if (status != STATUS_PENDING) + { + iosb->u.Status = status; + iosb->Information = fileio->already; + release_fileio( &fileio->io ); + } + return status; +} + +/* do a read call through the server */ +static NTSTATUS server_read_file( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_context, + IO_STATUS_BLOCK *io, void *buffer, ULONG size, + LARGE_INTEGER *offset, ULONG *key ) +{ + struct async_irp *async; + NTSTATUS status; + HANDLE wait_handle; + ULONG options; + + if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle ))) + return STATUS_NO_MEMORY; + + async->buffer = buffer; + async->size = size; + + SERVER_START_REQ( read ) + { + req->async = server_async( handle, &async->io, event, apc, apc_context, io ); + req->pos = offset ? offset->QuadPart : 0; + wine_server_set_reply( req, buffer, size ); + status = virtual_locked_server_call( req ); + wait_handle = wine_server_ptr_handle( reply->wait ); + options = reply->options; + if (wait_handle && status != STATUS_PENDING) + { + io->u.Status = status; + io->Information = wine_server_reply_size( reply ); + } + } + SERVER_END_REQ; + + if (status != STATUS_PENDING) RtlFreeHeap( GetProcessHeap(), 0, async ); + + if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT), io ); + return status; +} + +/* do a write call through the server */ +static NTSTATUS server_write_file( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_context, + IO_STATUS_BLOCK *io, const void *buffer, ULONG size, + LARGE_INTEGER *offset, ULONG *key ) +{ + struct async_irp *async; + NTSTATUS status; + HANDLE wait_handle; + ULONG options; + + if (!(async = (struct async_irp *)alloc_fileio( sizeof(*async), irp_completion, handle ))) + return STATUS_NO_MEMORY; + + async->buffer = NULL; + async->size = 0; + + SERVER_START_REQ( write ) + { + req->async = server_async( handle, &async->io, event, apc, apc_context, io ); + req->pos = offset ? offset->QuadPart : 0; + wine_server_add_data( req, buffer, size ); + status = wine_server_call( req ); + wait_handle = wine_server_ptr_handle( reply->wait ); + options = reply->options; + if (wait_handle && status != STATUS_PENDING) + { + io->u.Status = status; + io->Information = reply->size; + } + } + SERVER_END_REQ; + + if (status != STATUS_PENDING) RtlFreeHeap( GetProcessHeap(), 0, async ); + + if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT), io ); + return status; +} + + +struct io_timeouts +{ + int interval; /* max interval between two bytes */ + int total; /* total timeout for the whole operation */ + int end_time; /* absolute time of end of operation */ +}; + +/* retrieve the I/O timeouts to use for a given handle */ +static NTSTATUS get_io_timeouts( HANDLE handle, enum server_fd_type type, ULONG count, BOOL is_read, + struct io_timeouts *timeouts ) +{ + NTSTATUS status = STATUS_SUCCESS; + + timeouts->interval = timeouts->total = -1; + + switch(type) + { + case FD_TYPE_SERIAL: + { + /* GetCommTimeouts */ + SERIAL_TIMEOUTS st; + IO_STATUS_BLOCK io; + + status = NtDeviceIoControlFile( handle, NULL, NULL, NULL, &io, + IOCTL_SERIAL_GET_TIMEOUTS, NULL, 0, &st, sizeof(st) ); + if (status) break; + + if (is_read) + { + if (st.ReadIntervalTimeout) + timeouts->interval = st.ReadIntervalTimeout; + + if (st.ReadTotalTimeoutMultiplier || st.ReadTotalTimeoutConstant) + { + timeouts->total = st.ReadTotalTimeoutConstant; + if (st.ReadTotalTimeoutMultiplier != MAXDWORD) + timeouts->total += count * st.ReadTotalTimeoutMultiplier; + } + else if (st.ReadIntervalTimeout == MAXDWORD) + timeouts->interval = timeouts->total = 0; + } + else /* write */ + { + if (st.WriteTotalTimeoutMultiplier || st.WriteTotalTimeoutConstant) + { + timeouts->total = st.WriteTotalTimeoutConstant; + if (st.WriteTotalTimeoutMultiplier != MAXDWORD) + timeouts->total += count * st.WriteTotalTimeoutMultiplier; + } + } + break; + } + case FD_TYPE_MAILSLOT: + if (is_read) + { + timeouts->interval = 0; /* return as soon as we got something */ + SERVER_START_REQ( set_mailslot_info ) + { + req->handle = wine_server_obj_handle( handle ); + req->flags = 0; + if (!(status = wine_server_call( req )) && + reply->read_timeout != TIMEOUT_INFINITE) + timeouts->total = reply->read_timeout / -10000; + } + SERVER_END_REQ; + } + break; + case FD_TYPE_SOCKET: + case FD_TYPE_CHAR: + if (is_read) timeouts->interval = 0; /* return as soon as we got something */ + break; + default: + break; + } + if (timeouts->total != -1) timeouts->end_time = NtGetTickCount() + timeouts->total; + return STATUS_SUCCESS; +} + + +/* retrieve the timeout for the next wait, in milliseconds */ +static inline int get_next_io_timeout( const struct io_timeouts *timeouts, ULONG already ) +{ + int ret = -1; + + if (timeouts->total != -1) + { + ret = timeouts->end_time - NtGetTickCount(); + if (ret < 0) ret = 0; + } + if (already && timeouts->interval != -1) + { + if (ret == -1 || ret > timeouts->interval) ret = timeouts->interval; + } + return ret; +} + + +/* retrieve the avail_mode flag for async reads */ +static NTSTATUS get_io_avail_mode( HANDLE handle, enum server_fd_type type, BOOL *avail_mode ) +{ + NTSTATUS status = STATUS_SUCCESS; + + switch(type) + { + case FD_TYPE_SERIAL: + { + /* GetCommTimeouts */ + SERIAL_TIMEOUTS st; + IO_STATUS_BLOCK io; + + status = NtDeviceIoControlFile( handle, NULL, NULL, NULL, &io, + IOCTL_SERIAL_GET_TIMEOUTS, NULL, 0, &st, sizeof(st) ); + if (status) break; + *avail_mode = (!st.ReadTotalTimeoutMultiplier && + !st.ReadTotalTimeoutConstant && + st.ReadIntervalTimeout == MAXDWORD); + break; + } + case FD_TYPE_MAILSLOT: + case FD_TYPE_SOCKET: + case FD_TYPE_CHAR: + *avail_mode = TRUE; + break; + default: + *avail_mode = FALSE; + break; + } + return status; +} + +/* register an async I/O for a file read; helper for NtReadFile */ +static NTSTATUS register_async_file_read( HANDLE handle, HANDLE event, + PIO_APC_ROUTINE apc, void *apc_user, + IO_STATUS_BLOCK *iosb, void *buffer, + ULONG already, ULONG length, BOOL avail_mode ) +{ + struct async_fileio_read *fileio; + NTSTATUS status; + + if (!(fileio = (struct async_fileio_read *)alloc_fileio( sizeof(*fileio), async_read_proc, handle ))) + return STATUS_NO_MEMORY; + + fileio->already = already; + fileio->count = length; + fileio->buffer = buffer; + fileio->avail_mode = avail_mode; + + SERVER_START_REQ( register_async ) + { + req->type = ASYNC_TYPE_READ; + req->count = length; + req->async = server_async( handle, &fileio->io, event, apc, apc_user, iosb ); + status = wine_server_call( req ); + } + SERVER_END_REQ; + + if (status != STATUS_PENDING) RtlFreeHeap( GetProcessHeap(), 0, fileio ); + return status; +} + +static void add_completion( HANDLE handle, ULONG_PTR value, NTSTATUS status, ULONG info, BOOL async ) +{ + SERVER_START_REQ( add_fd_completion ) + { + req->handle = wine_server_obj_handle( handle ); + req->cvalue = value; + req->status = status; + req->information = info; + req->async = async; + wine_server_call( req ); + } + SERVER_END_REQ; +} + +static NTSTATUS set_pending_write( HANDLE device ) +{ + NTSTATUS status; + + SERVER_START_REQ( set_serial_info ) + { + req->handle = wine_server_obj_handle( device ); + req->flags = SERIALINFO_PENDING_WRITE; + status = wine_server_call( req ); + } + SERVER_END_REQ; + return status; +} + + +/****************************************************************************** + * NtReadFile (NTDLL.@) + */ +NTSTATUS WINAPI NtReadFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user, + IO_STATUS_BLOCK *io, void *buffer, ULONG length, + LARGE_INTEGER *offset, ULONG *key ) +{ + int result, unix_handle, needs_close; + unsigned int options; + struct io_timeouts timeouts; + NTSTATUS status, ret_status; + ULONG total = 0; + enum server_fd_type type; + ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user; + BOOL send_completion = FALSE, async_read, timeout_init_done = FALSE; + + TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p)\n", + handle, event, apc, apc_user, io, buffer, length, offset, key ); + + if (!io) return STATUS_ACCESS_VIOLATION; + + status = server_get_unix_fd( handle, FILE_READ_DATA, &unix_handle, &needs_close, &type, &options ); + if (status && status != STATUS_BAD_DEVICE_TYPE) return status; + + if (!virtual_check_buffer_for_write( buffer, length )) return STATUS_ACCESS_VIOLATION; + + if (status == STATUS_BAD_DEVICE_TYPE) + return server_read_file( handle, event, apc, apc_user, io, buffer, length, offset, key ); + + async_read = !(options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)); + + if (type == FD_TYPE_FILE) + { + if (async_read && (!offset || offset->QuadPart < 0)) + { + status = STATUS_INVALID_PARAMETER; + goto done; + } + + if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION) + { + /* async I/O doesn't make sense on regular files */ + while ((result = virtual_locked_pread( unix_handle, buffer, length, offset->QuadPart )) == -1) + { + if (errno != EINTR) + { + status = errno_to_status( errno ); + goto done; + } + } + if (!async_read) /* update file pointer position */ + lseek( unix_handle, offset->QuadPart + result, SEEK_SET ); + + total = result; + status = (total || !length) ? STATUS_SUCCESS : STATUS_END_OF_FILE; + goto done; + } + } + else if (type == FD_TYPE_SERIAL || type == FD_TYPE_DEVICE) + { + if (async_read && (!offset || offset->QuadPart < 0)) + { + status = STATUS_INVALID_PARAMETER; + goto done; + } + } + + if (type == FD_TYPE_SERIAL && async_read && length) + { + /* an asynchronous serial port read with a read interval timeout needs to + skip the synchronous read to make sure that the server starts the read + interval timer after the first read */ + if ((status = get_io_timeouts( handle, type, length, TRUE, &timeouts ))) goto err; + if (timeouts.interval) + { + status = register_async_file_read( handle, event, apc, apc_user, io, + buffer, total, length, FALSE ); + goto err; + } + } + + for (;;) + { + if ((result = virtual_locked_read( unix_handle, (char *)buffer + total, length - total )) >= 0) + { + total += result; + if (!result || total == length) + { + if (total) + { + status = STATUS_SUCCESS; + goto done; + } + switch (type) + { + case FD_TYPE_FILE: + case FD_TYPE_CHAR: + case FD_TYPE_DEVICE: + status = length ? STATUS_END_OF_FILE : STATUS_SUCCESS; + goto done; + case FD_TYPE_SERIAL: + if (!length) + { + status = STATUS_SUCCESS; + goto done; + } + break; + default: + status = STATUS_PIPE_BROKEN; + goto err; + } + } + else if (type == FD_TYPE_FILE) continue; /* no async I/O on regular files */ + } + else if (errno != EAGAIN) + { + if (errno == EINTR) continue; + if (!total) status = errno_to_status( errno ); + goto err; + } + + if (async_read) + { + BOOL avail_mode; + + if ((status = get_io_avail_mode( handle, type, &avail_mode ))) goto err; + if (total && avail_mode) + { + status = STATUS_SUCCESS; + goto done; + } + status = register_async_file_read( handle, event, apc, apc_user, io, + buffer, total, length, avail_mode ); + goto err; + } + else /* synchronous read, wait for the fd to become ready */ + { + struct pollfd pfd; + int ret, timeout; + + if (!timeout_init_done) + { + timeout_init_done = TRUE; + if ((status = get_io_timeouts( handle, type, length, TRUE, &timeouts ))) goto err; + if (event) NtResetEvent( event, NULL ); + } + timeout = get_next_io_timeout( &timeouts, total ); + + pfd.fd = unix_handle; + pfd.events = POLLIN; + + if (!timeout || !(ret = poll( &pfd, 1, timeout ))) + { + if (total) /* return with what we got so far */ + status = STATUS_SUCCESS; + else + status = (type == FD_TYPE_MAILSLOT) ? STATUS_IO_TIMEOUT : STATUS_TIMEOUT; + goto done; + } + if (ret == -1 && errno != EINTR) + { + status = errno_to_status( errno ); + goto done; + } + /* will now restart the read */ + } + } + +done: + send_completion = cvalue != 0; + +err: + if (needs_close) close( unix_handle ); + if (status == STATUS_SUCCESS || (status == STATUS_END_OF_FILE && (!async_read || type == FD_TYPE_FILE))) + { + io->u.Status = status; + io->Information = total; + TRACE("= SUCCESS (%u)\n", total); + if (event) NtSetEvent( event, NULL ); + if (apc && (!status || async_read)) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc, + (ULONG_PTR)apc_user, (ULONG_PTR)io, 0 ); + } + else + { + TRACE("= 0x%08x\n", status); + if (status != STATUS_PENDING && event) NtResetEvent( event, NULL ); + } + + ret_status = async_read && type == FD_TYPE_FILE && (status == STATUS_SUCCESS || status == STATUS_END_OF_FILE) + ? STATUS_PENDING : status; + + if (send_completion) add_completion( handle, cvalue, status, total, ret_status == STATUS_PENDING ); + return ret_status; +} + + +/****************************************************************************** + * NtReadFileScatter (NTDLL.@) + */ +NTSTATUS WINAPI NtReadFileScatter( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user, + IO_STATUS_BLOCK *io, FILE_SEGMENT_ELEMENT *segments, + ULONG length, LARGE_INTEGER *offset, ULONG *key ) +{ + int result, unix_handle, needs_close; + unsigned int options; + NTSTATUS status; + ULONG pos = 0, total = 0; + enum server_fd_type type; + ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user; + BOOL send_completion = FALSE; + + TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p),partial stub!\n", + file, event, apc, apc_user, io, segments, length, offset, key ); + + if (!io) return STATUS_ACCESS_VIOLATION; + + status = server_get_unix_fd( file, FILE_READ_DATA, &unix_handle, &needs_close, &type, &options ); + if (status) return status; + + if ((type != FD_TYPE_FILE) || + (options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)) || + !(options & FILE_NO_INTERMEDIATE_BUFFERING)) + { + status = STATUS_INVALID_PARAMETER; + goto error; + } + + while (length) + { + if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION) + result = pread( unix_handle, (char *)segments->Buffer + pos, + min( length - pos, page_size - pos ), offset->QuadPart + total ); + else + result = read( unix_handle, (char *)segments->Buffer + pos, min( length - pos, page_size - pos ) ); + + if (result == -1) + { + if (errno == EINTR) continue; + status = errno_to_status( errno ); + break; + } + if (!result) break; + total += result; + length -= result; + if ((pos += result) == page_size) + { + pos = 0; + segments++; + } + } + + if (total == 0) status = STATUS_END_OF_FILE; + + send_completion = cvalue != 0; + + if (needs_close) close( unix_handle ); + io->u.Status = status; + io->Information = total; + TRACE("= 0x%08x (%u)\n", status, total); + if (event) NtSetEvent( event, NULL ); + if (apc) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc, + (ULONG_PTR)apc_user, (ULONG_PTR)io, 0 ); + if (send_completion) add_completion( file, cvalue, status, total, TRUE ); + + return STATUS_PENDING; + +error: + if (needs_close) close( unix_handle ); + if (event) NtResetEvent( event, NULL ); + TRACE("= 0x%08x\n", status); + return status; +} + + +/****************************************************************************** + * NtWriteFile (NTDLL.@) + */ +NTSTATUS WINAPI NtWriteFile( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user, + IO_STATUS_BLOCK *io, const void *buffer, ULONG length, + LARGE_INTEGER *offset, ULONG *key ) +{ + int result, unix_handle, needs_close; + unsigned int options; + struct io_timeouts timeouts; + NTSTATUS status, ret_status; + ULONG total = 0; + enum server_fd_type type; + ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user; + BOOL send_completion = FALSE, async_write, append_write = FALSE, timeout_init_done = FALSE; + LARGE_INTEGER offset_eof; + + TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p)\n", + handle, event, apc, apc_user, io, buffer, length, offset, key ); + + if (!io) return STATUS_ACCESS_VIOLATION; + + status = server_get_unix_fd( handle, FILE_WRITE_DATA, &unix_handle, &needs_close, &type, &options ); + if (status == STATUS_ACCESS_DENIED) + { + status = server_get_unix_fd( handle, FILE_APPEND_DATA, &unix_handle, + &needs_close, &type, &options ); + append_write = TRUE; + } + if (status && status != STATUS_BAD_DEVICE_TYPE) return status; + + async_write = !(options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)); + + if (!virtual_check_buffer_for_read( buffer, length )) + { + status = STATUS_INVALID_USER_BUFFER; + goto done; + } + + if (status == STATUS_BAD_DEVICE_TYPE) + return server_write_file( handle, event, apc, apc_user, io, buffer, length, offset, key ); + + if (type == FD_TYPE_FILE) + { + if (async_write && + (!offset || (offset->QuadPart < 0 && offset->QuadPart != FILE_WRITE_TO_END_OF_FILE))) + { + status = STATUS_INVALID_PARAMETER; + goto done; + } + + if (append_write) + { + offset_eof.QuadPart = FILE_WRITE_TO_END_OF_FILE; + offset = &offset_eof; + } + + if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION) + { + off_t off = offset->QuadPart; + + if (offset->QuadPart == FILE_WRITE_TO_END_OF_FILE) + { + struct stat st; + + if (fstat( unix_handle, &st ) == -1) + { + status = errno_to_status( errno ); + goto done; + } + off = st.st_size; + } + else if (offset->QuadPart < 0) + { + status = STATUS_INVALID_PARAMETER; + goto done; + } + + /* async I/O doesn't make sense on regular files */ + while ((result = pwrite( unix_handle, buffer, length, off )) == -1) + { + if (errno != EINTR) + { + if (errno == EFAULT) status = STATUS_INVALID_USER_BUFFER; + else status = errno_to_status( errno ); + goto done; + } + } + + if (!async_write) /* update file pointer position */ + lseek( unix_handle, off + result, SEEK_SET ); + + total = result; + status = STATUS_SUCCESS; + goto done; + } + } + else if (type == FD_TYPE_SERIAL || type == FD_TYPE_DEVICE) + { + if (async_write && + (!offset || (offset->QuadPart < 0 && offset->QuadPart != FILE_WRITE_TO_END_OF_FILE))) + { + status = STATUS_INVALID_PARAMETER; + goto done; + } + } + + for (;;) + { + /* zero-length writes on sockets may not work with plain write(2) */ + if (!length && (type == FD_TYPE_MAILSLOT || type == FD_TYPE_SOCKET)) + result = send( unix_handle, buffer, 0, 0 ); + else + result = write( unix_handle, (const char *)buffer + total, length - total ); + + if (result >= 0) + { + total += result; + if (total == length) + { + status = STATUS_SUCCESS; + goto done; + } + if (type == FD_TYPE_FILE) continue; /* no async I/O on regular files */ + } + else if (errno != EAGAIN) + { + if (errno == EINTR) continue; + if (!total) + { + if (errno == EFAULT) status = STATUS_INVALID_USER_BUFFER; + else status = errno_to_status( errno ); + } + goto err; + } + + if (async_write) + { + struct async_fileio_write *fileio; + + fileio = (struct async_fileio_write *)alloc_fileio( sizeof(*fileio), async_write_proc, handle ); + if (!fileio) + { + status = STATUS_NO_MEMORY; + goto err; + } + fileio->already = total; + fileio->count = length; + fileio->buffer = buffer; + + SERVER_START_REQ( register_async ) + { + req->type = ASYNC_TYPE_WRITE; + req->count = length; + req->async = server_async( handle, &fileio->io, event, apc, apc_user, io ); + status = wine_server_call( req ); + } + SERVER_END_REQ; + + if (status != STATUS_PENDING) RtlFreeHeap( GetProcessHeap(), 0, fileio ); + goto err; + } + else /* synchronous write, wait for the fd to become ready */ + { + struct pollfd pfd; + int ret, timeout; + + if (!timeout_init_done) + { + timeout_init_done = TRUE; + if ((status = get_io_timeouts( handle, type, length, FALSE, &timeouts ))) + goto err; + if (event) NtResetEvent( event, NULL ); + } + timeout = get_next_io_timeout( &timeouts, total ); + + pfd.fd = unix_handle; + pfd.events = POLLOUT; + + if (!timeout || !(ret = poll( &pfd, 1, timeout ))) + { + /* return with what we got so far */ + status = total ? STATUS_SUCCESS : STATUS_TIMEOUT; + goto done; + } + if (ret == -1 && errno != EINTR) + { + status = errno_to_status( errno ); + goto done; + } + /* will now restart the write */ + } + } + +done: + send_completion = cvalue != 0; + +err: + if (needs_close) close( unix_handle ); + + if (type == FD_TYPE_SERIAL && (status == STATUS_SUCCESS || status == STATUS_PENDING)) + set_pending_write( handle ); + + if (status == STATUS_SUCCESS) + { + io->u.Status = status; + io->Information = total; + TRACE("= SUCCESS (%u)\n", total); + if (event) NtSetEvent( event, NULL ); + if (apc) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc, + (ULONG_PTR)apc_user, (ULONG_PTR)io, 0 ); + } + else + { + TRACE("= 0x%08x\n", status); + if (status != STATUS_PENDING && event) NtResetEvent( event, NULL ); + } + + ret_status = async_write && type == FD_TYPE_FILE && status == STATUS_SUCCESS ? STATUS_PENDING : status; + if (send_completion) add_completion( handle, cvalue, status, total, ret_status == STATUS_PENDING ); + return ret_status; +} + + +/****************************************************************************** + * NtWriteFileGather (NTDLL.@) + */ +NTSTATUS WINAPI NtWriteFileGather( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user, + IO_STATUS_BLOCK *io, FILE_SEGMENT_ELEMENT *segments, + ULONG length, LARGE_INTEGER *offset, ULONG *key ) +{ + int result, unix_handle, needs_close; + unsigned int options; + NTSTATUS status; + ULONG pos = 0, total = 0; + enum server_fd_type type; + ULONG_PTR cvalue = apc ? 0 : (ULONG_PTR)apc_user; + BOOL send_completion = FALSE; + + TRACE( "(%p,%p,%p,%p,%p,%p,0x%08x,%p,%p),partial stub!\n", + file, event, apc, apc_user, io, segments, length, offset, key ); + + if (length % page_size) return STATUS_INVALID_PARAMETER; + if (!io) return STATUS_ACCESS_VIOLATION; + + status = server_get_unix_fd( file, FILE_WRITE_DATA, &unix_handle, &needs_close, &type, &options ); + if (status) return status; + + if ((type != FD_TYPE_FILE) || + (options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)) || + !(options & FILE_NO_INTERMEDIATE_BUFFERING)) + { + status = STATUS_INVALID_PARAMETER; + goto done; + } + + while (length) + { + if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION) + result = pwrite( unix_handle, (char *)segments->Buffer + pos, + page_size - pos, offset->QuadPart + total ); + else + result = write( unix_handle, (char *)segments->Buffer + pos, page_size - pos ); + + if (result == -1) + { + if (errno == EINTR) continue; + if (errno == EFAULT) + { + status = STATUS_INVALID_USER_BUFFER; + goto done; + } + status = errno_to_status( errno ); + break; + } + if (!result) + { + status = STATUS_DISK_FULL; + break; + } + total += result; + length -= result; + if ((pos += result) == page_size) + { + pos = 0; + segments++; + } + } + + send_completion = cvalue != 0; + + done: + if (needs_close) close( unix_handle ); + if (status == STATUS_SUCCESS) + { + io->u.Status = status; + io->Information = total; + TRACE("= SUCCESS (%u)\n", total); + if (event) NtSetEvent( event, NULL ); + if (apc) NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)apc, + (ULONG_PTR)apc_user, (ULONG_PTR)io, 0 ); + } + else + { + TRACE("= 0x%08x\n", status); + if (status != STATUS_PENDING && event) NtResetEvent( event, NULL ); + } + if (send_completion) add_completion( file, cvalue, status, total, FALSE ); + return status; +} diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index 178f8e18d1b..474064f629e 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -878,6 +878,8 @@ static struct unix_funcs unix_funcs = NtQueryVirtualMemory, NtQueueApcThread, NtRaiseException, + NtReadFile, + NtReadFileScatter, NtReadVirtualMemory, NtReleaseKeyedEvent, NtReleaseMutant, @@ -906,6 +908,8 @@ static struct unix_funcs unix_funcs = NtWaitForKeyedEvent, NtWaitForMultipleObjects, NtWaitForSingleObject, + NtWriteFile, + NtWriteFileGather, NtWriteVirtualMemory, NtYieldExecution, DbgUiIssueRemoteBreakin, @@ -939,10 +943,7 @@ static struct unix_funcs unix_funcs = virtual_create_builtin_view, virtual_alloc_thread_stack, virtual_locked_server_call, - virtual_locked_read, - virtual_locked_pread, virtual_locked_recvmsg, - virtual_check_buffer_for_read, virtual_check_buffer_for_write, virtual_release_address_space, virtual_set_large_address_space, diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index 6eab6052bf9..fdd0261ed4a 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -54,6 +54,8 @@ static inline struct ntdll_thread_data *ntdll_get_thread_data(void) return (struct ntdll_thread_data *)&NtCurrentTeb()->GdiTebBatch; } +static const UINT_PTR page_size = 0x1000; + NTSTATUS WINAPI KiUserExceptionDispatcher(EXCEPTION_RECORD*,CONTEXT*); void WINAPI LdrInitializeThunk(CONTEXT*,void**,ULONG_PTR,ULONG_PTR); @@ -89,10 +91,7 @@ extern void CDECL virtual_get_system_info( SYSTEM_BASIC_INFORMATION *info ) DECL extern NTSTATUS CDECL virtual_create_builtin_view( void *module ) DECLSPEC_HIDDEN; extern NTSTATUS CDECL virtual_alloc_thread_stack( INITIAL_TEB *stack, SIZE_T reserve_size, SIZE_T commit_size, SIZE_T *pthread_size ) DECLSPEC_HIDDEN; extern unsigned int CDECL virtual_locked_server_call( void *req_ptr ) DECLSPEC_HIDDEN; -extern ssize_t CDECL virtual_locked_read( int fd, void *addr, size_t size ) DECLSPEC_HIDDEN; -extern ssize_t CDECL virtual_locked_pread( int fd, void *addr, size_t size, off_t offset ) DECLSPEC_HIDDEN; extern ssize_t CDECL virtual_locked_recvmsg( int fd, struct msghdr *hdr, int flags ) DECLSPEC_HIDDEN; -extern BOOL CDECL virtual_check_buffer_for_read( const void *ptr, SIZE_T size ) DECLSPEC_HIDDEN; extern BOOL CDECL virtual_check_buffer_for_write( void *ptr, SIZE_T size ) DECLSPEC_HIDDEN; extern void CDECL virtual_release_address_space(void) DECLSPEC_HIDDEN; extern void CDECL virtual_set_large_address_space(void) DECLSPEC_HIDDEN; @@ -174,8 +173,11 @@ extern NTSTATUS virtual_alloc_teb( TEB **ret_teb ) DECLSPEC_HIDDEN; extern void virtual_free_teb( TEB *teb ) DECLSPEC_HIDDEN; extern void virtual_map_user_shared_data(void) DECLSPEC_HIDDEN; extern NTSTATUS virtual_handle_fault( LPCVOID addr, DWORD err, BOOL on_signal_stack ) DECLSPEC_HIDDEN; +extern ssize_t virtual_locked_read( int fd, void *addr, size_t size ) DECLSPEC_HIDDEN; +extern ssize_t virtual_locked_pread( int fd, void *addr, size_t size, off_t offset ) DECLSPEC_HIDDEN; extern BOOL virtual_is_valid_code_address( const void *addr, SIZE_T size ) DECLSPEC_HIDDEN; extern int virtual_handle_stack_fault( void *addr ) DECLSPEC_HIDDEN; +extern BOOL virtual_check_buffer_for_read( const void *ptr, SIZE_T size ) DECLSPEC_HIDDEN; extern SIZE_T virtual_uninterrupted_read_memory( const void *addr, void *buffer, SIZE_T size ) DECLSPEC_HIDDEN; extern NTSTATUS virtual_uninterrupted_write_memory( void *addr, const void *buffer, SIZE_T size ) DECLSPEC_HIDDEN; extern void virtual_set_force_exec( BOOL enable ) DECLSPEC_HIDDEN; diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 22cf724b819..af44b9c2a27 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -134,7 +134,6 @@ static RTL_CRITICAL_SECTION csVirtual = { &critsect_debug, -1, 0, 0, 0, 0 }; static const BOOL is_win64 = (sizeof(void *) > sizeof(int)); static const UINT page_shift = 12; -static const UINT_PTR page_size = 0x1000; static const UINT_PTR page_mask = 0xfff; static const UINT_PTR granularity_mask = 0xffff; @@ -2866,7 +2865,7 @@ unsigned int CDECL virtual_locked_server_call( void *req_ptr ) /*********************************************************************** * virtual_locked_read */ -ssize_t CDECL virtual_locked_read( int fd, void *addr, size_t size ) +ssize_t virtual_locked_read( int fd, void *addr, size_t size ) { sigset_t sigset; BOOL has_write_watch = FALSE; @@ -2891,7 +2890,7 @@ ssize_t CDECL virtual_locked_read( int fd, void *addr, size_t size ) /*********************************************************************** * virtual_locked_pread */ -ssize_t CDECL virtual_locked_pread( int fd, void *addr, size_t size, off_t offset ) +ssize_t virtual_locked_pread( int fd, void *addr, size_t size, off_t offset ) { sigset_t sigset; BOOL has_write_watch = FALSE; @@ -3007,7 +3006,7 @@ int virtual_handle_stack_fault( void *addr ) * * Check if a memory buffer can be read, triggering page faults if needed for DIB section access. */ -BOOL CDECL virtual_check_buffer_for_read( const void *ptr, SIZE_T size ) +BOOL virtual_check_buffer_for_read( const void *ptr, SIZE_T size ) { if (!size) return TRUE; if (!ptr) return FALSE; diff --git a/dlls/ntdll/unixlib.h b/dlls/ntdll/unixlib.h index 15b44b6735e..0bc1d599a3b 100644 --- a/dlls/ntdll/unixlib.h +++ b/dlls/ntdll/unixlib.h @@ -28,7 +28,7 @@ struct ldt_copy; struct msghdr; /* increment this when you change the function table */ -#define NTDLL_UNIXLIB_VERSION 48 +#define NTDLL_UNIXLIB_VERSION 49 struct unix_funcs { @@ -159,6 +159,13 @@ struct unix_funcs NTSTATUS (WINAPI *NtQueueApcThread)( HANDLE handle, PNTAPCFUNC func, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3 ); NTSTATUS (WINAPI *NtRaiseException)( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_chance ); + NTSTATUS (WINAPI *NtReadFile)( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user, + IO_STATUS_BLOCK *io, void *buffer, ULONG length, + LARGE_INTEGER *offset, ULONG *key ); + NTSTATUS (WINAPI *NtReadFileScatter)( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, + void *apc_user, IO_STATUS_BLOCK *io, + FILE_SEGMENT_ELEMENT *segments, ULONG length, + LARGE_INTEGER *offset, ULONG *key ); NTSTATUS (WINAPI *NtReadVirtualMemory)( HANDLE process, const void *addr, void *buffer, SIZE_T size, SIZE_T *bytes_read ); NTSTATUS (WINAPI *NtReleaseKeyedEvent)( HANDLE handle, const void *key, @@ -204,6 +211,13 @@ struct unix_funcs const LARGE_INTEGER *timeout ); NTSTATUS (WINAPI *NtWaitForSingleObject)( HANDLE handle, BOOLEAN alertable, const LARGE_INTEGER *timeout ); + NTSTATUS (WINAPI *NtWriteFile)( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user, + IO_STATUS_BLOCK *io, const void *buffer, ULONG length, + LARGE_INTEGER *offset, ULONG *key ); + NTSTATUS (WINAPI *NtWriteFileGather)( HANDLE file, HANDLE event, PIO_APC_ROUTINE apc, + void *apc_user, IO_STATUS_BLOCK *io, + FILE_SEGMENT_ELEMENT *segments, ULONG length, + LARGE_INTEGER *offset, ULONG *key ); NTSTATUS (WINAPI *NtWriteVirtualMemory)( HANDLE process, void *addr, const void *buffer, SIZE_T size, SIZE_T *bytes_written ); NTSTATUS (WINAPI *NtYieldExecution)(void); @@ -253,10 +267,7 @@ struct unix_funcs NTSTATUS (CDECL *virtual_create_builtin_view)( void *module ); NTSTATUS (CDECL *virtual_alloc_thread_stack)( INITIAL_TEB *stack, SIZE_T reserve_size, SIZE_T commit_size, SIZE_T *pthread_size ); unsigned int (CDECL *virtual_locked_server_call)( void *req_ptr ); - ssize_t (CDECL *virtual_locked_read)( int fd, void *addr, size_t size ); - ssize_t (CDECL *virtual_locked_pread)( int fd, void *addr, size_t size, off_t offset ); ssize_t (CDECL *virtual_locked_recvmsg)( int fd, struct msghdr *hdr, int flags ); - BOOL (CDECL *virtual_check_buffer_for_read)( const void *ptr, SIZE_T size ); BOOL (CDECL *virtual_check_buffer_for_write)( void *ptr, SIZE_T size ); void (CDECL *virtual_release_address_space)(void); void (CDECL *virtual_set_large_address_space)(void);