// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // // INSTDATA.CPP // // HTTP instance data cache implementation. // // // Copyright 1997 Microsoft Corporation, All Rights Reserved // #include "_davprs.h" #include #include "instdata.h" // ======================================================================== // // class CInstData // // ------------------------------------------------------------------------ // // CInstData::CInstData() // // Constructor. Init all variables. Make a copy of the name string // for us to keep. // // NOTE: This object must be constructed while we are REVERTED. // CInstData::CInstData( LPCWSTR pwszName ) { // Make copy of wide instance name // m_wszVRoot = WszDupWsz(pwszName); // Parse out and store service instance, otherwise // sometimes referred as the server ID. // m_lServerID = LInstFromVroot( m_wszVRoot ); // Create our objects. Please read the notes about the // relative costs and reasons behind creating these objects // now rather than on demand. Don't create anything here that // can be created on demand unless at least one of the // following is true: // // 1. The object is lightweight and cheap to create. // Example: an empty cache. // // 2. The object is used in the processing of a vast // majority of HTTP requests. // Example: any object used on every GET request. // } // ======================================================================== // // class CInstDataCache // // ------------------------------------------------------------------------ // // CInstDataCache::GetInstData() // // Fetch a row from the cache. // CInstData& CInstDataCache::GetInstData( const IEcb& ecb ) { auto_ref_ptr pinst; CStackBuffer pwszMetaPath; UINT cchMetaPath; CStackBuffer pwszVRoot; UINT cchVRoot; LPCWSTR pwszRootName; UINT cchRootName; // Build up a unique string from the vroot and instance: // lm/w3svc//root/ // // Get the virtual root from the ecb (/). // cchRootName = ecb.CchGetVirtualRootW( &pwszRootName ); // pwszRootName should still be NULL-terminated. Check it, 'cause // we're going to put the next string after there, and we don't want // to get them mixed... // Assert( pwszRootName ); Assert( L'\0' == pwszRootName[cchRootName] ); // Ask IIS for the metabase prefix (lm/w3svc/) for the // virtual server (site) we're on... // cchMetaPath = pwszMetaPath.celems(); if (!ecb.FGetServerVariable( "INSTANCE_META_PATH", pwszMetaPath.get(), reinterpret_cast(&cchMetaPath) )) { if (ERROR_INSUFFICIENT_BUFFER != GetLastError()) { DebugTrace( "CInstDataCache::GetInstData() - FGetServerVariable() failed" " to get INSTANCE_META_PATH\n" ); throw CLastErrorException(); } if (NULL == pwszMetaPath.resize(cchMetaPath * sizeof(WCHAR))) { SetLastError(E_OUTOFMEMORY); DebugTrace( "CInstDataCache::GetInstData() - failed to allocate memory\n" ); throw CLastErrorException(); } if (!ecb.FGetServerVariable( "INSTANCE_META_PATH", pwszMetaPath.get(), reinterpret_cast(&cchMetaPath) )) { DebugTrace( "CInstDataCache::GetInstData() - FGetServerVariable() failed" " to get INSTANCE_META_PATH\n" ); throw CLastErrorException(); } } // The function returning server variable returns total number of characters // written to the output buffer, so it will include '\0' termination. Let us make // sure that the return is what is expected. // Assert(0 == cchMetaPath || L'\0' == pwszMetaPath[cchMetaPath - 1]); // Adjust the cchMetaPath to reflect the actual character count // if (0 != cchMetaPath) { cchMetaPath--; } // Check that the root name is either NULL (zero-length string) // or has its own separator. // // NOTE: This is conditional because IF we are installed at the ROOT // (on w3svc/1 or w3svc/1/root) and you throw a method against // a file that doesn't live under a registered K2 vroot // (like /default.asp) -- we DO get called, but the mi.cchMatchingURL // comes back as 0, so pwszRootName is a zero-len string. // (In IIS terms, you are not really hitting a virtual root, // so your vroot is "".) // I'm making this assert CONDITIONAL until we figure out more about // how we'll handle this particular install case. //$REVIEW: The installed-at-the-root case needs to be examined more //$REVIEW: becuase we DON'T always build the same instance string //$REVIEW: in that case -- we'll still get vroot strings when the URI //$REVIEW: hits a resource under a REGISTERED vroot, and so we'll //$REVIEW: build different strings for the different vroots, even though //$REVIEW: we are running from a single, global install of DAV. //$REVIEW: The name might need to be treated as a starting point for a lookup. // NTBug #168188: On OPTIONS, "*" is a valid URI. Need to handle this // special case without asserting. // AssertSz( (L'*' == pwszRootName[0] && 1 == cchRootName) || (0 == cchRootName) || (L'/' == pwszRootName[0]), "(Non-zero) VRoot name doesn't have expected slash delimiter. Instance name string may be malformed!" ); // NTBug # 168188: Special case for OPTIONS * -- map us to the root // instdata name of "/w3svc/#/root" (don't want an instdata named // "/w3svc/#/root*" that noone else can ever use!). // cchVRoot = pwszVRoot.celems(); if (cchVRoot < cchMetaPath + gc_cch_Root + cchRootName + 1) { cchVRoot = cchMetaPath + gc_cch_Root + cchRootName; if (NULL == pwszVRoot.resize(CbSizeWsz(cchVRoot))) { SetLastError(E_OUTOFMEMORY); DebugTrace( "CInstDataCache::GetInstData() - failed to allocate memory\n" ); throw CLastErrorException(); } } // Copy first 2 portions: 'lm/w3svc/' and '/root' // memcpy(pwszVRoot.get(), pwszMetaPath.get(), cchMetaPath * sizeof(WCHAR)); memcpy(pwszVRoot.get() + cchMetaPath, gc_wsz_Root, gc_cch_Root * sizeof(WCHAR)); // Copy remaining 3-rd portion: '/' and terminate the string. // NTBug # 168188: Special case for OPTIONS * -- map us to the root // instdata name of "/w3svc/#/root" (don't want an instdata named // "/w3svc/#/root*" that noone else can ever use!). // if (L'*' == pwszRootName[0] && 1 == cchRootName) { (pwszVRoot.get())[cchMetaPath + gc_cch_Root] = L'\0'; } else { memcpy(pwszVRoot.get() + cchMetaPath + gc_cch_Root, pwszRootName, cchRootName * sizeof(WCHAR)); (pwszVRoot.get())[cchMetaPath + gc_cch_Root + cchRootName] = L'\0'; } // Slam the string to lower-case so that all variations on the vroot // name will match. (IIS doesn't allow vroots with the same name -- // and "VRoot" and "vroot" count as the same!) // _wcslwr( pwszVRoot.get() ); // Demand load the instance data for this vroot // { CRCWsz crcwszVRoot( pwszVRoot.get() ); while ( !Instance().m_cache.FFetch( crcwszVRoot, &pinst ) ) { CInitGate ig( L"DAV/CInstDataCache::GetInstData/", pwszVRoot.get() ); if ( ig.FInit() ) { // Setup instance data from the system security context, // not the client's security context. // safe_revert sr(ecb.HitUser()); pinst = new CInstData(pwszVRoot.get()); // Since we are going to use this crcsz as a key in a cache, // need to make sure that it's built on a name string that // WON'T EVER MOVE (go away, get realloc'd). The stack-based one // above just isn't good enough. SO, build a new little CRC-dude // on the UNMOVABLE name data in the inst object. // (And check that this new crc matches the old one!) // CRCWsz crcwszAdd( pinst->GetNameW() ); AssertSz( crcwszVRoot.isequal(crcwszAdd), "Two CRC's from the same string don't match!" ); Instance().m_cache.Add( crcwszAdd, pinst ); // Log the fact that we've got a new instance pluged in. // Message DAVPRS_VROOT_ATTACH takes two parameters: // the signature of the impl and the vroot. // //$ RAID:NT:283650: Logging each attach causes a large // number of events to be registered. We really should // only Log one-time-startup/failure events. // #undef LOG_STARTUP_EVENT #ifdef LOG_STARTUP_EVENT { LPCWSTR pwszStrings[2]; pwszStrings[0] = gc_wszSignature; pwszStrings[1] = pwszVRoot.get(); LogEventW(DAVPRS_VROOT_ATTACH, EVENTLOG_INFORMATION_TYPE, 2, pwszStrings, 0, NULL ); } #endif // LOG_STARTUP_EVENT // //$ RAID:X5:283650: end. break; } } } return *pinst; }