|
|
/*++
Copyright (c) 2001 Microsoft Corporation
Abstract:
@doc @module stssites.cxx | Implementation of CSTSSites @end
Author:
Brian Berkowitz [brianb] 10/15/2001
Revision History:
Name Date Comments brianb 10/15/2001 Created
--*/
#include "stdafx.hxx"
#include "vs_inc.hxx"
#include "vs_reg.hxx"
#include "iadmw.h"
#include "iiscnfg.h"
#include "mdmsg.h"
#include "stssites.hxx"
#include "vswriter.h"
////////////////////////////////////////////////////////////////////////
// Standard foo for file name aliasing. This code block must be after
// all includes of VSS header files.
//
#ifdef VSS_FILE_ALIAS
#undef VSS_FILE_ALIAS
#endif
#define VSS_FILE_ALIAS "STSSITEC"
//
////////////////////////////////////////////////////////////////////////
// toplevel key for enumeration of sites and their DSNs
static LPCWSTR x_STSSECUREKEY = L"Software\\Microsoft\\Shared Tools\\Web Server Extensions\\Secure";
// bottom level keys for sharepoint sites
static LPCWSTR x_STSRegistryKey = L"/LM/W3SVC/"; static LPCWSTR x_STSRegistryKeyFormat = L"/LM/W3SVC/%d:"; static LPCWSTR x_STSMetabaseKey = L"/LM/W3SVC"; static LPCWSTR x_STSMetabaseKeyFormat= L"/LM/W3SVC/%d"; // DSN value
static LPCWSTR x_ValueDSN = L"DSN";
// version key for Sharepoint Team Services 5.0"
static LPCWSTR x_STSVERSIONKEY = L"Software\\Microsoft\\Shared Tools\\Web Server Extensions\\5.0";
// key to Shell Folders properties. Used to get Applications Data directory"
static LPCWSTR x_ShellFoldersKey = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders";
// value of applications data directory under Shell folders key
static LPCWSTR x_AppDataValue = L"Common AppData";
// subfolder under Applications data directory for STS data"
static LPCWSTR x_STSSubfolder = L"\\Microsoft\\Web Server Extensions\\50";
// constructor
CSTSSites::CSTSSites() : m_cSites(0), m_rgSiteIds(NULL), m_rootKey(KEY_READ|KEY_ENUMERATE_SUB_KEYS), m_hQuotaLock(INVALID_HANDLE_VALUE), m_wszAppDataFolder(NULL) { }
// destructor
CSTSSites::~CSTSSites() { delete m_rgSiteIds; UnlockSites(); UnlockQuotaDatabase(); CoTaskMemFree(m_wszAppDataFolder); }
// initialize the array of site ids. Returns FALSE if there are no sites
bool CSTSSites::Initialize() { CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSSites::Initialize");
if (!m_rootKey.Open(HKEY_LOCAL_MACHINE, x_STSSECUREKEY)) return false;
CVssRegistryKeyIterator iter; iter.Attach(m_rootKey); try { m_cSites = iter.GetSubkeysCount(); m_rgSiteIds = new DWORD[m_cSites]; if (m_rgSiteIds == NULL) ft.Throw(VSSDBG_STSWRITER, E_OUTOFMEMORY, L"can't allocate site id array");
for(UINT iSite = 0; iSite < m_cSites; iSite++) { DWORD siteId;
BS_ASSERT(!iter.IsEOF()); LPCWSTR wszSiteName = iter.GetCurrentKeyName(); BS_ASSERT(wcslen(wszSiteName) > wcslen(x_STSRegistryKey)); BS_ASSERT(wcsncmp(wszSiteName, x_STSRegistryKey, wcslen(x_STSRegistryKey)) == 0); #ifdef DEBUG
DWORD cFields = #endif
swscanf(wszSiteName, x_STSRegistryKeyFormat, &siteId); BS_ASSERT(cFields == 1); m_rgSiteIds[iSite] = siteId; iter.MoveNext(); }
} catch(...) { iter.Detach(); throw; }
iter.Detach(); return true; }
// return the id of a specific site
VSS_PWSZ CSTSSites::GetSiteDSN(DWORD iSite) { CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSSites::GetSiteDSN");
CVssRegistryKey key(KEY_READ); BS_ASSERT(iSite < m_cSites); DWORD siteId = m_rgSiteIds[iSite]; WCHAR buf[MAX_PATH]; swprintf(buf, x_STSRegistryKeyFormat, siteId); if (!key.Open(m_rootKey.GetHandle(), buf)) ft.Throw(VSSDBG_STSWRITER, E_UNEXPECTED, L"missing registry key");
VSS_PWSZ pwszDSN = NULL; key.GetValue(x_ValueDSN, pwszDSN); BS_ASSERT(pwszDSN); return pwszDSN; }
// determine if this is the correct sharepoint version
bool CSTSSites::ValidateSharepointVersion() { CVssRegistryKey key;
return key.Open(HKEY_LOCAL_MACHINE, x_STSVERSIONKEY); }
void CSTSSites::SetupMetabaseInterface() { CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSSites::SetupMetabaseInterface");
if (!m_pMetabase) { ft.hr = CoCreateInstance(CLSID_MSAdminBase, NULL, CLSCTX_ALL, IID_IMSAdminBase, (void **) &m_pMetabase); ft.CheckForError(VSSDBG_STSWRITER, L"CoCreateInstance MSAdminBase"); } }
// return pointer to site content root. The return value should be
// freed using CoTaskMemFree by the caller
VSS_PWSZ CSTSSites::GetSiteRoot(DWORD iSite) { CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSSites::GetSiteRoles"); METADATA_HANDLE h; static const DWORD x_MDTimeout = 2000;
BS_ASSERT(iSite < m_cSites); DWORD siteId = m_rgSiteIds[iSite]; WCHAR buf[METADATA_MAX_NAME_LEN];
SetupMetabaseInterface();
swprintf(buf, x_STSMetabaseKeyFormat, siteId);
ft.hr = m_pMetabase->OpenKey(METADATA_MASTER_ROOT_HANDLE, buf, METADATA_PERMISSION_READ, x_MDTimeout, &h); ft.CheckForError(VSSDBG_STSWRITER, L"IMSAdminBase::OpenKey"); CVssAutoMetabaseHandle MetaHandle(m_pMetabase, h); METADATA_RECORD rec; DWORD dwBufLen = METADATA_MAX_NAME_LEN; PBYTE pbBuffer = (BYTE *) CoTaskMemAlloc(dwBufLen); if (pbBuffer == NULL) ft.Throw(VSSDBG_STSWRITER, E_OUTOFMEMORY, L"out of memory");
rec.dwMDAttributes = METADATA_INHERIT; rec.dwMDUserType = IIS_MD_UT_SERVER, rec.dwMDDataType = ALL_METADATA; rec.dwMDDataLen = dwBufLen; rec.pbMDData = pbBuffer; rec.dwMDIdentifier = MD_VR_PATH;
DWORD dwReqSize; ft.hr = m_pMetabase->GetData(h, L"/root", &rec, &dwReqSize); if (ft.hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) { CoTaskMemFree(pbBuffer); pbBuffer = (BYTE *) CoTaskMemAlloc(dwReqSize); if (pbBuffer == NULL) ft.Throw(VSSDBG_STSWRITER, E_OUTOFMEMORY, L"out of memory");
rec.dwMDDataLen = dwReqSize; rec.pbMDData = pbBuffer; ft.hr = m_pMetabase->GetData(h, L"/root", &rec, &dwReqSize); }
ft.CheckForError(VSSDBG_STSWRITER, L"IMSAdminBase::GetData");
return (WCHAR *) pbBuffer; }
void CSTSSites::LockSiteContents(DWORD iSite) { CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSSites::LockSiteContents");
VSS_PWSZ wszContentRoot = GetSiteRoot(iSite); WCHAR *wszCurrentFile = NULL; char *szFile = NULL; WCHAR *wszFile = NULL; WCHAR *wszNewRoot = NULL; CSimpleArray<VSS_PWSZ> rgwszRoots; try { wszCurrentFile = new WCHAR[wcslen(wszContentRoot) + 1]; if (wszCurrentFile == NULL) ft.Throw(VSSDBG_STSWRITER, E_OUTOFMEMORY, L"out of memory");
wcscpy(wszCurrentFile, wszContentRoot); rgwszRoots.Add(wszCurrentFile); wszCurrentFile = NULL; DWORD iCurrentPos = 0; DWORD iEndLevel = 1; CoTaskMemFree(wszContentRoot); wszContentRoot = NULL; while(iCurrentPos < iEndLevel) { for(; iCurrentPos < iEndLevel; iCurrentPos++) { LPCWSTR wszCurrentRoot = rgwszRoots[iCurrentPos]; wszCurrentFile = new WCHAR[wcslen(wszCurrentRoot) + MAX_PATH + 2]; if (wszCurrentFile == NULL) ft.Throw(VSSDBG_STSWRITER, E_OUTOFMEMORY, L"out of memory");
// get front page lock
wcscpy(wszCurrentFile, wszCurrentRoot); wcscat(wszCurrentFile, L"\\_vti_pvt\\frontpg.lck"); TryLock(wszCurrentFile, false);
// get service lock
wcscpy(wszCurrentFile, wszCurrentRoot); wcscat(wszCurrentFile, L"\\_vti_pvt\\service.lck"); TryLock(wszCurrentFile, false);
// find child sub webs
wcscpy(wszCurrentFile, wszCurrentRoot); wcscat(wszCurrentFile, L"\\_vti_pvt\\services.cnf"); CVssAutoWin32Handle h = CreateFile ( wszCurrentFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
if (h == INVALID_HANDLE_VALUE) { DWORD dwErr = GetLastError(); if (dwErr == ERROR_PATH_NOT_FOUND || dwErr == ERROR_FILE_NOT_FOUND) continue;
ft.TranslateGenericError ( VSSDBG_STSWRITER, HRESULT_FROM_WIN32(GetLastError()), L"CreateFile(%s)", wszCurrentFile ); }
DWORD dwSize = GetFileSize(h, NULL); if (dwSize == 0) ft.TranslateGenericError ( VSSDBG_STSWRITER, HRESULT_FROM_WIN32(GetLastError()), L"GetFileSize(%s)", wszCurrentFile );
szFile = new char[dwSize + 1]; wszFile = new WCHAR[dwSize + 1]; if (szFile == NULL || wszFile == NULL) ft.Throw(VSSDBG_STSWRITER, E_OUTOFMEMORY, L"out of memory");
DWORD dwRead; if (!ReadFile(h, szFile, dwSize, &dwRead, NULL)) ft.TranslateGenericError ( VSSDBG_STSWRITER, HRESULT_FROM_WIN32(GetLastError()), L"ReadFile(%s)", wszCurrentFile );
szFile[dwSize] = '\0';
if (!MultiByteToWideChar ( CP_ACP, MB_ERR_INVALID_CHARS, szFile, dwSize, wszFile, dwSize)) { ft.hr = HRESULT_FROM_WIN32(GetLastError()); ft.CheckForError(VSSDBG_STSWRITER, L"MultiByteToWideChar"); }
WCHAR *pwcCur = wszFile; WCHAR *pwcMax = wszFile + dwSize; while(pwcCur < pwcMax) { WCHAR *pwcEnd = wcschr(pwcCur, L'\n'); if (pwcEnd == NULL) pwcEnd = pwcMax; *pwcEnd = L'\0'; stripWhiteChars(pwcCur); if (*pwcCur == L'\0' || *pwcCur != L'/' || pwcCur[1] == L'\0') { pwcCur = pwcEnd + 1; continue; }
wszNewRoot = new WCHAR[wcslen(pwcCur) + wcslen(wszCurrentRoot) + 1]; if (wszNewRoot == NULL) ft.Throw(VSSDBG_STSWRITER, E_OUTOFMEMORY, L"out of memory");
wcscpy(wszNewRoot, wszCurrentRoot);
// use backslash instead of forward slash
wcscat(wszNewRoot, L"\\");
// root of subweb
wcscat(wszNewRoot, pwcCur+1); rgwszRoots.Add(wszNewRoot); wszNewRoot = NULL; pwcCur = pwcEnd + 1; }
delete [] wszFile; wszFile = NULL; delete [] szFile; szFile = NULL; }
iCurrentPos = iEndLevel; iEndLevel = rgwszRoots.GetSize(); } } catch(...) { UnlockSites(); delete [] wszNewRoot; delete [] wszFile; delete [] szFile; delete wszCurrentFile; CoTaskMemFree(wszContentRoot);
for (int x = 0; x < rgwszRoots.GetSize(); x++) delete [] rgwszRoots[x]; throw; }
for (int x = 0; x < rgwszRoots.GetSize(); x++) delete [] rgwszRoots[x]; }
// remove white characters from beginning and ending of the string
void CSTSSites::stripWhiteChars(LPWSTR &wsz) { static LPCWSTR x_wszWhiteChars = L"^[ \t]+"; while(*wsz != L'\0') { if (wcschr(x_wszWhiteChars, *wsz) == NULL) break;
wsz++; }
if (*wsz == L'\0') return;
LPWSTR wszEnd = wsz + wcslen(wsz) - 1; while(wszEnd > wsz) { if (wcschr(x_wszWhiteChars, *wszEnd) == NULL && *wszEnd != L'\r') break;
*wszEnd = L'\0'; wszEnd--; } }
void CSTSSites::TryLock(LPCWSTR wszFile, bool bQuotaFile) { static const DWORD x_MAX_RETRIES = 60; static const DWORD x_SLEEP_INTERVAL = 1000;
HANDLE h = INVALID_HANDLE_VALUE; for(DWORD i = 0; i < x_MAX_RETRIES; i++) { h = CreateFile ( wszFile, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL );
if (h != INVALID_HANDLE_VALUE) break;
DWORD dwErr = GetLastError(); if (dwErr == ERROR_SHARING_VIOLATION) { Sleep(x_SLEEP_INTERVAL); continue; }
// assume file doesn't exist
return; }
if (i >= x_MAX_RETRIES) throw VSS_E_WRITERERROR_TIMEOUT;
BS_ASSERT(h != INVALID_HANDLE_VALUE);
try { if (bQuotaFile) { BS_ASSERT(m_hQuotaLock == INVALID_HANDLE_VALUE); m_hQuotaLock = h; } else m_rgContentLocks.Add(h); } catch(...) { // add failed, close handle and rethrow error
CloseHandle(h); throw; } }
void CSTSSites::UnlockSites() { DWORD dwSize = m_rgContentLocks.GetSize(); for(DWORD i = 0; i < dwSize; i++) CloseHandle(m_rgContentLocks[i]);
m_rgContentLocks.RemoveAll(); }
// lock the quota database
void CSTSSites::LockQuotaDatabase() { CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSSites::LockQuotaDatabase");
// do nothing if quota database is already locked
if (m_hQuotaLock != INVALID_HANDLE_VALUE) return;
static LPCWSTR x_wszLockFile = L"\\owsuser.lck";
VSS_PWSZ wszDbRoot = GetQuotaDatabase(); LPWSTR wsz = NULL; try { wsz = new WCHAR[wcslen(wszDbRoot) + 1 + wcslen(x_wszLockFile)]; if (wsz == NULL) ft.Throw(VSSDBG_STSWRITER, E_OUTOFMEMORY, L"out of memory");
wcscpy(wsz, wszDbRoot); wcscat(wsz, x_wszLockFile); TryLock(wsz, true); delete wsz; CoTaskMemFree(wszDbRoot); } catch(...) { delete wsz; CoTaskMemFree(wszDbRoot); throw; } }
// unlock the quota database
void CSTSSites::UnlockQuotaDatabase() { if (m_hQuotaLock != INVALID_HANDLE_VALUE) { CloseHandle(m_hQuotaLock); m_hQuotaLock = INVALID_HANDLE_VALUE; } }
// get location of Documents And Settings/All Users folder
LPCWSTR CSTSSites::GetAppDataFolder() { CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSSites::GetAppDataFolder"); if (m_wszAppDataFolder) return m_wszAppDataFolder;
CVssRegistryKey key; if (!key.Open(HKEY_LOCAL_MACHINE, x_ShellFoldersKey)) ft.Throw(VSSDBG_STSWRITER, E_UNEXPECTED, L"shell folders key is missing");
key.GetValue(x_AppDataValue, m_wszAppDataFolder); return m_wszAppDataFolder; }
// get location of directory containing the quota database for
// sharepoint. Caller should call CoTaskMemFree on the returned value
VSS_PWSZ CSTSSites::GetQuotaDatabase() { CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSSites::GetQuotaDatabase"); LPCWSTR wszAppData = GetAppDataFolder(); DWORD cwc = (DWORD) (wcslen(wszAppData) + wcslen(x_STSSubfolder) + 1); VSS_PWSZ wsz = (VSS_PWSZ) CoTaskMemAlloc(cwc * sizeof(WCHAR)); if (wsz == NULL) ft.Throw(VSSDBG_STSWRITER, E_OUTOFMEMORY, L"out of memory"); wcscpy(wsz, wszAppData); wcscat(wsz, x_STSSubfolder); return wsz; }
// return of the root directory for the sites roles database under
// the app data folder
VSS_PWSZ CSTSSites::GetSiteRoles(DWORD iSite) { CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSSites::GetSiteRoles");
DWORD siteId = GetSiteId(iSite); WCHAR buf[32]; swprintf(buf, L"\\W3SVC%d", siteId); LPCWSTR wszAppData = GetAppDataFolder(); DWORD cwc = (DWORD) (wcslen(wszAppData) + wcslen(x_STSSubfolder) + wcslen(buf) + 1); VSS_PWSZ wsz = (VSS_PWSZ) CoTaskMemAlloc(cwc * sizeof(WCHAR)); if (wsz == NULL) ft.Throw(VSSDBG_STSWRITER, E_OUTOFMEMORY, L"out of memory"); wcscpy(wsz, wszAppData); wcscat(wsz, x_STSSubfolder); wcscat(wsz, buf); return wsz; }
VSS_PWSZ CSTSSites::GetSiteBasicInfo(DWORD iSite, DWORD propId) { CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSSites::GetSiteBasicInfo");
METADATA_HANDLE h; static const DWORD x_MDTimeout = 2000;
BS_ASSERT(iSite < m_cSites); DWORD siteId = m_rgSiteIds[iSite];
SetupMetabaseInterface();
ft.hr = m_pMetabase->OpenKey(METADATA_MASTER_ROOT_HANDLE, x_STSMetabaseKey, METADATA_PERMISSION_READ, x_MDTimeout, &h); ft.CheckForError(VSSDBG_STSWRITER, L"IMSAdminBase::OpenKey"); CVssAutoMetabaseHandle MetaHandle(m_pMetabase, h); METADATA_RECORD rec; DWORD dwBufLen = METADATA_MAX_NAME_LEN; PBYTE pbBuffer = (BYTE *) CoTaskMemAlloc(dwBufLen); if (pbBuffer == NULL) ft.Throw(VSSDBG_STSWRITER, E_OUTOFMEMORY, L"out of memory");
rec.dwMDAttributes = METADATA_INHERIT; rec.dwMDUserType = IIS_MD_UT_SERVER, rec.dwMDDataType = ALL_METADATA; rec.dwMDDataLen = dwBufLen; rec.pbMDData = pbBuffer; rec.dwMDIdentifier = propId;
WCHAR buf[16]; swprintf(buf, L"/%d", siteId);
DWORD dwReqSize; ft.hr = m_pMetabase->GetData(h, buf, &rec, &dwReqSize); if (ft.hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) { CoTaskMemFree(pbBuffer); pbBuffer = (BYTE *) CoTaskMemAlloc(dwReqSize); if (pbBuffer == NULL) ft.Throw(VSSDBG_STSWRITER, E_OUTOFMEMORY, L"out of memory");
rec.dwMDDataLen = dwReqSize; rec.pbMDData = pbBuffer; ft.hr = m_pMetabase->GetData(h, buf, &rec, &dwReqSize); }
if (ft.hr == MD_ERROR_DATA_NOT_FOUND) return NULL;
ft.CheckForError(VSSDBG_STSWRITER, L"IMSAdminBase::GetData");
return (WCHAR *) pbBuffer; }
// return comments for site (site description). Caller is responsible
// for freeing up the memory using CoTaskMemFree
VSS_PWSZ CSTSSites::GetSiteComment(DWORD iSite) { return GetSiteBasicInfo(iSite, MD_SERVER_COMMENT); }
// return ip address of the site if it exists. Caller is responsible for
// freeing up the memory using CoTaskMemFree
VSS_PWSZ CSTSSites::GetSiteIpAddress(DWORD iSite) { CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSSites::GetSiteIpAddress");
VSS_PWSZ wszBindings = GetSiteBasicInfo(iSite, MD_SERVER_BINDINGS);
if (wszBindings == NULL) return NULL;
LPWSTR wszHost = wcsrchr(wszBindings, L':'); if (wszHost == NULL) { CoTaskMemFree(wszBindings); return NULL; }
*wszHost = '\0'; LPCWSTR wszPort = wcsrchr(wszBindings, L':'); if (wszPort == NULL || wszPort == wszBindings) { CoTaskMemFree(wszBindings); return NULL; }
DWORD cwc = (DWORD) (wszPort - wszBindings); VSS_PWSZ pwszRet = (VSS_PWSZ) CoTaskMemAlloc(cwc * sizeof(WCHAR)); if (pwszRet == NULL) { CoTaskMemFree(wszBindings); ft.Throw(VSSDBG_STSWRITER, E_OUTOFMEMORY, L"out of memory"); }
wcsncpy(pwszRet, wszBindings, cwc - 1); pwszRet[cwc - 1] = L'\0'; return pwszRet; }
// return port address of the site if it exists. Caller is responsible for
// freeing up the memory using CoTaskMemFree
DWORD CSTSSites::GetSitePort(DWORD iSite) { CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSSites::GetSiteIpAddress");
VSS_PWSZ wszBindings = GetSiteBasicInfo(iSite, MD_SERVER_BINDINGS);
if (wszBindings == NULL) return NULL;
LPWSTR wszHost = wcsrchr(wszBindings, L':'); if (wszHost == NULL) { CoTaskMemFree(wszBindings); return NULL; }
*wszHost = L'\0'; LPWSTR wszPort = wcsrchr(wszBindings, L':'); if (wszPort == NULL || wszPort + 1 == wszHost) { CoTaskMemFree(wszBindings); return 0; }
DWORD dwPort = _wtoi(wszPort + 1); CoTaskMemFree(wszBindings); return dwPort; }
// return port address of the site if it exists. Caller is responsible for
// freeing up the memory using CoTaskMemFree
VSS_PWSZ CSTSSites::GetSiteHost(DWORD iSite) { CVssFunctionTracer ft(VSSDBG_STSWRITER, L"CSTSSites::GetSiteIpAddress");
VSS_PWSZ wszBindings = GetSiteBasicInfo(iSite, MD_SERVER_BINDINGS);
if (wszBindings == NULL) return NULL;
LPCWSTR wszHost = wcsrchr(wszBindings, L':'); if (wszHost == NULL || wcslen(wszHost) == 1) { CoTaskMemFree(wszBindings); return NULL; }
VSS_PWSZ pwszRet = (VSS_PWSZ) CoTaskMemAlloc(wcslen(wszHost)); if (pwszRet == NULL) { CoTaskMemFree(wszBindings); ft.Throw(VSSDBG_STSWRITER, E_OUTOFMEMORY, L"out of memory"); }
wcscpy(pwszRet, wszHost + 1); CoTaskMemFree(wszBindings); return pwszRet; }
|