1962 lines
74 KiB
C
1962 lines
74 KiB
C
/*++
|
||
|
||
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);
|
||
|
||
//
|
||
// 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<6F>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<6F>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<6F>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<6F>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<6F>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<6F>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;
|
||
}
|
||
|