676 lines
17 KiB
C++
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
|