Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1969 lines
74 KiB

/*++
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;
}