|
|
/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
querydir.c
Abstract:
This module implements the DAV mini redirector call down routines pertaining to query directory.
Author:
Joe Linn Rohan Kumar [RohanK] 20-Sept-1999
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#include "webdav.h"
//
// Mentioned below are the prototypes of functions tht are used only within
// this module (file). These functions should not be exposed outside.
//
NTSTATUS MRxDAVQueryDirectoryContinuation( UMRX_ASYNCENGINE_ARGUMENT_SIGNATURE );
NTSTATUS MRxDAVFormatUserModeQueryDirectoryRequest( IN UMRX_ASYNCENGINE_ARGUMENT_SIGNATURE, IN OUT PUMRX_USERMODE_WORKITEM_HEADER WorkItemHeader, IN ULONG WorkItemLength, OUT PULONG_PTR ReturnedLength );
BOOL MRxDAVPrecompleteUserModeQueryDirectoryRequest( UMRX_ASYNCENGINE_ARGUMENT_SIGNATURE, PUMRX_USERMODE_WORKITEM_HEADER WorkItemHeader, ULONG WorkItemLength, BOOL OperationCancelled );
NTSTATUS MRxDAVQueryDirectoryFromCache( IN PRX_CONTEXT RxContext, IN PBYTE Buffer, IN PFILE_BASIC_INFORMATION Basic, IN PFILE_STANDARD_INFORMATION Standard, IN ULONG FileIndex );
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, MRxDAVQueryDirectory)
#pragma alloc_text(PAGE, MRxDAVQueryDirectoryFromCache)
#pragma alloc_text(PAGE, MRxDAVQueryDirectoryContinuation)
#pragma alloc_text(PAGE, MRxDAVFormatUserModeQueryDirectoryRequest)
#pragma alloc_text(PAGE, MRxDAVPrecompleteUserModeQueryDirectoryRequest)
#endif
//
// Implementation of functions begins here.
//
NTSTATUS MRxDAVQueryDirectory( IN PRX_CONTEXT RxContext ) /*++
Routine Description:
This routine handles querydir requests for the DAV mini--redir.
Arguments:
RxContext - The RDBSS context.
Return Value:
RXSTATUS - The return status for the operation
--*/ { NTSTATUS NtStatus = STATUS_SUCCESS; RxCaptureFcb; RxCaptureFobx; UNICODE_STRING CacheName; PUNICODE_STRING DirectoryName = GET_ALREADY_PREFIXED_NAME_FROM_CONTEXT(RxContext);
PAGED_CODE();
DavDbgTrace(DAV_TRACE_ENTRYEXIT, ("%ld: Entering MRxDAVQueryDirectory.\n", PsGetCurrentThreadId())); DavDbgTrace(DAV_TRACE_CONTEXT, ("%ld: MRxDAVQueryDirectory: RxContext: %08lx\n", PsGetCurrentThreadId(), RxContext));
CacheName.Buffer = RxAllocatePoolWithTag(PagedPool, MAX_PATH * sizeof(WCHAR), DAV_QUERYDIR_POOLTAG);
if (CacheName.Buffer == NULL) { NtStatus = STATUS_INSUFFICIENT_RESOURCES; goto EXIT_THE_FUNCTION; }
RtlZeroMemory(CacheName.Buffer, MAX_PATH * sizeof(WCHAR)); RtlCopyMemory(CacheName.Buffer,DirectoryName->Buffer,DirectoryName->Length); CacheName.Buffer[DirectoryName->Length/2] = L'\\'; RtlCopyMemory(&CacheName.Buffer[DirectoryName->Length/2 + 1], capFobx->UnicodeQueryTemplate.Buffer, capFobx->UnicodeQueryTemplate.Length); CacheName.Length = ( DirectoryName->Length + capFobx->UnicodeQueryTemplate.Length + sizeof(WCHAR) ); CacheName.MaximumLength = ( DirectoryName->Length + capFobx->UnicodeQueryTemplate.Length + sizeof(WCHAR) );
if (!FsRtlDoesNameContainWildCards(&capFobx->UnicodeQueryTemplate)) {
DAV_USERMODE_CREATE_RETURNED_FILEINFO FileInfo; PWEBDAV_FOBX DavFobx = MRxDAVGetFobxExtension(capFobx);
if (DavFobx->CurrentFileIndex > 0) { DavFobx->NumOfFileEntries = 0; DavFobx->CurrentFileIndex = 0; NtStatus = STATUS_NO_MORE_FILES; goto EXIT_THE_FUNCTION; }
if (MRxDAVIsFileNotFoundCachedWithName(&CacheName,capFcb->pNetRoot)) { DavDbgTrace(DAV_TRACE_DETAIL, ("MRxDAVCreateContinuation file not found %wZ\n",&CacheName)); NtStatus = STATUS_OBJECT_NAME_NOT_FOUND; goto EXIT_THE_FUNCTION; }
if (MRxDAVIsFileInfoCacheFound(RxContext, &FileInfo, &NtStatus, &CacheName)) {
PBYTE Buffer = RxContext->Info.Buffer; ULONG BufferLength = RxContext->Info.LengthRemaining;
//
// Zero the buffer supplied.
//
RtlZeroMemory(Buffer, BufferLength);
NtStatus = MRxDAVQueryDirectoryFromCache(RxContext, Buffer, &FileInfo.BasicInformation, &FileInfo.StandardInformation, 1);
DavFobx->NumOfFileEntries = 1; DavFobx->CurrentFileIndex = 1;
goto EXIT_THE_FUNCTION;
}
}
NtStatus = UMRxAsyncEngOuterWrapper(RxContext, SIZEOF_DAV_SPECIFIC_CONTEXT, MRxDAVFormatTheDAVContext, DAV_MINIRDR_ENTRY_FROM_QUERYDIR, MRxDAVQueryDirectoryContinuation, "MRxDAVQueryDirectory"); DavDbgTrace(DAV_TRACE_ENTRYEXIT, ("%ld: Leaving MRxDAVQueryDirectory with NtStatus = %08lx\n", PsGetCurrentThreadId(), NtStatus));
if (NtStatus == STATUS_NO_SUCH_FILE || NtStatus == STATUS_OBJECT_PATH_NOT_FOUND || NtStatus == STATUS_OBJECT_NAME_NOT_FOUND) { MRxDAVCacheFileNotFoundWithName(&CacheName,RxContext->pFcb->pNetRoot); MRxDAVInvalidateFileInfoCacheWithName(&CacheName,RxContext->pFcb->pNetRoot); }
EXIT_THE_FUNCTION:
if (CacheName.Buffer != NULL) { RxFreePool(CacheName.Buffer); }
return(NtStatus); }
NTSTATUS MRxDAVQueryDirectoryContinuation( UMRX_ASYNCENGINE_ARGUMENT_SIGNATURE ) /*++
Routine Description:
This is the continuation routine for query directory operation.
Arguments:
AsyncEngineContext - The Reflectors context.
RxContext - The RDBSS context. Return Value:
RXSTATUS - The return status for the operation.
--*/ { NTSTATUS NtStatus; BOOL SynchronousIo;
PAGED_CODE();
DavDbgTrace(DAV_TRACE_ENTRYEXIT, ("%ld: Entering MRxDAVQueryDirectoryContinuation!!!!\n", PsGetCurrentThreadId()));
DavDbgTrace(DAV_TRACE_CONTEXT, ("%ld: MRxDAVQueryDirectoryContinuation: " "AsyncEngineContext: %08lx, RxContext: %08lx.\n", PsGetCurrentThreadId(), AsyncEngineContext, RxContext));
SynchronousIo = !BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION); if (!SynchronousIo) {
//
// Set the asynchronous flag. This is done since we do not want this
// thread to block in the UMRxSubmitAsyncEngUserModeRequest function.
// Also, since we need to call RxLowIoCompletion once we are done, set
// ShouldCallLowIoCompletion in the context to TRUE.
//
SetFlag(AsyncEngineContext->Flags, UMRX_ASYNCENG_CTX_FLAG_ASYNC_OPERATION); AsyncEngineContext->ShouldCallLowIoCompletion = TRUE;
//
// Set the CancelRoutine on the RxContext. Since this is an Async
// operation, it can be cancelled.
//
NtStatus = RxSetMinirdrCancelRoutine(RxContext, MRxDAVCancelRoutine); if (NtStatus != STATUS_SUCCESS) { ASSERT(NtStatus == STATUS_CANCELLED); DavDbgTrace(DAV_TRACE_ERROR, ("%ld: MRxDAVQueryDirectoryContinuation: " "AsyncEngineContext: %08lx. STATUS_CANCELLED\n", PsGetCurrentThreadId(), AsyncEngineContext)); goto EXIT_THE_FUNCTION; }
//
// Since this is an Asyncchronous operation, mark the IRP as pending.
// Its OK if you mark an IRP pending and complete it on the same thread
// without returning STATUS_PENDING.
//
IoMarkIrpPending(RxContext->CurrentIrp);
}
//
// Try usermode.
//
NtStatus = UMRxSubmitAsyncEngUserModeRequest( UMRX_ASYNCENGINE_ARGUMENTS, MRxDAVFormatUserModeQueryDirectoryRequest, MRxDAVPrecompleteUserModeQueryDirectoryRequest );
EXIT_THE_FUNCTION:
DavDbgTrace(DAV_TRACE_ENTRYEXIT, ("%ld: Leaving MRxDAVQueryDirectoryContinuation with NtStatus " "= %08lx.\n", PsGetCurrentThreadId(), NtStatus));
return NtStatus; }
NTSTATUS MRxDAVFormatUserModeQueryDirectoryRequest( IN UMRX_ASYNCENGINE_ARGUMENT_SIGNATURE, IN OUT PUMRX_USERMODE_WORKITEM_HEADER WorkItemHeader, IN ULONG WorkItemLength, OUT PULONG_PTR ReturnedLength ) /*++
Routine Description:
This routine formats the QueryDirectory request being sent to the user mode for processing.
Arguments: RxContext - The RDBSS context. AsyncEngineContext - The reflctor's context. WorkItem - The work item buffer. WorkItemLength - The length of the work item buffer. ReturnedLength - Return Value:
STATUS_SUCCESS or STATUS_INSUFFICIENT_RESOURCES.
--*/ { NTSTATUS NtStatus = STATUS_SUCCESS; PMRX_SRV_CALL SrvCall = NULL; PWEBDAV_SRV_CALL DavSrvCall = NULL; PDAV_USERMODE_WORKITEM DavWorkItem = (PDAV_USERMODE_WORKITEM)WorkItemHeader; PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen; PWEBDAV_V_NET_ROOT DavVNetRoot = NULL; PMRX_NET_ROOT NetRoot = NULL; PWEBDAV_SRV_OPEN davSrvOpen = MRxDAVGetSrvOpenExtension(SrvOpen); PWCHAR ServerName = NULL, NetRootName = NULL, JustTheNetRootName = NULL; PBYTE PathName = NULL; ULONG ServerNameLengthInBytes, PathNameLengthInBytes, NetRootNameLengthInBytes; PDAV_USERMODE_QUERYDIR_REQUEST QueryDirRequest = NULL; PSECURITY_CLIENT_CONTEXT SecurityClientContext = NULL; PWEBDAV_FOBX DavFobx = NULL; BOOLEAN ReturnVal; PUNICODE_STRING Template; RxCaptureFobx;
PAGED_CODE();
DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: Entering MRxDAVFormatUserModeQueryDirectoryRequest.\n", PsGetCurrentThreadId()));
DavDbgTrace(DAV_TRACE_CONTEXT, ("%ld: MRxDAVFormatUserModeQueryDirectoryRequest: " "AsyncEngineContext: %08lx, RxContext: %08lx.\n", PsGetCurrentThreadId(), AsyncEngineContext, RxContext)); IF_DEBUG { ASSERT (capFobx != NULL); ASSERT (capFobx->pSrvOpen == RxContext->pRelevantSrvOpen); }
DavWorkItem->WorkItemType = UserModeQueryDirectory; QueryDirRequest = &(DavWorkItem->QueryDirRequest);
DavFobx = MRxDAVGetFobxExtension(capFobx); ASSERT(DavFobx != NULL);
NetRoot = SrvOpen->pFcb->pNetRoot;
DavVNetRoot = (PWEBDAV_V_NET_ROOT)SrvOpen->pVNetRoot->Context; ASSERT(DavVNetRoot != NULL); DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: MRxDAVFormatUserModeQueryDirectoryRequest: SrvCallName = %wZ, " "SrvCallNameLength = %d\n", PsGetCurrentThreadId(), NetRoot->pSrvCall->pSrvCallName, NetRoot->pSrvCall->pSrvCallName->Length));
DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: MRxDAVFormatUserModeQueryDirectoryRequest: NetRootName = %wZ, " "NetRootNameLength = %d\n", PsGetCurrentThreadId(), NetRoot->pNetRootName, NetRoot->pNetRootName->Length));
DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: MRxDAVFormatUserModeQueryDirectoryRequest: PathName = %wZ, " "PathNameLength = %d\n", PsGetCurrentThreadId(), SrvOpen->pAlreadyPrefixedName, SrvOpen->pAlreadyPrefixedName->Length));
//
// Have we already created the DavFileAttributes list. If we have, then we
// tell the user mode process to do nothing and return. Here we do need to
// impersonate becuase the usermode will fail otherwise. This is becuase
// of the way the usermode code is structured.
//
if (DavFobx->DavFileAttributes) { QueryDirRequest->AlreadyDone = TRUE; goto IMPERSONATE_AND_EXIT; } QueryDirRequest->AlreadyDone = FALSE; SrvCall = SrvOpen->pVNetRoot->pNetRoot->pSrvCall; DavSrvCall = MRxDAVGetSrvCallExtension(SrvCall);
//
// Copy the ServerName.
//
ServerNameLengthInBytes = ( SrvCall->pSrvCallName->Length + sizeof(WCHAR) ); ServerName = (PWCHAR) UMRxAllocateSecondaryBuffer(AsyncEngineContext, ServerNameLengthInBytes); if (ServerName == NULL) { NtStatus = STATUS_INSUFFICIENT_RESOURCES; DavDbgTrace(DAV_TRACE_ERROR, ("%ld: ERROR: MRxDAVFormatUserModeQueryDirectoryRequest/" "UMRxAllocateSecondaryBuffer. NtStatus = %08lx.\n", PsGetCurrentThreadId(), NtStatus)); goto EXIT_THE_FUNCTION; } RtlCopyBytes(ServerName, SrvCall->pSrvCallName->Buffer, SrvCall->pSrvCallName->Length);
ServerName[( ( (ServerNameLengthInBytes) / sizeof(WCHAR) ) - 1 )] = L'\0'; QueryDirRequest->ServerName = ServerName;
DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: MRxDAVFormatUserModeQueryDirectoryRequest: ServerName: " "%ws\n", PsGetCurrentThreadId(), ServerName)); //
// Copy the ServerID.
//
QueryDirRequest->ServerID = DavSrvCall->ServerID;
Template = &(capFobx->UnicodeQueryTemplate); //
// The NetRootName (pNetRootName) includes the ServerName. Hence to get the
// NetRootNameLengthInBytes, we do the following.
//
NetRootNameLengthInBytes = (NetRoot->pNetRootName->Length - NetRoot->pSrvCall->pSrvCallName->Length);
NetRootName = &(NetRoot->pNetRootName->Buffer[1]); JustTheNetRootName = wcschr(NetRootName, L'\\');
//
// Copy the PathName of the Directory. If the template does not contain any
// wild cards, then we just need to get the attributes of this file from
// the server. We only get the attributes of all the files, if a wild card
// is specified in the template.
//
ReturnVal = FsRtlDoesNameContainWildCards(Template);
if (ReturnVal) { //
// The sizeof(WCHAR) is for the final '\0' char.
//
PathNameLengthInBytes = ( NetRootNameLengthInBytes + sizeof(WCHAR) ); //
// We need to allocate memory for the backslash and the Remaining name
// only if the remaining name exists.
//
if (SrvOpen->pAlreadyPrefixedName->Length) { //
// The sizeof(WCHAR) is for the backslash after the NetRootName.
//
PathNameLengthInBytes += ( SrvOpen->pAlreadyPrefixedName->Length + sizeof(WCHAR) ); }
PathName = (PBYTE) UMRxAllocateSecondaryBuffer(AsyncEngineContext, PathNameLengthInBytes); if (PathName == NULL) { NtStatus = STATUS_INSUFFICIENT_RESOURCES; DavDbgTrace(DAV_TRACE_ERROR, ("%ld: ERROR: MRxDAVFormatUserModeQueryDirectoryRequest/" "UMRxAllocateSecondaryBuffer. NtStatus = %08lx.\n", PsGetCurrentThreadId(), NtStatus)); goto EXIT_THE_FUNCTION; } QueryDirRequest->PathName = (PWCHAR)PathName; RtlZeroMemory(QueryDirRequest->PathName, PathNameLengthInBytes); //
// Copy the NetRootName.
//
RtlCopyMemory(PathName, JustTheNetRootName, NetRootNameLengthInBytes);
//
// We need to copy the backclash and the remaining path name only if
// the remaining path name exists.
//
if (SrvOpen->pAlreadyPrefixedName->Length) { if (SrvOpen->pAlreadyPrefixedName->Buffer[0] != L'\\') {
//
// Copy the backslash.
//
RtlCopyMemory( (PathName + NetRootNameLengthInBytes), L"\\", sizeof(WCHAR) );
//
// Copy the remaining path name after the NetRootName.
//
RtlCopyMemory( ( PathName + NetRootNameLengthInBytes + sizeof(WCHAR) ), SrvOpen->pAlreadyPrefixedName->Buffer, SrvOpen->pAlreadyPrefixedName->Length); } else { //
// Copy the remaining path name after the NetRootName which has the leading
// backslash already.
//
RtlCopyMemory( ( PathName + NetRootNameLengthInBytes ), SrvOpen->pAlreadyPrefixedName->Buffer, SrvOpen->pAlreadyPrefixedName->Length); } } QueryDirRequest->NoWildCards = FALSE;
} else {
//
// The Template is just a filename without any wild card chars. We copy
// the filaname after the pathname and send it to the user mode. First,
// we need to figure out if the path name has a trailing '\'.
//
BOOL trailingSlash = FALSE; PWCHAR PName = SrvOpen->pAlreadyPrefixedName->Buffer; ULONG PLen = SrvOpen->pAlreadyPrefixedName->Length;
if (PLen) { if ( PName[ ( ( PLen / sizeof(WCHAR) ) - 1 ) ] == L'\\' ) { trailingSlash = TRUE; } } else { PName = NULL; }
if (trailingSlash) { //
// The first sizeof(WCHAR) is for the backslash after the NetRootName.
// The second sizeof(WCHAR) for the final \0.
//
PathNameLengthInBytes = ( NetRootNameLengthInBytes + sizeof(WCHAR) + SrvOpen->pAlreadyPrefixedName->Length + Template->Length + sizeof(WCHAR) ); } else { //
// The first sizeof(WCHAR) is for the backslash after the NetRootName.
// The second sizeof(WCHAR) is for the final '\0' char.
//
PathNameLengthInBytes = ( NetRootNameLengthInBytes + sizeof(WCHAR) + Template->Length + sizeof(WCHAR) ); //
// The sizeof(WCHAR) if for the '\\' between the pathname and the
// template name. We need to add this only if the remaining path
// name exists.
//
if (PName) { PathNameLengthInBytes += ( SrvOpen->pAlreadyPrefixedName->Length + sizeof(WCHAR) ); } }
PathName = (PBYTE)UMRxAllocateSecondaryBuffer(AsyncEngineContext, PathNameLengthInBytes); if (PathName == NULL) { NtStatus = STATUS_INSUFFICIENT_RESOURCES; DavDbgTrace(DAV_TRACE_ERROR, ("%ld: ERROR: MRxDAVFormatUserModeQueryDirectoryRequest/" "UMRxAllocateSecondaryBuffer. NtStatus = %08lx.\n", PsGetCurrentThreadId(), NtStatus)); goto EXIT_THE_FUNCTION; }
QueryDirRequest->PathName = (PWCHAR)PathName; RtlZeroMemory(QueryDirRequest->PathName, PathNameLengthInBytes); //
// Copy the NetRootName.
//
RtlCopyMemory(PathName, JustTheNetRootName, NetRootNameLengthInBytes);
//
// Copy the backclash.
//
RtlCopyMemory( (PathName + NetRootNameLengthInBytes), L"\\", sizeof(WCHAR) ); //
// If PName is not NULL, we need to copy the remaining name and then
// the template name.
//
if (PName) { RtlCopyMemory( ( PathName + NetRootNameLengthInBytes + sizeof(WCHAR) ), SrvOpen->pAlreadyPrefixedName->Buffer, SrvOpen->pAlreadyPrefixedName->Length); if (trailingSlash) { RtlCopyMemory( (PathName + NetRootNameLengthInBytes + sizeof(WCHAR) + SrvOpen->pAlreadyPrefixedName->Length), Template->Buffer, Template->Length ); } else { RtlCopyMemory( (PathName + NetRootNameLengthInBytes + sizeof(WCHAR) + SrvOpen->pAlreadyPrefixedName->Length), L"\\", sizeof(WCHAR) ); RtlCopyMemory( ( PathName + NetRootNameLengthInBytes + sizeof(WCHAR) + SrvOpen->pAlreadyPrefixedName->Length + sizeof(WCHAR) ), Template->Buffer, Template->Length ); } } else { //
// A backslash has already been copied after the NetRootName.
//
RtlCopyMemory( ( PathName + NetRootNameLengthInBytes + sizeof(WCHAR) ), Template->Buffer, Template->Length ); }
QueryDirRequest->NoWildCards = TRUE; }
DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: MRxDAVFormatUserModeQueryDirectoryRequest. PathName =" " %ws\n", PsGetCurrentThreadId(), PathName));
//
// Set the LogonID stored in the Dav V_NET_ROOT. This value is used in the
// user mode.
//
QueryDirRequest->LogonID.LowPart = DavVNetRoot->LogonID.LowPart; QueryDirRequest->LogonID.HighPart = DavVNetRoot->LogonID.HighPart;
DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: MRxDAVFormatUserModeQueryDirectoryRequest. DavVNetRoot" " = %08lx\n", PsGetCurrentThreadId(), DavVNetRoot)); DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: MRxDAVFormatUserModeQueryDirectoryRequest. LogonID.LowPart" " = %08lx\n", PsGetCurrentThreadId(), DavVNetRoot->LogonID.LowPart)); DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: MRxDAVFormatUserModeQueryDirectoryRequest. LogonID.HighPart" " = %08lx\n", PsGetCurrentThreadId(), DavVNetRoot->LogonID.HighPart)); IMPERSONATE_AND_EXIT:
SecurityClientContext = &(DavVNetRoot->SecurityClientContext); //
// Impersonate the client who initiated the request. If we fail to
// impersonate, tough luck.
//
if (SecurityClientContext != NULL) { NtStatus = UMRxImpersonateClient(SecurityClientContext, WorkItemHeader); if (!NT_SUCCESS(NtStatus)) { DavDbgTrace(DAV_TRACE_ERROR, ("%ld: ERROR: MRxDAVFormatUserModeQueryDirectoryRequest/" "UMRxImpersonateClient. NtStatus = %08lx.\n", PsGetCurrentThreadId(), NtStatus)); } } else { NtStatus = STATUS_INVALID_PARAMETER; DavDbgTrace(DAV_TRACE_ERROR, ("%ld: ERROR: MRxDAVFormatUserModeQueryDirectoryRequest: " "SecurityClientContext is NULL.\n", PsGetCurrentThreadId())); }
EXIT_THE_FUNCTION:
DavDbgTrace(DAV_TRACE_ENTRYEXIT, ("%ld: Leaving MRxDAVFormatUserModeQueryDirectoryRequest with " "NtStatus = %08lx.\n", PsGetCurrentThreadId(), NtStatus));
return(NtStatus); }
BOOL MRxDAVPrecompleteUserModeQueryDirectoryRequest( UMRX_ASYNCENGINE_ARGUMENT_SIGNATURE, PUMRX_USERMODE_WORKITEM_HEADER WorkItemHeader, ULONG WorkItemLength, BOOL OperationCancelled ) /*++
Routine Description:
The precompletion routine for the create SrvCall request.
Arguments:
RxContext - The RDBSS context. AsyncEngineContext - The reflctor's context. WorkItem - The work item buffer. WorkItemLength - The length of the work item buffer.
OperationCancelled - TRUE if this operation was cancelled by the user.
Return Value:
TRUE - UMRxAsyncEngineCalldownIrpCompletion is called by the function UMRxCompleteUserModeRequest after we return.
--*/ { NTSTATUS NtStatus = STATUS_SUCCESS; PDAV_USERMODE_QUERYDIR_REQUEST QueryDirRequest = NULL; PDAV_USERMODE_QUERYDIR_RESPONSE QueryDirResponse = NULL; PDAV_USERMODE_WORKITEM DavWorkItem = NULL; PFILE_NAMES_INFORMATION FileNamesInfo = NULL; PFILE_DIRECTORY_INFORMATION FileDirInfo = NULL; PFILE_FULL_DIR_INFORMATION FileFullDirInfo = NULL; PFILE_BOTH_DIR_INFORMATION FileBothDirInfo = NULL; FILE_INFORMATION_CLASS FileInformationClass; PBYTE Buffer = NULL; BOOL SingleEntry, InitialQuery, IndexSpecified, EndOfBuffer = FALSE; BOOLEAN ReturnVal, RestartScan, NoWildCards = FALSE, AsyncOperation = FALSE; ULONG FileIndex, BufferLength, BufferLengthUsed = 0, NextEntryOffset = 0; PUNICODE_STRING Template = NULL; UNICODE_STRING UnicodeFileName; PDAV_FILE_ATTRIBUTES DavFileAttributes = NULL, TempDFA = NULL; PLIST_ENTRY listEntry = NULL; PWEBDAV_FOBX DavFobx = NULL; PVOID PreviousBlock = NULL; FILE_BASIC_INFORMATION BasicInfo; FILE_STANDARD_INFORMATION StandardInfo; UNICODE_STRING CacheName; PUNICODE_STRING DirectoryName = NULL; RxCaptureFobx;
PAGED_CODE();
DavDbgTrace(DAV_TRACE_ENTRYEXIT, ("%ld: Entering MRxDAVPrecompleteUserModeQueryDirectoryRequest.\n", PsGetCurrentThreadId()));
DavDbgTrace(DAV_TRACE_CONTEXT, ("%ld: MRxDAVPrecompleteUserModeQueryDirectoryRequest: " "AsyncEngineContext: %08lx, RxContext: %08lx.\n", PsGetCurrentThreadId(), AsyncEngineContext, RxContext));
DavWorkItem = (PDAV_USERMODE_WORKITEM)WorkItemHeader; QueryDirRequest = &(DavWorkItem->QueryDirRequest); QueryDirResponse = &(DavWorkItem->QueryDirResponse);
CacheName.Buffer = NULL; CacheName.Length = 0; CacheName.MaximumLength = 0;
//
// If the operation is cancelled, then there is no guarantee that the FCB,
// FOBX etc are still valid. All that we need to do is cleanup and bail.
//
if (!OperationCancelled) { //
// We store the DavFileAttributes in the DAV FOBX extension. These will
// be used on subsequent calls to the Enumerate directory call.
//
DirectoryName = GET_ALREADY_PREFIXED_NAME_FROM_CONTEXT(RxContext); DavFobx = MRxDAVGetFobxExtension(capFobx); ASSERT(DavFobx != NULL); }
if ( QueryDirRequest->AlreadyDone == FALSE ) { //
// If the operation is cancelled, then there is no guarantee that the FCB,
// FOBX etc are still valid. All that we need to do is cleanup and bail.
//
if (!OperationCancelled) {
//
// Get the response items only if we succeeded in the user mode and if
// we got the properties of all the files in the directory.
//
if ( AsyncEngineContext->Status == STATUS_SUCCESS && QueryDirResponse->DavFileAttributes != NULL ) {
DavFobx->DavFileAttributes = QueryDirResponse->DavFileAttributes;
DavFobx->NumOfFileEntries = QueryDirResponse->NumOfFileEntries;
DavFobx->CurrentFileIndex = 0;
DavFobx->listEntry = &(DavFobx->DavFileAttributes->NextEntry);
DavDbgTrace(( DAV_TRACE_DETAIL | DAV_TRACE_QUERYDIR ), ("%ld: MRxDAVPrecompleteUserModeQueryDirectoryRequest: " "DavFileAttributes = %08lx, NumOfFileEntries = %d.\n", PsGetCurrentThreadId(), DavFobx->DavFileAttributes, DavFobx->NumOfFileEntries));
}
} else {
//
// If the operation was cancelled and we allocated the
// DavFileAttributeList in the usermode, we need to set
// callWorkItemCleanup to TRUE, so that it gets cleaned up.
//
if ( AsyncEngineContext->Status == STATUS_SUCCESS && QueryDirResponse->DavFileAttributes != NULL ) { DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: ERROR: MRxDAVPrecompleteUserModeQueryDirectoryRequest: " "callWorkItemCleanup\n", PsGetCurrentThreadId())); DavWorkItem->callWorkItemCleanup = TRUE; }
}
//
// We need to free up the heaps, we allocated in the format routine.
//
if (QueryDirRequest->ServerName != NULL) {
NtStatus = UMRxFreeSecondaryBuffer(AsyncEngineContext, (PBYTE)QueryDirRequest->ServerName); if (NtStatus != STATUS_SUCCESS) { DavDbgTrace(DAV_TRACE_ERROR, ("%ld: ERROR: MRxDAVPrecompleteUserModeQueryDirectoryRequest/" "UMRxFreeSecondaryBuffer: NtStatus = %08lx.\n", PsGetCurrentThreadId(), NtStatus)); goto EXIT_THE_FUNCTION; }
}
if (QueryDirRequest->PathName != NULL) { NtStatus = UMRxFreeSecondaryBuffer(AsyncEngineContext, (PBYTE)QueryDirRequest->PathName); if (NtStatus != STATUS_SUCCESS) { DavDbgTrace(DAV_TRACE_ERROR, ("%ld: ERROR: MRxDAVPrecompleteUserModeQueryDirectoryRequest/" "UMRxFreeSecondaryBuffer: NtStatus = %08lx.\n", PsGetCurrentThreadId(), NtStatus)); goto EXIT_THE_FUNCTION; }
} }
//
// Before proceeding further, we need to check the following. Its very
// important that these checks (Async and Cancel) are done before anything
// else is done.
//
AsyncOperation = FlagOn(AsyncEngineContext->Flags, UMRX_ASYNCENG_CTX_FLAG_ASYNC_OPERATION);
if (AsyncOperation) { //
// If this was an Async operation then we need to remove a reference on
// the AsyncEngineContext which was taken before it was placed on the
// KQueue to go to the usermode. Also, the context should have one more
// reference.
//
ReturnVal = UMRxFinalizeAsyncEngineContext( &(AsyncEngineContext) ); ASSERT(!ReturnVal); }
//
// If this operation was cancelled, then all that we need to do is finalize
// the AsyncEngineContext, if the call was Async and return FALSE. If the
// call was sync then we don't need to finalize.
//
if (OperationCancelled) { DavDbgTrace(DAV_TRACE_ERROR, ("%ld: ERROR: MRxDAVPrecompleteUserModeQueryDirectoryRequest: " "Operation Cancelled.\n", PsGetCurrentThreadId())); if (AsyncOperation) { ReturnVal = UMRxFinalizeAsyncEngineContext( &(AsyncEngineContext) ); ASSERT(!ReturnVal); } return FALSE; }
CacheName.Buffer = RxAllocatePoolWithTag(PagedPool, MAX_PATH * sizeof(WCHAR), DAV_QUERYDIR_POOLTAG); if (CacheName.Buffer == NULL) { NtStatus = STATUS_INSUFFICIENT_RESOURCES; goto EXIT_THE_FUNCTION; }
RtlZeroMemory(CacheName.Buffer,MAX_PATH * sizeof(WCHAR)); RtlCopyMemory(CacheName.Buffer,DirectoryName->Buffer,DirectoryName->Length); CacheName.Buffer[DirectoryName->Length/2] = L'\\'; RtlCopyMemory(&CacheName.Buffer[DirectoryName->Length/2 + 1], capFobx->UnicodeQueryTemplate.Buffer, capFobx->UnicodeQueryTemplate.Length); CacheName.Length = CacheName.MaximumLength = DirectoryName->Length + capFobx->UnicodeQueryTemplate.Length + sizeof(WCHAR);
NtStatus = AsyncEngineContext->Status;
if (NtStatus != STATUS_SUCCESS) { //
// We failed in the user mode.
//
DavDbgTrace(DAV_TRACE_ERROR, ("%ld: ERROR: MRxDAVPrecompleteUserModeQueryDirectoryRequest:" "QueryDirectory failed with NtStatus = %08lx.\n", PsGetCurrentThreadId(), NtStatus)); goto EXIT_THE_FUNCTION; }
ASSERT(DavFobx->DavFileAttributes != NULL);
SingleEntry = RxContext->QueryDirectory.ReturnSingleEntry; InitialQuery = RxContext->QueryDirectory.InitialQuery; RestartScan = RxContext->QueryDirectory.RestartScan; IndexSpecified = RxContext->QueryDirectory.IndexSpecified; FileIndex = RxContext->QueryDirectory.FileIndex; Buffer = RxContext->Info.Buffer; BufferLength = RxContext->Info.LengthRemaining; Template = &(capFobx->UnicodeQueryTemplate); FileInformationClass = RxContext->Info.FileInformationClass;
DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: MRxDAVPrecompleteUserModeQueryDirectoryRequest: " "FileInformationClass = %d.\n", PsGetCurrentThreadId(), FileInformationClass)); //
// Zero the buffer supplied.
//
RtlZeroMemory(Buffer, BufferLength);
//
// See, if we need to restart from the beginning.
//
if (RestartScan) { DavFobx->CurrentFileIndex = 0; DavFobx->listEntry = &(DavFobx->DavFileAttributes->NextEntry); }
//
// Response has a pointer to the list of DavFileAttributes.
//
DavFileAttributes = DavFobx->DavFileAttributes; listEntry = DavFobx->listEntry;
//
// If we have returned all the entries, inform the user that they are no
// more entries to return.
//
if ( DavFobx->CurrentFileIndex == DavFobx->NumOfFileEntries ) { DavDbgTrace(( DAV_TRACE_DETAIL | DAV_TRACE_QUERYDIR ), ("%ld: MRxDAVPrecompleteUserModeQueryDirectoryRequest: " "No more entries to return.\n", PsGetCurrentThreadId())); NtStatus = STATUS_NO_MORE_FILES; //
// Reset the index for the next call.
//
DavFobx->CurrentFileIndex = 0; DavFobx->listEntry = &(DavFobx->DavFileAttributes->NextEntry); goto EXIT_THE_FUNCTION; }
DavDbgTrace(( DAV_TRACE_DETAIL | DAV_TRACE_QUERYDIR ), ("%ld: MRxDAVPrecompleteUserModeQueryDirectoryRequest: " "TLength = %d, TMaxLength = %d, Template = %wZ.\n", PsGetCurrentThreadId(), Template->Length, Template->MaximumLength, Template));
do {
TempDFA = CONTAINING_RECORD(listEntry, DAV_FILE_ATTRIBUTES, NextEntry);
//
// If this file did not come back with a 200 OK in the PROPFIND response
// then we need to skip it. The response of a PROPFIND is a multi-status
// with each file/directory having its own status.
//
if (TempDFA->InvalidNode) { listEntry = listEntry->Flink; DavFobx->listEntry = listEntry; DavFobx->CurrentFileIndex++; continue; }
//
// Check to see if the name of this entry matches the pattern supplied
// by the user. If it does not, then we don't need to return it.
//
RtlInitUnicodeString(&(UnicodeFileName), TempDFA->FileName);
DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: MRxDAVPrecompleteUserModeQueryDirectoryRequest: " "FileName = %ws\n", PsGetCurrentThreadId(), TempDFA->FileName)); //
// If the template does not contain any wild cards then we need to just
// check if the unicode strings are equal. If it does contain wild cards,
// then upcase the characters of the template and call
// FsRtlIsNameInExpression.
//
ReturnVal = FsRtlDoesNameContainWildCards(Template);
if (ReturnVal) {
UNICODE_STRING UpperCaseString;
UpperCaseString.Buffer = NULL; UpperCaseString.Length = UpperCaseString.MaximumLength = 0; NtStatus = RtlUpcaseUnicodeString(&(UpperCaseString), Template, TRUE); if (NtStatus != STATUS_SUCCESS) { DavDbgTrace(DAV_TRACE_ERROR, ("%ld: MRxDAVPrecompleteUserModeQueryDirectoryRequest:" "/RtlUpcaseUnicodeString. NtStatus = %08lx.\n", PsGetCurrentThreadId(), NtStatus)); goto EXIT_THE_FUNCTION; } ReturnVal = FsRtlIsNameInExpression(&(UpperCaseString), &(UnicodeFileName), TRUE, FALSE); //
// RtlUpcaseUnicodeString allocates memory for the buffer field of
// the UpperCaseString. We need to free it now.
//
RtlFreeUnicodeString( &(UpperCaseString) ); } else {
NoWildCards = TRUE; ReturnVal = RtlEqualUnicodeString(Template, &(UnicodeFileName), TRUE); }
if (!ReturnVal) { //
// This name does not match the pattern, so ignore it. Get the
// next listEntry.
//
listEntry = listEntry->Flink; DavDbgTrace(( DAV_TRACE_DETAIL | DAV_TRACE_QUERYDIR ), ("%ld: MRxDAVPrecompleteUserModeQueryDirectoryRequest: " "FileName %ws does not belong to pattern.\n", PsGetCurrentThreadId(), TempDFA->FileName));
DavFobx->listEntry = listEntry; DavFobx->CurrentFileIndex++; continue; }
//
// The first entry in the DavFileAttributes list is the directory being
// enumerated. In this case NoWildCards == FALSE. We shouldn't be
// including this in the list of files returned. If we did a FindFirst
// on a particular file, then the only entry is for the file itself. In
// this case NoWildCards == TRUE.
//
if ( DavFobx->CurrentFileIndex == 0 && !NoWildCards ) { listEntry = listEntry->Flink; DavFobx->listEntry = listEntry; DavFobx->CurrentFileIndex++; continue; }
//
// If we did not get any FileAttributes for this file from the server,
// set the attribute value to FILE_ATTRIBUTE_ARCHIVE since the apps
// expect this.
//
if (TempDFA->dwFileAttributes == 0) { TempDFA->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE; }
RtlCopyMemory(&CacheName.Buffer[DirectoryName->Length/2+1], UnicodeFileName.Buffer, UnicodeFileName.Length); CacheName.Length = CacheName.MaximumLength = DirectoryName->Length + UnicodeFileName.Length + sizeof(WCHAR);
switch (FileInformationClass) { case FileNamesInformation: DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: MRxDAVPrecompleteUserModeQueryDirectoryRequest: " "FileInformationClass = FileNamesInformation.\n", PsGetCurrentThreadId()));
//
// Set the offset field of the previous block.
//
if (PreviousBlock) { FileNamesInfo = (PFILE_NAMES_INFORMATION)PreviousBlock; FileNamesInfo->NextEntryOffset = NextEntryOffset; }
NextEntryOffset = sizeof(FILE_NAMES_INFORMATION); NextEntryOffset += ( (TempDFA->FileNameLength + 1) * sizeof(WCHAR) ); //
// We need to round up NextEntryOffset to the next multiple of 8.
// We do this to maintain pointer alignment.
//
NextEntryOffset = ( ( ( NextEntryOffset + 7 ) / 8 ) * 8 );
//
// Is there enough space in the user supplied buffer to store the
// next entry ? If not, we need to return now since we cannot store
// any more entries.
//
if (NextEntryOffset > BufferLength) { DavDbgTrace(DAV_TRACE_ERROR, ("%ld: MRxDAVPrecompleteUserModeQueryDirectoryRequest: " "Insufficient buffer length.\n", PsGetCurrentThreadId())); if (PreviousBlock) { FileNamesInfo = (PFILE_NAMES_INFORMATION)PreviousBlock; FileNamesInfo->NextEntryOffset = 0; } EndOfBuffer = TRUE; break; } FileNamesInfo = (PFILE_NAMES_INFORMATION)Buffer; //
// The NextEntryOffset gets set on the next cycle. This way, for
// the last entry it will be zero.
//
FileNamesInfo->NextEntryOffset = 0; FileNamesInfo->FileIndex = TempDFA->FileIndex; FileNamesInfo->FileNameLength = ( (TempDFA->FileNameLength + 1) * sizeof(WCHAR) ); wcscpy(FileNamesInfo->FileName, TempDFA->FileName);
PreviousBlock = (PVOID)FileNamesInfo; //
// Increment the pointer to point at the next byte.
//
Buffer += NextEntryOffset;
//
// We have written "NextEntryOffset" bytes, so decrement the number
// of bytes available pointer.
//
BufferLength -= NextEntryOffset;
//
// Increment the total number of bytes written.
//
BufferLengthUsed += NextEntryOffset;
break;
case FileDirectoryInformation:
DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: MRxDAVPrecompleteUserModeQueryDirectoryRequest: " "FileInformationClass = FileDirectoryInformation.\n", PsGetCurrentThreadId()));
//
// Set the offset field of the previous block.
//
if (PreviousBlock) { FileDirInfo = (PFILE_DIRECTORY_INFORMATION)PreviousBlock; FileDirInfo->NextEntryOffset = NextEntryOffset; }
NextEntryOffset = sizeof(FILE_DIRECTORY_INFORMATION); NextEntryOffset += ( (TempDFA->FileNameLength + 1) * sizeof(WCHAR) );
//
// We need to round up NextEntryOffset to the next multiple of 8.
// We do this to maintain pointer alignment.
//
NextEntryOffset = ( ( ( NextEntryOffset + 7 ) / 8 ) * 8 );
if (NextEntryOffset > BufferLength) { DavDbgTrace(DAV_TRACE_ERROR, ("%ld: MRxDAVPrecompleteUserModeQueryDirectoryRequest: " "Insufficient buffer length.\n", PsGetCurrentThreadId())); if (PreviousBlock) { FileDirInfo = (PFILE_DIRECTORY_INFORMATION)PreviousBlock; FileDirInfo->NextEntryOffset = 0; } EndOfBuffer = TRUE; break; }
FileDirInfo = (PFILE_DIRECTORY_INFORMATION)Buffer;
FileDirInfo->NextEntryOffset = 0; FileDirInfo->FileIndex = TempDFA->FileIndex; FileDirInfo->CreationTime.LowPart = TempDFA->CreationTime.LowPart; FileDirInfo->CreationTime.HighPart = TempDFA->CreationTime.HighPart; FileDirInfo->LastAccessTime.LowPart = TempDFA->LastModifiedTime.LowPart; FileDirInfo->LastAccessTime.HighPart = TempDFA->LastModifiedTime.HighPart; FileDirInfo->LastWriteTime.LowPart = TempDFA->LastModifiedTime.LowPart; FileDirInfo->LastWriteTime.HighPart = TempDFA->LastModifiedTime.HighPart; FileDirInfo->ChangeTime.LowPart = TempDFA->LastModifiedTime.LowPart; FileDirInfo->ChangeTime.HighPart = TempDFA->LastModifiedTime.HighPart;
FileDirInfo->EndOfFile.LowPart = TempDFA->FileSize.LowPart; FileDirInfo->EndOfFile.HighPart = TempDFA->FileSize.HighPart;
FileDirInfo->AllocationSize.LowPart = TempDFA->FileSize.LowPart; FileDirInfo->AllocationSize.HighPart = TempDFA->FileSize.HighPart;
FileDirInfo->FileAttributes = TempDFA->dwFileAttributes;
if (TempDFA->isCollection) { FileDirInfo->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY; } if (TempDFA->isHidden) { FileDirInfo->FileAttributes |= FILE_ATTRIBUTE_HIDDEN; }
FileDirInfo->FileNameLength = ( (TempDFA->FileNameLength + 1) * sizeof(WCHAR) );
wcscpy(FileDirInfo->FileName, TempDFA->FileName); PreviousBlock = (PVOID)FileDirInfo; Buffer += NextEntryOffset;
BufferLength -= NextEntryOffset;
BufferLengthUsed += NextEntryOffset; if (!MRxDAVIsBasicFileInfoCacheFound(RxContext,&BasicInfo,&NtStatus,&CacheName)) { if (TempDFA->isCollection) { UNICODE_STRING DirName;
NtStatus = MRxDAVGetFullDirectoryPath(RxContext,&CacheName,&DirName);
if (DirName.Buffer != NULL) { if (FileDirInfo->FileAttributes & FILE_ATTRIBUTE_ENCRYPTED) { NtStatus = MRxDAVCreateEncryptedDirectoryKey(&DirName); } else { NtStatus = MRxDAVQueryEncryptedDirectoryKey(&DirName);
if (NtStatus == STATUS_SUCCESS) { FileDirInfo->FileAttributes |= FILE_ATTRIBUTE_ENCRYPTED; } else if (NtStatus == STATUS_OBJECT_NAME_NOT_FOUND) { NtStatus = STATUS_SUCCESS; } }
// The buffer was allocated in MRxDAVGetFullDirectoryPath
RxFreePool(DirName.Buffer); } if (NtStatus != STATUS_SUCCESS) { goto EXIT_THE_FUNCTION; } }
BasicInfo.CreationTime = FileDirInfo->CreationTime; BasicInfo.LastAccessTime = FileDirInfo->LastAccessTime; BasicInfo.LastWriteTime = FileDirInfo->LastWriteTime; BasicInfo.ChangeTime = FileDirInfo->ChangeTime; BasicInfo.FileAttributes = FileDirInfo->FileAttributes;
StandardInfo.AllocationSize = FileDirInfo->AllocationSize; StandardInfo.EndOfFile = FileDirInfo->EndOfFile; StandardInfo.NumberOfLinks = 1; StandardInfo.DeletePending = FALSE; StandardInfo.Directory = TempDFA->isCollection;
MRxDAVCreateFileInfoCacheWithName(&CacheName, RxContext->pFcb->pNetRoot, &BasicInfo, &StandardInfo, STATUS_SUCCESS); } else { if (TempDFA->isCollection && (BasicInfo.FileAttributes & FILE_ATTRIBUTE_ENCRYPTED)) { FileDirInfo->FileAttributes |= FILE_ATTRIBUTE_ENCRYPTED; } } //
// We filter the FILE_ATTRIBUTE_TEMPORARY flag since on FAT (which
// we emulate), FindFirstFile and FindNextFile don�t return
// FILE_ATTRIBUTE_TEMPORARY flag even though GetFileAttributes
// returns it. Hence we only filter this in the attributes that
// are being returned in this call and not in the attributes that
// have been saved.
//
FileDirInfo->FileAttributes &= ~FILE_ATTRIBUTE_TEMPORARY;
break;
case FileFullDirectoryInformation:
DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: MRxDAVPrecompleteUserModeQueryDirectoryRequest: " "FileInformationClass = FileFullDirectoryInformation.\n", PsGetCurrentThreadId()));
//
// Set the offset field of the previous block.
//
if (PreviousBlock) { FileFullDirInfo = (PFILE_FULL_DIR_INFORMATION)PreviousBlock; FileFullDirInfo->NextEntryOffset = NextEntryOffset; }
NextEntryOffset = sizeof(FILE_FULL_DIR_INFORMATION); NextEntryOffset += ( (TempDFA->FileNameLength + 1) * sizeof(WCHAR) );
//
// We need to round up NextEntryOffset to the next multiple of 8.
// We do this to maintain pointer alignment.
//
NextEntryOffset = ( ( ( NextEntryOffset + 7 ) / 8 ) * 8 );
if (NextEntryOffset > BufferLength) { DavDbgTrace(DAV_TRACE_ERROR, ("%ld: MRxDAVPrecompleteUserModeQueryDirectoryRequest: " "Insufficient buffer length.\n", PsGetCurrentThreadId())); if (PreviousBlock) { FileFullDirInfo = (PFILE_FULL_DIR_INFORMATION)PreviousBlock; FileFullDirInfo->NextEntryOffset = 0; } EndOfBuffer = TRUE; break; }
FileFullDirInfo = (PFILE_FULL_DIR_INFORMATION)Buffer;
FileFullDirInfo->NextEntryOffset = 0; FileFullDirInfo->FileIndex = TempDFA->FileIndex; FileFullDirInfo->CreationTime.LowPart = TempDFA->CreationTime.LowPart; FileFullDirInfo->CreationTime.HighPart = TempDFA->CreationTime.HighPart; FileFullDirInfo->LastAccessTime.LowPart = TempDFA->LastModifiedTime.LowPart; FileFullDirInfo->LastAccessTime.HighPart = TempDFA->LastModifiedTime.HighPart; FileFullDirInfo->LastWriteTime.LowPart = TempDFA->LastModifiedTime.LowPart; FileFullDirInfo->LastWriteTime.HighPart = TempDFA->LastModifiedTime.HighPart; FileFullDirInfo->ChangeTime.LowPart = TempDFA->LastModifiedTime.LowPart; FileFullDirInfo->ChangeTime.HighPart = TempDFA->LastModifiedTime.HighPart;
FileFullDirInfo->EndOfFile.LowPart = TempDFA->FileSize.LowPart; FileFullDirInfo->EndOfFile.HighPart = TempDFA->FileSize.HighPart;
FileFullDirInfo->AllocationSize.LowPart = TempDFA->FileSize.LowPart; FileFullDirInfo->AllocationSize.HighPart = TempDFA->FileSize.HighPart;
FileFullDirInfo->FileAttributes = TempDFA->dwFileAttributes;
if (TempDFA->isCollection) { FileFullDirInfo->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY; } if (TempDFA->isHidden) { FileFullDirInfo->FileAttributes |= FILE_ATTRIBUTE_HIDDEN; }
FileFullDirInfo->EaSize = 0;
FileFullDirInfo->FileNameLength = ( (TempDFA->FileNameLength + 1) * sizeof(WCHAR) );
wcscpy(FileFullDirInfo->FileName, TempDFA->FileName); PreviousBlock = (PVOID)FileFullDirInfo; Buffer += NextEntryOffset;
BufferLength -= NextEntryOffset;
BufferLengthUsed += NextEntryOffset;
if (!MRxDAVIsBasicFileInfoCacheFound(RxContext,&BasicInfo,&NtStatus,&CacheName)) { if (TempDFA->isCollection) { UNICODE_STRING DirName;
NtStatus = MRxDAVGetFullDirectoryPath(RxContext,&CacheName,&DirName);
if (DirName.Buffer != NULL) { if (FileFullDirInfo->FileAttributes & FILE_ATTRIBUTE_ENCRYPTED) { NtStatus = MRxDAVCreateEncryptedDirectoryKey(&DirName); } else { NtStatus = MRxDAVQueryEncryptedDirectoryKey(&DirName);
if (NtStatus == STATUS_SUCCESS) { FileFullDirInfo->FileAttributes |= FILE_ATTRIBUTE_ENCRYPTED; } else if (NtStatus == STATUS_OBJECT_NAME_NOT_FOUND) { NtStatus = STATUS_SUCCESS; } }
// The buffer was allocated in MRxDAVGetFullDirectoryPath
RxFreePool(DirName.Buffer); } if (NtStatus != STATUS_SUCCESS) { goto EXIT_THE_FUNCTION; } } BasicInfo.CreationTime = FileFullDirInfo->CreationTime; BasicInfo.LastAccessTime = FileFullDirInfo->LastAccessTime; BasicInfo.LastWriteTime = FileFullDirInfo->LastWriteTime; BasicInfo.ChangeTime = FileFullDirInfo->ChangeTime; BasicInfo.FileAttributes = FileFullDirInfo->FileAttributes; StandardInfo.AllocationSize = FileFullDirInfo->AllocationSize; StandardInfo.EndOfFile = FileFullDirInfo->EndOfFile; StandardInfo.NumberOfLinks = 1; StandardInfo.DeletePending = FALSE; StandardInfo.Directory = TempDFA->isCollection; MRxDAVCreateFileInfoCacheWithName(&CacheName, RxContext->pFcb->pNetRoot, &BasicInfo, &StandardInfo, STATUS_SUCCESS); } else { if (TempDFA->isCollection && (BasicInfo.FileAttributes & FILE_ATTRIBUTE_ENCRYPTED)) { FileFullDirInfo->FileAttributes |= FILE_ATTRIBUTE_ENCRYPTED; } }
//
// We filter the FILE_ATTRIBUTE_TEMPORARY flag since on FAT (which
// we emulate), FindFirstFile and FindNextFile don�t return
// FILE_ATTRIBUTE_TEMPORARY flag even though GetFileAttributes
// returns it. Hence we only filter this in the attributes that
// are being returned in this call and not in the attributes that
// have been saved.
//
FileFullDirInfo->FileAttributes &= ~FILE_ATTRIBUTE_TEMPORARY;
break;
case FileBothDirectoryInformation:
DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: MRxDAVPrecompleteUserModeQueryDirectoryRequest: " "FileInformationClass = FileBothDirectoryInformation.\n", PsGetCurrentThreadId()));
//
// Set the offset field of the previous block.
//
if (PreviousBlock) { FileBothDirInfo = (PFILE_BOTH_DIR_INFORMATION)PreviousBlock; FileBothDirInfo->NextEntryOffset = NextEntryOffset; }
NextEntryOffset = sizeof(FILE_BOTH_DIR_INFORMATION); NextEntryOffset += ( (TempDFA->FileNameLength + 1) * sizeof(WCHAR) );
//
// We need to round up NextEntryOffset to the next multiple of 8.
// We do this to maintain pointer alignment.
//
NextEntryOffset = ( ( ( NextEntryOffset + 7 ) / 8 ) * 8 );
if (NextEntryOffset > BufferLength) { DavDbgTrace(DAV_TRACE_ERROR, ("%ld: MRxDAVPrecompleteUserModeQueryDirectoryRequest:" " Insufficient buffer length.\n", PsGetCurrentThreadId())); if (PreviousBlock) { FileBothDirInfo = (PFILE_BOTH_DIR_INFORMATION)PreviousBlock; FileBothDirInfo->NextEntryOffset = 0; } EndOfBuffer = TRUE; break; }
FileBothDirInfo = (PFILE_BOTH_DIR_INFORMATION)Buffer;
FileBothDirInfo->NextEntryOffset = 0; FileBothDirInfo->FileIndex = TempDFA->FileIndex; FileBothDirInfo->CreationTime.LowPart = TempDFA->CreationTime.LowPart; FileBothDirInfo->CreationTime.HighPart = TempDFA->CreationTime.HighPart; FileBothDirInfo->LastAccessTime.LowPart = TempDFA->LastModifiedTime.LowPart; FileBothDirInfo->LastAccessTime.HighPart = TempDFA->LastModifiedTime.HighPart; FileBothDirInfo->LastWriteTime.LowPart = TempDFA->LastModifiedTime.LowPart; FileBothDirInfo->LastWriteTime.HighPart = TempDFA->LastModifiedTime.HighPart; FileBothDirInfo->ChangeTime.LowPart = TempDFA->LastModifiedTime.LowPart; FileBothDirInfo->ChangeTime.HighPart = TempDFA->LastModifiedTime.HighPart;
FileBothDirInfo->EndOfFile.LowPart = TempDFA->FileSize.LowPart; FileBothDirInfo->EndOfFile.HighPart = TempDFA->FileSize.HighPart;
FileBothDirInfo->AllocationSize.LowPart = TempDFA->FileSize.LowPart; FileBothDirInfo->AllocationSize.HighPart = TempDFA->FileSize.HighPart; FileBothDirInfo->FileAttributes = TempDFA->dwFileAttributes;
if (TempDFA->isCollection) { FileBothDirInfo->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY; } if (TempDFA->isHidden) { FileBothDirInfo->FileAttributes |= FILE_ATTRIBUTE_HIDDEN; }
FileBothDirInfo->EaSize = 0;
//
// We don't support short file names. We add L'\0' as the first
// character in the ShortName string to make it a zero length name.
//
FileBothDirInfo->ShortNameLength = 0; FileBothDirInfo->ShortName[0] = L'\0';
FileBothDirInfo->FileNameLength = ( (TempDFA->FileNameLength + 1) * sizeof(WCHAR) );
wcscpy(FileBothDirInfo->FileName, TempDFA->FileName); PreviousBlock = (PVOID)FileBothDirInfo; Buffer += NextEntryOffset;
BufferLength -= NextEntryOffset;
BufferLengthUsed += NextEntryOffset;
if (!MRxDAVIsBasicFileInfoCacheFound(RxContext,&BasicInfo,&NtStatus,&CacheName)) { if (TempDFA->isCollection) { UNICODE_STRING DirName;
NtStatus = MRxDAVGetFullDirectoryPath(RxContext,&CacheName,&DirName);
if (DirName.Buffer != NULL) { if (FileBothDirInfo->FileAttributes & FILE_ATTRIBUTE_ENCRYPTED) { NtStatus = MRxDAVCreateEncryptedDirectoryKey(&DirName); } else { NtStatus = MRxDAVQueryEncryptedDirectoryKey(&DirName);
if (NtStatus == STATUS_SUCCESS) { FileBothDirInfo->FileAttributes |= FILE_ATTRIBUTE_ENCRYPTED; } else if (NtStatus == STATUS_OBJECT_NAME_NOT_FOUND) { NtStatus = STATUS_SUCCESS; } }
// The buffer was allocated in MRxDAVGetFullDirectoryPath
RxFreePool(DirName.Buffer); }
if (NtStatus != STATUS_SUCCESS) { goto EXIT_THE_FUNCTION; } } BasicInfo.CreationTime = FileBothDirInfo->CreationTime; BasicInfo.LastAccessTime = FileBothDirInfo->LastAccessTime; BasicInfo.LastWriteTime = FileBothDirInfo->LastWriteTime; BasicInfo.ChangeTime = FileBothDirInfo->ChangeTime; BasicInfo.FileAttributes = FileBothDirInfo->FileAttributes; StandardInfo.AllocationSize = FileBothDirInfo->AllocationSize; StandardInfo.EndOfFile = FileBothDirInfo->EndOfFile; StandardInfo.NumberOfLinks = 1; StandardInfo.DeletePending = FALSE; StandardInfo.Directory = TempDFA->isCollection; MRxDAVCreateFileInfoCacheWithName(&CacheName, RxContext->pFcb->pNetRoot, &BasicInfo, &StandardInfo, STATUS_SUCCESS); } else { if (TempDFA->isCollection && (BasicInfo.FileAttributes & FILE_ATTRIBUTE_ENCRYPTED)) { FileBothDirInfo->FileAttributes |= FILE_ATTRIBUTE_ENCRYPTED; } }
//
// We filter the FILE_ATTRIBUTE_TEMPORARY flag since on FAT (which
// we emulate), FindFirstFile and FindNextFile don�t return
// FILE_ATTRIBUTE_TEMPORARY flag even though GetFileAttributes
// returns it. Hence we only filter this in the attributes that
// are being returned in this call and not in the attributes that
// have been saved.
//
FileBothDirInfo->FileAttributes &= ~FILE_ATTRIBUTE_TEMPORARY;
break;
default:
DavDbgTrace(DAV_TRACE_ERROR, ("%ld: MRxDAVPrecompleteUserModeQueryDirectoryRequest: " "FileInformationClass = UnKnown(%d).\n", PsGetCurrentThreadId(), FileInformationClass));
NtStatus = STATUS_NOT_SUPPORTED; goto EXIT_THE_FUNCTION;
break;
} // end of switch(FileInformationClass)
//
// If the user supplied buffer is not enough to store any more
// information, we are done. This check should be done before
// changing the values below.
//
if (EndOfBuffer) { NtStatus = STATUS_SUCCESS; break; }
//
// These values should be changed after the "EndOfBuffer" check and
// before the "SingleEntry" check.
//
listEntry = listEntry->Flink;
DavFobx->listEntry = listEntry;
DavFobx->CurrentFileIndex++; //
// If the user only asked for a single entry, we are done. This check
// should be done, after changing the values above.
//
if (SingleEntry) { break; }
} while ( listEntry != &(DavFileAttributes->NextEntry) );
//
// If we have gone through all the entries and the BufferLengthUsed is 0,
// then we need to return
//
if ( BufferLengthUsed == 0 && listEntry == &(DavFileAttributes->NextEntry) ) { NtStatus = STATUS_NO_MORE_FILES; //
// Reset the index for the next call.
//
DavFobx->CurrentFileIndex = 0; DavFobx->listEntry = &(DavFobx->DavFileAttributes->NextEntry); goto EXIT_THE_FUNCTION; }
RxContext->Info.LengthRemaining -= BufferLengthUsed; DavDbgTrace(DAV_TRACE_ENTRYEXIT, ("%ld: Leaving MRxDAVPrecompleteUserModeQueryDirectoryRequest.\n", PsGetCurrentThreadId())); EXIT_THE_FUNCTION:
AsyncEngineContext->Status = NtStatus;
if (CacheName.Buffer != NULL) { RxFreePool(CacheName.Buffer); }
return(TRUE); }
NTSTATUS MRxDAVQueryDirectoryFromCache( IN PRX_CONTEXT RxContext, IN PBYTE Buffer, IN PFILE_BASIC_INFORMATION BasicInfo, IN PFILE_STANDARD_INFORMATION StandardInfo, IN ULONG FileIndex ) /*++
Routine Description:
The precompletion routine for the create SrvCall request.
Arguments:
RxContext - The RDBSS context. AsyncEngineContext - The reflctor's context. WorkItem - The work item buffer. WorkItemLength - The length of the work item buffer.
Return Value:
TRUE or FALSE.
--*/ { RxCaptureFobx; NTSTATUS NtStatus = STATUS_SUCCESS; PFILE_NAMES_INFORMATION FileNamesInfo = NULL; PFILE_DIRECTORY_INFORMATION FileDirInfo = NULL; PFILE_FULL_DIR_INFORMATION FileFullDirInfo = NULL; PFILE_BOTH_DIR_INFORMATION FileBothDirInfo = NULL; ULONG BufferLength; PUNICODE_STRING FileName = &capFobx->UnicodeQueryTemplate; ULONG SpaceNeeded = 0;
PAGED_CODE();
BufferLength = RxContext->Info.LengthRemaining; SpaceNeeded = FileName->Length; switch (RxContext->Info.FileInformationClass) { case FileNamesInformation: DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: MRxDAVQueryDirectoryFromCache: " "FileInformationClass = FileNamesInformation.\n", PsGetCurrentThreadId()));
SpaceNeeded += sizeof(FILE_NAMES_INFORMATION); //
// Is there enough space in the user supplied buffer to store the
// next entry ? If not, we need to return now since we cannot store
// any more entries.
//
if (SpaceNeeded > BufferLength) { NtStatus = STATUS_BUFFER_OVERFLOW; goto EXIT_THE_FUNCTION; }
FileNamesInfo = (PFILE_NAMES_INFORMATION)Buffer; FileNamesInfo->NextEntryOffset = 0; FileNamesInfo->FileIndex = FileIndex; FileNamesInfo->FileNameLength = FileName->Length; RtlCopyMemory(FileNamesInfo->FileName,FileName->Buffer,FileName->Length); break;
case FileDirectoryInformation:
DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: MRxDAVQueryDirectoryFromCache: " "FileInformationClass = FileDirectoryInformation.\n", PsGetCurrentThreadId()));
SpaceNeeded += sizeof(FILE_DIRECTORY_INFORMATION);
if (SpaceNeeded > BufferLength) { DavDbgTrace(DAV_TRACE_ERROR, ("%ld: MRxDAVQueryDirectoryFromCache: " "Insufficient buffer length.\n", PsGetCurrentThreadId())); NtStatus = STATUS_BUFFER_OVERFLOW; goto EXIT_THE_FUNCTION; }
FileDirInfo = (PFILE_DIRECTORY_INFORMATION)Buffer; FileDirInfo->NextEntryOffset = 0; FileDirInfo->FileIndex = FileIndex; FileDirInfo->CreationTime.QuadPart = BasicInfo->CreationTime.QuadPart; FileDirInfo->LastAccessTime.QuadPart = BasicInfo->LastAccessTime.QuadPart; FileDirInfo->LastWriteTime.QuadPart = BasicInfo->LastWriteTime.QuadPart; FileDirInfo->ChangeTime.QuadPart = BasicInfo->ChangeTime.QuadPart; FileDirInfo->FileAttributes = BasicInfo->FileAttributes;
//
// We filter the FILE_ATTRIBUTE_TEMPORARY flag since on FAT (which
// we emulate), FindFirstFile and FindNextFile don�t return
// FILE_ATTRIBUTE_TEMPORARY flag even though GetFileAttributes
// returns it. Hence we only filter this in the attributes that
// are being returned in this call and not in the attributes that
// have been saved.
//
FileDirInfo->FileAttributes &= ~FILE_ATTRIBUTE_TEMPORARY;
FileDirInfo->EndOfFile.QuadPart = StandardInfo->EndOfFile.QuadPart; FileDirInfo->AllocationSize.QuadPart = StandardInfo->AllocationSize.QuadPart;
FileDirInfo->FileNameLength = FileName->Length; RtlCopyMemory(FileDirInfo->FileName,FileName->Buffer,FileName->Length); break;
case FileFullDirectoryInformation:
DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: MRxDAVQueryDirectoryFromCache: " "FileInformationClass = FileFullDirectoryInformation.\n", PsGetCurrentThreadId()));
SpaceNeeded += sizeof(FILE_FULL_DIR_INFORMATION);
if (SpaceNeeded > BufferLength) { DavDbgTrace(DAV_TRACE_ERROR, ("%ld: MRxDAVQueryDirectoryFromCache: " "Insufficient buffer length.\n", PsGetCurrentThreadId())); NtStatus = STATUS_BUFFER_OVERFLOW; goto EXIT_THE_FUNCTION; }
FileFullDirInfo = (PFILE_FULL_DIR_INFORMATION)Buffer;
FileFullDirInfo->NextEntryOffset = 0; FileFullDirInfo->FileIndex = FileIndex; FileFullDirInfo->CreationTime.QuadPart = BasicInfo->CreationTime.QuadPart; FileFullDirInfo->LastAccessTime.QuadPart = BasicInfo->LastAccessTime.QuadPart; FileFullDirInfo->LastWriteTime.QuadPart = BasicInfo->LastWriteTime.QuadPart; FileFullDirInfo->ChangeTime.QuadPart = BasicInfo->ChangeTime.QuadPart; FileFullDirInfo->FileAttributes = BasicInfo->FileAttributes;
//
// We filter the FILE_ATTRIBUTE_TEMPORARY flag since on FAT (which
// we emulate), FindFirstFile and FindNextFile don�t return
// FILE_ATTRIBUTE_TEMPORARY flag even though GetFileAttributes
// returns it. Hence we only filter this in the attributes that
// are being returned in this call and not in the attributes that
// have been saved.
//
FileFullDirInfo->FileAttributes &= ~FILE_ATTRIBUTE_TEMPORARY;
FileFullDirInfo->EndOfFile.QuadPart = StandardInfo->EndOfFile.QuadPart; FileFullDirInfo->AllocationSize.QuadPart = StandardInfo->AllocationSize.QuadPart;
FileFullDirInfo->EaSize = 0;
FileFullDirInfo->FileNameLength = FileName->Length; RtlCopyMemory(FileFullDirInfo->FileName,FileName->Buffer,FileName->Length); break;
case FileBothDirectoryInformation:
DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: MRxDAVQueryDirectoryFromCache: " "FileInformationClass = FileBothDirectoryInformation.\n", PsGetCurrentThreadId()));
SpaceNeeded += sizeof(FILE_BOTH_DIR_INFORMATION);
if (SpaceNeeded > BufferLength) { DavDbgTrace(DAV_TRACE_ERROR, ("%ld: MRxDAVQueryDirectoryFromCache: " "Insufficient buffer length.\n", PsGetCurrentThreadId())); NtStatus = STATUS_BUFFER_OVERFLOW; goto EXIT_THE_FUNCTION; }
FileBothDirInfo = (PFILE_BOTH_DIR_INFORMATION)Buffer;
FileBothDirInfo->NextEntryOffset = 0; FileBothDirInfo->FileIndex = FileIndex; FileBothDirInfo->CreationTime.QuadPart = BasicInfo->CreationTime.QuadPart; FileBothDirInfo->LastAccessTime.QuadPart = BasicInfo->LastAccessTime.QuadPart; FileBothDirInfo->LastWriteTime.QuadPart = BasicInfo->LastWriteTime.QuadPart; FileBothDirInfo->ChangeTime.QuadPart = BasicInfo->ChangeTime.QuadPart; FileBothDirInfo->FileAttributes = BasicInfo->FileAttributes;
//
// We filter the FILE_ATTRIBUTE_TEMPORARY flag since on FAT (which
// we emulate), FindFirstFile and FindNextFile don�t return
// FILE_ATTRIBUTE_TEMPORARY flag even though GetFileAttributes
// returns it. Hence we only filter this in the attributes that
// are being returned in this call and not in the attributes that
// have been saved.
//
FileBothDirInfo->FileAttributes &= ~FILE_ATTRIBUTE_TEMPORARY;
FileBothDirInfo->EndOfFile.QuadPart = StandardInfo->EndOfFile.QuadPart; FileBothDirInfo->AllocationSize.QuadPart = StandardInfo->AllocationSize.QuadPart;
FileBothDirInfo->EaSize = 0;
//
// We don't support short file names. We add L'\0' as the first
// character in the ShortName string to make it a zero length name.
//
FileBothDirInfo->ShortNameLength = 0; FileBothDirInfo->ShortName[0] = L'\0'; FileBothDirInfo->FileNameLength = FileName->Length; RtlCopyMemory(FileBothDirInfo->FileName,FileName->Buffer,FileName->Length); break;
default:
DavDbgTrace(DAV_TRACE_ERROR, ("%ld: MRxDAVQueryDirectoryFromCache: " "FileInformationClass = UnKnown(%d).\n", PsGetCurrentThreadId(), RxContext->Info.FileInformationClass));
NtStatus = STATUS_NOT_SUPPORTED; goto EXIT_THE_FUNCTION;
break;
} // end of switch(FileInformationClass)
RxContext->Info.LengthRemaining -= SpaceNeeded;
EXIT_THE_FUNCTION:
return NtStatus; }
|