Author: ion Date: Tue Feb 21 23:27:45 2012 New Revision: 55801
URL: http://svn.reactos.org/svn/reactos?rev=55801&view=rev Log: [KERNEL32]: Rewrite the File Change Notification APIs since they were pretty much wrong (not surprising as we don't implement the functionality in the kernel/filesystems). [KERNEL32]: Enable support for asynchronous I/O callbacks with SxS/Fusion activation context support. Right now only added to the file Change notification APIs, but ReadFileEx and WriteFileEx should use them too.
Modified: trunk/reactos/dll/win32/kernel32/client/file/cnotify.c trunk/reactos/dll/win32/kernel32/include/kernel32.h trunk/reactos/dll/win32/kernel32/wine/actctx.c
Modified: trunk/reactos/dll/win32/kernel32/client/file/cnotify.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/kernel32/client/f... ============================================================================== --- trunk/reactos/dll/win32/kernel32/client/file/cnotify.c [iso-8859-1] (original) +++ trunk/reactos/dll/win32/kernel32/client/file/cnotify.c [iso-8859-1] Tue Feb 21 23:27:45 2012 @@ -9,30 +9,106 @@ * Created 01/11/98 */
-/* INCLUDES *****************************************************************/ +/* INCLUDES *******************************************************************/
#include <k32.h> #define NDEBUG #include <debug.h>
-/* FUNCTIONS ****************************************************************/ - -/* - * @implemented - */ -BOOL WINAPI -FindCloseChangeNotification (HANDLE hChangeHandle) -{ - NTSTATUS Status = NtClose(hChangeHandle); - if(!NT_SUCCESS(Status)) - { - BaseSetLastNTError(Status); - return FALSE; - } - - return TRUE; -} - +/* GLOBALS ********************************************************************/ + +CHAR staticchangebuff[sizeof(FILE_NOTIFY_INFORMATION) + 16]; +IO_STATUS_BLOCK staticIoStatusBlock; + +/* PRIVATE FUNCTIONS **********************************************************/ + +VOID +WINAPI +BasepIoCompletion(IN PVOID ApcContext, + IN PIO_STATUS_BLOCK IoStatusBlock, + IN DWORD Reserved) +{ + PBASEP_ACTCTX_BLOCK ActivationBlock = ApcContext; + LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine; + DWORD BytesTransfered, Result; + RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME ActCtx; + PVOID ActivationContext = NULL; + + /* Setup the activation frame */ + RtlZeroMemory(&ActCtx, sizeof(ActCtx)); + ActCtx.Size = sizeof(ActCtx); + ActCtx.Format = RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER; + + /* Check if the routine returned an error */ + if (NT_ERROR(IoStatusBlock->Status)) + { + /* Convert the error code and don't copy anything */ + Result = RtlNtStatusToDosError(IoStatusBlock->Status); + BytesTransfered = 0; + } + else + { + /* Set success code and copy the bytes transferred */ + Result = ERROR_SUCCESS; + BytesTransfered = IoStatusBlock->Information; + } + + /* Read context and routine out from the activation block */ + ActivationContext = ActivationBlock->ActivationContext; + CompletionRoutine = ActivationBlock->CompletionRoutine; + + /* Check if the block should be freed */ + if (!(ActivationBlock->Flags & 1)) + { + /* Free it */ + BasepFreeActivationContextActivationBlock(ActivationBlock); + } + + /* Activate the context, call the routine, and then deactivate the context */ + RtlActivateActivationContextUnsafeFast(&ActCtx, ActivationContext); + CompletionRoutine(Result, BytesTransfered, (LPOVERLAPPED)IoStatusBlock); + RtlDeactivateActivationContextUnsafeFast(&ActCtx); +} + +VOID +WINAPI +BasepIoCompletionSimple(IN PVOID ApcContext, + IN PIO_STATUS_BLOCK IoStatusBlock, + IN DWORD Reserved) +{ + LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine = ApcContext; + DWORD Result, BytesTransfered; + + /* Check if the routine returned an error */ + if (NT_ERROR(IoStatusBlock->Status)) + { + /* Convert the error code and don't copy anything */ + Result = RtlNtStatusToDosError(IoStatusBlock->Status); + BytesTransfered = 0; + } + else + { + /* Set success code and copy the bytes transferred */ + Result = ERROR_SUCCESS; + BytesTransfered = IoStatusBlock->Information; + } + + /* Call the callback routine */ + CompletionRoutine(Result, BytesTransfered, (LPOVERLAPPED)IoStatusBlock); +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +/* + * @implemented + */ +BOOL +WINAPI +FindCloseChangeNotification(IN HANDLE hChangeHandle) +{ + /* Just close the handle */ + return CloseHandle(hChangeHandle); +}
/* * @implemented @@ -50,206 +126,255 @@ dwNotifyFilter); }
- /* * @implemented */ HANDLE WINAPI -FindFirstChangeNotificationW ( - LPCWSTR lpPathName, - BOOL bWatchSubtree, - DWORD dwNotifyFilter - ) -{ - NTSTATUS Status; - UNICODE_STRING NtPathU; - IO_STATUS_BLOCK IoStatus; - OBJECT_ATTRIBUTES ObjectAttributes; - HANDLE hDir; - - if (!RtlDosPathNameToNtPathName_U (lpPathName, - &NtPathU, - NULL, - NULL)) - { - BaseSetLastNTError(STATUS_OBJECT_PATH_SYNTAX_BAD); - return INVALID_HANDLE_VALUE; - } - - - - InitializeObjectAttributes (&ObjectAttributes, +FindFirstChangeNotificationW(IN LPCWSTR lpPathName, + IN BOOL bWatchSubtree, + IN DWORD dwNotifyFilter) +{ + NTSTATUS Status; + UNICODE_STRING NtPathU; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE hDir; + RTL_RELATIVE_NAME_U RelativeName; + PWCHAR PathBuffer; + IO_STATUS_BLOCK IoStatusBlock; + + /* Convert to NT path and get the relative name too */ + if (!RtlDosPathNameToNtPathName_U(lpPathName, + &NtPathU, + NULL, + &RelativeName)) + { + /* Bail out if the path name makes no sense */ + SetLastError(ERROR_PATH_NOT_FOUND); + return INVALID_HANDLE_VALUE; + } + + /* Save the path buffer in case we free it later */ + PathBuffer = NtPathU.Buffer; + + /* If we have a relative name... */ + if (RelativeName.RelativeName.Length) + { + /* Do a relative open with only the relative path set */ + NtPathU = RelativeName.RelativeName; + } + else + { + /* Do a full path open with no containing directory */ + RelativeName.ContainingDirectory = NULL; + } + + /* Now open the directory name that was passed in */ + InitializeObjectAttributes(&ObjectAttributes, &NtPathU, OBJ_CASE_INSENSITIVE, - NULL, + RelativeName.ContainingDirectory, NULL); - - Status = NtOpenFile (&hDir, - SYNCHRONIZE|FILE_LIST_DIRECTORY, + Status = NtOpenFile(&hDir, + SYNCHRONIZE | FILE_LIST_DIRECTORY, &ObjectAttributes, - &IoStatus, - FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, + &IoStatusBlock, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT);
- RtlFreeHeap(RtlGetProcessHeap(), - 0, - NtPathU.Buffer); - - - - if (!NT_SUCCESS(Status)) - { - BaseSetLastNTError(Status); - return INVALID_HANDLE_VALUE; - } - - Status = NtNotifyChangeDirectoryFile(hDir, - NULL, - NULL, - NULL, - &IoStatus, - NULL,//Buffer, - 0,//BufferLength, - dwNotifyFilter, - (BOOLEAN)bWatchSubtree); - if (!NT_SUCCESS(Status)) - { - NtClose(hDir); - BaseSetLastNTError(Status); - return INVALID_HANDLE_VALUE; - } - - return hDir; -} - + /* Release our buffer and relative name structure */ + RtlReleaseRelativeName(&RelativeName); + RtlFreeHeap(RtlGetProcessHeap(), 0, PathBuffer); + + /* Check if the open failed */ + if (!NT_SUCCESS(Status)) + { + /* Bail out in that case */ + BaseSetLastNTError(Status); + return INVALID_HANDLE_VALUE; + } + + /* Now setup the notification on the directory as requested */ + Status = NtNotifyChangeDirectoryFile(hDir, + NULL, + NULL, + NULL, + &staticIoStatusBlock, + staticchangebuff, + sizeof(staticchangebuff), + dwNotifyFilter, + (BOOLEAN)bWatchSubtree); + if (!NT_SUCCESS(Status)) + { + /* We failed, close the handle and convert the error */ + NtClose(hDir); + BaseSetLastNTError(Status); + hDir = INVALID_HANDLE_VALUE; + } + + /* Return the directory handle on success, or invalid handle otherwise */ + return hDir; +}
/* * @implemented */ BOOL WINAPI -FindNextChangeNotification ( - HANDLE hChangeHandle - ) -{ - IO_STATUS_BLOCK IoStatus; - NTSTATUS Status; - - Status = NtNotifyChangeDirectoryFile(hChangeHandle, - NULL, - NULL, - NULL, - &IoStatus, - NULL,//Buffer, - 0,//BufferLength, - FILE_NOTIFY_CHANGE_SECURITY,//meaningless/ignored for subsequent calls, but must contain a valid flag - 0 //meaningless/ignored for subsequent calls - ); - - if (!NT_SUCCESS(Status)) - { - BaseSetLastNTError(Status); - return FALSE; - } - - return TRUE; -} - - -extern VOID -(WINAPI ApcRoutine)(PVOID ApcContext, - struct _IO_STATUS_BLOCK* IoStatusBlock, - ULONG Reserved); - +FindNextChangeNotification(IN HANDLE hChangeHandle) +{ + NTSTATUS Status; + + /* Just call the native API directly, dealing with the non-optional parameters */ + Status = NtNotifyChangeDirectoryFile(hChangeHandle, + NULL, + NULL, + NULL, + &staticIoStatusBlock, + staticchangebuff, + sizeof(staticchangebuff), + FILE_NOTIFY_CHANGE_SECURITY, + TRUE); + if (!NT_SUCCESS(Status)) + { + /* Convert the error code and fail */ + BaseSetLastNTError(Status); + return FALSE; + } + + /* All went well */ + return TRUE; +}
/* * @implemented */ BOOL WINAPI -ReadDirectoryChangesW( - HANDLE hDirectory, - LPVOID lpBuffer OPTIONAL, - DWORD nBufferLength, - BOOL bWatchSubtree, - DWORD dwNotifyFilter, - LPDWORD lpBytesReturned, /* undefined for asych. operations */ - LPOVERLAPPED lpOverlapped OPTIONAL, - LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine /* OPTIONAL???????? */ - ) -{ - PVOID CompletionRoutine; - NTSTATUS Status; - IO_STATUS_BLOCK IoStatus; - HANDLE EventHandle; - PIO_APC_ROUTINE IoApcRoutine; - - if (lpOverlapped ) - { - if (lpCompletionRoutine) - { - CompletionRoutine = (PVOID) lpCompletionRoutine; - EventHandle = NULL; - IoApcRoutine = ApcRoutine; - } - else - { - if (((ULONG_PTR) lpOverlapped->hEvent & 1) == 0) - CompletionRoutine = (PVOID) lpOverlapped; - else - CompletionRoutine = NULL; - - EventHandle = lpOverlapped->hEvent; - IoApcRoutine = NULL; - } - - lpOverlapped->Internal = STATUS_PENDING; - } - else - { - EventHandle = NULL; - IoApcRoutine = NULL; - CompletionRoutine = NULL; - } - - Status = NtNotifyChangeDirectoryFile( - hDirectory, - EventHandle, - IoApcRoutine, - CompletionRoutine, /* ApcContext */ - lpOverlapped ? (PIO_STATUS_BLOCK) lpOverlapped : &IoStatus, - lpBuffer, - nBufferLength, - dwNotifyFilter, - (BOOLEAN)bWatchSubtree - ); - - if ((Status == STATUS_PENDING) && (!lpOverlapped)) - { - Status = NtWaitForSingleObject(hDirectory, FALSE, NULL); - - if (NT_SUCCESS(Status)) - { - Status = IoStatus.Status; - } - } - - if (!NT_SUCCESS(Status)) - { - BaseSetLastNTError(Status); - return FALSE; - } - - - /* NOTE: lpBytesReturned is undefined for asynch. operations */ - *lpBytesReturned = IoStatus.Information; - - return TRUE; -} - - - - +ReadDirectoryChangesW(IN HANDLE hDirectory, + IN LPVOID lpBuffer OPTIONAL, + IN DWORD nBufferLength, + IN BOOL bWatchSubtree, + IN DWORD dwNotifyFilter, + OUT LPDWORD lpBytesReturned, + IN LPOVERLAPPED lpOverlapped OPTIONAL, + IN LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) +{ + + NTSTATUS Status; + HANDLE EventHandle; + PVOID ApcContext; + PIO_APC_ROUTINE ApcRoutine; + PBASEP_ACTCTX_BLOCK ActivationContext = NULL; + BOOL Result = TRUE; + IO_STATUS_BLOCK IoStatusBlock; + + /* Is the caller doing this synchronously? */ + if (!lpOverlapped) + { + /* Great, just pass in the parameters */ + Status = NtNotifyChangeDirectoryFile(hDirectory, + NULL, + NULL, + NULL, + &IoStatusBlock, + lpBuffer, + nBufferLength, + dwNotifyFilter, + bWatchSubtree); + if (Status == STATUS_PENDING) + { + /* Wait for completion since we are synchronous */ + Status = NtWaitForSingleObject(hDirectory, FALSE, NULL); + if (!NT_SUCCESS(Status)) + { + /* The wait failed, bail out */ + BaseSetLastNTError(Status); + return FALSE; + } + + /* Retrieve the final status code */ + Status = IoStatusBlock.Status; + } + + /* Did the operation succeed? */ + if (NT_SUCCESS(Status)) + { + /* Return the bytes transferd and success */ + *lpBytesReturned = IoStatusBlock.Information; + return Result; + } + + /* Convert error code and return failure */ + BaseSetLastNTError(Status); + return FALSE; + } + + /* Does the caller want an APC callbac? */ + if (lpCompletionRoutine) + { + /* Don't use an event in this case */ + EventHandle = NULL; + + /* Allocate a Fusion/SxS activation context for the callback routine */ + Status = BasepAllocateActivationContextActivationBlock(1 | 2, + lpCompletionRoutine, + lpOverlapped, + &ActivationContext); + if (!NT_SUCCESS(Status)) + { + /* This failed, so abandon the call */ + BaseSetLastNTError(Status); + return FALSE; + } + + /* Use the SxS context as the APC context */ + ApcContext = ActivationContext; + if (ActivationContext) + { + /* And use a special stub routine that deals with activation */ + ApcRoutine = BasepIoCompletion; + } + else + { + /* If there was no context, however, use the simple stub routine */ + ApcContext = lpCompletionRoutine; + ApcRoutine = BasepIoCompletionSimple; + } + } + else + { + /* Use the even with no APC routine */ + EventHandle = lpOverlapped->hEvent; + ApcRoutine = 0; + + /* LPOVERLAPPED should be ignored if event is ORed with 1 */ + ApcContext = (ULONG_PTR)lpOverlapped->hEvent & 1 ? NULL : lpOverlapped; + } + + /* Set the initial status to pending and call the native API */ + lpOverlapped->Internal = STATUS_PENDING; + Status = NtNotifyChangeDirectoryFile(hDirectory, + EventHandle, + ApcRoutine, + ApcContext, + (PIO_STATUS_BLOCK)lpOverlapped, + lpBuffer, + nBufferLength, + dwNotifyFilter, + (BOOLEAN)bWatchSubtree); + if (NT_ERROR(Status)) + { + /* Normally we cleanup the context in the completon routine, but we failed */ + if (ActivationContext) BasepFreeActivationContextActivationBlock(ActivationContext); + + /* Convert the error and fail */ + BaseSetLastNTError(Status); + return FALSE; + } + + /* Return success */ + return Result; +}
/* EOF */
Modified: trunk/reactos/dll/win32/kernel32/include/kernel32.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/kernel32/include/... ============================================================================== --- trunk/reactos/dll/win32/kernel32/include/kernel32.h [iso-8859-1] (original) +++ trunk/reactos/dll/win32/kernel32/include/kernel32.h [iso-8859-1] Tue Feb 21 23:27:45 2012 @@ -143,6 +143,14 @@ BaseCurrentDirPlacementMax } BASE_CURRENT_DIR_PLACEMENT;
+typedef struct _BASEP_ACTCTX_BLOCK +{ + ULONG Flags; + PVOID ActivationContext; + PVOID CompletionContext; + LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine; +} BASEP_ACTCTX_BLOCK, *PBASEP_ACTCTX_BLOCK; + #define BASEP_GET_MODULE_HANDLE_EX_PARAMETER_VALIDATION_ERROR 1 #define BASEP_GET_MODULE_HANDLE_EX_PARAMETER_VALIDATION_SUCCESS 2 #define BASEP_GET_MODULE_HANDLE_EX_PARAMETER_VALIDATION_CONTINUE 3 @@ -246,6 +254,21 @@ VOID WINAPI BaseProcessStartThunk(VOID); + +VOID +NTAPI +BasepFreeActivationContextActivationBlock( + IN PBASEP_ACTCTX_BLOCK ActivationBlock +); + +NTSTATUS +NTAPI +BasepAllocateActivationContextActivationBlock( + IN DWORD Flags, + IN PVOID CompletionRoutine, + IN PVOID CompletionContext, + OUT PBASEP_ACTCTX_BLOCK *ActivationBlock +);
__declspec(noreturn) VOID
Modified: trunk/reactos/dll/win32/kernel32/wine/actctx.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/kernel32/wine/act... ============================================================================== --- trunk/reactos/dll/win32/kernel32/wine/actctx.c [iso-8859-1] (original) +++ trunk/reactos/dll/win32/kernel32/wine/actctx.c [iso-8859-1] Tue Feb 21 23:27:45 2012 @@ -280,3 +280,117 @@ } return TRUE; } + +/* REACTOS PRIVATE ************************************************************/ + +VOID +NTAPI +BasepFreeActivationContextActivationBlock(IN PBASEP_ACTCTX_BLOCK ActivationBlock) +{ + /* Exit if there was nothing passed in */ + if (!ActivationBlock) return; + + /* Do we have a context? */ + if (ActivationBlock->ActivationContext) + { + /* Release and clear it */ + RtlReleaseActivationContext(ActivationBlock->ActivationContext); + ActivationBlock->ActivationContext = NULL; + } + + /* Free the block */ + RtlFreeHeap(RtlGetProcessHeap(), 0, ActivationBlock); +} + +NTSTATUS +NTAPI +BasepAllocateActivationContextActivationBlock(IN DWORD Flags, + IN PVOID CompletionRoutine, + IN PVOID CompletionContext, + OUT PBASEP_ACTCTX_BLOCK *ActivationBlock) +{ + NTSTATUS Status; + ACTIVATION_CONTEXT_BASIC_INFORMATION ContextInfo; + + /* Clear the info structure */ + ContextInfo.dwFlags = 0; + ContextInfo.hActCtx = NULL; + + /* Assume failure */ + if (ActivationBlock) *ActivationBlock = NULL; + + /* Only support valid flags */ + if (Flags & ~(1 | 2)) // FIXME: What are they? 2 looks like BASEP_ACTCTX_FORCE_BLOCK + { + /* Fail if unknown flags are passed in */ + Status = STATUS_INVALID_PARAMETER_1; + goto Quickie; + } + + /* Caller should have passed in an activation block */ + if (!ActivationBlock) + { + /* Fail otherwise */ + Status = STATUS_INVALID_PARAMETER_4; + goto Quickie; + } + + /* Query RTL for information on the current activation context */ + Status = RtlQueryInformationActivationContext(1, + NULL, + 0, + ActivationContextBasicInformation, + &ContextInfo, + sizeof(ContextInfo), + NULL); + if (!NT_SUCCESS(Status)) + { + /* Failed -- bail out */ + DPRINT1("SXS: %s - Failure getting active activation context; ntstatus %08lx\n", + __FUNCTION__, Status); + goto Quickie; + } + + /* Check if the current one should be freed */ + if (ContextInfo.dwFlags & 1) + { + /* Release and clear it */ + RtlReleaseActivationContext(ContextInfo.hActCtx); + ContextInfo.hActCtx = NULL; + } + + /* Check if there's an active context, or if the caller is forcing one */ + if (!(Flags & 2) || (ContextInfo.hActCtx)) + { + /* Allocate the block */ + *ActivationBlock = RtlAllocateHeap(RtlGetProcessHeap(), + 0, + sizeof(BASEP_ACTCTX_BLOCK)); + if (!(*ActivationBlock)) + { + /* Ran out of memory, fail */ + Status = STATUS_NO_MEMORY; + goto Quickie; + } + + /* Fill it out */ + (*ActivationBlock)->ActivationContext = ContextInfo.hActCtx; + (*ActivationBlock)->Flags = 0; + if (Flags & 1) (*ActivationBlock)->Flags |= 1; // Not sure about this flag + (*ActivationBlock)->CompletionRoutine = CompletionRoutine; + (*ActivationBlock)->CompletionContext = CompletionContext; + + /* Tell Quickie below not to free anything, since this is success */ + ContextInfo.hActCtx = NULL; + } + + /* Set success status */ + Status = STATUS_SUCCESS; + +Quickie: + /* Failure or success path, return to caller and free on failure */ + if (ContextInfo.hActCtx) RtlReleaseActivationContext(ContextInfo.hActCtx); + return Status; +} + +/* EOF */