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.
1301 lines
45 KiB
1301 lines
45 KiB
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
querydir.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the user mode DAV miniredir routine(s) pertaining to
|
|
the QueryDirectory call.
|
|
|
|
Author:
|
|
|
|
Rohan Kumar [RohanK] 20-Sept-1999
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
#pragma hdrstop
|
|
|
|
#include "ntumrefl.h"
|
|
#include "usrmddav.h"
|
|
#include "global.h"
|
|
#include "nodefac.h"
|
|
#include "UniUtf.h"
|
|
|
|
#define MSN_SPACE_FAKE_DELTA 52428800 // 50 MB
|
|
|
|
|
|
ULONG
|
|
DavFsQueryDirectory(
|
|
PDAV_USERMODE_WORKITEM DavWorkItem
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles QueryDirectory requests for the DAV Mini-Redir that
|
|
get reflected from the kernel.
|
|
|
|
Arguments:
|
|
|
|
DavWorkItem - The buffer that contains the request parameters and options.
|
|
|
|
Return Value:
|
|
|
|
The return status for the operation
|
|
|
|
--*/
|
|
{
|
|
ULONG WStatus = ERROR_SUCCESS;
|
|
PDAV_USERMODE_QUERYDIR_REQUEST QueryDirRequest;
|
|
PWCHAR ServerName = NULL, DirectoryPath = NULL, CanName = NULL;
|
|
ULONG_PTR CallBackContext = (ULONG_PTR)0;
|
|
BOOL EnCriSec = FALSE, ReturnVal, CallBackContextInitialized = FALSE;
|
|
ULONG ServerID;
|
|
PPER_USER_ENTRY PerUserEntry = NULL;
|
|
PHASH_SERVER_ENTRY ServerHashEntry = NULL;
|
|
HINTERNET DavConnHandle, DavOpenHandle;
|
|
BOOL didImpersonate = FALSE;
|
|
PUMRX_USERMODE_WORKITEM_HEADER UserWorkItem = NULL;
|
|
BOOL BStatus = FALSE;
|
|
|
|
//
|
|
// Get the request and response buffer pointers from the DavWorkItem.
|
|
//
|
|
QueryDirRequest = &(DavWorkItem->QueryDirRequest);
|
|
|
|
//
|
|
// Check to see if we have already created the DavFileAttributes list. If
|
|
// we have, we are already done and just need to return.
|
|
//
|
|
if (QueryDirRequest->AlreadyDone) {
|
|
DavPrint((DEBUG_MISC,
|
|
"DavFsQueryDirectory: DavFileAttributes already created.\n"));
|
|
WStatus = ERROR_SUCCESS;
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
//
|
|
// The first character is a '\' which has to be stripped.
|
|
//
|
|
ServerName = &(QueryDirRequest->ServerName[1]);
|
|
if (!ServerName) {
|
|
DavPrint((DEBUG_ERRORS, "DavFsQueryDirectory: ServerName is NULL.\n"));
|
|
WStatus = ERROR_INVALID_PARAMETER; // STATUS_INVALID_PARAMETER;
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
DavPrint((DEBUG_MISC,
|
|
"DavFsQueryDirectory: ServerName = %ws.\n", ServerName));
|
|
|
|
ServerID = QueryDirRequest->ServerID;
|
|
DavPrint((DEBUG_MISC, "DavFsQueryDirectory: ServerID = %d.\n", ServerID));
|
|
|
|
//
|
|
// The first character is a '\' which has to be stripped.
|
|
//
|
|
DirectoryPath = &(QueryDirRequest->PathName[1]);
|
|
if (!DirectoryPath) {
|
|
DavPrint((DEBUG_ERRORS, "DavFsQueryDirectory: DirectoryPath is NULL.\n"));
|
|
WStatus = ERROR_INVALID_PARAMETER; // STATUS_INVALID_PARAMETER;
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
DavPrint((DEBUG_MISC,
|
|
"DavFsQueryDirectory: DirectoryPath = %ws.\n", DirectoryPath));
|
|
|
|
//
|
|
// The DirectoryPath can contain \ characters. Replace them by / characters.
|
|
//
|
|
CanName = DirectoryPath;
|
|
while (*CanName) {
|
|
if (*CanName == L'\\') {
|
|
*CanName = L'/';
|
|
}
|
|
CanName++;
|
|
}
|
|
|
|
//
|
|
// If we have a dummy share name in the DirectoryPath, we need to remove it
|
|
// right now before we contact the server.
|
|
//
|
|
DavRemoveDummyShareFromFileName(DirectoryPath);
|
|
|
|
//
|
|
// If there are no wild cards, we set the depth of the DAV request to 0,
|
|
// otherwise, we set the depth to 1.
|
|
//
|
|
DavWorkItem->AsyncQueryDirectoryCall.NoWildCards = QueryDirRequest->NoWildCards;
|
|
DavPrint((DEBUG_MISC,
|
|
"DavFsQueryDirectory: NoWildCards = %d.\n", QueryDirRequest->NoWildCards));
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavFsQueryDirectory: LogonId.LowPart = %08lx.\n",
|
|
QueryDirRequest->LogonID.LowPart));
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavFsQueryDirectory: LogonId.HighPart = %08lx.\n",
|
|
QueryDirRequest->LogonID.HighPart));
|
|
|
|
|
|
UserWorkItem = (PUMRX_USERMODE_WORKITEM_HEADER)DavWorkItem;
|
|
|
|
//
|
|
// If we are using WinInet synchronously, then we need to impersonate the
|
|
// clients context now.
|
|
//
|
|
|
|
WStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle);
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsQueryDirectory/UMReflectorImpersonate. Error Val = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
didImpersonate = TRUE;
|
|
|
|
|
|
|
|
//
|
|
// Allocate memory for the INTERNET_ASYNC_RESULT structure.
|
|
//
|
|
DavWorkItem->AsyncResult = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
|
|
sizeof(INTERNET_ASYNC_RESULT));
|
|
if (DavWorkItem->AsyncResult == NULL) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsQueryDirectory/LocalAlloc. Error Val = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
//
|
|
// A User Entry for this user must have been created during the create call
|
|
// earlier. The user entry contains the handle used to send an HttpOpen
|
|
// request.
|
|
//
|
|
|
|
EnterCriticalSection( &(HashServerEntryTableLock) );
|
|
EnCriSec = TRUE;
|
|
|
|
ReturnVal = DavDoesUserEntryExist(ServerName,
|
|
ServerID,
|
|
&(QueryDirRequest->LogonID),
|
|
&PerUserEntry,
|
|
&ServerHashEntry);
|
|
|
|
//
|
|
// If the following request in the kernel get cancelled even before the
|
|
// corresponding usermode thread gets a chance to execute this code, then
|
|
// it possible that the VNetRoot (hence the PerUserEntry) and SrvCall get
|
|
// finalized before the thread that is handling the create comes here. This
|
|
// could happen if this request was the only one for this share and the
|
|
// server as well. This is why we need to check if the ServerHashEntry and
|
|
// the PerUserEntry are valid before proceeding.
|
|
//
|
|
if (ReturnVal == FALSE || ServerHashEntry == NULL || PerUserEntry == NULL) {
|
|
WStatus = ERROR_CANCELLED;
|
|
DavPrint((DEBUG_ERRORS, "DavFsQueryDirectory: (ServerHashEntry == NULL || PerUserEntry == NULL)\n"));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
DavWorkItem->AsyncQueryDirectoryCall.ServerHashEntry = ServerHashEntry;
|
|
|
|
DavWorkItem->AsyncQueryDirectoryCall.PerUserEntry = PerUserEntry;
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavFsQueryDirectory: PerUserEntry = %08lx.\n",
|
|
PerUserEntry));
|
|
|
|
//
|
|
// Add a reference to the user entry.
|
|
//
|
|
PerUserEntry->UserEntryRefCount++;
|
|
|
|
//
|
|
// Since a create had succeeded earlier, the entry must be good.
|
|
//
|
|
ASSERT(PerUserEntry->UserEntryState == UserEntryInitialized);
|
|
ASSERT(PerUserEntry->DavConnHandle != NULL);
|
|
DavConnHandle = PerUserEntry->DavConnHandle;
|
|
|
|
//
|
|
// And yes, we obviously have to leave the critical section
|
|
// before returning.
|
|
//
|
|
LeaveCriticalSection( &(HashServerEntryTableLock) );
|
|
EnCriSec = FALSE;
|
|
|
|
//
|
|
// We now call the HttpOpenRequest function and return.
|
|
//
|
|
DavWorkItem->DavOperation = DAV_CALLBACK_HTTP_OPEN;
|
|
DavWorkItem->DavMinorOperation = DavMinorReadData;
|
|
DavWorkItem->AsyncQueryDirectoryCall.DataBuff = NULL;
|
|
DavWorkItem->AsyncQueryDirectoryCall.didRead = NULL;
|
|
DavWorkItem->AsyncQueryDirectoryCall.Context1 = NULL;
|
|
DavWorkItem->AsyncQueryDirectoryCall.Context2 = NULL;
|
|
|
|
// convert the unicode directory path to UTF-8 URL format
|
|
// space and other white characters will remain untouched - these should
|
|
// be taken care of by wininet calls
|
|
|
|
BStatus = DavHttpOpenRequestW(DavConnHandle,
|
|
L"PROPFIND",
|
|
DirectoryPath,
|
|
L"HTTP/1.1",
|
|
NULL,
|
|
NULL,
|
|
INTERNET_FLAG_KEEP_CONNECTION |
|
|
INTERNET_FLAG_NO_COOKIES,
|
|
CallBackContext,
|
|
L"DavFsQueryDirectory",
|
|
&DavOpenHandle);
|
|
if(BStatus == FALSE) {
|
|
WStatus = GetLastError();
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
if (DavOpenHandle == NULL) {
|
|
WStatus = GetLastError();
|
|
if (WStatus != ERROR_IO_PENDING) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsQueryDirectory/HttpOpenRequest. Error Val = %d\n",
|
|
WStatus));
|
|
}
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
//
|
|
// Cache the DavOpenHandle in the DavWorkItem.
|
|
//
|
|
DavWorkItem->AsyncQueryDirectoryCall.DavOpenHandle = DavOpenHandle;
|
|
|
|
WStatus = DavAsyncCommonStates(DavWorkItem, FALSE);
|
|
if (WStatus != ERROR_SUCCESS && WStatus != ERROR_IO_PENDING) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsQueryDirectory/DavAsyncCommonStates. Error Val = %08lx\n",
|
|
WStatus));
|
|
}
|
|
|
|
EXIT_THE_FUNCTION:
|
|
|
|
if (EnCriSec) {
|
|
LeaveCriticalSection( &(HashServerEntryTableLock) );
|
|
EnCriSec = FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// If we are using WinInet synchronously, then we should never get back
|
|
// ERROR_IO_PENDING from WinInet.
|
|
//
|
|
ASSERT(WStatus != ERROR_IO_PENDING);
|
|
|
|
//
|
|
// If this thread impersonated a user, we need to revert back.
|
|
//
|
|
if (didImpersonate) {
|
|
RevertToSelf();
|
|
}
|
|
|
|
//
|
|
// Set the return status of the operation. This is used by the kernel
|
|
// mode routines to figure out the completion status of the user mode
|
|
// request. This is done here because the async completion routine that is
|
|
// called immediately afterwards needs the status set.
|
|
//
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
DavWorkItem->Status = DavMapErrorToNtStatus(WStatus);
|
|
} else {
|
|
DavWorkItem->Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
DavAsyncQueryDirectoryCompletion(DavWorkItem);
|
|
|
|
return WStatus;
|
|
}
|
|
|
|
|
|
DWORD
|
|
DavAsyncQueryDirectory(
|
|
PDAV_USERMODE_WORKITEM DavWorkItem,
|
|
BOOLEAN CalledByCallBackThread
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the callback routine for the query directory operation.
|
|
|
|
Arguments:
|
|
|
|
DavWorkItem - The DAV_USERMODE_WORKITEM value.
|
|
|
|
CalledByCallbackThread - TRUE, if this function was called by the thread
|
|
which picks of the DavWorkItem from the Callback
|
|
function. This happens when an Async WinInet call
|
|
returns ERROR_IO_PENDING and completes later.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS or the appropriate error value.
|
|
|
|
--*/
|
|
{
|
|
ULONG WStatus = ERROR_SUCCESS;
|
|
ULONG NumOfFileEntries = 0;
|
|
PUMRX_USERMODE_WORKITEM_HEADER UserWorkItem = NULL;
|
|
BOOL didImpersonate = FALSE, ReturnVal, readDone = FALSE;
|
|
HINTERNET DavOpenHandle = NULL;
|
|
DWORD didRead, DataBuffBytes, TotalDataBytesRead = 0;
|
|
PCHAR DataBuff = NULL;
|
|
LPDWORD NumRead = NULL;
|
|
PDAV_FILE_ATTRIBUTES DavFileAttributes = NULL;
|
|
PVOID Ctx1 = NULL, Ctx2 = NULL;
|
|
PDAV_USERMODE_QUERYDIR_RESPONSE QueryDirResponse = NULL;
|
|
PDAV_FILE_ATTRIBUTES DFA1 = NULL, DFA2 = NULL, TempDFA = NULL;
|
|
BOOL fFreeDFAs = TRUE;
|
|
PDAV_FILE_ATTRIBUTES parentDFA = NULL;
|
|
|
|
UserWorkItem = (PUMRX_USERMODE_WORKITEM_HEADER)DavWorkItem;
|
|
|
|
|
|
ASSERT(CalledByCallBackThread == FALSE);
|
|
|
|
|
|
switch (DavWorkItem->DavOperation) {
|
|
|
|
case DAV_CALLBACK_HTTP_END: {
|
|
|
|
DavOpenHandle = DavWorkItem->AsyncQueryDirectoryCall.DavOpenHandle;
|
|
|
|
//
|
|
// If the file for which the PROPFIND was done does not exist, then
|
|
// we need to fail right away.
|
|
//
|
|
|
|
WStatus = DavQueryAndParseResponse(DavOpenHandle);
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
//
|
|
// The file/directory for which the PROPFIND was done, does not
|
|
// exist.
|
|
//
|
|
|
|
if (WStatus != ERROR_FILE_NOT_FOUND) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncQueryDirectory/DavQueryAndParseResponse. "
|
|
"WStatus = %d\n", WStatus));
|
|
}
|
|
|
|
WStatus = ERROR_FILE_NOT_FOUND; // STATUS_OBJECT_NAME_NOT_FOUND;
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
//
|
|
// The file exists. The next thing we do is read the properties
|
|
// of the file (or files in the directory).
|
|
//
|
|
DavWorkItem->DavOperation = DAV_CALLBACK_HTTP_READ;
|
|
|
|
}
|
|
//
|
|
// Lack of break is intentional.
|
|
//
|
|
|
|
case DAV_CALLBACK_HTTP_READ: {
|
|
|
|
DavOpenHandle = DavWorkItem->AsyncQueryDirectoryCall.DavOpenHandle;
|
|
|
|
if (DavWorkItem->AsyncQueryDirectoryCall.DataBuff == NULL) {
|
|
//
|
|
// Need to allocate memory for the read buffer.
|
|
//
|
|
DataBuffBytes = NUM_OF_BYTES_TO_READ;
|
|
DataBuff = LocalAlloc (LMEM_FIXED | LMEM_ZEROINIT, DataBuffBytes);
|
|
if (DataBuff == NULL) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncQueryDirectory/LocalAlloc. Error Val = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
DavWorkItem->AsyncQueryDirectoryCall.DataBuff = DataBuff;
|
|
}
|
|
|
|
if (DavWorkItem->AsyncQueryDirectoryCall.didRead == NULL) {
|
|
//
|
|
// Allocate memory for the DWORD that stores the number of bytes
|
|
// read.
|
|
//
|
|
NumRead = LocalAlloc (LMEM_FIXED | LMEM_ZEROINIT, sizeof(DWORD));
|
|
if (NumRead == NULL) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncQueryDirectory/LocalAlloc. Error Val = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
DavWorkItem->AsyncQueryDirectoryCall.didRead = NumRead;
|
|
}
|
|
|
|
DavWorkItem->DavOperation = DAV_CALLBACK_HTTP_READ;
|
|
|
|
NumRead = DavWorkItem->AsyncQueryDirectoryCall.didRead;
|
|
DataBuff = DavWorkItem->AsyncQueryDirectoryCall.DataBuff;
|
|
Ctx1 = DavWorkItem->AsyncQueryDirectoryCall.Context1;
|
|
Ctx2 = DavWorkItem->AsyncQueryDirectoryCall.Context2;
|
|
|
|
do {
|
|
|
|
switch (DavWorkItem->DavMinorOperation) {
|
|
|
|
case DavMinorReadData:
|
|
|
|
DavWorkItem->DavMinorOperation = DavMinorPushData;
|
|
|
|
ReturnVal = InternetReadFile(DavOpenHandle,
|
|
(LPVOID)DataBuff,
|
|
NUM_OF_BYTES_TO_READ,
|
|
NumRead);
|
|
if (!ReturnVal) {
|
|
WStatus = GetLastError();
|
|
if (WStatus != ERROR_IO_PENDING) {
|
|
DavCloseContext(Ctx1, Ctx2);
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncQueryDirectory/InternetReadFile. "
|
|
"Error Val = %d\n", WStatus));
|
|
}
|
|
DavPrint((DEBUG_MISC,
|
|
"DavAsyncQueryDirectory/InternetReadFile. "
|
|
"ERROR_IO_PENDING.\n"));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
//
|
|
// We reject files whose attributes are greater than a
|
|
// certain size (DavFileAttributesLimitInBytes). This
|
|
// is a parameter that can be set in the registry. This
|
|
// is done to avoid attacks by rogue servers. For PROPFIND
|
|
// with depth 1, we add a multiple of 10.
|
|
//
|
|
TotalDataBytesRead += *NumRead;
|
|
if (DavWorkItem->AsyncQueryDirectoryCall.NoWildCards) {
|
|
if (TotalDataBytesRead > DavFileAttributesLimitInBytes) {
|
|
WStatus = ERROR_BAD_NET_RESP;
|
|
DavPrint((DEBUG_ERRORS, "DavAsyncQueryDirectory. FileAttributesSize > %d\n", DavFileAttributesLimitInBytes));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
} else {
|
|
if (TotalDataBytesRead > (10 * DavFileAttributesLimitInBytes)) {
|
|
WStatus = ERROR_BAD_NET_RESP;
|
|
DavPrint((DEBUG_ERRORS, "DavAsyncQueryDirectory. FileAttributesSize > %d\n", (10 * DavFileAttributesLimitInBytes)));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Lack of break is intentional.
|
|
//
|
|
|
|
case DavMinorPushData:
|
|
|
|
DavWorkItem->DavMinorOperation = DavMinorReadData;
|
|
|
|
didRead = *NumRead;
|
|
|
|
readDone = (didRead == 0) ? TRUE : FALSE;
|
|
|
|
WStatus = DavPushData(DataBuff, &Ctx1, &Ctx2, didRead, readDone);
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncQueryDirectory/DavPushData."
|
|
" Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
if (DavWorkItem->AsyncQueryDirectoryCall.Context1 == NULL) {
|
|
DavWorkItem->AsyncQueryDirectoryCall.Context1 = Ctx1;
|
|
}
|
|
|
|
if (DavWorkItem->AsyncQueryDirectoryCall.Context2 == NULL) {
|
|
DavWorkItem->AsyncQueryDirectoryCall.Context2 = Ctx2;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
WStatus = ERROR_INVALID_PARAMETER;
|
|
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncQueryDirectory. Invalid DavMinorOperation ="
|
|
" %d.\n", DavWorkItem->DavMinorOperation));
|
|
|
|
goto EXIT_THE_FUNCTION;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (readDone) {
|
|
break;
|
|
}
|
|
|
|
} while ( TRUE );
|
|
|
|
//
|
|
// We now need to parse the data.
|
|
//
|
|
|
|
DavFileAttributes = LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT,
|
|
sizeof(DAV_FILE_ATTRIBUTES) );
|
|
if (DavFileAttributes == NULL) {
|
|
WStatus = GetLastError();
|
|
DavCloseContext(Ctx1, Ctx2);
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncQueryDirectory/LocalAlloc. "
|
|
"Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
InitializeListHead( &(DavFileAttributes->NextEntry) );
|
|
|
|
WStatus = DavParseDataEx(DavFileAttributes, Ctx1, Ctx2, &NumOfFileEntries, &parentDFA);
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
DavFinalizeFileAttributesList(DavFileAttributes, TRUE);
|
|
DavFileAttributes = NULL;
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncQueryDirectory/DavParseDataEx. "
|
|
"Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
QueryDirResponse = &(DavWorkItem->QueryDirResponse);
|
|
|
|
//
|
|
// If we queried the server for a file which did not exist, it may
|
|
// return 200 OK with no files in the XML response.
|
|
//
|
|
if (DavWorkItem->AsyncQueryDirectoryCall.NoWildCards) {
|
|
|
|
if (NumOfFileEntries != 1) {
|
|
|
|
PLIST_ENTRY listEntry = &(DavFileAttributes->NextEntry);
|
|
PDAV_FILE_ATTRIBUTES DavFA = NULL;
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavAsyncQueryDirectory. NumOfFileEntries = %d\n",
|
|
NumOfFileEntries));
|
|
|
|
do {
|
|
DavFA = CONTAINING_RECORD(listEntry, DAV_FILE_ATTRIBUTES, NextEntry);
|
|
DavPrint((DEBUG_MISC,
|
|
"DavAsyncQueryDirectory. FileName = %ws\n",
|
|
DavFA->FileName));
|
|
listEntry = listEntry->Flink;
|
|
} while ( listEntry != &(DavFileAttributes->NextEntry) );
|
|
|
|
ASSERT(NumOfFileEntries == 0);
|
|
|
|
DavCloseContext(Ctx1, Ctx2);
|
|
|
|
DavFinalizeFileAttributesList(DavFileAttributes, TRUE);
|
|
DavFileAttributes = NULL;
|
|
|
|
WStatus = ERROR_FILE_NOT_FOUND; // STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
goto EXIT_THE_FUNCTION;
|
|
|
|
}
|
|
} else {
|
|
//
|
|
// This Query is done for a Directory or for a collection of files
|
|
// (ex. dir Z:\ab*).
|
|
//
|
|
|
|
// In the DFA list (DavFileAttributes) returned by DavParseDataEx(...),
|
|
// we want to have DFA of the "directory being queried" at the head
|
|
// of the list.
|
|
// List (DavFileAttributes) returned by DavParseDataEx(...) may not
|
|
// necessarily have this TRUE.
|
|
// Since DavFileAttributes is a cyclic linked list (all entries are allocated
|
|
// and are to be freed by this function), we will set DavFileAttributes to
|
|
// point to DFA pointed by parentDFA (points to DFA of "directory being
|
|
// queried").
|
|
//
|
|
// Note: DavFileAttributes->FileIndex which is set in an increasing order
|
|
// starting from 0 in DavParseDataEx(...), may no longer remain in this valid
|
|
// order after re-pointing of DavFileAttributes pointer. We will set them
|
|
// in valid order again here.
|
|
//
|
|
if (parentDFA != NULL && parentDFA != DavFileAttributes) {
|
|
PLIST_ENTRY listEntry = NULL;
|
|
PDAV_FILE_ATTRIBUTES TempDFA = NULL;
|
|
ULONG Count = DavFileAttributes->FileIndex;
|
|
|
|
DavPrint((DEBUG_DEBUG, "DavAsyncQueryDirectory. CollectionDFA=0x%x",
|
|
parentDFA));
|
|
|
|
DavFileAttributes = parentDFA;
|
|
|
|
//
|
|
// We start the Count with first value DavParseDataEx (value of Head
|
|
// entry in the List) is setting in DavFileAttributes List.
|
|
//
|
|
|
|
listEntry = DavFileAttributes->NextEntry.Flink;
|
|
|
|
//
|
|
// Set the file indices.
|
|
//
|
|
DavFileAttributes->FileIndex = Count;
|
|
Count++;
|
|
while ( listEntry != &(DavFileAttributes->NextEntry) ) {
|
|
|
|
TempDFA = CONTAINING_RECORD(listEntry, DAV_FILE_ATTRIBUTES, NextEntry);
|
|
|
|
listEntry = listEntry->Flink;
|
|
|
|
TempDFA->FileIndex = Count;
|
|
|
|
Count++;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If this was a query for all the files under the directory, then we
|
|
// need to add the files . (current directory) and .. (parent directory)
|
|
// since these are not returned by the server.
|
|
//
|
|
if ( !(DavWorkItem->AsyncQueryDirectoryCall.NoWildCards) ) {
|
|
|
|
PLIST_ENTRY listEntry = NULL;
|
|
PLIST_ENTRY TempEntry = NULL;
|
|
ULONG Count = 0;
|
|
|
|
|
|
//
|
|
// We first create the two entires and copy the file names in them.
|
|
//
|
|
|
|
DFA1 = LocalAlloc(LPTR, sizeof(DAV_FILE_ATTRIBUTES));
|
|
if (DFA1 == NULL) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncQueryDirectory/LocalAlloc. "
|
|
"Error Val = %d\n", WStatus));
|
|
DavCloseContext(Ctx1, Ctx2);
|
|
DavFinalizeFileAttributesList(DavFileAttributes, TRUE);
|
|
DavFileAttributes = NULL;
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
InitializeListHead( &(DFA1->NextEntry) );
|
|
|
|
//
|
|
// Since the file name is ".", the amount of memory required to hold
|
|
// this name is 2 * sizeof(WCHAR). The extra 1 is for the final L'\0'.
|
|
//
|
|
DFA1->FileName = LocalAlloc(LPTR, (2 * sizeof(WCHAR)));
|
|
if (DFA1->FileName == NULL) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncQueryDirectory/LocalAlloc. "
|
|
"Error Val = %d\n", WStatus));
|
|
DavCloseContext(Ctx1, Ctx2);
|
|
DavFinalizeFileAttributesList(DavFileAttributes, TRUE);
|
|
DavFileAttributes = NULL;
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
wcscpy(DFA1->FileName, L".");
|
|
DFA1->FileNameLength = 1;
|
|
|
|
|
|
DFA2 = LocalAlloc(LPTR, sizeof(DAV_FILE_ATTRIBUTES));
|
|
if (DFA2 == NULL) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncQueryDirectory/LocalAlloc. "
|
|
"Error Val = %d\n", WStatus));
|
|
DavCloseContext(Ctx1, Ctx2);
|
|
DavFinalizeFileAttributesList(DavFileAttributes, TRUE);
|
|
DavFileAttributes = NULL;
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
InitializeListHead( &(DFA2->NextEntry) );
|
|
|
|
//
|
|
// Since the file name is "..", the amount of memory required to hold
|
|
// this name is 3 * sizeof(WCHAR). The extra 1 is for the final L'\0'.
|
|
//
|
|
DFA2->FileName = LocalAlloc(LPTR, (3 * sizeof(WCHAR)));
|
|
if (DFA2->FileName == NULL) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncQueryDirectory/LocalAlloc. "
|
|
"Error Val = %d\n", WStatus));
|
|
DavCloseContext(Ctx1, Ctx2);
|
|
DavFinalizeFileAttributesList(DavFileAttributes, TRUE);
|
|
DavFileAttributes = NULL;
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
wcscpy(DFA2->FileName, L"..");
|
|
DFA2->FileNameLength = 2;
|
|
|
|
|
|
//
|
|
// Both these are collections ofcourse.
|
|
//
|
|
DFA1->isCollection = DFA2->isCollection = TRUE;
|
|
|
|
//
|
|
// We set the following time values of the new entries to the value
|
|
// of the first entry in the DavFileAttributes list which is the
|
|
// directory being enumerated.
|
|
//
|
|
|
|
DFA1->CreationTime.HighPart = DFA2->CreationTime.HighPart = DavFileAttributes->CreationTime.HighPart;
|
|
DFA1->CreationTime.LowPart = DFA2->CreationTime.LowPart = DavFileAttributes->CreationTime.LowPart;
|
|
|
|
DFA1->DavCreationTime.HighPart = DFA2->DavCreationTime.HighPart = DavFileAttributes->DavCreationTime.HighPart;
|
|
DFA1->DavCreationTime.LowPart = DFA2->DavCreationTime.LowPart = DavFileAttributes->DavCreationTime.LowPart;
|
|
|
|
DFA1->LastModifiedTime.HighPart = DFA2->LastModifiedTime.HighPart = DavFileAttributes->LastModifiedTime.HighPart;
|
|
DFA1->LastModifiedTime.LowPart = DFA2->LastModifiedTime.LowPart = DavFileAttributes->LastModifiedTime.LowPart;
|
|
|
|
DFA1->DavLastModifiedTime.HighPart = DFA2->DavLastModifiedTime.HighPart = DavFileAttributes->DavLastModifiedTime.HighPart;
|
|
DFA1->DavLastModifiedTime.LowPart = DFA2->DavLastModifiedTime.LowPart = DavFileAttributes->DavLastModifiedTime.LowPart;
|
|
|
|
DFA1->LastAccessTime.HighPart = DFA2->LastAccessTime.HighPart = DavFileAttributes->LastAccessTime.HighPart;
|
|
DFA1->LastAccessTime.LowPart = DFA2->LastAccessTime.LowPart = DavFileAttributes->LastAccessTime.LowPart;
|
|
|
|
//
|
|
// We need to add these two after the first entry. This is because
|
|
// the first entry is always ignored when dealing with WildCard
|
|
// queries in the kernel. This is done because the first entry is
|
|
// the directory being enumerated and we don't need to show that.
|
|
// So, if we had 1->2->3->....->n->1 (cyclic list), we need to insert
|
|
// DFA1 and DFA2 in the following manner.
|
|
// 1->DFA1->DFA2->2->3->......->n->1 (cyclic list)
|
|
// ^
|
|
// |
|
|
// TempEntry
|
|
// where DFA1 = L"." and DFA2 = L".."
|
|
// We do this insertion below.
|
|
//
|
|
|
|
TempEntry = DavFileAttributes->NextEntry.Flink;
|
|
InsertTailList(TempEntry, &(DFA1->NextEntry));
|
|
InsertTailList(TempEntry, &(DFA2->NextEntry));
|
|
TempEntry = NULL;
|
|
fFreeDFAs = FALSE;
|
|
|
|
//
|
|
// We need to increment the number of file entries by 2 to take into
|
|
// account the two new entries we added above.
|
|
//
|
|
NumOfFileEntries += 2;
|
|
|
|
listEntry = DavFileAttributes->NextEntry.Flink;
|
|
|
|
//
|
|
// We start the Count with first value DavParseDataEx (value of Head
|
|
// entry in the List) is setting in DavFileAttributes List.
|
|
//
|
|
Count = DavFileAttributes->FileIndex;
|
|
|
|
//
|
|
// Set the file indices.
|
|
//
|
|
DavFileAttributes->FileIndex = Count;
|
|
Count++;
|
|
while ( listEntry != &(DavFileAttributes->NextEntry) ) {
|
|
|
|
TempDFA = CONTAINING_RECORD(listEntry, DAV_FILE_ATTRIBUTES, NextEntry);
|
|
|
|
listEntry = listEntry->Flink;
|
|
|
|
TempDFA->FileIndex = Count;
|
|
|
|
Count++;
|
|
|
|
}
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavAsyncQueryDirectory: NumOfFileEntries = %d, Count = %d\n",
|
|
NumOfFileEntries, Count));
|
|
|
|
}
|
|
|
|
//
|
|
// Set the response to be sent down to the kernel. We send the pointer
|
|
// to the head of the list that was allocated during parsing.
|
|
//
|
|
QueryDirResponse->DavFileAttributes = DavFileAttributes;
|
|
QueryDirResponse->NumOfFileEntries = NumOfFileEntries;
|
|
|
|
DavCloseContext(Ctx1, Ctx2);
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavAsyncQueryDirectory: DavFileAttributes = %08lx.\n",
|
|
DavFileAttributes));
|
|
|
|
}
|
|
break;
|
|
|
|
default:
|
|
|
|
WStatus = ERROR_INVALID_PARAMETER;
|
|
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncQueryDirectory: Invalid DavOperation = %d.\n",
|
|
DavWorkItem->DavOperation));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
EXIT_THE_FUNCTION:
|
|
|
|
|
|
if(fFreeDFAs == TRUE) {
|
|
if(DFA1 != NULL) {
|
|
DavFinalizeFileAttributesList(DFA1, TRUE);
|
|
DFA1 = NULL;
|
|
}
|
|
if(DFA2 != NULL) {
|
|
DavFinalizeFileAttributesList(DFA2, TRUE);
|
|
DFA2 = NULL;
|
|
}
|
|
fFreeDFAs = FALSE;
|
|
}
|
|
|
|
//
|
|
// If we did impersonate, we need to revert back.
|
|
//
|
|
if (didImpersonate) {
|
|
ULONG RStatus;
|
|
RStatus = UMReflectorRevert(UserWorkItem);
|
|
if (RStatus != ERROR_SUCCESS) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncQueryDirectory/UMReflectorRevert. Error Val"
|
|
" = %d\n", RStatus));
|
|
}
|
|
}
|
|
|
|
|
|
return WStatus;
|
|
}
|
|
|
|
|
|
VOID
|
|
DavAsyncQueryDirectoryCompletion(
|
|
PDAV_USERMODE_WORKITEM DavWorkItem
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles the QueryDirectory completion. It basically frees up
|
|
the resources allocated during the QueryDirectory operation.
|
|
|
|
Arguments:
|
|
|
|
DavWorkItem - The DAV_USERMODE_WORKITEM value.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
{
|
|
if (DavWorkItem->AsyncQueryDirectoryCall.DavOpenHandle != NULL) {
|
|
BOOL ReturnVal;
|
|
ULONG FreeStatus;
|
|
HINTERNET DavOpenHandle = DavWorkItem->AsyncQueryDirectoryCall.DavOpenHandle;
|
|
ReturnVal = InternetCloseHandle( DavOpenHandle );
|
|
if (!ReturnVal) {
|
|
FreeStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncQueryDirectoryCompletion/InternetCloseHandle. "
|
|
"Error Val = %d\n", FreeStatus));
|
|
}
|
|
}
|
|
|
|
if (DavWorkItem->AsyncQueryDirectoryCall.DataBuff != NULL) {
|
|
HLOCAL FreeHandle;
|
|
ULONG FreeStatus;
|
|
FreeHandle = LocalFree((HLOCAL)DavWorkItem->AsyncQueryDirectoryCall.DataBuff);
|
|
if (FreeHandle != NULL) {
|
|
FreeStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncQueryDirectoryCompletion/LocalFree. Error Val = %d\n",
|
|
FreeStatus));
|
|
}
|
|
}
|
|
|
|
if (DavWorkItem->AsyncQueryDirectoryCall.didRead != NULL) {
|
|
HLOCAL FreeHandle;
|
|
ULONG FreeStatus;
|
|
FreeHandle = LocalFree((HLOCAL)DavWorkItem->AsyncQueryDirectoryCall.didRead);
|
|
if (FreeHandle != NULL) {
|
|
FreeStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncQueryDirectoryCompletion/LocalFree. Error Val = %d\n",
|
|
FreeStatus));
|
|
}
|
|
}
|
|
|
|
if (DavWorkItem->AsyncResult != NULL) {
|
|
HLOCAL FreeHandle;
|
|
ULONG FreeStatus;
|
|
FreeHandle = LocalFree((HLOCAL)DavWorkItem->AsyncResult);
|
|
if (FreeHandle != NULL) {
|
|
FreeStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncQueryDirectoryCompletion/LocalFree. Error Val ="
|
|
" %d\n", FreeStatus));
|
|
}
|
|
}
|
|
|
|
//
|
|
// The callback context should not be finalized if we are returning
|
|
// ERROR_IO_PENDING.
|
|
//
|
|
DavFsFinalizeTheDavCallBackContext(DavWorkItem);
|
|
|
|
//
|
|
// We are done with the per user entry, so finalize it.
|
|
//
|
|
if (DavWorkItem->AsyncQueryDirectoryCall.PerUserEntry) {
|
|
DavFinalizePerUserEntry( &(DavWorkItem->AsyncQueryDirectoryCall.PerUserEntry) );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
ULONG
|
|
DavFsQueryVolumeInformation(
|
|
PDAV_USERMODE_WORKITEM DavWorkItem
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles QueryVolumeInformationRequest requests for the DAV Mini-Redir that
|
|
get reflected from the kernel.
|
|
|
|
Arguments:
|
|
|
|
DavWorkItem - The buffer that contains the request parameters and options.
|
|
|
|
Return Value:
|
|
|
|
The return status for the operation
|
|
|
|
--*/
|
|
{
|
|
ULONG WStatus = ERROR_SUCCESS;
|
|
PDAV_USERMODE_QUERYVOLUMEINFORMATION_REQUEST QueryVolumeInformationRequest;
|
|
PWCHAR ServerName = NULL, ShareName = NULL;
|
|
BOOL EnCriSec = FALSE, ReturnVal, CallBackContextInitialized = FALSE;
|
|
ULONG ServerID;
|
|
PPER_USER_ENTRY PerUserEntry = NULL;
|
|
PHASH_SERVER_ENTRY ServerHashEntry = NULL;
|
|
HINTERNET DavConnHandle, DavOpenHandle;
|
|
BOOL didImpersonate = FALSE;
|
|
PUMRX_USERMODE_WORKITEM_HEADER UserWorkItem = NULL;
|
|
BOOL BStatus = FALSE;
|
|
|
|
|
|
UserWorkItem = (PUMRX_USERMODE_WORKITEM_HEADER)DavWorkItem;
|
|
|
|
//
|
|
// Get the request buffer from the DavWorkItem.
|
|
//
|
|
QueryVolumeInformationRequest = &(DavWorkItem->QueryVolumeInformationRequest);
|
|
|
|
//
|
|
// The first character is a '\' which has to be stripped.
|
|
//
|
|
ServerName = &(QueryVolumeInformationRequest->ServerName[1]);
|
|
if (!ServerName) {
|
|
DavPrint((DEBUG_ERRORS, "DavFsQueryVolumeInformation: ServerName is NULL.\n"));
|
|
WStatus = ERROR_INVALID_PARAMETER; // STATUS_INVALID_PARAMETER;
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
DavPrint((DEBUG_MISC, "DavFsQueryVolumeInformation: ServerName = %ws.\n", ServerName));
|
|
|
|
ServerID = QueryVolumeInformationRequest->ServerID;
|
|
DavPrint((DEBUG_MISC, "DavFsQueryVolumeInformation: ServerID = %d.\n", ServerID));
|
|
|
|
//
|
|
// The first character is a '\' which has to be stripped.
|
|
//
|
|
ShareName = &(QueryVolumeInformationRequest->ShareName[1]);
|
|
if (!ServerName) {
|
|
DavPrint((DEBUG_ERRORS, "DavFsQueryVolumeInformation: ShareName is NULL.\n"));
|
|
WStatus = ERROR_INVALID_PARAMETER; // STATUS_INVALID_PARAMETER;
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
DavPrint((DEBUG_MISC, "DavFsQueryVolumeInformation: ShareName = %ws.\n", ShareName));
|
|
|
|
//
|
|
// If ShareName is a dummy share, we need to remove it right now before we
|
|
// contact the server.
|
|
//
|
|
DavRemoveDummyShareFromFileName(ShareName);
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavFsQueryVolumeInformation: LogonId.LowPart = %d, LogonId.HighPart = %d\n",
|
|
QueryVolumeInformationRequest->LogonID.LowPart,
|
|
QueryVolumeInformationRequest->LogonID.HighPart));
|
|
|
|
WStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle);
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsCreateVNetRoot/UMReflectorImpersonate. Error Val = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
didImpersonate = TRUE;
|
|
//
|
|
// Allocate memory for the INTERNET_ASYNC_RESULT structure.
|
|
//
|
|
DavWorkItem->AsyncResult = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
|
|
sizeof(INTERNET_ASYNC_RESULT));
|
|
if (DavWorkItem->AsyncResult == NULL) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsQueryVolumeInformation/LocalAlloc. Error Val = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
//
|
|
// A User Entry for this user must have been created during the create call
|
|
// earlier. The user entry contains the handle used to send an HttpOpen
|
|
// request.
|
|
//
|
|
|
|
EnterCriticalSection( &(HashServerEntryTableLock) );
|
|
EnCriSec = TRUE;
|
|
|
|
ReturnVal = DavDoesUserEntryExist(ServerName,
|
|
ServerID,
|
|
&(QueryVolumeInformationRequest->LogonID),
|
|
&PerUserEntry,
|
|
&ServerHashEntry);
|
|
|
|
//
|
|
// If the following request in the kernel get cancelled even before the
|
|
// corresponding usermode thread gets a chance to execute this code, then
|
|
// it possible that the VNetRoot (hence the PerUserEntry) and SrvCall get
|
|
// finalized before the thread that is handling the create comes here. This
|
|
// could happen if this request was the only one for this share and the
|
|
// server as well. This is why we need to check if the ServerHashEntry and
|
|
// the PerUserEntry are valid before proceeding.
|
|
//
|
|
if (ReturnVal == FALSE || ServerHashEntry == NULL || PerUserEntry == NULL) {
|
|
WStatus = ERROR_CANCELLED;
|
|
DavPrint((DEBUG_ERRORS, "DavFsQueryVolumeInformation: (ServerHashEntry == NULL || PerUserEntry == NULL)\n"));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
DavWorkItem->AsyncQueryVolumeInformation.ServerHashEntry = ServerHashEntry;
|
|
DavWorkItem->AsyncQueryVolumeInformation.PerUserEntry = PerUserEntry;
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavFsQueryVolumeInformation: PerUserEntry = %08lx.\n",
|
|
PerUserEntry));
|
|
|
|
//
|
|
// Add a reference to the user entry.
|
|
//
|
|
PerUserEntry->UserEntryRefCount++;
|
|
|
|
//
|
|
// Since a create had succeeded earlier, the entry must be good.
|
|
//
|
|
ASSERT(PerUserEntry->UserEntryState == UserEntryInitialized);
|
|
ASSERT(PerUserEntry->DavConnHandle != NULL);
|
|
|
|
//
|
|
// And yes, we obviously have to leave the critical section
|
|
// before returning.
|
|
//
|
|
LeaveCriticalSection( &(HashServerEntryTableLock) );
|
|
EnCriSec = FALSE;
|
|
|
|
//
|
|
// We now call the HttpOpenRequest function and return.
|
|
//
|
|
DavWorkItem->DavOperation = DAV_CALLBACK_INTERNET_CONNECT;
|
|
DavWorkItem->DavMinorOperation = DavMinorReadData;
|
|
DavWorkItem->AsyncQueryVolumeInformation.PerUserEntry = PerUserEntry;
|
|
|
|
WStatus = DavAsyncCommonStates(DavWorkItem, FALSE);
|
|
|
|
EXIT_THE_FUNCTION:
|
|
|
|
if (EnCriSec) {
|
|
LeaveCriticalSection( &(HashServerEntryTableLock) );
|
|
EnCriSec = FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// If we are using WinInet synchronously, then we should never get back
|
|
// ERROR_IO_PENDING from WinInet.
|
|
//
|
|
ASSERT(WStatus != ERROR_IO_PENDING);
|
|
|
|
//
|
|
// If this thread impersonated a user, we need to revert back.
|
|
//
|
|
if (didImpersonate) {
|
|
RevertToSelf();
|
|
}
|
|
|
|
//
|
|
// Set the return status of the operation. This is used by the kernel
|
|
// mode routines to figure out the completion status of the user mode
|
|
// request. This is done here because the async completion routine that is
|
|
// called immediately afterwards needs the status set.
|
|
//
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
DavWorkItem->Status = DavMapErrorToNtStatus(WStatus);
|
|
} else {
|
|
DavWorkItem->Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
DavAsyncQueryVolumeInformationCompletion(DavWorkItem);
|
|
|
|
return WStatus;
|
|
}
|
|
|
|
|
|
DWORD
|
|
DavAsyncQueryVolumeInformation(
|
|
PDAV_USERMODE_WORKITEM DavWorkItem,
|
|
BOOLEAN CalledByCallBackThread
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the callback routine for the query directory operation.
|
|
|
|
Arguments:
|
|
|
|
DavWorkItem - The DAV_USERMODE_WORKITEM value.
|
|
|
|
CalledByCallbackThread - TRUE, if this function was called by the thread
|
|
which picks of the DavWorkItem from the Callback
|
|
function. This happens when an Async WinInet call
|
|
returns ERROR_IO_PENDING and completes later.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS or the appropriate error value.
|
|
|
|
--*/
|
|
{
|
|
ULONG WStatus = ERROR_SUCCESS;
|
|
DAV_FILE_ATTRIBUTES DavFileAttributes;
|
|
|
|
WStatus = DavParseXmlResponse(DavWorkItem->AsyncQueryVolumeInformation.DavOpenHandle, &DavFileAttributes, NULL);
|
|
if (WStatus == ERROR_SUCCESS)
|
|
{
|
|
DavWorkItem->QueryVolumeInformationResponse.TotalSpace =
|
|
DavFileAttributes.TotalSpace;
|
|
|
|
DavWorkItem->QueryVolumeInformationResponse.AvailableSpace =
|
|
DavFileAttributes.AvailableSpace;
|
|
|
|
if (!*(LONGLONG *)&(DavWorkItem->QueryVolumeInformationResponse.TotalSpace))
|
|
{
|
|
*(LONGLONG *)&(DavWorkItem->QueryVolumeInformationResponse.TotalSpace) =
|
|
*(LONGLONG *)&(DavWorkItem->QueryVolumeInformationResponse.AvailableSpace)+MSN_SPACE_FAKE_DELTA;
|
|
}
|
|
|
|
DavFinalizeFileAttributesList(&DavFileAttributes, FALSE);
|
|
}
|
|
|
|
return WStatus;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
DavAsyncQueryVolumeInformationCompletion(
|
|
PDAV_USERMODE_WORKITEM DavWorkItem
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles the QueryVolumeInformation completion. It basically frees up
|
|
the resources allocated during the QueryVolumeInformation operation.
|
|
|
|
Arguments:
|
|
|
|
DavWorkItem - The DAV_USERMODE_WORKITEM value.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
{
|
|
if (DavWorkItem->AsyncQueryVolumeInformation.DavOpenHandle != NULL) {
|
|
BOOL ReturnVal;
|
|
ULONG FreeStatus;
|
|
ReturnVal = InternetCloseHandle(DavWorkItem->AsyncQueryVolumeInformation.DavOpenHandle);
|
|
if (!ReturnVal) {
|
|
FreeStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncQueryVolumeInformationCompletion/InternetCloseHandle. "
|
|
"Error Val = %d\n", FreeStatus));
|
|
}
|
|
}
|
|
|
|
|
|
if (DavWorkItem->AsyncResult != NULL) {
|
|
HLOCAL FreeHandle;
|
|
ULONG FreeStatus;
|
|
FreeHandle = LocalFree((HLOCAL)DavWorkItem->AsyncResult);
|
|
if (FreeHandle != NULL) {
|
|
FreeStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncQueryVolumeInformationCompletion/LocalFree. Error Val ="
|
|
" %d\n", FreeStatus));
|
|
}
|
|
}
|
|
|
|
//
|
|
// The callback context should not be finalized if we are returning
|
|
// ERROR_IO_PENDING.
|
|
//
|
|
DavFsFinalizeTheDavCallBackContext(DavWorkItem);
|
|
|
|
//
|
|
// We are done with the per user entry, so finalize it.
|
|
//
|
|
if (DavWorkItem->AsyncQueryVolumeInformation.PerUserEntry) {
|
|
DavFinalizePerUserEntry( &(DavWorkItem->AsyncQueryVolumeInformation.PerUserEntry) );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|