windows-nt/Source/XPSP1/NT/shell/osshell/lmui/ntshrui/cache.cxx
2020-09-26 16:20:57 +08:00

676 lines
17 KiB
C++

//+---------------------------------------------------------------------------
//
// 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