- made async.h ready for use in ntdll: replaced all calls to kernel32
functions with ntdll equivalent - replaced status setter/getter for wine async structures with direct access to a (now included) IO_STATUS_BLOCK structure - since we now have a IO_STATUS_BLOCK in async_private, we no longer need in most of the user (derivated) structures a field for LPOVERLAPPED (it's stored as the IO_STATUS_BLOCK) - rewrote the async.h users accordingly - implemented ntdll.Nt{Read|Write}File and let kernel32.{Read|Write}File(Ex)? use those new ntdll functions - rewrote smb read/write interfaces to be more ntdll stylish (no overlapped yet)
This commit is contained in:
parent
6235e9359c
commit
9bd4f6bf15
|
@ -84,6 +84,8 @@
|
|||
# include <sys/strtio.h>
|
||||
#endif
|
||||
|
||||
#define NONAMELESSUNION
|
||||
#define NONAMELESSSTRUCT
|
||||
#include "winbase.h"
|
||||
#include "winerror.h"
|
||||
|
||||
|
@ -104,15 +106,11 @@ WINE_DEFAULT_DEBUG_CHANNEL(comm);
|
|||
* Asynchronous I/O for asynchronous wait requests *
|
||||
*/
|
||||
|
||||
static DWORD commio_get_async_status (const async_private *ovp);
|
||||
static DWORD commio_get_async_count (const async_private *ovp);
|
||||
static void commio_set_async_status (async_private *ovp, const DWORD status);
|
||||
static void commio_async_cleanup (async_private *ovp);
|
||||
|
||||
static async_ops commio_async_ops =
|
||||
{
|
||||
commio_get_async_status, /* get_status */
|
||||
commio_set_async_status, /* set_status */
|
||||
commio_get_async_count, /* get_count */
|
||||
NULL, /* call_completion */
|
||||
commio_async_cleanup /* cleanup */
|
||||
|
@ -121,20 +119,9 @@ static async_ops commio_async_ops =
|
|||
typedef struct async_commio
|
||||
{
|
||||
struct async_private async;
|
||||
LPOVERLAPPED lpOverlapped;
|
||||
char *buffer;
|
||||
} async_commio;
|
||||
|
||||
static DWORD commio_get_async_status (const struct async_private *ovp)
|
||||
{
|
||||
return ((async_commio*) ovp)->lpOverlapped->Internal;
|
||||
}
|
||||
|
||||
static void commio_set_async_status (async_private *ovp, const DWORD status)
|
||||
{
|
||||
((async_commio*) ovp)->lpOverlapped->Internal = status;
|
||||
}
|
||||
|
||||
static DWORD commio_get_async_count (const struct async_private *ovp)
|
||||
{
|
||||
return 0;
|
||||
|
@ -1664,14 +1651,14 @@ BOOL WINAPI GetCommModemStatus(
|
|||
static void COMM_WaitCommEventService(async_private *ovp)
|
||||
{
|
||||
async_commio *commio = (async_commio*) ovp;
|
||||
LPOVERLAPPED lpOverlapped = commio->lpOverlapped;
|
||||
IO_STATUS_BLOCK* iosb = commio->async.iosb;
|
||||
|
||||
TRACE("overlapped %p\n",lpOverlapped);
|
||||
TRACE("iosb %p\n",iosb);
|
||||
|
||||
/* FIXME: detect other events */
|
||||
*commio->buffer = EV_RXCHAR;
|
||||
|
||||
lpOverlapped->Internal = STATUS_SUCCESS;
|
||||
iosb->u.Status = STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1714,7 +1701,7 @@ static BOOL COMM_WaitCommEvent(
|
|||
ovp->async.type = ASYNC_TYPE_WAIT;
|
||||
ovp->async.func = COMM_WaitCommEventService;
|
||||
ovp->async.event = lpOverlapped->hEvent;
|
||||
ovp->lpOverlapped = lpOverlapped;
|
||||
ovp->async.iosb = (IO_STATUS_BLOCK*)lpOverlapped;
|
||||
ovp->buffer = (char *)lpdwEvents;
|
||||
|
||||
lpOverlapped->InternalHigh = 0;
|
||||
|
|
|
@ -32,11 +32,12 @@
|
|||
#include "windef.h"
|
||||
#include "winbase.h"
|
||||
#include "winternl.h"
|
||||
|
||||
#include "wincon.h"
|
||||
#include "kernel_private.h"
|
||||
|
||||
#include "wine/unicode.h"
|
||||
#include "wine/debug.h"
|
||||
#include "async.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(file);
|
||||
|
||||
|
@ -44,6 +45,106 @@ WINE_DEFAULT_DEBUG_CHANNEL(file);
|
|||
* Operations on file handles *
|
||||
**************************************************************************/
|
||||
|
||||
/***********************************************************************
|
||||
* GetOverlappedResult (KERNEL32.@)
|
||||
*
|
||||
* Check the result of an Asynchronous data transfer from a file.
|
||||
*
|
||||
* Parameters
|
||||
* HANDLE hFile [in] handle of file to check on
|
||||
* LPOVERLAPPED lpOverlapped [in/out] pointer to overlapped
|
||||
* LPDWORD lpTransferred [in/out] number of bytes transferred
|
||||
* BOOL bWait [in] wait for the transfer to complete ?
|
||||
*
|
||||
* RETURNS
|
||||
* TRUE on success
|
||||
* FALSE on failure
|
||||
*
|
||||
* If successful (and relevant) lpTransferred will hold the number of
|
||||
* bytes transferred during the async operation.
|
||||
*
|
||||
* BUGS
|
||||
*
|
||||
* Currently only works for WaitCommEvent, ReadFile, WriteFile
|
||||
* with communications ports.
|
||||
*
|
||||
*/
|
||||
BOOL WINAPI GetOverlappedResult(HANDLE hFile, LPOVERLAPPED lpOverlapped,
|
||||
LPDWORD lpTransferred, BOOL bWait)
|
||||
{
|
||||
DWORD r;
|
||||
|
||||
TRACE("(%p %p %p %x)\n", hFile, lpOverlapped, lpTransferred, bWait);
|
||||
|
||||
if (lpOverlapped==NULL)
|
||||
{
|
||||
ERR("lpOverlapped was null\n");
|
||||
return FALSE;
|
||||
}
|
||||
if (!lpOverlapped->hEvent)
|
||||
{
|
||||
ERR("lpOverlapped->hEvent was null\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ( bWait )
|
||||
{
|
||||
do {
|
||||
TRACE("waiting on %p\n",lpOverlapped);
|
||||
r = WaitForSingleObjectEx(lpOverlapped->hEvent, INFINITE, TRUE);
|
||||
TRACE("wait on %p returned %ld\n",lpOverlapped,r);
|
||||
} while (r==STATUS_USER_APC);
|
||||
}
|
||||
else if ( lpOverlapped->Internal == STATUS_PENDING )
|
||||
{
|
||||
/* Wait in order to give APCs a chance to run. */
|
||||
/* This is cheating, so we must set the event again in case of success -
|
||||
it may be a non-manual reset event. */
|
||||
do {
|
||||
TRACE("waiting on %p\n",lpOverlapped);
|
||||
r = WaitForSingleObjectEx(lpOverlapped->hEvent, 0, TRUE);
|
||||
TRACE("wait on %p returned %ld\n",lpOverlapped,r);
|
||||
} while (r==STATUS_USER_APC);
|
||||
if ( r == WAIT_OBJECT_0 )
|
||||
NtSetEvent ( lpOverlapped->hEvent, NULL );
|
||||
}
|
||||
|
||||
if(lpTransferred)
|
||||
*lpTransferred = lpOverlapped->InternalHigh;
|
||||
|
||||
switch ( lpOverlapped->Internal )
|
||||
{
|
||||
case STATUS_SUCCESS:
|
||||
return TRUE;
|
||||
case STATUS_PENDING:
|
||||
SetLastError ( ERROR_IO_INCOMPLETE );
|
||||
if ( bWait ) ERR ("PENDING status after waiting!\n");
|
||||
return FALSE;
|
||||
default:
|
||||
SetLastError ( RtlNtStatusToDosError ( lpOverlapped->Internal ) );
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* CancelIo (KERNEL32.@)
|
||||
*/
|
||||
BOOL WINAPI CancelIo(HANDLE handle)
|
||||
{
|
||||
async_private *ovp,*t;
|
||||
|
||||
TRACE("handle = %p\n",handle);
|
||||
|
||||
for (ovp = NtCurrentTeb()->pending_list; ovp; ovp = t)
|
||||
{
|
||||
t = ovp->next;
|
||||
if ( ovp->handle == handle )
|
||||
cancel_async ( ovp );
|
||||
}
|
||||
WaitForMultipleObjectsEx(0,NULL,FALSE,1,TRUE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* _hread (KERNEL32.@)
|
||||
*/
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
@ -35,15 +36,16 @@
|
|||
#include "wine/unicode.h"
|
||||
#include "wine/debug.h"
|
||||
#include "wine/server.h"
|
||||
#include "async.h"
|
||||
#include "ntdll_misc.h"
|
||||
#include "file.h" /* FIXME */
|
||||
#include "../files/smb.h"
|
||||
|
||||
#include "winternl.h"
|
||||
#include "winioctl.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
* NtOpenFile [NTDLL.@]
|
||||
* ZwOpenFile [NTDLL.@]
|
||||
|
@ -154,31 +156,175 @@ NTSTATUS WINAPI NtCreateFile(
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* set the last error depending on errno */
|
||||
NTSTATUS NTFILE_errno_to_status(int val)
|
||||
/***********************************************************************
|
||||
* Asynchronous file I/O *
|
||||
*/
|
||||
static DWORD fileio_get_async_count(const async_private *ovp);
|
||||
static void CALLBACK fileio_call_completion_func(ULONG_PTR data);
|
||||
static void fileio_async_cleanup(async_private *ovp);
|
||||
|
||||
static async_ops fileio_async_ops =
|
||||
{
|
||||
switch (val)
|
||||
fileio_get_async_count, /* get_count */
|
||||
fileio_call_completion_func, /* call_completion */
|
||||
fileio_async_cleanup /* cleanup */
|
||||
};
|
||||
|
||||
static async_ops fileio_nocomp_async_ops =
|
||||
{
|
||||
fileio_get_async_count, /* get_count */
|
||||
NULL, /* call_completion */
|
||||
fileio_async_cleanup /* cleanup */
|
||||
};
|
||||
|
||||
typedef struct async_fileio
|
||||
{
|
||||
struct async_private async;
|
||||
PIO_APC_ROUTINE apc;
|
||||
void* apc_user;
|
||||
char *buffer;
|
||||
unsigned int count;
|
||||
unsigned long offset;
|
||||
enum fd_type fd_type;
|
||||
} async_fileio;
|
||||
|
||||
static DWORD fileio_get_async_count(const struct async_private *ovp)
|
||||
{
|
||||
async_fileio *fileio = (async_fileio*) ovp;
|
||||
|
||||
if (fileio->count < fileio->async.iosb->Information)
|
||||
return 0;
|
||||
return fileio->count - fileio->async.iosb->Information;
|
||||
}
|
||||
|
||||
static void CALLBACK fileio_call_completion_func(ULONG_PTR data)
|
||||
{
|
||||
async_fileio *ovp = (async_fileio*) data;
|
||||
TRACE("data: %p\n", ovp);
|
||||
|
||||
ovp->apc( ovp->apc_user, ovp->async.iosb, ovp->async.iosb->Information );
|
||||
|
||||
fileio_async_cleanup( &ovp->async );
|
||||
}
|
||||
|
||||
static void fileio_async_cleanup( struct async_private *ovp )
|
||||
{
|
||||
RtlFreeHeap( ntdll_get_process_heap(), 0, ovp );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* FILE_GetUnixHandleType
|
||||
*
|
||||
* Retrieve the Unix handle corresponding to a file handle.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
static int FILE_GetUnixHandleType( HANDLE handle, DWORD access, enum fd_type *type, int *flags_ptr, int *fd )
|
||||
{
|
||||
int ret, flags;
|
||||
|
||||
*fd = -1;
|
||||
ret = wine_server_handle_to_fd( handle, access, fd, type, &flags );
|
||||
if (flags_ptr) *flags_ptr = flags;
|
||||
if (!ret && (((access & GENERIC_READ) && (flags & FD_FLAG_RECV_SHUTDOWN)) ||
|
||||
((access & GENERIC_WRITE) && (flags & FD_FLAG_SEND_SHUTDOWN))))
|
||||
{
|
||||
case EAGAIN: return ( STATUS_SHARING_VIOLATION );
|
||||
case ESPIPE:
|
||||
case EBADF: return ( STATUS_INVALID_HANDLE );
|
||||
case ENOSPC: return ( STATUS_DISK_FULL );
|
||||
case EACCES:
|
||||
case ESRCH:
|
||||
case EPERM: return ( STATUS_ACCESS_DENIED );
|
||||
case EROFS: return ( STATUS_MEDIA_WRITE_PROTECTED );
|
||||
case EBUSY: return ( STATUS_FILE_LOCK_CONFLICT );
|
||||
case ENOENT: return ( STATUS_NO_SUCH_FILE );
|
||||
case EISDIR: return ( STATUS_FILE_IS_A_DIRECTORY );
|
||||
case ENFILE:
|
||||
case EMFILE: return ( STATUS_NO_MORE_FILES );
|
||||
case EEXIST: return ( STATUS_OBJECT_NAME_COLLISION );
|
||||
case EINVAL: return ( STATUS_INVALID_PARAMETER );
|
||||
case ENOTEMPTY: return ( STATUS_DIRECTORY_NOT_EMPTY );
|
||||
case EIO: return ( STATUS_ACCESS_VIOLATION );
|
||||
close(*fd);
|
||||
ret = STATUS_PIPE_DISCONNECTED;
|
||||
}
|
||||
perror("file_set_error");
|
||||
return ( STATUS_INVALID_PARAMETER );
|
||||
return ret;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* FILE_GetNtStatus(void)
|
||||
*
|
||||
* Retrieve the Nt Status code from errno.
|
||||
* Try to be consistent with FILE_SetDosError().
|
||||
*/
|
||||
static DWORD FILE_GetNtStatus(void)
|
||||
{
|
||||
int err = errno;
|
||||
DWORD nt;
|
||||
|
||||
TRACE( "errno = %d\n", errno );
|
||||
switch (err)
|
||||
{
|
||||
case EAGAIN: nt = STATUS_SHARING_VIOLATION; break;
|
||||
case EBADF: nt = STATUS_INVALID_HANDLE; break;
|
||||
case ENOSPC: nt = STATUS_DISK_FULL; break;
|
||||
case EPERM:
|
||||
case EROFS:
|
||||
case EACCES: nt = STATUS_ACCESS_DENIED; break;
|
||||
case ENOENT: nt = STATUS_SHARING_VIOLATION; break;
|
||||
case EISDIR: nt = STATUS_FILE_IS_A_DIRECTORY; break;
|
||||
case EMFILE:
|
||||
case ENFILE: nt = STATUS_NO_MORE_FILES; break;
|
||||
case EINVAL:
|
||||
case ENOTEMPTY: nt = STATUS_DIRECTORY_NOT_EMPTY; break;
|
||||
case EPIPE: nt = STATUS_PIPE_BROKEN; break;
|
||||
case ENOEXEC: /* ?? */
|
||||
case ESPIPE: /* ?? */
|
||||
case EEXIST: /* ?? */
|
||||
default:
|
||||
FIXME( "Converting errno %d to STATUS_UNSUCCESSFUL\n", err );
|
||||
nt = STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
return nt;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* FILE_AsyncReadService (INTERNAL)
|
||||
*
|
||||
* This function is called while the client is waiting on the
|
||||
* server, so we can't make any server calls here.
|
||||
*/
|
||||
static void FILE_AsyncReadService(async_private *ovp)
|
||||
{
|
||||
async_fileio *fileio = (async_fileio*) ovp;
|
||||
IO_STATUS_BLOCK* io_status = fileio->async.iosb;
|
||||
int result;
|
||||
int already = io_status->Information;
|
||||
|
||||
TRACE("%p %p\n", io_status, fileio->buffer );
|
||||
|
||||
/* check to see if the data is ready (non-blocking) */
|
||||
|
||||
if ( fileio->fd_type == FD_TYPE_SOCKET )
|
||||
result = read(ovp->fd, &fileio->buffer[already], fileio->count - already);
|
||||
else
|
||||
{
|
||||
result = pread(ovp->fd, &fileio->buffer[already], fileio->count - already,
|
||||
fileio->offset + already);
|
||||
if ((result < 0) && (errno == ESPIPE))
|
||||
result = read(ovp->fd, &fileio->buffer[already], fileio->count - already);
|
||||
}
|
||||
|
||||
if ((result < 0) && ((errno == EAGAIN) || (errno == EINTR)))
|
||||
{
|
||||
TRACE("Deferred read %d\n",errno);
|
||||
io_status->u.Status = STATUS_PENDING;
|
||||
return;
|
||||
}
|
||||
|
||||
/* check to see if the transfer is complete */
|
||||
if (result < 0)
|
||||
{
|
||||
io_status->u.Status = FILE_GetNtStatus();
|
||||
return;
|
||||
}
|
||||
else if (result == 0)
|
||||
{
|
||||
io_status->u.Status = io_status->Information ? STATUS_SUCCESS : STATUS_END_OF_FILE;
|
||||
return;
|
||||
}
|
||||
|
||||
io_status->Information += result;
|
||||
if (io_status->Information >= fileio->count || fileio->fd_type == FD_TYPE_SOCKET )
|
||||
io_status->u.Status = STATUS_SUCCESS;
|
||||
else
|
||||
io_status->u.Status = STATUS_PENDING;
|
||||
|
||||
TRACE("read %d more bytes %ld/%d so far\n",
|
||||
result, io_status->Information, fileio->count);
|
||||
}
|
||||
|
||||
|
||||
|
@ -187,67 +333,164 @@ NTSTATUS NTFILE_errno_to_status(int val)
|
|||
* ZwReadFile [NTDLL.@]
|
||||
*
|
||||
* Parameters
|
||||
* HANDLE32 FileHandle
|
||||
* HANDLE32 Event OPTIONAL
|
||||
* PIO_APC_ROUTINE ApcRoutine OPTIONAL
|
||||
* PVOID ApcContext OPTIONAL
|
||||
* PIO_STATUS_BLOCK IoStatusBlock
|
||||
* PVOID Buffer
|
||||
* ULONG Length
|
||||
* PLARGE_INTEGER ByteOffset OPTIONAL
|
||||
* PULONG Key OPTIONAL
|
||||
* HANDLE32 hFile
|
||||
* HANDLE32 hEvent OPTIONAL
|
||||
* PIO_APC_ROUTINE apc OPTIONAL
|
||||
* PVOID apc_user OPTIONAL
|
||||
* PIO_STATUS_BLOCK io_status
|
||||
* PVOID buffer
|
||||
* ULONG length
|
||||
* PLARGE_INTEGER offset OPTIONAL
|
||||
* PULONG key OPTIONAL
|
||||
*
|
||||
* IoStatusBlock->Information contains the number of bytes read on return.
|
||||
*/
|
||||
NTSTATUS WINAPI NtReadFile (
|
||||
HANDLE FileHandle,
|
||||
HANDLE EventHandle,
|
||||
PIO_APC_ROUTINE ApcRoutine,
|
||||
PVOID ApcContext,
|
||||
PIO_STATUS_BLOCK IoStatusBlock,
|
||||
PVOID Buffer,
|
||||
ULONG Length,
|
||||
PLARGE_INTEGER ByteOffset,
|
||||
PULONG Key)
|
||||
NTSTATUS WINAPI NtReadFile(HANDLE hFile, HANDLE hEvent,
|
||||
PIO_APC_ROUTINE apc, void* apc_user,
|
||||
PIO_STATUS_BLOCK io_status, void* buffer, ULONG length,
|
||||
PLARGE_INTEGER offset, PULONG key)
|
||||
{
|
||||
int fd, result, flags, ret;
|
||||
enum fd_type type;
|
||||
int unix_handle, flags;
|
||||
enum fd_type type;
|
||||
|
||||
FIXME("(%p,%p,%p,%p,%p,%p,0x%08lx,%p,%p),partial stub!\n",
|
||||
FileHandle,EventHandle,ApcRoutine,ApcContext,IoStatusBlock,Buffer,Length,ByteOffset,Key);
|
||||
TRACE("(%p,%p,%p,%p,%p,%p,0x%08lx,%p,%p),partial stub!\n",
|
||||
hFile,hEvent,apc,apc_user,io_status,buffer,length,offset,key);
|
||||
|
||||
if (IsBadWritePtr( Buffer, Length ) ||
|
||||
IsBadWritePtr( IoStatusBlock, sizeof(*IoStatusBlock)) ||
|
||||
IsBadWritePtr( ByteOffset, sizeof(*ByteOffset)) )
|
||||
return STATUS_ACCESS_VIOLATION;
|
||||
io_status->Information = 0;
|
||||
io_status->u.Status = FILE_GetUnixHandleType( hFile, GENERIC_READ, &type, &flags, &unix_handle );
|
||||
if (io_status->u.Status) return io_status->u.Status;
|
||||
|
||||
IoStatusBlock->Information = 0;
|
||||
if (flags & FD_FLAG_TIMEOUT)
|
||||
{
|
||||
if (hEvent)
|
||||
{
|
||||
/* this shouldn't happen, but check it */
|
||||
FIXME("NIY-hEvent\n");
|
||||
return STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
io_status->u.Status = NtCreateEvent(&hEvent, SYNCHRONIZE, NULL, 0, 0);
|
||||
if (io_status->u.Status) return io_status->u.Status;
|
||||
}
|
||||
|
||||
if (flags & (FD_FLAG_OVERLAPPED|FD_FLAG_TIMEOUT))
|
||||
{
|
||||
async_fileio* ovp;
|
||||
|
||||
ret = wine_server_handle_to_fd( FileHandle, GENERIC_READ, &fd, &type, &flags );
|
||||
if(ret)
|
||||
return ret;
|
||||
if (unix_handle < 0) return STATUS_INVALID_HANDLE;
|
||||
|
||||
/* FIXME: this code only does synchronous reads so far */
|
||||
ovp = RtlAllocateHeap(ntdll_get_process_heap(), 0, sizeof(async_fileio));
|
||||
if (!ovp) return STATUS_NO_MEMORY;
|
||||
|
||||
/* FIXME: depending on how libc implements this, between two processes
|
||||
there could be a race condition between the seek and read here */
|
||||
do
|
||||
{
|
||||
result = pread( fd, Buffer, Length, ByteOffset->QuadPart);
|
||||
}
|
||||
while ( (result == -1) && ((errno == EAGAIN) || (errno == EINTR)) );
|
||||
ovp->async.ops = (apc ? &fileio_async_ops : &fileio_nocomp_async_ops );
|
||||
ovp->async.handle = hFile;
|
||||
ovp->async.fd = unix_handle;
|
||||
ovp->async.type = ASYNC_TYPE_READ;
|
||||
ovp->async.func = FILE_AsyncReadService;
|
||||
ovp->async.event = hEvent;
|
||||
ovp->async.iosb = io_status;
|
||||
ovp->count = length;
|
||||
ovp->offset = offset->s.LowPart;
|
||||
if (offset->s.HighPart) FIXME("NIY-high part\n");
|
||||
ovp->apc = apc;
|
||||
ovp->apc_user = apc_user;
|
||||
ovp->buffer = buffer;
|
||||
ovp->fd_type = type;
|
||||
|
||||
close( fd );
|
||||
io_status->Information = 0;
|
||||
io_status->u.Status = register_new_async(&ovp->async);
|
||||
if (io_status->u.Status == STATUS_PENDING && hEvent)
|
||||
{
|
||||
finish_async(&ovp->async);
|
||||
close(unix_handle);
|
||||
}
|
||||
return io_status->u.Status;
|
||||
}
|
||||
switch (type)
|
||||
{
|
||||
case FD_TYPE_SMB:
|
||||
FIXME("NIY-SMB\n");
|
||||
close(unix_handle);
|
||||
return SMB_ReadFile(hFile, buffer, length, io_status);
|
||||
|
||||
if (result == -1)
|
||||
{
|
||||
return IoStatusBlock->u.Status = NTFILE_errno_to_status(errno);
|
||||
}
|
||||
case FD_TYPE_DEFAULT:
|
||||
/* normal unix files */
|
||||
if (unix_handle == -1) return STATUS_INVALID_HANDLE;
|
||||
break;
|
||||
|
||||
IoStatusBlock->Information = result;
|
||||
IoStatusBlock->u.Status = 0;
|
||||
default:
|
||||
FIXME("Unsupported type of fd %d\n", type);
|
||||
if (unix_handle == -1) close(unix_handle);
|
||||
return STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
if (offset)
|
||||
{
|
||||
FILE_POSITION_INFORMATION fpi;
|
||||
|
||||
fpi.CurrentByteOffset = *offset;
|
||||
io_status->u.Status = NtSetInformationFile(hFile, io_status, &fpi, sizeof(fpi),
|
||||
FilePositionInformation);
|
||||
if (io_status->u.Status)
|
||||
{
|
||||
close(unix_handle);
|
||||
return io_status->u.Status;
|
||||
}
|
||||
}
|
||||
/* code for synchronous reads */
|
||||
while ((io_status->Information = read( unix_handle, buffer, length )) == -1)
|
||||
{
|
||||
if ((errno == EAGAIN) || (errno == EINTR)) continue;
|
||||
if (errno == EFAULT) FIXME( "EFAULT handling broken for now\n" );
|
||||
io_status->u.Status = FILE_GetNtStatus();
|
||||
break;
|
||||
}
|
||||
close( unix_handle );
|
||||
return io_status->u.Status;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* FILE_AsyncWriteService (INTERNAL)
|
||||
*
|
||||
* This function is called while the client is waiting on the
|
||||
* server, so we can't make any server calls here.
|
||||
*/
|
||||
static void FILE_AsyncWriteService(struct async_private *ovp)
|
||||
{
|
||||
async_fileio *fileio = (async_fileio *) ovp;
|
||||
PIO_STATUS_BLOCK io_status = fileio->async.iosb;
|
||||
int result;
|
||||
int already = io_status->Information;
|
||||
|
||||
TRACE("(%p %p)\n",io_status,fileio->buffer);
|
||||
|
||||
/* write some data (non-blocking) */
|
||||
|
||||
if ( fileio->fd_type == FD_TYPE_SOCKET )
|
||||
result = write(ovp->fd, &fileio->buffer[already], fileio->count - already);
|
||||
else
|
||||
{
|
||||
result = pwrite(ovp->fd, &fileio->buffer[already], fileio->count - already,
|
||||
fileio->offset + already);
|
||||
if ((result < 0) && (errno == ESPIPE))
|
||||
result = write(ovp->fd, &fileio->buffer[already], fileio->count - already);
|
||||
}
|
||||
|
||||
if ((result < 0) && ((errno == EAGAIN) || (errno == EINTR)))
|
||||
{
|
||||
io_status->u.Status = STATUS_PENDING;
|
||||
return;
|
||||
}
|
||||
|
||||
/* check to see if the transfer is complete */
|
||||
if (result < 0)
|
||||
{
|
||||
io_status->u.Status = FILE_GetNtStatus();
|
||||
return;
|
||||
}
|
||||
|
||||
io_status->Information += result;
|
||||
io_status->u.Status = (io_status->Information < fileio->count) ? STATUS_PENDING : STATUS_SUCCESS;
|
||||
TRACE("wrote %d more bytes %ld/%d so far\n",result,io_status->Information,fileio->count);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
|
@ -255,30 +498,112 @@ NTSTATUS WINAPI NtReadFile (
|
|||
* ZwWriteFile [NTDLL.@]
|
||||
*
|
||||
* Parameters
|
||||
* HANDLE32 FileHandle
|
||||
* HANDLE32 Event OPTIONAL
|
||||
* PIO_APC_ROUTINE ApcRoutine OPTIONAL
|
||||
* PVOID ApcContext OPTIONAL
|
||||
* PIO_STATUS_BLOCK IoStatusBlock
|
||||
* PVOID Buffer
|
||||
* ULONG Length
|
||||
* PLARGE_INTEGER ByteOffset OPTIONAL
|
||||
* PULONG Key OPTIONAL
|
||||
* HANDLE32 hFile
|
||||
* HANDLE32 hEvent OPTIONAL
|
||||
* PIO_APC_ROUTINE apc OPTIONAL
|
||||
* PVOID apc_user OPTIONAL
|
||||
* PIO_STATUS_BLOCK io_status
|
||||
* PVOID buffer
|
||||
* ULONG length
|
||||
* PLARGE_INTEGER offset OPTIONAL
|
||||
* PULONG key OPTIONAL
|
||||
*/
|
||||
NTSTATUS WINAPI NtWriteFile (
|
||||
HANDLE FileHandle,
|
||||
HANDLE EventHandle,
|
||||
PIO_APC_ROUTINE ApcRoutine,
|
||||
PVOID ApcContext,
|
||||
PIO_STATUS_BLOCK IoStatusBlock,
|
||||
PVOID Buffer,
|
||||
ULONG Length,
|
||||
PLARGE_INTEGER ByteOffset,
|
||||
PULONG Key)
|
||||
NTSTATUS WINAPI NtWriteFile(HANDLE hFile, HANDLE hEvent,
|
||||
PIO_APC_ROUTINE apc, void* apc_user,
|
||||
PIO_STATUS_BLOCK io_status,
|
||||
const void* buffer, ULONG length,
|
||||
PLARGE_INTEGER offset, PULONG key)
|
||||
{
|
||||
FIXME("(%p,%p,%p,%p,%p,%p,0x%08lx,%p,%p),stub!\n",
|
||||
FileHandle,EventHandle,ApcRoutine,ApcContext,IoStatusBlock,Buffer,Length,ByteOffset,Key);
|
||||
return 0;
|
||||
int unix_handle, flags;
|
||||
enum fd_type type;
|
||||
|
||||
TRACE("(%p,%p,%p,%p,%p,%p,0x%08lx,%p,%p)!\n",
|
||||
hFile,hEvent,apc,apc_user,io_status,buffer,length,offset,key);
|
||||
|
||||
TRACE("(%p,%p,%p,%p,%p,%p,0x%08lx,%p,%p),partial stub!\n",
|
||||
hFile,hEvent,apc,apc_user,io_status,buffer,length,offset,key);
|
||||
|
||||
io_status->Information = 0;
|
||||
|
||||
io_status->u.Status = FILE_GetUnixHandleType( hFile, GENERIC_WRITE, &type, &flags, &unix_handle );
|
||||
if (io_status->u.Status) return io_status->u.Status;
|
||||
|
||||
if (flags & (FD_FLAG_OVERLAPPED|FD_FLAG_TIMEOUT))
|
||||
{
|
||||
async_fileio* ovp;
|
||||
|
||||
if (unix_handle < 0) return STATUS_INVALID_HANDLE;
|
||||
|
||||
ovp = RtlAllocateHeap(ntdll_get_process_heap(), 0, sizeof(async_fileio));
|
||||
if (!ovp) return STATUS_NO_MEMORY;
|
||||
|
||||
ovp->async.ops = (apc ? &fileio_async_ops : &fileio_nocomp_async_ops );
|
||||
ovp->async.handle = hFile;
|
||||
ovp->async.fd = unix_handle;
|
||||
ovp->async.type = ASYNC_TYPE_WRITE;
|
||||
ovp->async.func = FILE_AsyncWriteService;
|
||||
ovp->async.event = hEvent;
|
||||
ovp->async.iosb = io_status;
|
||||
ovp->count = length;
|
||||
ovp->offset = offset->s.LowPart;
|
||||
if (offset->s.HighPart) FIXME("NIY-high part\n");
|
||||
ovp->apc = apc;
|
||||
ovp->apc_user = apc_user;
|
||||
ovp->buffer = (void*)buffer;
|
||||
ovp->fd_type = type;
|
||||
|
||||
io_status->Information = 0;
|
||||
io_status->u.Status = register_new_async(&ovp->async);
|
||||
if (io_status->u.Status == STATUS_PENDING && hEvent)
|
||||
{
|
||||
finish_async(&ovp->async);
|
||||
close(unix_handle);
|
||||
}
|
||||
return io_status->u.Status;
|
||||
}
|
||||
switch (type)
|
||||
{
|
||||
case FD_TYPE_SMB:
|
||||
FIXME("NIY-SMB\n");
|
||||
close(unix_handle);
|
||||
return STATUS_NOT_IMPLEMENTED;
|
||||
|
||||
case FD_TYPE_DEFAULT:
|
||||
/* normal unix files */
|
||||
if (unix_handle == -1) return STATUS_INVALID_HANDLE;
|
||||
break;
|
||||
|
||||
default:
|
||||
FIXME("Unsupported type of fd %d\n", type);
|
||||
if (unix_handle == -1) close(unix_handle);
|
||||
return STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (offset)
|
||||
{
|
||||
FILE_POSITION_INFORMATION fpi;
|
||||
|
||||
fpi.CurrentByteOffset = *offset;
|
||||
io_status->u.Status = NtSetInformationFile(hFile, io_status, &fpi, sizeof(fpi),
|
||||
FilePositionInformation);
|
||||
if (io_status->u.Status)
|
||||
{
|
||||
close(unix_handle);
|
||||
return io_status->u.Status;
|
||||
}
|
||||
}
|
||||
|
||||
/* synchronous file write */
|
||||
while ((io_status->Information = write( unix_handle, buffer, length )) == -1)
|
||||
{
|
||||
if ((errno == EAGAIN) || (errno == EINTR)) continue;
|
||||
if (errno == EFAULT) FIXME( "EFAULT handling broken for now\n" );
|
||||
if (errno == ENOSPC) io_status->u.Status = STATUS_DISK_FULL;
|
||||
else io_status->u.Status = FILE_GetNtStatus();
|
||||
break;
|
||||
}
|
||||
close( unix_handle );
|
||||
return io_status->u.Status;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
|
@ -665,7 +990,7 @@ NTSTATUS WINAPI NtFlushBuffersFile( HANDLE hFile, IO_STATUS_BLOCK* IoStatusBlock
|
|||
hEvent = reply->event;
|
||||
}
|
||||
SERVER_END_REQ;
|
||||
if( !ret && hEvent )
|
||||
if (!ret && hEvent)
|
||||
{
|
||||
ret = NtWaitForSingleObject( hEvent, FALSE, NULL );
|
||||
NtClose( hEvent );
|
||||
|
|
|
@ -446,9 +446,9 @@ static void WINAPI check_async_list(async_private *asp, DWORD status)
|
|||
if( status != STATUS_ALERTED )
|
||||
{
|
||||
ovp_status = status;
|
||||
ovp->ops->set_status (ovp, status);
|
||||
ovp->iosb->u.Status = status;
|
||||
}
|
||||
else ovp_status = ovp->ops->get_status (ovp);
|
||||
else ovp_status = ovp->iosb->u.Status;
|
||||
|
||||
if( ovp_status == STATUS_PENDING ) ovp->func( ovp );
|
||||
|
||||
|
|
|
@ -107,6 +107,8 @@
|
|||
# include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#define NONAMELESSUNION
|
||||
#define NONAMELESSSTRUCT
|
||||
#include "wine/winbase16.h"
|
||||
#include "wingdi.h"
|
||||
#include "winuser.h"
|
||||
|
@ -147,16 +149,12 @@ extern CRITICAL_SECTION csWSgetXXXbyYYY;
|
|||
****************************************************************/
|
||||
#include "async.h"
|
||||
|
||||
static DWORD ws2_async_get_status (const struct async_private *ovp);
|
||||
static DWORD ws2_async_get_count (const struct async_private *ovp);
|
||||
static void ws2_async_set_status (struct async_private *ovp, const DWORD status);
|
||||
static void CALLBACK ws2_async_call_completion (ULONG_PTR data);
|
||||
static void ws2_async_cleanup ( struct async_private *ovp );
|
||||
|
||||
static struct async_ops ws2_async_ops =
|
||||
{
|
||||
ws2_async_get_status,
|
||||
ws2_async_set_status,
|
||||
ws2_async_get_count,
|
||||
ws2_async_call_completion,
|
||||
ws2_async_cleanup
|
||||
|
@ -164,8 +162,6 @@ static struct async_ops ws2_async_ops =
|
|||
|
||||
static struct async_ops ws2_nocomp_async_ops =
|
||||
{
|
||||
ws2_async_get_status,
|
||||
ws2_async_set_status,
|
||||
ws2_async_get_count,
|
||||
NULL, /* call_completion */
|
||||
ws2_async_cleanup
|
||||
|
@ -174,12 +170,11 @@ static struct async_ops ws2_nocomp_async_ops =
|
|||
typedef struct ws2_async
|
||||
{
|
||||
async_private async;
|
||||
LPWSAOVERLAPPED overlapped;
|
||||
LPWSAOVERLAPPED user_overlapped;
|
||||
LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_func;
|
||||
struct iovec *iovec;
|
||||
int n_iovecs;
|
||||
struct WS_sockaddr *addr;
|
||||
struct WS_sockaddr *addr;
|
||||
union {
|
||||
int val; /* for send operations */
|
||||
int *ptr; /* for recv operations */
|
||||
|
@ -1021,31 +1016,24 @@ inline void ws_sockaddr_free(const struct sockaddr* uaddr, const struct WS_socka
|
|||
* Functions for handling overlapped I/O
|
||||
**************************************************************************/
|
||||
|
||||
static DWORD ws2_async_get_status (const struct async_private *ovp)
|
||||
{
|
||||
return ((ws2_async*) ovp)->overlapped->Internal;
|
||||
}
|
||||
|
||||
static VOID ws2_async_set_status (struct async_private *ovp, const DWORD status)
|
||||
{
|
||||
((ws2_async*) ovp)->overlapped->Internal = status;
|
||||
}
|
||||
|
||||
static DWORD ws2_async_get_count (const struct async_private *ovp)
|
||||
{
|
||||
return ((ws2_async*) ovp)->overlapped->InternalHigh;
|
||||
return ovp->iosb->Information;
|
||||
}
|
||||
|
||||
static void ws2_async_cleanup ( struct async_private *ap )
|
||||
{
|
||||
struct ws2_async *as = (struct ws2_async*) ap;
|
||||
|
||||
TRACE ( "as: %p uovl %p ovl %p\n", as, as->user_overlapped, as->overlapped );
|
||||
TRACE ( "as: %p uovl %p ovl %p\n", as, as->user_overlapped, as->async.iosb );
|
||||
if ( !as->user_overlapped )
|
||||
{
|
||||
#if 0
|
||||
/* FIXME: I don't think this is really used */
|
||||
if ( as->overlapped->hEvent != INVALID_HANDLE_VALUE )
|
||||
WSACloseEvent ( as->overlapped->hEvent );
|
||||
HeapFree ( GetProcessHeap(), 0, as->overlapped );
|
||||
#endif
|
||||
HeapFree ( GetProcessHeap(), 0, as->async.iosb );
|
||||
}
|
||||
|
||||
if ( as->iovec )
|
||||
|
@ -1060,8 +1048,8 @@ static void CALLBACK ws2_async_call_completion (ULONG_PTR data)
|
|||
|
||||
TRACE ("data: %p\n", as);
|
||||
|
||||
as->completion_func ( NtStatusToWSAError (as->overlapped->Internal),
|
||||
as->overlapped->InternalHigh,
|
||||
as->completion_func ( NtStatusToWSAError (as->async.iosb->u.Status),
|
||||
as->async.iosb->Information,
|
||||
as->user_overlapped,
|
||||
as->flags );
|
||||
ws2_async_cleanup ( &as->async );
|
||||
|
@ -1114,22 +1102,22 @@ WS2_make_async (SOCKET s, int fd, int type, struct iovec *iovec, DWORD dwBufferC
|
|||
|
||||
if ( lpOverlapped )
|
||||
{
|
||||
wsa->overlapped = lpOverlapped;
|
||||
wsa->async.iosb = (IO_STATUS_BLOCK*)lpOverlapped;
|
||||
wsa->async.event = ( lpCompletionRoutine ? INVALID_HANDLE_VALUE : lpOverlapped->hEvent );
|
||||
}
|
||||
else
|
||||
{
|
||||
wsa->overlapped = HeapAlloc ( GetProcessHeap(), 0,
|
||||
sizeof (WSAOVERLAPPED) );
|
||||
if ( !wsa->overlapped )
|
||||
wsa->async.iosb = HeapAlloc ( GetProcessHeap(), 0,
|
||||
sizeof (IO_STATUS_BLOCK) );
|
||||
if ( !wsa->async.iosb )
|
||||
goto error;
|
||||
wsa->async.event = wsa->overlapped->hEvent = INVALID_HANDLE_VALUE;
|
||||
wsa->async.event = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
wsa->overlapped->InternalHigh = 0;
|
||||
TRACE ( "wsa %p, ops %p, h %p, ev %p, fd %d, func %p, ov %p, uov %p, cfunc %p\n",
|
||||
wsa->async.iosb->Information = 0;
|
||||
TRACE ( "wsa %p, ops %p, h %p, ev %p, fd %d, func %p, iosb %p, uov %p, cfunc %p\n",
|
||||
wsa, wsa->async.ops, wsa->async.handle, wsa->async.event, wsa->async.fd, wsa->async.func,
|
||||
wsa->overlapped, wsa->user_overlapped, wsa->completion_func );
|
||||
wsa->async.iosb, wsa->user_overlapped, wsa->completion_func );
|
||||
|
||||
return wsa;
|
||||
|
||||
|
@ -1220,9 +1208,9 @@ static void WS2_async_recv ( async_private *as )
|
|||
|
||||
TRACE ( "async %p\n", wsa );
|
||||
|
||||
if ( wsa->overlapped->Internal != STATUS_PENDING )
|
||||
if ( wsa->async.iosb->u.Status != STATUS_PENDING )
|
||||
{
|
||||
TRACE ( "status: %ld\n", wsa->overlapped->Internal );
|
||||
TRACE ( "status: %ld\n", wsa->async.iosb->u.Status );
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1231,8 +1219,8 @@ static void WS2_async_recv ( async_private *as )
|
|||
|
||||
if (result >= 0)
|
||||
{
|
||||
wsa->overlapped->Internal = STATUS_SUCCESS;
|
||||
wsa->overlapped->InternalHigh = result;
|
||||
wsa->async.iosb->u.Status = STATUS_SUCCESS;
|
||||
wsa->async.iosb->Information = result;
|
||||
TRACE ( "received %d bytes\n", result );
|
||||
_enable_event ( wsa->async.handle, FD_READ, 0, 0 );
|
||||
return;
|
||||
|
@ -1241,13 +1229,13 @@ static void WS2_async_recv ( async_private *as )
|
|||
err = wsaErrno ();
|
||||
if ( err == WSAEINTR || err == WSAEWOULDBLOCK ) /* errno: EINTR / EAGAIN */
|
||||
{
|
||||
wsa->overlapped->Internal = STATUS_PENDING;
|
||||
wsa->async.iosb->u.Status = STATUS_PENDING;
|
||||
_enable_event ( wsa->async.handle, FD_READ, 0, 0 );
|
||||
TRACE ( "still pending\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
wsa->overlapped->Internal = err;
|
||||
wsa->async.iosb->u.Status = err;
|
||||
TRACE ( "Error: %x\n", err );
|
||||
}
|
||||
}
|
||||
|
@ -1312,9 +1300,9 @@ static void WS2_async_send ( async_private *as )
|
|||
|
||||
TRACE ( "async %p\n", wsa );
|
||||
|
||||
if ( wsa->overlapped->Internal != STATUS_PENDING )
|
||||
if ( wsa->async.iosb->u.Status != STATUS_PENDING )
|
||||
{
|
||||
TRACE ( "status: %ld\n", wsa->overlapped->Internal );
|
||||
TRACE ( "status: %ld\n", wsa->async.iosb->u.Status );
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1323,8 +1311,8 @@ static void WS2_async_send ( async_private *as )
|
|||
|
||||
if (result >= 0)
|
||||
{
|
||||
wsa->overlapped->Internal = STATUS_SUCCESS;
|
||||
wsa->overlapped->InternalHigh = result;
|
||||
wsa->async.iosb->u.Status = STATUS_SUCCESS;
|
||||
wsa->async.iosb->Information = result;
|
||||
TRACE ( "sent %d bytes\n", result );
|
||||
_enable_event ( wsa->async.handle, FD_WRITE, 0, 0 );
|
||||
return;
|
||||
|
@ -1333,7 +1321,7 @@ static void WS2_async_send ( async_private *as )
|
|||
err = wsaErrno ();
|
||||
if ( err == WSAEINTR )
|
||||
{
|
||||
wsa->overlapped->Internal = STATUS_PENDING;
|
||||
wsa->async.iosb->u.Status = STATUS_PENDING;
|
||||
_enable_event ( wsa->async.handle, FD_WRITE, 0, 0 );
|
||||
TRACE ( "still pending\n" );
|
||||
}
|
||||
|
@ -1341,7 +1329,7 @@ static void WS2_async_send ( async_private *as )
|
|||
{
|
||||
/* We set the status to a winsock error code and check for that
|
||||
later in NtStatusToWSAError () */
|
||||
wsa->overlapped->Internal = err;
|
||||
wsa->async.iosb->u.Status = err;
|
||||
TRACE ( "Error: %x\n", err );
|
||||
}
|
||||
}
|
||||
|
@ -1370,9 +1358,9 @@ static void WS2_async_shutdown ( async_private *as )
|
|||
}
|
||||
|
||||
if ( err )
|
||||
wsa->overlapped->Internal = wsaErrno ();
|
||||
wsa->async.iosb->u.Status = wsaErrno ();
|
||||
else
|
||||
wsa->overlapped->Internal = STATUS_SUCCESS;
|
||||
wsa->async.iosb->u.Status = STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
|
@ -2461,7 +2449,7 @@ INT WINAPI WSASendTo( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
|
|||
err = NtStatusToWSAError ( ret );
|
||||
|
||||
if ( !lpOverlapped )
|
||||
HeapFree ( GetProcessHeap(), 0, wsa->overlapped );
|
||||
HeapFree ( GetProcessHeap(), 0, wsa->async.iosb );
|
||||
HeapFree ( GetProcessHeap(), 0, wsa );
|
||||
goto err_free;
|
||||
}
|
||||
|
@ -3918,7 +3906,7 @@ INT WINAPI WSARecvFrom( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
|
|||
err = NtStatusToWSAError ( ret );
|
||||
|
||||
if ( !lpOverlapped )
|
||||
HeapFree ( GetProcessHeap(), 0, wsa->overlapped );
|
||||
HeapFree ( GetProcessHeap(), 0, wsa->async.iosb );
|
||||
HeapFree ( GetProcessHeap(), 0, wsa );
|
||||
goto err_free;
|
||||
}
|
||||
|
|
714
files/file.c
714
files/file.c
|
@ -66,7 +66,6 @@
|
|||
|
||||
#include "drive.h"
|
||||
#include "file.h"
|
||||
#include "async.h"
|
||||
#include "heap.h"
|
||||
#include "msdos.h"
|
||||
#include "wincon.h"
|
||||
|
@ -82,87 +81,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(file);
|
|||
#define MAP_ANON MAP_ANONYMOUS
|
||||
#endif
|
||||
|
||||
/* Macro to derive file offset from OVERLAPPED struct */
|
||||
#define OVERLAPPED_OFFSET(overlapped) ((off_t) (overlapped)->Offset + ((off_t) (overlapped)->OffsetHigh << 32))
|
||||
|
||||
HANDLE dos_handles[DOS_TABLE_SIZE];
|
||||
mode_t FILE_umask;
|
||||
|
||||
extern HANDLE WINAPI FILE_SmbOpen(LPCSTR name);
|
||||
|
||||
/***********************************************************************
|
||||
* Asynchronous file I/O *
|
||||
*/
|
||||
static DWORD fileio_get_async_status (const async_private *ovp);
|
||||
static DWORD fileio_get_async_count (const async_private *ovp);
|
||||
static void fileio_set_async_status (async_private *ovp, const DWORD status);
|
||||
static void CALLBACK fileio_call_completion_func (ULONG_PTR data);
|
||||
static void fileio_async_cleanup (async_private *ovp);
|
||||
|
||||
static async_ops fileio_async_ops =
|
||||
{
|
||||
fileio_get_async_status, /* get_status */
|
||||
fileio_set_async_status, /* set_status */
|
||||
fileio_get_async_count, /* get_count */
|
||||
fileio_call_completion_func, /* call_completion */
|
||||
fileio_async_cleanup /* cleanup */
|
||||
};
|
||||
|
||||
static async_ops fileio_nocomp_async_ops =
|
||||
{
|
||||
fileio_get_async_status, /* get_status */
|
||||
fileio_set_async_status, /* set_status */
|
||||
fileio_get_async_count, /* get_count */
|
||||
NULL, /* call_completion */
|
||||
fileio_async_cleanup /* cleanup */
|
||||
};
|
||||
|
||||
typedef struct async_fileio
|
||||
{
|
||||
struct async_private async;
|
||||
LPOVERLAPPED lpOverlapped;
|
||||
LPOVERLAPPED_COMPLETION_ROUTINE completion_func;
|
||||
char *buffer;
|
||||
unsigned int count;
|
||||
enum fd_type fd_type;
|
||||
} async_fileio;
|
||||
|
||||
static DWORD fileio_get_async_status (const struct async_private *ovp)
|
||||
{
|
||||
return ((async_fileio*) ovp)->lpOverlapped->Internal;
|
||||
}
|
||||
|
||||
static void fileio_set_async_status (async_private *ovp, const DWORD status)
|
||||
{
|
||||
((async_fileio*) ovp)->lpOverlapped->Internal = status;
|
||||
}
|
||||
|
||||
static DWORD fileio_get_async_count (const struct async_private *ovp)
|
||||
{
|
||||
async_fileio *fileio = (async_fileio*) ovp;
|
||||
|
||||
if (fileio->count < fileio->lpOverlapped->InternalHigh)
|
||||
return 0;
|
||||
return fileio->count - fileio->lpOverlapped->InternalHigh;
|
||||
}
|
||||
|
||||
static void CALLBACK fileio_call_completion_func (ULONG_PTR data)
|
||||
{
|
||||
async_fileio *ovp = (async_fileio*) data;
|
||||
TRACE ("data: %p\n", ovp);
|
||||
|
||||
ovp->completion_func( RtlNtStatusToDosError ( ovp->lpOverlapped->Internal ),
|
||||
ovp->lpOverlapped->InternalHigh,
|
||||
ovp->lpOverlapped );
|
||||
|
||||
fileio_async_cleanup ( &ovp->async );
|
||||
}
|
||||
|
||||
static void fileio_async_cleanup ( struct async_private *ovp )
|
||||
{
|
||||
HeapFree ( GetProcessHeap(), 0, ovp );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* FILE_ConvertOFMode
|
||||
*
|
||||
|
@ -217,42 +138,6 @@ int FILE_strncasecmp( const char *str1, const char *str2, int len )
|
|||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* FILE_GetNtStatus(void)
|
||||
*
|
||||
* Retrieve the Nt Status code from errno.
|
||||
* Try to be consistent with FILE_SetDosError().
|
||||
*/
|
||||
DWORD FILE_GetNtStatus(void)
|
||||
{
|
||||
int err = errno;
|
||||
DWORD nt;
|
||||
TRACE ( "errno = %d\n", errno );
|
||||
switch ( err )
|
||||
{
|
||||
case EAGAIN: nt = STATUS_SHARING_VIOLATION; break;
|
||||
case EBADF: nt = STATUS_INVALID_HANDLE; break;
|
||||
case ENOSPC: nt = STATUS_DISK_FULL; break;
|
||||
case EPERM:
|
||||
case EROFS:
|
||||
case EACCES: nt = STATUS_ACCESS_DENIED; break;
|
||||
case ENOENT: nt = STATUS_SHARING_VIOLATION; break;
|
||||
case EISDIR: nt = STATUS_FILE_IS_A_DIRECTORY; break;
|
||||
case EMFILE:
|
||||
case ENFILE: nt = STATUS_NO_MORE_FILES; break;
|
||||
case EINVAL:
|
||||
case ENOTEMPTY: nt = STATUS_DIRECTORY_NOT_EMPTY; break;
|
||||
case EPIPE: nt = STATUS_PIPE_BROKEN; break;
|
||||
case ENOEXEC: /* ?? */
|
||||
case ESPIPE: /* ?? */
|
||||
case EEXIST: /* ?? */
|
||||
default:
|
||||
FIXME ( "Converting errno %d to STATUS_UNSUCCESSFUL\n", err );
|
||||
nt = STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
return nt;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* FILE_SetDosError
|
||||
*
|
||||
|
@ -1533,575 +1418,180 @@ HFILE16 WINAPI _lclose16( HFILE16 hFile )
|
|||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* GetOverlappedResult (KERNEL32.@)
|
||||
/******************************************************************
|
||||
* FILE_ReadWriteApc (internal)
|
||||
*
|
||||
* Check the result of an Asynchronous data transfer from a file.
|
||||
*
|
||||
* RETURNS
|
||||
* TRUE on success
|
||||
* FALSE on failure
|
||||
*
|
||||
* If successful (and relevant) lpTransferred will hold the number of
|
||||
* bytes transferred during the async operation.
|
||||
*
|
||||
* BUGS
|
||||
*
|
||||
* Currently only works for WaitCommEvent, ReadFile, WriteFile
|
||||
* with communications ports.
|
||||
*
|
||||
*/
|
||||
BOOL WINAPI GetOverlappedResult(
|
||||
HANDLE hFile, /* [in] handle of file to check on */
|
||||
LPOVERLAPPED lpOverlapped, /* [in/out] pointer to overlapped */
|
||||
LPDWORD lpTransferred, /* [in/out] number of bytes transferred */
|
||||
BOOL bWait /* [in] wait for the transfer to complete ? */
|
||||
) {
|
||||
DWORD r;
|
||||
|
||||
TRACE("(%p %p %p %x)\n", hFile, lpOverlapped, lpTransferred, bWait);
|
||||
|
||||
if(lpOverlapped==NULL)
|
||||
{
|
||||
ERR("lpOverlapped was null\n");
|
||||
return FALSE;
|
||||
}
|
||||
if(!lpOverlapped->hEvent)
|
||||
{
|
||||
ERR("lpOverlapped->hEvent was null\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ( bWait )
|
||||
{
|
||||
do {
|
||||
TRACE("waiting on %p\n",lpOverlapped);
|
||||
r = WaitForSingleObjectEx(lpOverlapped->hEvent, INFINITE, TRUE);
|
||||
TRACE("wait on %p returned %ld\n",lpOverlapped,r);
|
||||
} while (r==STATUS_USER_APC);
|
||||
}
|
||||
else if ( lpOverlapped->Internal == STATUS_PENDING )
|
||||
{
|
||||
/* Wait in order to give APCs a chance to run. */
|
||||
/* This is cheating, so we must set the event again in case of success -
|
||||
it may be a non-manual reset event. */
|
||||
do {
|
||||
TRACE("waiting on %p\n",lpOverlapped);
|
||||
r = WaitForSingleObjectEx(lpOverlapped->hEvent, 0, TRUE);
|
||||
TRACE("wait on %p returned %ld\n",lpOverlapped,r);
|
||||
} while (r==STATUS_USER_APC);
|
||||
if ( r == WAIT_OBJECT_0 )
|
||||
NtSetEvent ( lpOverlapped->hEvent, NULL );
|
||||
}
|
||||
|
||||
if(lpTransferred)
|
||||
*lpTransferred = lpOverlapped->InternalHigh;
|
||||
|
||||
switch ( lpOverlapped->Internal )
|
||||
{
|
||||
case STATUS_SUCCESS:
|
||||
return TRUE;
|
||||
case STATUS_PENDING:
|
||||
SetLastError ( ERROR_IO_INCOMPLETE );
|
||||
if ( bWait ) ERR ("PENDING status after waiting!\n");
|
||||
return FALSE;
|
||||
default:
|
||||
SetLastError ( RtlNtStatusToDosError ( lpOverlapped->Internal ) );
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* CancelIo (KERNEL32.@)
|
||||
*/
|
||||
BOOL WINAPI CancelIo(HANDLE handle)
|
||||
static void WINAPI FILE_ReadWriteApc(void* apc_user, PIO_STATUS_BLOCK io_status, ULONG len)
|
||||
{
|
||||
async_private *ovp,*t;
|
||||
|
||||
TRACE("handle = %p\n",handle);
|
||||
|
||||
for (ovp = NtCurrentTeb()->pending_list; ovp; ovp = t)
|
||||
{
|
||||
t = ovp->next;
|
||||
if ( ovp->handle == handle )
|
||||
cancel_async ( ovp );
|
||||
}
|
||||
WaitForMultipleObjectsEx(0,NULL,FALSE,1,TRUE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* FILE_AsyncReadService (INTERNAL)
|
||||
*
|
||||
* This function is called while the client is waiting on the
|
||||
* server, so we can't make any server calls here.
|
||||
*/
|
||||
static void FILE_AsyncReadService(async_private *ovp)
|
||||
{
|
||||
async_fileio *fileio = (async_fileio*) ovp;
|
||||
LPOVERLAPPED lpOverlapped = fileio->lpOverlapped;
|
||||
int result, r;
|
||||
int already = lpOverlapped->InternalHigh;
|
||||
|
||||
TRACE("%p %p\n", lpOverlapped, fileio->buffer );
|
||||
|
||||
/* check to see if the data is ready (non-blocking) */
|
||||
|
||||
if ( fileio->fd_type == FD_TYPE_SOCKET )
|
||||
result = read (ovp->fd, &fileio->buffer[already], fileio->count - already);
|
||||
else
|
||||
{
|
||||
result = pread (ovp->fd, &fileio->buffer[already], fileio->count - already,
|
||||
OVERLAPPED_OFFSET (lpOverlapped) + already);
|
||||
if ((result < 0) && (errno == ESPIPE))
|
||||
result = read (ovp->fd, &fileio->buffer[already], fileio->count - already);
|
||||
}
|
||||
|
||||
if ( (result<0) && ((errno == EAGAIN) || (errno == EINTR)))
|
||||
{
|
||||
TRACE("Deferred read %d\n",errno);
|
||||
r = STATUS_PENDING;
|
||||
goto async_end;
|
||||
}
|
||||
|
||||
/* check to see if the transfer is complete */
|
||||
if(result<0)
|
||||
{
|
||||
r = FILE_GetNtStatus ();
|
||||
goto async_end;
|
||||
}
|
||||
else if ( result == 0 )
|
||||
{
|
||||
r = ( lpOverlapped->InternalHigh ? STATUS_SUCCESS : STATUS_END_OF_FILE );
|
||||
goto async_end;
|
||||
}
|
||||
|
||||
lpOverlapped->InternalHigh += result;
|
||||
TRACE("read %d more bytes %ld/%d so far\n",result,lpOverlapped->InternalHigh,fileio->count);
|
||||
|
||||
if(lpOverlapped->InternalHigh >= fileio->count || fileio->fd_type == FD_TYPE_SOCKET )
|
||||
r = STATUS_SUCCESS;
|
||||
else
|
||||
r = STATUS_PENDING;
|
||||
|
||||
async_end:
|
||||
lpOverlapped->Internal = r;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* FILE_ReadFileEx (INTERNAL)
|
||||
*/
|
||||
static BOOL FILE_ReadFileEx(HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
|
||||
LPOVERLAPPED overlapped,
|
||||
LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
|
||||
HANDLE hEvent)
|
||||
{
|
||||
async_fileio *ovp;
|
||||
int fd;
|
||||
int flags;
|
||||
enum fd_type type;
|
||||
|
||||
TRACE("file %p to buf %p num %ld %p func %p\n",
|
||||
hFile, buffer, bytesToRead, overlapped, lpCompletionRoutine);
|
||||
|
||||
/* check that there is an overlapped struct */
|
||||
if (overlapped==NULL)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
fd = FILE_GetUnixHandleType ( hFile, GENERIC_READ, &type, &flags);
|
||||
if ( fd < 0 )
|
||||
{
|
||||
WARN ( "Couldn't get FD\n" );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ovp = (async_fileio*) HeapAlloc(GetProcessHeap(), 0, sizeof (async_fileio));
|
||||
if(!ovp)
|
||||
{
|
||||
TRACE("HeapAlloc Failed\n");
|
||||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||||
goto error;
|
||||
}
|
||||
|
||||
ovp->async.ops = ( lpCompletionRoutine ? &fileio_async_ops : &fileio_nocomp_async_ops );
|
||||
ovp->async.handle = hFile;
|
||||
ovp->async.fd = fd;
|
||||
ovp->async.type = ASYNC_TYPE_READ;
|
||||
ovp->async.func = FILE_AsyncReadService;
|
||||
ovp->async.event = hEvent;
|
||||
ovp->lpOverlapped = overlapped;
|
||||
ovp->count = bytesToRead;
|
||||
ovp->completion_func = lpCompletionRoutine;
|
||||
ovp->buffer = buffer;
|
||||
ovp->fd_type = type;
|
||||
|
||||
return !register_new_async (&ovp->async);
|
||||
|
||||
error:
|
||||
close (fd);
|
||||
return FALSE;
|
||||
LPOVERLAPPED_COMPLETION_ROUTINE cr = (LPOVERLAPPED_COMPLETION_ROUTINE)apc_user;
|
||||
|
||||
cr(RtlNtStatusToDosError(io_status->u.Status), len, (LPOVERLAPPED)io_status);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* ReadFileEx (KERNEL32.@)
|
||||
*/
|
||||
BOOL WINAPI ReadFileEx(HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
|
||||
LPOVERLAPPED overlapped,
|
||||
LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
|
||||
LPOVERLAPPED overlapped,
|
||||
LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
|
||||
{
|
||||
overlapped->InternalHigh = 0;
|
||||
return FILE_ReadFileEx(hFile,buffer,bytesToRead,overlapped,lpCompletionRoutine, INVALID_HANDLE_VALUE);
|
||||
}
|
||||
LARGE_INTEGER offset;
|
||||
NTSTATUS status;
|
||||
PIO_STATUS_BLOCK io_status;
|
||||
|
||||
static BOOL FILE_TimeoutRead(HANDLE hFile, LPVOID buffer, DWORD bytesToRead, LPDWORD bytesRead)
|
||||
{
|
||||
OVERLAPPED ov;
|
||||
BOOL r = FALSE;
|
||||
|
||||
TRACE("%p %p %ld %p\n", hFile, buffer, bytesToRead, bytesRead );
|
||||
|
||||
ZeroMemory(&ov, sizeof (OVERLAPPED));
|
||||
if(STATUS_SUCCESS==NtCreateEvent(&ov.hEvent, SYNCHRONIZE, NULL, 0, 0))
|
||||
if (!overlapped)
|
||||
{
|
||||
if(FILE_ReadFileEx(hFile, buffer, bytesToRead, &ov, NULL, ov.hEvent))
|
||||
{
|
||||
r = GetOverlappedResult(hFile, &ov, bytesRead, TRUE);
|
||||
}
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
CloseHandle(ov.hEvent);
|
||||
return r;
|
||||
|
||||
offset.s.LowPart = overlapped->Offset;
|
||||
offset.s.HighPart = overlapped->OffsetHigh;
|
||||
io_status = (PIO_STATUS_BLOCK)overlapped;
|
||||
io_status->u.Status = STATUS_PENDING;
|
||||
|
||||
status = NtReadFile(hFile, NULL, FILE_ReadWriteApc, lpCompletionRoutine,
|
||||
io_status, buffer, bytesToRead, &offset, NULL);
|
||||
|
||||
if (status)
|
||||
{
|
||||
SetLastError( RtlNtStatusToDosError(status) );
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* ReadFile (KERNEL32.@)
|
||||
*/
|
||||
BOOL WINAPI ReadFile( HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
|
||||
LPDWORD bytesRead, LPOVERLAPPED overlapped )
|
||||
LPDWORD bytesRead, LPOVERLAPPED overlapped )
|
||||
{
|
||||
int unix_handle, result, flags;
|
||||
enum fd_type type;
|
||||
|
||||
LARGE_INTEGER offset;
|
||||
PLARGE_INTEGER poffset = NULL;
|
||||
IO_STATUS_BLOCK iosb;
|
||||
PIO_STATUS_BLOCK io_status = &iosb;
|
||||
HANDLE hEvent = 0;
|
||||
NTSTATUS status;
|
||||
|
||||
TRACE("%p %p %ld %p %p\n", hFile, buffer, bytesToRead,
|
||||
bytesRead, overlapped );
|
||||
|
||||
if (bytesRead) *bytesRead = 0; /* Do this before anything else */
|
||||
if (!bytesToRead) return TRUE;
|
||||
|
||||
if (IsBadReadPtr(buffer, bytesToRead))
|
||||
{
|
||||
SetLastError(ERROR_WRITE_FAULT); /* FIXME */
|
||||
return FALSE;
|
||||
}
|
||||
if (is_console_handle(hFile))
|
||||
return FILE_ReadConsole(hFile, buffer, bytesToRead, bytesRead, NULL);
|
||||
|
||||
unix_handle = FILE_GetUnixHandleType( hFile, GENERIC_READ, &type, &flags );
|
||||
|
||||
if (flags & FD_FLAG_OVERLAPPED)
|
||||
if (overlapped != NULL)
|
||||
{
|
||||
if (unix_handle == -1) return FALSE;
|
||||
if ( (overlapped==NULL) || NtResetEvent( overlapped->hEvent, NULL ) )
|
||||
{
|
||||
TRACE("Overlapped not specified or invalid event flag\n");
|
||||
close(unix_handle);
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
close(unix_handle);
|
||||
overlapped->InternalHigh = 0;
|
||||
|
||||
if(!FILE_ReadFileEx(hFile, buffer, bytesToRead, overlapped, NULL, overlapped->hEvent))
|
||||
return FALSE;
|
||||
|
||||
if ( !GetOverlappedResult (hFile, overlapped, bytesRead, FALSE) )
|
||||
{
|
||||
if ( GetLastError() == ERROR_IO_INCOMPLETE )
|
||||
SetLastError ( ERROR_IO_PENDING );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
offset.s.LowPart = overlapped->Offset;
|
||||
offset.s.HighPart = overlapped->OffsetHigh;
|
||||
poffset = &offset;
|
||||
hEvent = overlapped->hEvent;
|
||||
io_status = (PIO_STATUS_BLOCK)overlapped;
|
||||
}
|
||||
if (flags & FD_FLAG_TIMEOUT)
|
||||
io_status->u.Status = STATUS_PENDING;
|
||||
io_status->Information = 0;
|
||||
|
||||
status = NtReadFile(hFile, hEvent, NULL, NULL, io_status, buffer, bytesToRead, poffset, NULL);
|
||||
|
||||
if (status != STATUS_PENDING && bytesRead)
|
||||
*bytesRead = io_status->Information;
|
||||
|
||||
if (status && status != STATUS_END_OF_FILE)
|
||||
{
|
||||
close(unix_handle);
|
||||
return FILE_TimeoutRead(hFile, buffer, bytesToRead, bytesRead);
|
||||
SetLastError( RtlNtStatusToDosError(status) );
|
||||
return FALSE;
|
||||
}
|
||||
switch(type)
|
||||
{
|
||||
case FD_TYPE_SMB:
|
||||
return SMB_ReadFile(hFile, buffer, bytesToRead, bytesRead, NULL);
|
||||
|
||||
case FD_TYPE_DEFAULT:
|
||||
/* normal unix files */
|
||||
if (unix_handle == -1) return FALSE;
|
||||
if (overlapped)
|
||||
{
|
||||
DWORD highOffset = overlapped->OffsetHigh;
|
||||
if ( (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, overlapped->Offset,
|
||||
&highOffset, FILE_BEGIN)) &&
|
||||
(GetLastError() != NO_ERROR) )
|
||||
{
|
||||
close(unix_handle);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (unix_handle == -1)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(overlapped)
|
||||
{
|
||||
off_t offset = OVERLAPPED_OFFSET(overlapped);
|
||||
if(lseek(unix_handle, offset, SEEK_SET) == -1)
|
||||
{
|
||||
close(unix_handle);
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* code for synchronous reads */
|
||||
while ((result = read( unix_handle, buffer, bytesToRead )) == -1)
|
||||
{
|
||||
if ((errno == EAGAIN) || (errno == EINTR)) continue;
|
||||
if ((errno == EFAULT) && !IsBadWritePtr( buffer, bytesToRead )) continue;
|
||||
FILE_SetDosError();
|
||||
break;
|
||||
}
|
||||
close( unix_handle );
|
||||
if (result == -1) return FALSE;
|
||||
if (bytesRead) *bytesRead = result;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* FILE_AsyncWriteService (INTERNAL)
|
||||
*
|
||||
* This function is called while the client is waiting on the
|
||||
* server, so we can't make any server calls here.
|
||||
* WriteFileEx (KERNEL32.@)
|
||||
*/
|
||||
static void FILE_AsyncWriteService(struct async_private *ovp)
|
||||
BOOL WINAPI WriteFileEx(HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite,
|
||||
LPOVERLAPPED overlapped,
|
||||
LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
|
||||
{
|
||||
async_fileio *fileio = (async_fileio *) ovp;
|
||||
LPOVERLAPPED lpOverlapped = fileio->lpOverlapped;
|
||||
int result, r;
|
||||
int already = lpOverlapped->InternalHigh;
|
||||
LARGE_INTEGER offset;
|
||||
NTSTATUS status;
|
||||
PIO_STATUS_BLOCK io_status;
|
||||
|
||||
TRACE("(%p %p)\n",lpOverlapped,fileio->buffer);
|
||||
|
||||
/* write some data (non-blocking) */
|
||||
|
||||
if ( fileio->fd_type == FD_TYPE_SOCKET )
|
||||
result = write(ovp->fd, &fileio->buffer[already], fileio->count - already);
|
||||
else
|
||||
{
|
||||
result = pwrite(ovp->fd, &fileio->buffer[already], fileio->count - already,
|
||||
OVERLAPPED_OFFSET (lpOverlapped) + already);
|
||||
if ((result < 0) && (errno == ESPIPE))
|
||||
result = write(ovp->fd, &fileio->buffer[already], fileio->count - already);
|
||||
}
|
||||
|
||||
if ( (result<0) && ((errno == EAGAIN) || (errno == EINTR)))
|
||||
{
|
||||
r = STATUS_PENDING;
|
||||
goto async_end;
|
||||
}
|
||||
|
||||
/* check to see if the transfer is complete */
|
||||
if(result<0)
|
||||
{
|
||||
r = FILE_GetNtStatus ();
|
||||
goto async_end;
|
||||
}
|
||||
|
||||
lpOverlapped->InternalHigh += result;
|
||||
|
||||
TRACE("wrote %d more bytes %ld/%d so far\n",result,lpOverlapped->InternalHigh,fileio->count);
|
||||
|
||||
if(lpOverlapped->InternalHigh < fileio->count)
|
||||
r = STATUS_PENDING;
|
||||
else
|
||||
r = STATUS_SUCCESS;
|
||||
|
||||
async_end:
|
||||
lpOverlapped->Internal = r;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* FILE_WriteFileEx
|
||||
*/
|
||||
static BOOL FILE_WriteFileEx(HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite,
|
||||
LPOVERLAPPED overlapped,
|
||||
LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
|
||||
HANDLE hEvent)
|
||||
{
|
||||
async_fileio *ovp;
|
||||
int fd;
|
||||
int flags;
|
||||
enum fd_type type;
|
||||
|
||||
TRACE("file %p to buf %p num %ld %p func %p handle %p\n",
|
||||
hFile, buffer, bytesToWrite, overlapped, lpCompletionRoutine, hEvent);
|
||||
TRACE("%p %p %ld %p %p\n",
|
||||
hFile, buffer, bytesToWrite, overlapped, lpCompletionRoutine);
|
||||
|
||||
if (overlapped == NULL)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
offset.s.LowPart = overlapped->Offset;
|
||||
offset.s.HighPart = overlapped->OffsetHigh;
|
||||
|
||||
fd = FILE_GetUnixHandleType ( hFile, GENERIC_WRITE, &type, &flags );
|
||||
if ( fd < 0 )
|
||||
{
|
||||
TRACE( "Couldn't get FD\n" );
|
||||
return FALSE;
|
||||
}
|
||||
io_status = (PIO_STATUS_BLOCK)overlapped;
|
||||
io_status->u.Status = STATUS_PENDING;
|
||||
|
||||
ovp = (async_fileio*) HeapAlloc(GetProcessHeap(), 0, sizeof (async_fileio));
|
||||
if(!ovp)
|
||||
{
|
||||
TRACE("HeapAlloc Failed\n");
|
||||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||||
goto error;
|
||||
}
|
||||
status = NtWriteFile(hFile, NULL, FILE_ReadWriteApc, lpCompletionRoutine,
|
||||
io_status, buffer, bytesToWrite, &offset, NULL);
|
||||
|
||||
ovp->async.ops = ( lpCompletionRoutine ? &fileio_async_ops : &fileio_nocomp_async_ops );
|
||||
ovp->async.handle = hFile;
|
||||
ovp->async.fd = fd;
|
||||
ovp->async.type = ASYNC_TYPE_WRITE;
|
||||
ovp->async.func = FILE_AsyncWriteService;
|
||||
ovp->lpOverlapped = overlapped;
|
||||
ovp->async.event = hEvent;
|
||||
ovp->buffer = (LPVOID) buffer;
|
||||
ovp->count = bytesToWrite;
|
||||
ovp->completion_func = lpCompletionRoutine;
|
||||
ovp->fd_type = type;
|
||||
|
||||
return !register_new_async (&ovp->async);
|
||||
|
||||
error:
|
||||
close (fd);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* WriteFileEx (KERNEL32.@)
|
||||
*/
|
||||
BOOL WINAPI WriteFileEx(HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite,
|
||||
LPOVERLAPPED overlapped,
|
||||
LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
|
||||
{
|
||||
overlapped->InternalHigh = 0;
|
||||
|
||||
return FILE_WriteFileEx(hFile, buffer, bytesToWrite, overlapped, lpCompletionRoutine, INVALID_HANDLE_VALUE);
|
||||
if (status) SetLastError( RtlNtStatusToDosError(status) );
|
||||
return !status;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* WriteFile (KERNEL32.@)
|
||||
*/
|
||||
BOOL WINAPI WriteFile( HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite,
|
||||
LPDWORD bytesWritten, LPOVERLAPPED overlapped )
|
||||
LPDWORD bytesWritten, LPOVERLAPPED overlapped )
|
||||
{
|
||||
int unix_handle, result, flags;
|
||||
enum fd_type type;
|
||||
HANDLE hEvent = NULL;
|
||||
LARGE_INTEGER offset;
|
||||
PLARGE_INTEGER poffset = NULL;
|
||||
NTSTATUS status;
|
||||
IO_STATUS_BLOCK iosb;
|
||||
PIO_STATUS_BLOCK piosb = &iosb;
|
||||
|
||||
TRACE("%p %p %ld %p %p\n", hFile, buffer, bytesToWrite,
|
||||
bytesWritten, overlapped );
|
||||
|
||||
if (bytesWritten) *bytesWritten = 0; /* Do this before anything else */
|
||||
if (!bytesToWrite) return TRUE;
|
||||
TRACE("%p %p %ld %p %p\n",
|
||||
hFile, buffer, bytesToWrite, bytesWritten, overlapped );
|
||||
|
||||
if (is_console_handle(hFile))
|
||||
return FILE_WriteConsole(hFile, buffer, bytesToWrite, bytesWritten, NULL);
|
||||
|
||||
unix_handle = FILE_GetUnixHandleType( hFile, GENERIC_WRITE, &type, &flags );
|
||||
|
||||
if (flags & FD_FLAG_OVERLAPPED)
|
||||
|
||||
if (IsBadReadPtr(buffer, bytesToWrite))
|
||||
{
|
||||
if (unix_handle == -1) return FALSE;
|
||||
if ( (overlapped==NULL) || NtResetEvent( overlapped->hEvent, NULL ) )
|
||||
{
|
||||
TRACE("Overlapped not specified or invalid event flag\n");
|
||||
close(unix_handle);
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
close(unix_handle);
|
||||
overlapped->InternalHigh = 0;
|
||||
|
||||
if(!FILE_WriteFileEx(hFile, buffer, bytesToWrite, overlapped, NULL, overlapped->hEvent))
|
||||
return FALSE;
|
||||
|
||||
if ( !GetOverlappedResult (hFile, overlapped, bytesWritten, FALSE) )
|
||||
{
|
||||
if ( GetLastError() == ERROR_IO_INCOMPLETE )
|
||||
SetLastError ( ERROR_IO_PENDING );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
SetLastError(ERROR_READ_FAULT); /* FIXME */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
switch(type)
|
||||
if (overlapped)
|
||||
{
|
||||
case FD_TYPE_DEFAULT:
|
||||
if (unix_handle == -1) return FALSE;
|
||||
|
||||
if(overlapped)
|
||||
{
|
||||
DWORD highOffset = overlapped->OffsetHigh;
|
||||
if ( (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, overlapped->Offset,
|
||||
&highOffset, FILE_BEGIN)) &&
|
||||
(GetLastError() != NO_ERROR) )
|
||||
{
|
||||
close(unix_handle);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (unix_handle == -1)
|
||||
return FALSE;
|
||||
if (overlapped)
|
||||
{
|
||||
close(unix_handle);
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
offset.s.LowPart = overlapped->Offset;
|
||||
offset.s.HighPart = overlapped->OffsetHigh;
|
||||
poffset = &offset;
|
||||
hEvent = overlapped->hEvent;
|
||||
piosb = (PIO_STATUS_BLOCK)overlapped;
|
||||
}
|
||||
piosb->u.Status = STATUS_PENDING;
|
||||
piosb->Information = 0;
|
||||
|
||||
if(overlapped)
|
||||
status = NtWriteFile(hFile, hEvent, NULL, NULL, piosb,
|
||||
buffer, bytesToWrite, poffset, NULL);
|
||||
if (status)
|
||||
{
|
||||
off_t offset = OVERLAPPED_OFFSET(overlapped);
|
||||
if(lseek(unix_handle, offset, SEEK_SET) == -1)
|
||||
{
|
||||
close(unix_handle);
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
SetLastError( RtlNtStatusToDosError(status) );
|
||||
return FALSE;
|
||||
}
|
||||
if (bytesWritten) *bytesWritten = piosb->Information;
|
||||
|
||||
/* synchronous file write */
|
||||
while ((result = write( unix_handle, buffer, bytesToWrite )) == -1)
|
||||
{
|
||||
if ((errno == EAGAIN) || (errno == EINTR)) continue;
|
||||
if ((errno == EFAULT) && !IsBadReadPtr( buffer, bytesToWrite )) continue;
|
||||
if (errno == ENOSPC)
|
||||
SetLastError( ERROR_DISK_FULL );
|
||||
else
|
||||
FILE_SetDosError();
|
||||
break;
|
||||
}
|
||||
close( unix_handle );
|
||||
if (result == -1) return FALSE;
|
||||
if (bytesWritten) *bytesWritten = result;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
|
57
files/smb.c
57
files/smb.c
|
@ -100,6 +100,8 @@
|
|||
#include <netdb.h>
|
||||
#endif
|
||||
|
||||
#define NONAMELESSUNION
|
||||
#define NONAMELESSSTRUCT
|
||||
#include "winerror.h"
|
||||
#include "windef.h"
|
||||
#include "winbase.h"
|
||||
|
@ -202,7 +204,7 @@ static inline CHAR *SMB_nextSepA (CHAR *s) {while (*s && !SMB_isSepA (*s)) s++;
|
|||
* replacing separators with null characters
|
||||
*/
|
||||
|
||||
USHORT SMB_MultiplexId = 0;
|
||||
static USHORT SMB_MultiplexId = 0;
|
||||
|
||||
struct NB_Buffer
|
||||
{
|
||||
|
@ -1532,16 +1534,15 @@ done:
|
|||
return handle;
|
||||
}
|
||||
|
||||
static BOOL SMB_GetSmbInfo(HANDLE hFile, USHORT *tree_id, USHORT *user_id, USHORT *dialect, USHORT *file_id, LPDWORD offset)
|
||||
static NTSTATUS SMB_GetSmbInfo(HANDLE hFile, USHORT *tree_id, USHORT *user_id, USHORT *dialect, USHORT *file_id, LPDWORD offset)
|
||||
{
|
||||
int r;
|
||||
NTSTATUS status;
|
||||
|
||||
SERVER_START_REQ( get_smb_info )
|
||||
{
|
||||
req->handle = hFile;
|
||||
req->flags = 0;
|
||||
SetLastError(0);
|
||||
r = wine_server_call_err( req );
|
||||
status = wine_server_call( req );
|
||||
if(tree_id)
|
||||
*tree_id = reply->tree_id;
|
||||
if(user_id)
|
||||
|
@ -1555,12 +1556,12 @@ static BOOL SMB_GetSmbInfo(HANDLE hFile, USHORT *tree_id, USHORT *user_id, USHOR
|
|||
}
|
||||
SERVER_END_REQ;
|
||||
|
||||
return !r;
|
||||
return status;
|
||||
}
|
||||
|
||||
static BOOL SMB_SetOffset(HANDLE hFile, DWORD offset)
|
||||
static NTSTATUS SMB_SetOffset(HANDLE hFile, DWORD offset)
|
||||
{
|
||||
int r;
|
||||
NTSTATUS status;
|
||||
|
||||
TRACE("offset = %08lx\n",offset);
|
||||
|
||||
|
@ -1569,61 +1570,53 @@ static BOOL SMB_SetOffset(HANDLE hFile, DWORD offset)
|
|||
req->handle = hFile;
|
||||
req->flags = SMBINFO_SET_OFFSET;
|
||||
req->offset = offset;
|
||||
SetLastError(0);
|
||||
r = wine_server_call_err( req );
|
||||
status = wine_server_call( req );
|
||||
/* if(offset)
|
||||
*offset = reply->offset; */
|
||||
}
|
||||
SERVER_END_REQ;
|
||||
|
||||
return !r;
|
||||
return status;
|
||||
}
|
||||
|
||||
BOOL WINAPI SMB_ReadFile(HANDLE hFile, LPVOID buffer, DWORD bytesToRead, LPDWORD bytesRead, LPOVERLAPPED lpOverlapped)
|
||||
NTSTATUS WINAPI SMB_ReadFile(HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
|
||||
PIO_STATUS_BLOCK io_status)
|
||||
{
|
||||
int fd;
|
||||
DWORD total, count, offset;
|
||||
DWORD count, offset;
|
||||
USHORT user_id, tree_id, dialect, file_id, read;
|
||||
BOOL r=TRUE;
|
||||
|
||||
TRACE("%p %p %ld %p\n", hFile, buffer, bytesToRead, bytesRead);
|
||||
TRACE("%p %p %ld %p\n", hFile, buffer, bytesToRead, io_status);
|
||||
|
||||
if(!SMB_GetSmbInfo(hFile, &tree_id, &user_id, &dialect, &file_id, &offset))
|
||||
return FALSE;
|
||||
io_status->Information = 0;
|
||||
|
||||
io_status->u.Status = SMB_GetSmbInfo(hFile, &tree_id, &user_id, &dialect, &file_id, &offset);
|
||||
if (io_status->u.Status) return io_status->u.Status;
|
||||
|
||||
fd = FILE_GetUnixHandle(hFile, GENERIC_READ);
|
||||
if(fd<0)
|
||||
return FALSE;
|
||||
if (fd<0) return io_status->u.Status = STATUS_INVALID_HANDLE;
|
||||
|
||||
total = 0;
|
||||
while(1)
|
||||
{
|
||||
count = bytesToRead - total;
|
||||
count = bytesToRead - io_status->Information;
|
||||
if(count>0x400)
|
||||
count = 0x400;
|
||||
if(count==0)
|
||||
break;
|
||||
read = 0;
|
||||
r = SMB_Read(fd, tree_id, user_id, dialect, file_id, offset, buffer, count, &read);
|
||||
if(!r)
|
||||
if (!SMB_Read(fd, tree_id, user_id, dialect, file_id, offset, buffer, count, &read))
|
||||
break;
|
||||
if(!read)
|
||||
break;
|
||||
total += read;
|
||||
io_status->Information += read;
|
||||
buffer = (char*)buffer + read;
|
||||
offset += read;
|
||||
if(total>=bytesToRead)
|
||||
if(io_status->Information >= bytesToRead)
|
||||
break;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
if(bytesRead)
|
||||
*bytesRead = total;
|
||||
|
||||
if(!SMB_SetOffset(hFile, offset))
|
||||
return FALSE;
|
||||
|
||||
return r;
|
||||
return io_status->u.Status = SMB_SetOffset(hFile, offset);
|
||||
}
|
||||
|
||||
SMB_DIR* WINAPI SMB_FindFirst(LPCWSTR name)
|
||||
|
|
|
@ -22,7 +22,8 @@
|
|||
extern inline int SMB_isSepW (WCHAR c) {return (c == '\\' || c == '/');}
|
||||
extern inline int SMB_isUNCW (LPCWSTR filename) {return (filename && SMB_isSepW (filename[0]) && SMB_isSepW (filename[1]));}
|
||||
|
||||
extern BOOL WINAPI SMB_ReadFile(HANDLE hFile, LPVOID buffer, DWORD bytesToRead, LPDWORD bytesRead, LPOVERLAPPED lpOverlapped);
|
||||
NTSTATUS WINAPI SMB_ReadFile(HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
|
||||
PIO_STATUS_BLOCK io_status);
|
||||
extern HANDLE WINAPI SMB_CreateFileW( LPCWSTR filename, DWORD access, DWORD sharing,
|
||||
LPSECURITY_ATTRIBUTES sa, DWORD creation,
|
||||
DWORD attributes, HANDLE template );
|
||||
|
|
|
@ -27,20 +27,17 @@
|
|||
#define __WINE_ASYNC_H
|
||||
|
||||
#include "wine/server.h"
|
||||
#include "winternl.h"
|
||||
|
||||
struct async_private;
|
||||
|
||||
typedef void (*async_handler)(struct async_private *ovp);
|
||||
typedef void (CALLBACK *async_call_completion_func)(ULONG_PTR data);
|
||||
typedef DWORD (*async_get_status)(const struct async_private *ovp);
|
||||
typedef DWORD (*async_get_count)(const struct async_private *ovp);
|
||||
typedef void (*async_set_status)(struct async_private *ovp, const DWORD status);
|
||||
typedef void (*async_cleanup)(struct async_private *ovp);
|
||||
|
||||
typedef struct async_ops
|
||||
{
|
||||
async_get_status get_status;
|
||||
async_set_status set_status;
|
||||
async_get_count get_count;
|
||||
async_call_completion_func call_completion;
|
||||
async_cleanup cleanup;
|
||||
|
@ -48,43 +45,50 @@ typedef struct async_ops
|
|||
|
||||
typedef struct async_private
|
||||
{
|
||||
struct async_ops *ops;
|
||||
HANDLE handle;
|
||||
HANDLE event;
|
||||
int fd;
|
||||
async_handler func;
|
||||
int type;
|
||||
struct async_private *next;
|
||||
struct async_private *prev;
|
||||
struct async_ops* ops;
|
||||
HANDLE handle;
|
||||
HANDLE event;
|
||||
int fd;
|
||||
async_handler func;
|
||||
int type;
|
||||
IO_STATUS_BLOCK* iosb;
|
||||
struct async_private* next;
|
||||
struct async_private* prev;
|
||||
} async_private;
|
||||
|
||||
/* All functions declared static for Dll separation purposes */
|
||||
static void CALLBACK call_user_apc( ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3 )
|
||||
{
|
||||
PAPCFUNC func = (PAPCFUNC)arg1;
|
||||
func( arg2 );
|
||||
}
|
||||
|
||||
inline static void finish_async( async_private *ovp )
|
||||
{
|
||||
if(ovp->prev)
|
||||
if (ovp->prev)
|
||||
ovp->prev->next = ovp->next;
|
||||
else
|
||||
NtCurrentTeb()->pending_list = ovp->next;
|
||||
|
||||
if(ovp->next)
|
||||
if (ovp->next)
|
||||
ovp->next->prev = ovp->prev;
|
||||
|
||||
ovp->next = ovp->prev = NULL;
|
||||
|
||||
close( ovp->fd );
|
||||
if( ovp->event != INVALID_HANDLE_VALUE )
|
||||
close(ovp->fd);
|
||||
if ( ovp->event != INVALID_HANDLE_VALUE )
|
||||
NtSetEvent( ovp->event, NULL );
|
||||
|
||||
if ( ovp->ops->call_completion )
|
||||
QueueUserAPC( ovp->ops->call_completion, GetCurrentThread(), (ULONG_PTR)ovp );
|
||||
NtQueueApcThread( GetCurrentThread(), call_user_apc,
|
||||
(ULONG_PTR)ovp->ops->call_completion, (ULONG_PTR)ovp, 0 );
|
||||
else
|
||||
ovp->ops->cleanup ( ovp );
|
||||
ovp->ops->cleanup( ovp );
|
||||
}
|
||||
|
||||
inline static BOOL __register_async( async_private *ovp, const DWORD status )
|
||||
inline static NTSTATUS __register_async( async_private *ovp, const DWORD status )
|
||||
{
|
||||
BOOL ret;
|
||||
NTSTATUS ret;
|
||||
|
||||
SERVER_START_REQ( register_async )
|
||||
{
|
||||
|
@ -97,23 +101,20 @@ inline static BOOL __register_async( async_private *ovp, const DWORD status )
|
|||
}
|
||||
SERVER_END_REQ;
|
||||
|
||||
if ( ret ) {
|
||||
SetLastError( RtlNtStatusToDosError(ret) );
|
||||
ovp->ops->set_status ( ovp, ret );
|
||||
}
|
||||
if (ret) ovp->iosb->u.Status = ret;
|
||||
|
||||
if ( ovp->ops->get_status (ovp) != STATUS_PENDING )
|
||||
finish_async (ovp);
|
||||
if ( ovp->iosb->u.Status != STATUS_PENDING )
|
||||
finish_async(ovp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define register_old_async(ovp) \
|
||||
__register_async (ovp, ovp->ops->get_status( ovp ));
|
||||
__register_async(ovp, ovp->iosb->u.Status);
|
||||
|
||||
inline static BOOL register_new_async( async_private *ovp )
|
||||
inline static NTSTATUS register_new_async( async_private *ovp )
|
||||
{
|
||||
ovp->ops->set_status ( ovp, STATUS_PENDING );
|
||||
ovp->iosb->u.Status = STATUS_PENDING;
|
||||
|
||||
ovp->next = NtCurrentTeb()->pending_list;
|
||||
ovp->prev = NULL;
|
||||
|
@ -123,13 +124,13 @@ inline static BOOL register_new_async( async_private *ovp )
|
|||
return __register_async( ovp, STATUS_PENDING );
|
||||
}
|
||||
|
||||
inline static BOOL cancel_async ( async_private *ovp )
|
||||
inline static NTSTATUS cancel_async( async_private *ovp )
|
||||
{
|
||||
/* avoid multiple cancellations */
|
||||
if ( ovp->ops->get_status( ovp ) != STATUS_PENDING )
|
||||
return 0;
|
||||
ovp->ops->set_status ( ovp, STATUS_CANCELLED );
|
||||
return __register_async ( ovp, STATUS_CANCELLED );
|
||||
if ( ovp->iosb->u.Status != STATUS_PENDING )
|
||||
return STATUS_SUCCESS;
|
||||
ovp->iosb->u.Status = STATUS_CANCELLED;
|
||||
return __register_async( ovp, STATUS_CANCELLED );
|
||||
}
|
||||
|
||||
#endif /* __WINE_ASYNC_H */
|
||||
|
|
|
@ -991,6 +991,7 @@ NTSTATUS WINAPI NtUnlockVirtualMemory(HANDLE,PVOID*,ULONG*,ULONG);
|
|||
NTSTATUS WINAPI NtUnmapViewOfSection(HANDLE,PVOID);
|
||||
NTSTATUS WINAPI NtWaitForSingleObject(HANDLE,BOOLEAN,PLARGE_INTEGER);
|
||||
NTSTATUS WINAPI NtWaitForMultipleObjects(ULONG,const HANDLE*,BOOLEAN,BOOLEAN,PLARGE_INTEGER);
|
||||
NTSTATUS WINAPI NtWriteFile(HANDLE,HANDLE,PIO_APC_ROUTINE,PVOID,PIO_STATUS_BLOCK,const void*,ULONG,PLARGE_INTEGER,PULONG);
|
||||
|
||||
void WINAPI RtlAcquirePebLock(void);
|
||||
BYTE WINAPI RtlAcquireResourceExclusive(LPRTL_RWLOCK,BYTE);
|
||||
|
|
Loading…
Reference in New Issue