Windows-Server-2003/shell/shlwapi/gcount.cpp

223 lines
6.6 KiB
C++

#include "priv.h"
//
// #defines
//
#ifdef DEBUG
#define GLOBAL_COUNTER_WAIT_TIMEOUT 30*1000 // on debug we set this to 30 seconds
#else
#define GLOBAL_COUNTER_WAIT_TIMEOUT 0 // on retail its zero so we test the objects state and return immedaeately
#endif
//
// Globals
//
SECURITY_ATTRIBUTES* g_psa = NULL;
// There are three kinds of null-type DACL's.
//
// 1. No DACL. This means that we inherit the ambient DACL from our thread.
// 2. Null DACL. This means "full access to everyone".
// 3. Empty DACL. This means "deny all access to everyone".
//
// NONE of these are correct for our needs. We used to use Null DACL's (2),
// but the issue with these is that someone can change the ACL on the object thus
// locking us out so we can't synchronize to the object anymore.
//
// So now we create a specific DACL with 3 ACE's in it:
//
// ACE #1: Everyone - GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | SYNCHRONIZE
// ACE #2: SYSTEM - GENERIC_ALL (full control)
// ACE #3: Administrators - GENERIC_ALL (full control)
//
STDAPI_(SECURITY_ATTRIBUTES*) SHGetAllAccessSA()
{
if (g_psa == NULL)
{
SECURITY_ATTRIBUTES* psa = (SECURITY_ATTRIBUTES*)LocalAlloc(LPTR, sizeof(*psa));
if (psa)
{
SECURITY_DESCRIPTOR* psd;
SHELL_USER_PERMISSION supEveryone;
SHELL_USER_PERMISSION supSystem;
SHELL_USER_PERMISSION supAdministrators;
PSHELL_USER_PERMISSION apUserPerm[3] = {&supEveryone, &supAdministrators, &supSystem};
// we want the everyone to have read, write, exec and sync only
supEveryone.susID = susEveryone;
supEveryone.dwAccessType = ACCESS_ALLOWED_ACE_TYPE;
supEveryone.dwAccessMask = (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | SYNCHRONIZE);
supEveryone.fInherit = FALSE;
supEveryone.dwInheritMask = 0;
supEveryone.dwInheritAccessMask = 0;
// we want the SYSTEM to have full control
supSystem.susID = susSystem;
supSystem.dwAccessType = ACCESS_ALLOWED_ACE_TYPE;
supSystem.dwAccessMask = GENERIC_ALL;
supSystem.fInherit = FALSE;
supSystem.dwInheritMask = 0;
supSystem.dwInheritAccessMask = 0;
// we want the Administrators to have full control
supAdministrators.susID = susAdministrators;
supAdministrators.dwAccessType = ACCESS_ALLOWED_ACE_TYPE;
supAdministrators.dwAccessMask = GENERIC_ALL;
supAdministrators.fInherit = FALSE;
supAdministrators.dwInheritMask = 0;
supAdministrators.dwInheritAccessMask = 0;
// allocate the global SECURITY_DESCRIPTOR
psd = GetShellSecurityDescriptor(apUserPerm, ARRAYSIZE(apUserPerm));
if (psd)
{
// setup the psa
psa->nLength = sizeof(*psa);
psa->lpSecurityDescriptor = psd;
psa->bInheritHandle = FALSE;
if (InterlockedCompareExchangePointer((void**)&g_psa, psa, NULL))
{
// someone else beat us to initing s_psa, free ours
LocalFree(psd);
LocalFree(psa);
}
}
else
{
LocalFree(psa);
}
}
}
return g_psa;
}
//
// called at process detach to release our global all-access SA
//
STDAPI_(void) FreeAllAccessSA()
{
SECURITY_ATTRIBUTES* psa = (SECURITY_ATTRIBUTES*)InterlockedExchangePointer((void**)&g_psa, NULL);
if (psa)
{
LocalFree(psa->lpSecurityDescriptor);
LocalFree(psa);
}
}
STDAPI_(HANDLE) SHGlobalCounterCreateNamedW(LPCWSTR szName, LONG lInitialValue)
{
HANDLE hSem = NULL;
WCHAR szCounterName[MAX_PATH]; // "shell.szName"
if (SUCCEEDED(StringCchCopyW(szCounterName, ARRAYSIZE(szCounterName), L"shell.")) &&
SUCCEEDED(StringCchCatW(szCounterName, ARRAYSIZE(szCounterName), szName)))
{
SECURITY_ATTRIBUTES* psa = SHGetAllAccessSA();
if (psa)
{
hSem = CreateSemaphoreW(psa, lInitialValue, 0x7FFFFFFF, szCounterName);
}
if (!hSem)
{
hSem = OpenSemaphoreW(SEMAPHORE_MODIFY_STATE | SYNCHRONIZE, FALSE, szCounterName);
}
}
return hSem;
}
STDAPI_(HANDLE) SHGlobalCounterCreateNamedA(LPCSTR szName, LONG lInitialValue)
{
HANDLE hSem = NULL;
WCHAR szCounterName[MAX_PATH];
if (SHAnsiToUnicode(szName, szCounterName, ARRAYSIZE(szCounterName)))
{
hSem = SHGlobalCounterCreateNamedW(szCounterName, lInitialValue);
}
return hSem;
}
//
// This lets the user pass a GUID. The name of the global counter will be "shell.{guid}",
// and its initial value will be zero.
//
STDAPI_(HANDLE) SHGlobalCounterCreate(REFGUID rguid)
{
HANDLE hSem = NULL;
WCHAR szGUIDString[GUIDSTR_MAX];
if (SHStringFromGUIDW(rguid, szGUIDString, ARRAYSIZE(szGUIDString)))
{
hSem = SHGlobalCounterCreateNamedW(szGUIDString, 0);
}
return hSem;
}
// returns current value of the global counter
// Note: The result is not thread-safe in the sense that if two threads
// look at the value at the same time, one of them might read the wrong
// value.
STDAPI_(long) SHGlobalCounterGetValue(HANDLE hCounter)
{
long lPreviousValue = 0;
DWORD dwRet;
ReleaseSemaphore(hCounter, 1, &lPreviousValue); // poll and bump the count
dwRet = WaitForSingleObject(hCounter, GLOBAL_COUNTER_WAIT_TIMEOUT); // reduce the count
// this shouldnt happen since we just bumped up the count above
ASSERT(dwRet != WAIT_TIMEOUT);
return lPreviousValue;
}
// returns new value
// Note: this _is_ thread safe
STDAPI_(long) SHGlobalCounterIncrement(HANDLE hCounter)
{
long lPreviousValue = 0;
ReleaseSemaphore(hCounter, 1, &lPreviousValue); // bump the count
return lPreviousValue + 1;
}
// returns new value
// Note: The result is not thread-safe in the sense that if two threads
// try to decrement the value at the same time, whacky stuff can happen.
STDAPI_(long) SHGlobalCounterDecrement(HANDLE hCounter)
{
DWORD dwRet;
long lCurrentValue = SHGlobalCounterGetValue(hCounter);
#ifdef DEBUG
// extra sanity check
if (lCurrentValue == 0)
{
ASSERTMSG(FALSE, "SHGlobalCounterDecrement called on a counter that was already equal to 0 !!");
return 0;
}
#endif
dwRet = WaitForSingleObject(hCounter, GLOBAL_COUNTER_WAIT_TIMEOUT); // reduce the count
ASSERT(dwRet != WAIT_TIMEOUT);
return lCurrentValue - 1;
}