/*++ Copyright (c) 1999 Microsoft Corporation Module Name : urlinfo.cxx Abstract: Gets metadata for URL Author: Bilal Alam (balam) 8-Jan-2000 Environment: Win32 - User Mode Project: ULW3.DLL --*/ #include "precomp.hxx" #include // // Utility to guard against ~ inconsistency // DWORD CheckIfShortFileName( IN WCHAR * pszPath, IN HANDLE hImpersonation, OUT BOOL * pfShort ); W3_STATE_URLINFO::W3_STATE_URLINFO() { _hr = URL_CONTEXT::Initialize(); } W3_STATE_URLINFO::~W3_STATE_URLINFO() { URL_CONTEXT::Terminate(); } CONTEXT_STATUS W3_STATE_URLINFO::OnCompletion( W3_MAIN_CONTEXT * pMainContext, DWORD cbCompletion, DWORD dwCompletionStatus ) /*++ Routine Description: Handle URLINFO completions. CheckAccess() is called in DoWork() and this call is asynchronous. Arguments: pMainContext - W3_MAIN_CONTEXT representing execution of state machine cbCompletion - Number of bytes in an async completion dwCompletionStatus - Error status of a completion Return Value: CONTEXT_STATUS_CONTINUE - if we should continue in state machine else stop executing the machine and free up the current thread --*/ { CONTEXT_STATUS contextStatus; BOOL fAccessAllowed; contextStatus = pMainContext->CheckAccess( TRUE, // this is a completion cbCompletion, dwCompletionStatus, &fAccessAllowed ); if ( contextStatus == CONTEXT_STATUS_PENDING ) { return CONTEXT_STATUS_PENDING; } // // If access is not allowed, then just finish state machine ( // response has already been sent) // if ( !fAccessAllowed ) { pMainContext->SetFinishedResponse(); } return CONTEXT_STATUS_CONTINUE; } CONTEXT_STATUS W3_STATE_URLINFO::DoWork( W3_MAIN_CONTEXT * pMainContext, DWORD cbCompletion, DWORD dwCompletionStatus ) /*++ Routine Description: Handle retrieving the metadata for this request Arguments: pMainContext - W3_MAIN_CONTEXT representing execution of state machine cbCompletion - Number of bytes in an async completion dwCompletionStatus - Error status of a completion Return Value: CONTEXT_STATUS_CONTINUE - if we should continue in state machine else stop executing the machine and free up the current thread --*/ { URL_CONTEXT * pUrlContext = NULL; BOOL fFinished = FALSE; HRESULT hr = NO_ERROR; W3_METADATA * pMetaData = NULL; CONTEXT_STATUS contextStatus = CONTEXT_STATUS_CONTINUE; W3_REQUEST * pHttpRequest = pMainContext->QueryRequest(); W3_RESPONSE * pResponse = pMainContext->QueryResponse(); BOOL fAccessAllowed = FALSE; DBG_ASSERT( pHttpRequest != NULL ); DBG_ASSERT( pResponse != NULL ); // // Set the context state. Note that passing TRUE as the final // argument pushes the URL_CONTEXT into the main context. As // a result, errors in this function should not attempt to // clean it up. // hr = URL_CONTEXT::RetrieveUrlContext( pMainContext, pMainContext->QueryRequest(), &pUrlContext, &fFinished, TRUE ); if ( FAILED( hr ) ) { goto Failure; } DBG_ASSERT( fFinished || ( pUrlContext != NULL ) ); // // From now on, errors in this function should not cleanup the URL // context since it is owned by the main context // pUrlContext = NULL; // // If filter wants out, leave // if ( fFinished ) { pMainContext->SetDone(); return CONTEXT_STATUS_CONTINUE; } // // Check access now. That means checking for IP/SSL/Certs. We will // avoid the authentication type check since the others (IP/SSL/Certs) // take priority. // contextStatus = pMainContext->CheckAccess( FALSE, // not a completion 0, // cbCompletion, NO_ERROR, &fAccessAllowed ); if ( contextStatus == CONTEXT_STATUS_PENDING ) { return CONTEXT_STATUS_PENDING; } // // If we don't have access, then the appropriate error response was // already sent. Just finish the state machine // if ( !fAccessAllowed ) { pMainContext->SetFinishedResponse(); } return CONTEXT_STATUS_CONTINUE; Failure: if ( pUrlContext != NULL ) { delete pUrlContext; } if ( !pMainContext->QueryResponseSent() ) { if ( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) ) { // For the non-8dot3 case pMainContext->QueryResponse()->SetStatus( HttpStatusNotFound ); } else if ( hr == HRESULT_FROM_WIN32( ERROR_INVALID_DATA ) ) { pMainContext->QueryResponse()->SetStatus( HttpStatusServerError, Http500BadMetadata ); } else if ( hr == HRESULT_FROM_WIN32( ERROR_LOGON_FAILURE ) ) { pMainContext->QueryResponse()->SetStatus( HttpStatusServerError, Http500UNCAccess ); } else { pMainContext->QueryResponse()->SetStatus( HttpStatusServerError ); } } pMainContext->SetFinishedResponse(); pMainContext->SetErrorStatus( hr ); return CONTEXT_STATUS_CONTINUE; } //static HRESULT URL_CONTEXT::RetrieveUrlContext( W3_CONTEXT * pW3Context, W3_REQUEST * pRequest, OUT URL_CONTEXT ** ppUrlContext, BOOL * pfFinished, BOOL fSetInW3Context ) /*++ Routine Description: For a given request, get a URL_CONTEXT which represents the metadata and URI-specific info for that request Arguments: pW3Context - W3_CONTEXT for the request pRequest - New request to lookup ppUrlContext - Set to point to new URL_CONTEXT pfFinished - Set to true if isapi filter said we're finished fSetInW3Context - OPTIONAL default FALSE Return Value: HRESULT --*/ { STACK_STRU( strUrl, MAX_PATH ); W3_URL_INFO * pUrlInfo = NULL; W3_METADATA * pMetaData = NULL; TOKEN_CACHE_ENTRY * pTokenEntry = NULL; URL_CONTEXT * pUrlContext = NULL; HRESULT hr = NO_ERROR; HANDLE hToken = NULL; W3_TRACE_LOG * pTraceLog; if ( pW3Context == NULL || pRequest == NULL || ppUrlContext == NULL || pfFinished == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); } *ppUrlContext = NULL; hr = pRequest->GetUrl( &strUrl ); if ( FAILED( hr ) ) { goto Failure; } // // Lookup the URI info for this request // DBG_ASSERT( g_pW3Server->QueryUrlInfoCache() != NULL ); hr = g_pW3Server->QueryUrlInfoCache()->GetUrlInfo( pW3Context, strUrl, &pUrlInfo ); if ( FAILED( hr ) ) { goto Failure; } // // Now, create a URL_CONTEXT object which contains the W3_URL_INFO and // W3_METADATA pointers as well as state information for use on cleanup // DBG_ASSERT( pUrlInfo != NULL ); pMetaData = (W3_METADATA*) pUrlInfo->QueryMetaData(); DBG_ASSERT( pMetaData != NULL ); pUrlContext = new (pW3Context) URL_CONTEXT( pMetaData, pUrlInfo ); if ( pUrlContext == NULL ) { hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY ); goto Failure; } else if( fSetInW3Context ) { // // If specified, set the UrlContext in pW3Context. This is necessary // since the below filter notification depends on having this context // for APPL_MD_PATH and APPL_PHYSICAL_PATH. Once set the context owns // cleaning it up. // ((W3_MAIN_CONTEXT*)pW3Context)->SetUrlContext( pUrlContext ); } // // Now notify URL_MAP filters // if ( pW3Context->IsNotificationNeeded( SF_NOTIFY_URL_MAP ) ) { STACK_STRA( straPhys, MAX_PATH + 1 ); STACK_STRA( straSavePhys, MAX_PATH + 1 ); STACK_STRA( straUrl, MAX_PATH + 1 ); STACK_STRA( straScriptMap, MAX_PATH + 1 ); BOOL fRet; HTTP_FILTER_URL_MAP_EX filterMap; STACK_STRU( strPhysicalPath, MAX_PATH ); hr = straPhys.CopyW( pUrlInfo->QueryPhysicalPath()->QueryStr() ); if ( FAILED( hr ) ) { goto Failure; } // // Save a copy for comparison after filter notification // hr = straSavePhys.Copy( straPhys ); if ( FAILED( hr ) ) { goto Failure; } hr = straUrl.CopyW( strUrl.QueryStr() ); if ( FAILED( hr ) ) { goto Failure; } filterMap.pszURL = straUrl.QueryStr(); filterMap.pszPhysicalPath = straPhys.QueryStr(); filterMap.cbPathBuff = straPhys.QuerySize(); filterMap.dwFlags = pMetaData->QueryAccessPerms(); filterMap.cchMatchingPath = pMetaData->QueryCBMatchingPathA(); filterMap.cchMatchingURL = pMetaData->QueryCBMatchingUrlA(); filterMap.pszScriptMapEntry = NULL; if ( pUrlInfo->QueryScriptMapEntry() ) { hr = straScriptMap.CopyW( pUrlInfo->QueryScriptMapEntry()->QueryExecutable()->QueryStr() ); if ( FAILED( hr ) ) { goto Failure; } filterMap.pszScriptMapEntry = straScriptMap.QueryStr(); } fRet = pW3Context->NotifyFilters( SF_NOTIFY_URL_MAP, &filterMap, pfFinished ); if ( !fRet ) { W3_MAIN_CONTEXT * pMainContext = pW3Context->QueryMainContext(); DWORD dwError = GetLastError(); hr = HRESULT_FROM_WIN32( dwError ); pMainContext->SetErrorStatus( hr ); if ( dwError == ERROR_ACCESS_DENIED ) { pMainContext->QueryResponse()->SetStatus( HttpStatusUnauthorized, Http401Filter ); } else if ( dwError == ERROR_FILE_NOT_FOUND || dwError == ERROR_PATH_NOT_FOUND ) { pMainContext->QueryResponse()->SetStatus( HttpStatusNotFound ); } else { pMainContext->QueryResponse()->SetStatus( HttpStatusServerError ); } goto Failure; } // // If the filter is done, then we're done // if ( *pfFinished ) { hr = NO_ERROR; goto Failure; } // // If the physical path was changed, remember it here // if ( strcmp( straSavePhys.QueryStr(), filterMap.pszPhysicalPath ) != 0 ) { hr = strPhysicalPath.CopyA( (CHAR*) filterMap.pszPhysicalPath ); if ( FAILED( hr ) ) { goto Failure; } hr = pUrlContext->SetPhysicalPath( strPhysicalPath ); if ( FAILED( hr ) ) { goto Failure; } } } // // We don't accept short filename since they can break metabase // equivalency // if ( wcschr( pUrlContext->QueryPhysicalPath()->QueryStr(), L'~' ) ) { BOOL fShort = FALSE; hr = pMetaData->GetAndRefVrAccessToken( &pTokenEntry ); if( FAILED( hr ) ) { goto Failure; } if ( pTokenEntry != NULL ) { hToken = pTokenEntry->QueryImpersonationToken(); } else { hToken = NULL; } DWORD dwError = CheckIfShortFileName( pUrlContext->QueryPhysicalPath()->QueryStr(), hToken, &fShort ); if ( dwError != ERROR_SUCCESS ) { hr = HRESULT_FROM_WIN32( dwError ); goto Failure; } if ( fShort ) { hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ); goto Failure; } } // // Check whether the UNC user was valid. If not, fail // if ( pMetaData->QueryUNCUserInvalid() ) { hr = HRESULT_FROM_WIN32( ERROR_LOGON_FAILURE ); goto Failure; } pTraceLog = pW3Context->QueryMainContext()->QueryTraceLog(); if ( pTraceLog != NULL ) { pTraceLog->Trace( L"%I64x: Successfully metadata for URL '%ws'\n", pRequest->QueryRequestId(), strUrl.QueryStr() ); } *ppUrlContext = pUrlContext; if( pTokenEntry != NULL ) { pTokenEntry->DereferenceCacheEntry(); pTokenEntry = NULL; } return S_OK; Failure: pTraceLog = pW3Context->QueryMainContext()->QueryTraceLog(); if ( pTraceLog != NULL ) { pTraceLog->Trace( L"%I64x: Failed to retrieve metadata for URL '%ws'. hr = %08X\n", pRequest->QueryRequestId(), strUrl.QueryStr(), hr ); } if ( pUrlContext != NULL ) { if( !fSetInW3Context ) { delete pUrlContext; } } else { if ( pUrlInfo != NULL ) { pUrlInfo->DereferenceCacheEntry(); } } if( pTokenEntry != NULL ) { pTokenEntry->DereferenceCacheEntry(); pTokenEntry = NULL; } return hr; } //static HRESULT W3_STATE_URLINFO::MapPath( W3_CONTEXT * pW3Context, STRU & strUrl, STRU * pstrPhysicalPath, BOOL fDoFiltering, DWORD * pcchDirRoot, DWORD * pcchVRoot, DWORD * pcbAnsiDirRoot, DWORD * pcbAnsiVRoot, DWORD * pdwMask ) /*++ Routine Description: Send a URL/Physical-Path pair to a filter for processing Arguments: pW3Context - W3_CONTEXT for the request strUrl - The URL to be mapped pstrPhysicalPath - Filled with the mapped path upon return. Set with metadata physical path on entry fDoFiltering - Enable filters pcchDirRoot - Set to point to number of characters in found physical path pcchVRoot - Set to point to number of characters in found virtual path pcbAnsiDirRoot - Set to point to number of bytes in found ANSI physical path pcbAnsiVRoot - Set to point to number of bytes in found ANSI virtual path pdwMask - Set to point to the access perms mask of virtual path Return Value: SUCCEEDED()/FAILED() --*/ { HRESULT hr = S_OK; W3_URL_INFO * pUrlInfo = NULL; W3_METADATA * pMetaData = NULL; DBG_ASSERT( pstrPhysicalPath ); // // Get and keep the metadata and urlinfo for this path // DBG_ASSERT( g_pW3Server->QueryUrlInfoCache() != NULL ); hr = g_pW3Server->QueryUrlInfoCache()->GetUrlInfo( pW3Context, strUrl, &pUrlInfo ); if ( FAILED( hr ) ) { goto Exit; } DBG_ASSERT( pUrlInfo != NULL ); // // Call the filters if we should do so // if ( fDoFiltering ) { hr = FilterMapPath( pW3Context, pUrlInfo, pstrPhysicalPath ); } else { hr = pstrPhysicalPath->Copy( *( pUrlInfo->QueryUrlTranslated() ) ); } if ( FAILED( hr ) ) { goto Exit; } pMetaData = pUrlInfo->QueryMetaData(); DBG_ASSERT( pMetaData != NULL ); // // Return the other goodies // if ( pcchDirRoot != NULL ) { *pcchDirRoot = pMetaData->QueryVrPath()->QueryCCH(); } if ( pcchVRoot != NULL ) { if (strUrl.QueryCCH()) { *pcchVRoot = pMetaData->QueryVrLen(); } else { *pcchVRoot = 0; } } if ( pcbAnsiDirRoot != NULL ) { *pcbAnsiDirRoot = pMetaData->QueryCBMatchingPathA(); } if ( pcbAnsiVRoot != NULL ) { if ( strUrl.QueryCCH() ) { *pcbAnsiVRoot = pMetaData->QueryCBMatchingUrlA(); } else { *pcbAnsiVRoot = 0; } } if ( pdwMask != NULL ) { *pdwMask = pMetaData->QueryAccessPerms(); } Exit: if ( pUrlInfo != NULL ) { pUrlInfo->DereferenceCacheEntry(); pUrlInfo = NULL; } return hr; } // static HRESULT W3_STATE_URLINFO::FilterMapPath( W3_CONTEXT * pW3Context, W3_URL_INFO * pUrlInfo, STRU * pstrPhysicalPath ) /*++ Routine Description: Have URL_MAP filters do their thing Arguments: pW3Context - Context pUrlInfo - Contains virtual/physical path pstrPhysicalPath - Filled with physical path Return Value: SUCCEEDED()/FAILED() --*/ { HRESULT hr = S_OK; BOOL fFinished = FALSE; W3_METADATA * pMetaData = NULL; STACK_STRU( strFilterPath, MAX_PATH ); STRU * pstrFinalPhysical = NULL; if ( pW3Context == NULL || pUrlInfo == NULL || pstrPhysicalPath == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); } pMetaData = pUrlInfo->QueryMetaData(); DBG_ASSERT( pMetaData != NULL ); // // We now have the metadata physical path. Let filters change it here // if ( pW3Context->IsNotificationNeeded( SF_NOTIFY_URL_MAP ) ) { STACK_STRA( straPhys, MAX_PATH + 1 ); STACK_STRA( straSavePhys, MAX_PATH + 1 ); STACK_STRA( straUrl, MAX_PATH + 1 ); STACK_STRA( straScriptMap, MAX_PATH + 1 ); BOOL fRet; HTTP_FILTER_URL_MAP_EX filterMap; hr = straPhys.CopyW( pUrlInfo->QueryUrlTranslated()->QueryStr() ); if ( FAILED( hr ) ) { goto Exit; } // // Save a copy for comparison after filter notification // hr = straSavePhys.Copy( straPhys ); if ( FAILED( hr ) ) { goto Exit; } hr = straUrl.CopyW( pUrlInfo->QueryUrl() ); if ( FAILED( hr ) ) { goto Exit; } filterMap.pszURL = straUrl.QueryStr(); filterMap.pszPhysicalPath = straPhys.QueryStr(); filterMap.cbPathBuff = straPhys.QuerySize(); filterMap.dwFlags = pMetaData->QueryAccessPerms(); filterMap.cchMatchingPath = pMetaData->QueryCBMatchingPathA(); filterMap.cchMatchingURL = pMetaData->QueryCBMatchingUrlA(); filterMap.pszScriptMapEntry = NULL; if ( pUrlInfo->QueryScriptMapEntry() ) { hr = straScriptMap.CopyW( pUrlInfo->QueryScriptMapEntry()->QueryExecutable()->QueryStr() ); if ( FAILED( hr ) ) { goto Exit; } filterMap.pszScriptMapEntry = straScriptMap.QueryStr(); } fRet = pW3Context->NotifyFilters( SF_NOTIFY_URL_MAP, &filterMap, &fFinished ); // // Ignore finished flag in this case since we really can't do much // to advance to finish (since an ISAPI is calling this) // if ( !fRet ) { hr = HRESULT_FROM_WIN32( GetLastError() ); goto Exit; } // // Remember the mapped path // if ( strcmp( straSavePhys.QueryStr(), filterMap.pszPhysicalPath ) != 0 ) { hr = strFilterPath.CopyA( (CHAR*) filterMap.pszPhysicalPath ); if ( FAILED( hr ) ) { goto Exit; } pstrFinalPhysical = &strFilterPath; } else { pstrFinalPhysical = pUrlInfo->QueryUrlTranslated(); } } else { // // No filter is mapping, therefore just take the URL_INFO's physical // path // pstrFinalPhysical = pUrlInfo->QueryUrlTranslated(); DBG_ASSERT( pstrFinalPhysical != NULL ); } // // We don't accept short filename since they can break metabase // equivalency // if ( wcschr( pstrFinalPhysical->QueryStr(), L'~' ) ) { BOOL fShort = FALSE; DWORD dwError = CheckIfShortFileName( pstrFinalPhysical->QueryStr(), pW3Context->QueryImpersonationToken(), &fShort ); if ( dwError != ERROR_SUCCESS ) { hr = HRESULT_FROM_WIN32( dwError ); goto Exit; } if ( fShort ) { hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ); goto Exit; } } // // Copy the physical path is requested // hr = pstrPhysicalPath->Copy( *pstrFinalPhysical ); if ( FAILED( hr ) ) { goto Exit; } Exit: return hr; } DWORD CheckIfShortFileName( IN WCHAR * pszPath, IN HANDLE hImpersonation, OUT BOOL * pfShort ) /*++ Description: This function takes a suspected NT/Win95 short filename and checks if there's an equivalent long filename. For example, c:\foobar\ABCDEF~1.ABC is the same as c:\foobar\abcdefghijklmnop.abc. NOTE: This function should be called unimpersonated - the FindFirstFile() must be called in the system context since most systems have traverse checking turned off - except for the UNC case where we must be impersonated to get network access. Arguments: pszPath - Path to check hImpersonation - Impersonation handle if this is a UNC path - can be NULL if not UNC pfShort - Set to TRUE if an equivalent long filename is found Returns: Win32 error on failure --*/ { DWORD err = NO_ERROR; WIN32_FIND_DATA FindData; WCHAR * psz; BOOL fUNC; psz = wcschr( pszPath, L'~' ); *pfShort = FALSE; fUNC = (*pszPath == L'\\'); // // Loop for multiple tildas - watch for a # after the tilda // while ( psz++ ) { if ( *psz >= L'0' && *psz <= L'9' ) { WCHAR achTmp[MAX_PATH]; WCHAR * pchEndSeg; WCHAR * pchBeginSeg; HANDLE hFind; // // Isolate the path up to the segment with the // '~' and do the FindFirst with that path // pchEndSeg = wcschr( psz, L'\\' ); if ( !pchEndSeg ) { pchEndSeg = psz + wcslen( psz ); } // // If the string is beyond MAX_PATH then we allow it through // if ( ((INT) (pchEndSeg - pszPath)) >= MAX_PATH ) { return NO_ERROR; } memcpy( achTmp, pszPath, (INT) (pchEndSeg - pszPath) * sizeof( WCHAR ) ); achTmp[pchEndSeg - pszPath] = L'\0'; if ( fUNC && hImpersonation ) { if ( !SetThreadToken( NULL, hImpersonation )) { return GetLastError(); } } // // IVANPASH: Although it looks simpler to use GetLongPathName // instead of manually traversing the folders, we must use // FindFirstFileW, because GetLongPathName requires ACLs for // IIS_WPG from the root of the drive to work. // hFind = FindFirstFileW( achTmp, &FindData ); if ( fUNC && hImpersonation ) { RevertToSelf(); } if ( hFind == INVALID_HANDLE_VALUE ) { err = GetLastError(); DBGPRINTF(( DBG_CONTEXT, "FindFirst failed!! - \"%s\", error %d\n", achTmp, GetLastError() )); // // If the FindFirstFile() fails to find the file then return // success - the path doesn't appear to be a valid path which // is ok. // if ( err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND || err == ERROR_INVALID_NAME ) { return NO_ERROR; } return err; } DBG_REQUIRE( FindClose( hFind )); // // Isolate the last segment of the string which should be // the potential short name equivalency // pchBeginSeg = wcsrchr( achTmp, L'\\' ); DBG_ASSERT( pchBeginSeg ); pchBeginSeg++; // // If the last segment doesn't match the long name then this is // the short name version of the path // if ( _wcsicmp( FindData.cFileName, pchBeginSeg )) { *pfShort = TRUE; return NO_ERROR; } } psz = wcschr( psz, L'~' ); } return err; } HRESULT URL_CONTEXT::OpenFile( CACHE_USER * pFileUser, W3_FILE_INFO ** ppOpenFile, FILE_CACHE_ASYNC_CONTEXT * pAsyncContext, BOOL * pfHandledSync, BOOL fAllowNoBuffering, BOOL fCheckForExistenceOnly ) /*++ Routine Description: Open the physical path for this request. If a map path filter did some redirecting, we will use that path. Otherwise we will just use the path determined by metadata and cached in the W3_URL_INFO Arguments: pFileUser - User to open file as ppOpenFile - Set to file cache entry on success pAsyncContext - In case an async read is desired, context with callback information pfHandledSync - Did the open complete synchronously fAllowNoBuffering - Allow the file to be opened with FILE_FLAG_NO_BUFFERING fCheckForExistenceOnly - Only interested in existence of the file Return Value: HRESULT --*/ { HRESULT hr; BOOL fDoCache; DBG_ASSERT( QueryMetaData() != NULL ); fDoCache = !QueryMetaData()->QueryNoCache(); // // If an ISAPI filter changed the physical path, then we need to go // directly to the file cache. Otherwise, we can go thru the // W3_URL_INFO which may already have the cached file associated // if ( _strPhysicalPath.IsEmpty() ) { // // No filter. Fast path :-) // DBG_ASSERT( _pUrlInfo != NULL ); hr = _pUrlInfo->GetFileInfo( pFileUser, fDoCache, ppOpenFile, pAsyncContext, pfHandledSync, fAllowNoBuffering, fCheckForExistenceOnly ); } else { // // Filter case. Must lookup in file cache :-( // DBG_ASSERT( g_pW3Server->QueryFileCache() != NULL ); hr = g_pW3Server->QueryFileCache()->GetFileInfo( _strPhysicalPath, QueryMetaData()->QueryDirmonConfig(), pFileUser, fDoCache, ppOpenFile, pAsyncContext, pfHandledSync, fAllowNoBuffering, fCheckForExistenceOnly ); } return hr; } //static HRESULT URL_CONTEXT::Initialize( VOID ) /*++ Routine Description: Initialize URL_CONTEXT lookaside Arguments: None Return Value: HRESULT --*/ { return NO_ERROR; } //static VOID URL_CONTEXT::Terminate( VOID ) /*++ Routine Description: Clean up URL_CONTEXT lookaside Arguments: None Return Value: HRESULT --*/ { }