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