//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1995 - 1995. // // File: cache.cxx // // Contents: Functions to manage a cache of shares // // History: 11-Apr-95 BruceFo Created // 21-Aug-95 BruceFo Created CShareCache class to clean up // resource usage of resources protected // by critical section. // //---------------------------------------------------------------------------- #include "headers.hxx" #pragma hdrstop #include "critsec.hxx" #include "cache.hxx" #include "dllmain.hxx" #include "shrinfo.hxx" #include "strhash.hxx" #include "util.hxx" ////////////////////////////////////////////////////////////////////////////// #if DBG == 1 VOID DumpNetEnum( IN LPVOID pBufShares, IN ULONG entriesRead ); #endif // DBG == 1 ////////////////////////////////////////////////////////////////////////////// CShareCache g_ShareCache; // the main share cache ////////////////////////////////////////////////////////////////////////////// //+------------------------------------------------------------------------- // // Member: CShareCache::CShareCache // // Synopsis: Constructor. // // History: 21-Aug-95 BruceFo Created // //-------------------------------------------------------------------------- CShareCache::CShareCache( VOID ) : m_cShares(0), m_pBufShares(NULL), m_pHash(NULL) { InitializeCriticalSection(&m_csBuf); } //+------------------------------------------------------------------------- // // Member: CShareCache::~CShareCache // // Synopsis: Destructor // // History: 21-Aug-95 BruceFo Created // //-------------------------------------------------------------------------- CShareCache::~CShareCache() { Delete(); DeleteCriticalSection(&m_csBuf); } //+------------------------------------------------------------------------- // // Member: CShareCache::Delete // // Synopsis: Gets rid of cached memory. // // History: 21-Aug-95 BruceFo Created // //-------------------------------------------------------------------------- VOID CShareCache::Delete( VOID ) { CTakeCriticalSection t(&m_csBuf); if (NULL != m_pBufShares) { NetApiBufferFree(m_pBufShares); } m_pBufShares = NULL; delete m_pHash; m_pHash = NULL; m_cShares = 0; } //+------------------------------------------------------------------------- // // Member: CShareCache::IsPathShared // // Synopsis: See ::IsPathShared. // // History: 21-Aug-95 BruceFo Created // //-------------------------------------------------------------------------- BOOL CShareCache::IsPathShared( LPCTSTR lpPath, BOOL fRefresh ) { BOOL bOldSharingEnabled = g_fSharingEnabled; BOOL bRet = FALSE; { // scope the critical section taking CTakeCriticalSection t(&m_csBuf); // For plug and play: if the server service starts // or stops, we get a refresh call. If sharing is not currently // enabled but a refresh is request, see if sharing has just become // available. if (fRefresh) { appDebugOut((DEB_TRACE, "Forced cache refresh!\n")); RefreshNoCritSec(); } if (CacheOK()) { appAssert(NULL != m_pHash); bRet = m_pHash->IsMember(lpPath); } else { // the server doesn't seem to be running... bRet = FALSE; } } if (bOldSharingEnabled != g_fSharingEnabled) { // The server either came up or went down, and we refreshed based on // that fact. Force the shell/explorer to redraw *all* views. appDebugOut((DEB_TRACE, "Forcing the shell to redraw *all* views!\n")); SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL); } return bRet; } //+------------------------------------------------------------------------- // // Member: CShareCache::Refresh // // Synopsis: Refreshes the cache of shares // // History: 21-Aug-95 BruceFo Created // // Note: Sets g_fSharingEnabled // //-------------------------------------------------------------------------- VOID CShareCache::Refresh( VOID ) { CTakeCriticalSection t(&m_csBuf); RefreshNoCritSec(); } // in: // pShare share to inspect // bIncludeHidden -> admin shares (X$, ADMIN$) will not be skipped // otherwise they are included BOOL ShouldSkipShare(SHARE_INFO_502* pShare, BOOL bIncludeHidden) { // needs to have an associated path // needs to be STYPE_DISK (to skip IPC$) // STYPE_SPECIAL indicates hidden admin share return (pShare->shi502_path == NULL) || (pShare->shi502_path[0] == 0) || ((pShare->shi502_type & ~STYPE_SPECIAL) != STYPE_DISKTREE) || (bIncludeHidden ? FALSE : (pShare->shi502_type & STYPE_SPECIAL)); } //+------------------------------------------------------------------------- // // Member: CShareCache::RefreshNoCritSec // // Synopsis: Refreshes the cache of shares: the critical section must // already taken! // // History: 18-Aug-95 BruceFo Created // // Note: Sets g_fSharingEnabled // //-------------------------------------------------------------------------- VOID CShareCache::RefreshNoCritSec( VOID ) { Delete(); DWORD entriesRead, totalEntries; DWORD err = ::NetShareEnum( NULL, // local computer 502, &m_pBufShares, 0xffffffff, // no buffer limit; get them all! &entriesRead, &totalEntries, NULL); // no resume handle 'cause we're getting all if (err != NERR_Success) { appDebugOut((DEB_ERROR, "Error enumerating shares: 0x%08lx\n", err)); m_pBufShares = NULL; // just in case NetShareEnum munged it Delete(); } else { appAssert(entriesRead == totalEntries); m_cShares = entriesRead; } if (m_cShares > 0) { // // Now, create a hash table and put all the shares into it (strings are // cached; don't copy any data) // m_pHash = new CStrHashTable(m_cShares * 2 - 1); if ((NULL == m_pHash) || FAILED(m_pHash->QueryError())) { // out of memory; delete everything Delete(); } else { SHARE_INFO_502* pShareBase = (SHARE_INFO_502 *)m_pBufShares; for (UINT iShare = 0; iShare < m_cShares; iShare++) { SHARE_INFO_502* pShare = &pShareBase[iShare]; if (ShouldSkipShare(pShare, FALSE)) // don't include hidden continue; HRESULT hr = m_pHash->Insert(pShare->shi502_path); if (FAILED(hr)) { // out of memory; delete everything Delete(); break; } } } #if DBG == 1 if (NULL != m_pHash) { // if everything hasn't been deleted because of a memory problem... m_pHash->Print(); } #endif // DBG == 1 } g_fSharingEnabled = CacheOK(); } //+------------------------------------------------------------------------- // // Member: CShareCache::IsShareNameUsed // // Synopsis: Returns TRUE if the share name in question is already used // // History: 4-Apr-95 BruceFo Created // //-------------------------------------------------------------------------- BOOL CShareCache::IsShareNameUsed( IN PWSTR pszShareName ) { CTakeCriticalSection t(&m_csBuf); if (!CacheOK()) { return FALSE; } SHARE_INFO_502* pShareBase = (SHARE_INFO_502 *)m_pBufShares; for (UINT iShare = 0; iShare < m_cShares; iShare++) { SHARE_INFO_502* pShare = &pShareBase[iShare]; if (0 == _wcsicmp(pszShareName, pShare->shi502_netname)) { return TRUE; } } return FALSE; } //+------------------------------------------------------------------------- // // Member: CShareCache::IsExistingShare // // Synopsis: Finds out if a share name is already in use with a different // path. // // Arguments: [pszShareName] - name of share being replaced // [pszPath] - path to compare against // [pszOldPath] - If not null, filled with path of the share, // if found // // Returns: Returns TRUE if found and the paths are different, // FALSE otherwise // // History: 4-May-95 BruceFo Stolen // //-------------------------------------------------------------------------- BOOL CShareCache::IsExistingShare( IN PCWSTR pszShareName, IN PCWSTR pszPath, OUT PWSTR pszOldPath ) { appAssert(NULL != pszShareName); CTakeCriticalSection t(&m_csBuf); if (!CacheOK()) { return FALSE; } SHARE_INFO_502* pShareBase = (SHARE_INFO_502 *)m_pBufShares; for (UINT iShare = 0; iShare < m_cShares; iShare++) { SHARE_INFO_502* pShare = &pShareBase[iShare]; if (0 == _wcsicmp(pszShareName, pShare->shi502_netname)) { if (pszOldPath != NULL) { wcscpy(pszOldPath, pShare->shi502_path); } return TRUE; } } return FALSE; } //+------------------------------------------------------------------------- // // Member: CShareCache::ConstructList // // Synopsis: Construct a list of shares for a particular path // // Arguments: // // Returns: hresult // // History: 21-Aug-95 BruceFo Created // //-------------------------------------------------------------------------- HRESULT CShareCache::ConstructList( IN PCWSTR pszPath, IN OUT CShareInfo* pShareList, OUT ULONG* pcShares ) { CTakeCriticalSection t(&m_csBuf); SHARE_INFO_502* pShareBase = (SHARE_INFO_502 *)m_pBufShares; HRESULT hr; ULONG cShares = 0; for (UINT iShare = 0; iShare < m_cShares; iShare++) { SHARE_INFO_502* pShare = &pShareBase[iShare]; if (0 == _wcsicmp(pszPath, pShare->shi502_path)) { if (ShouldSkipShare(pShare, TRUE)) // include hidden continue; // // We found one! // appDebugOut((DEB_ITRACE, "ConstructList: adding %ws\n", pShare->shi502_netname)); CShareInfo* pNewInfo = new CShareInfo(); if (NULL == pNewInfo) { return E_OUTOFMEMORY; } hr = pNewInfo->InitInstance(); if (FAILED(hr)) { delete pNewInfo; return hr; } // We can't point into the data protected by a critical section, // so we must copy it. hr = pNewInfo->Copy(pShare); if (FAILED(hr)) { delete pNewInfo; return hr; } NET_API_STATUS ret = pNewInfo->ReadCacheFlags (); if ( NERR_Success != ret ) { delete pNewInfo; return HRESULT_FROM_WIN32 (ret); } pNewInfo->InsertBefore(pShareList); // add to end of list ++cShares; } } *pcShares = cShares; return S_OK; } //+------------------------------------------------------------------------- // // Member: CShareCache::ConstructParentWarnList // // Synopsis: Construct a new list of shares that are children or descendants // of the path passed in. // // Arguments: [pszPath] - the prefix path to check for // [ppShareList] - new share list, if success. Caller must delete // it using 'delete' on each element. This list is // doubly-linked with a dummy head node. NOTE: As an // optimization, this is set to NULL if there is no share. // This avoids allocating and deleting memory unless there // is something to warn the user about. // // Returns: hresult // // History: 21-Aug-95 BruceFo Created // //-------------------------------------------------------------------------- HRESULT CShareCache::ConstructParentWarnList( IN PCWSTR pszPath, OUT CShareInfo** ppShareList ) { CTakeCriticalSection t(&m_csBuf); HRESULT hr; CShareInfo* pShareList = NULL; SHARE_INFO_502* pShareBase = (SHARE_INFO_502 *)m_pBufShares; INT cchPath = wcslen(pszPath); for (UINT iShare = 0; iShare < m_cShares; iShare++) { SHARE_INFO_502* pShare = &pShareBase[iShare]; PWSTR pszSharePath = pShare->shi502_path; INT cchSharePath = wcslen(pszSharePath); if (cchSharePath >= cchPath) { // WARNING - the following won't work with LFN/shortname differences // PERF: we're doing a prefix match of the current directory // name on the set of share names. This could be expensive with // a linear search! if (0 == _wcsnicmp(pszSharePath, pszPath, cchPath) && ( *(pszSharePath + cchPath) == TEXT('\\') || *(pszSharePath + cchPath) == TEXT('\0') ) ) { appDebugOut((DEB_TRACE, "ConstructParentWarnList, share %ws, file %ws. Found a prefix!\n", pszSharePath, pszPath)); if (NULL == pShareList) { // do the lazy dummy head node creation if this is the // first prefix match pShareList = new CShareInfo(); // dummy head node if (NULL == pShareList) { return E_OUTOFMEMORY; } } CShareInfo* pNewInfo = new CShareInfo(); if (NULL == pNewInfo) { hr = E_OUTOFMEMORY; } else { hr = pNewInfo->InitInstance(); if (SUCCEEDED(hr)) { // We can't point into the data protected by a // critical section, so we must copy it. hr = pNewInfo->Copy(pShare); if ( SUCCEEDED (hr) ) { NET_API_STATUS ret = pNewInfo->ReadCacheFlags (); if ( NERR_Success != ret ) { delete pNewInfo; return HRESULT_FROM_WIN32 (ret); } } } } if (FAILED(hr)) { delete pNewInfo; DeleteShareInfoList(pShareList, TRUE); return hr; } pNewInfo->InsertBefore(pShareList); // add to end of list } } } *ppShareList = pShareList; return S_OK; } //+------------------------------------------------------------------------- // // Member: CShareCache::CacheOK // // Synopsis: Returns TRUE if the cache contains valid data. // // History: 24-Sep-95 BruceFo Created // // Note: The critical section must be held when calling this function // //-------------------------------------------------------------------------- BOOL CShareCache::CacheOK( VOID ) { // either both are valid or both are invalid appAssert( ((NULL != m_pHash) && (NULL != m_pBufShares)) || ((NULL == m_pHash) && (NULL == m_pBufShares)) ); return (NULL != m_pHash); } #if DBG == 1 //+------------------------------------------------------------------------- // // Function: DumpNetEnum // // Synopsis: Dumps an array of SHARE_INFO_502 structures. // // History: 4-Apr-95 BruceFo Created // //-------------------------------------------------------------------------- VOID DumpNetEnum( IN LPVOID pBufShares, IN ULONG entriesRead ) { SHARE_INFO_502* pBase = (SHARE_INFO_502*) pBufShares; appDebugOut((DEB_TRACE, "DumpNetEnum: %d entries\n", entriesRead)); for (ULONG i = 0; i < entriesRead; i++) { SHARE_INFO_502* p = &(pBase[i]); appDebugOut((DEB_TRACE | DEB_NOCOMPNAME, "\t Share name: %ws\n" "\t Type: %d (0x%08lx)\n" "\t Comment: %ws\n" "\tPermissions: %d (0x%08lx)\n" "\t Max uses: %d\n" "\t Path: %ws\n" "\t Password: %ws\n" "\t Reserved: %d\n" "\t Security? %ws\n" "\n" , p->shi502_netname, p->shi502_type, p->shi502_type, p->shi502_remark, p->shi502_permissions, p->shi502_permissions, p->shi502_max_uses, p->shi502_path, (NULL == p->shi502_passwd) ? L"none" : p->shi502_passwd, p->shi502_reserved, (NULL == p->shi502_security_descriptor) ? L"No" : L"Yes" )); } } #endif // DBG == 1