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
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;
|
|
}
|
|
|