Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

329 lines
9.2 KiB

  1. //
  2. // Global counters
  3. //
  4. //
  5. #include "priv.h"
  6. //
  7. // #defines
  8. //
  9. #ifdef DEBUG
  10. #define GLOBAL_COUNTER_WAIT_TIMEOUT 30*1000 // on debug we set this to 30 seconds
  11. #else
  12. #define GLOBAL_COUNTER_WAIT_TIMEOUT 0 // on retail its zero so we test the objects state and return immedaeately
  13. #endif
  14. //
  15. // Globals
  16. //
  17. SECURITY_ATTRIBUTES g_sa;
  18. SECURITY_DESCRIPTOR* g_psd = NULL;
  19. //
  20. // this function allocates the g_psd and fills in the g_sa
  21. //
  22. STDAPI_(BOOL) AllocGlobalSecurityAttributes()
  23. {
  24. BOOL bRet;
  25. SHELL_USER_PERMISSION supEveryone;
  26. SHELL_USER_PERMISSION supSystem;
  27. SHELL_USER_PERMISSION supAdministrators;
  28. PSHELL_USER_PERMISSION apUserPerm[3] = {&supEveryone, &supAdministrators, &supSystem};
  29. //
  30. // There are three kinds of null-type DACLs.
  31. //
  32. // 1. No DACL. This means that we inherit the ambient DACL
  33. // from our thread.
  34. // 2. Null DACL. This means "full access to everyone".
  35. // 3. Empty DACL. This means "deny all access to everyone".
  36. //
  37. // NONE of these are correct for our needs. We used to use Null DACL's (2),
  38. // but the issue with these is that someone can change the ACL on the object
  39. // locking us out.
  40. //
  41. // So now we create a specific DACL with 3 ACE's in it:
  42. //
  43. // ACE #1: Everyone - GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | SYNCHRONIZE
  44. // ACE #2: SYSTEM - GENERIC_ALL (full control)
  45. // ACE #3: Administrators - GENERIC_ALL (full control)
  46. //
  47. // we want the everyone to have read, write, exec and sync only
  48. supEveryone.susID = susEveryone;
  49. supEveryone.dwAccessType = ACCESS_ALLOWED_ACE_TYPE;
  50. supEveryone.dwAccessMask = (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | SYNCHRONIZE);
  51. supEveryone.fInherit = FALSE;
  52. supEveryone.dwInheritMask = 0;
  53. supEveryone.dwInheritAccessMask = 0;
  54. // we want the SYSTEM to have full control
  55. supSystem.susID = susSystem;
  56. supSystem.dwAccessType = ACCESS_ALLOWED_ACE_TYPE;
  57. supSystem.dwAccessMask = GENERIC_ALL;
  58. supSystem.fInherit = FALSE;
  59. supSystem.dwInheritMask = 0;
  60. supSystem.dwInheritAccessMask = 0;
  61. // we want the Administrators to have full control
  62. supAdministrators.susID = susAdministrators;
  63. supAdministrators.dwAccessType = ACCESS_ALLOWED_ACE_TYPE;
  64. supAdministrators.dwAccessMask = GENERIC_ALL;
  65. supAdministrators.fInherit = FALSE;
  66. supAdministrators.dwInheritMask = 0;
  67. supAdministrators.dwInheritAccessMask = 0;
  68. // allocate the global SECURITY_DESCRIPTOR
  69. g_psd = GetShellSecurityDescriptor(apUserPerm, ARRAYSIZE(apUserPerm));
  70. if (!g_psd)
  71. {
  72. TraceMsg(TF_WARNING, "InitGlobalSecurityAttributes: failed to create g_psd!");
  73. bRet = FALSE;
  74. }
  75. else
  76. {
  77. // we successfully alloced the g_psd, so set it into the g_sa
  78. // NOTE: we will free g_psd at process detach
  79. g_sa.nLength = sizeof(g_sa);
  80. g_sa.lpSecurityDescriptor = g_psd;
  81. g_sa.bInheritHandle = FALSE;
  82. bRet = TRUE;
  83. }
  84. return bRet;
  85. }
  86. //
  87. // called at process detach to release our global g_psd
  88. //
  89. STDAPI_(void) FreeGlobalSecurityAttributes()
  90. {
  91. if (g_psd)
  92. {
  93. ASSERT(g_sa.lpSecurityDescriptor == g_psd); // sanity check
  94. LocalFree(g_psd);
  95. }
  96. }
  97. //
  98. // Helper function that clones an ACL
  99. //
  100. PACL CloneACL(const ACL* pAclSource)
  101. {
  102. PACL pAclClone = (PACL)LocalAlloc(LPTR, pAclSource->AclSize);
  103. if (pAclClone)
  104. {
  105. CopyMemory((void*)pAclClone, (const void*)pAclSource, pAclSource->AclSize);
  106. }
  107. return pAclClone;
  108. }
  109. //
  110. // This function fills in a SECURITY_ATTRIBUTES struct such that it will be full access to everyone.
  111. // This is needed when multiple processes need access to the same named kernel objects.
  112. //
  113. // You should always pass (NULL, NULL, NULL) to this function for perf! (will return global security object)
  114. //
  115. // NOTE: for older callers who pass a non-null ppacl, must LocalFree it
  116. //
  117. STDAPI_(SECURITY_ATTRIBUTES*) CreateAllAccessSecurityAttributes(SECURITY_ATTRIBUTES* psa, SECURITY_DESCRIPTOR* psd, PACL* ppacl)
  118. {
  119. static BOOL s_bCreatedGlobalSA = FALSE;
  120. // Win9x doesn't use dacls
  121. if (!g_bRunningOnNT)
  122. {
  123. return NULL;
  124. }
  125. #ifdef DEBUG
  126. // only rip on whistler or greater since the old ie55/ie6 code was lame
  127. if (IsOS(OS_WHISTLERORGREATER))
  128. {
  129. // NOTE: (reinerf) - no shell caller should ever pass anything but NULL's to this api!
  130. RIPMSG(!(psa || psd || ppacl), "CreateAllAccessSecurityAttributes: ALL callers should pass (NULL, NULL, NULL) for params!");
  131. }
  132. #endif
  133. // always set this to null, we return a refrence to g_psd which
  134. // contains the ACL and we never want the user to free anything
  135. if (ppacl)
  136. {
  137. *ppacl = NULL;
  138. }
  139. if (!s_bCreatedGlobalSA)
  140. {
  141. // we have not inited g_sa, so do so now.
  142. ENTERCRITICAL;
  143. // check again within the critsec
  144. if (!s_bCreatedGlobalSA)
  145. {
  146. if (AllocGlobalSecurityAttributes())
  147. {
  148. s_bCreatedGlobalSA = TRUE;
  149. }
  150. }
  151. LEAVECRITICAL;
  152. if (!s_bCreatedGlobalSA)
  153. {
  154. // failed to create the global! doh!
  155. return NULL;
  156. }
  157. // if we got this far, this had better be created
  158. ASSERT(g_psd);
  159. }
  160. // check to see if an older caller passed in non-null params
  161. if (psa)
  162. {
  163. PACL pAcl = CloneACL(g_psd->Dacl);
  164. if (pAcl &&
  165. InitializeSecurityDescriptor(psd, SECURITY_DESCRIPTOR_REVISION) &&
  166. SetSecurityDescriptorDacl(psd, TRUE, pAcl, FALSE))
  167. {
  168. psa->nLength = sizeof(*psa);
  169. psa->lpSecurityDescriptor = psd;
  170. psa->bInheritHandle = FALSE;
  171. if (ppacl)
  172. {
  173. *ppacl = pAcl;
  174. }
  175. // NOTE: (reinerf) - we used to call LocalFree(pAcl) if the caller passed null for ppacl.
  176. // This is a bad thing to do since SetSecurityDescriptor sets a refrence to the ACL instead
  177. // of copying it. Better to leak it... :-(
  178. }
  179. else
  180. {
  181. return NULL;
  182. }
  183. return psa;
  184. }
  185. else
  186. {
  187. // for newer callers (who pass all NULL's), we just return a refrence to our global
  188. return &g_sa;
  189. }
  190. }
  191. //
  192. // This lets the user pass an ANSI String as the name of the global counter, as well as an inital value
  193. //
  194. STDAPI_(HANDLE) SHGlobalCounterCreateNamedA(LPCSTR szName, LONG lInitialValue)
  195. {
  196. HANDLE hSem;
  197. //
  198. // Explicitly ANSI so it runs on Win95.
  199. //
  200. char szCounterName[MAX_PATH]; // "shell.szName"
  201. LPSECURITY_ATTRIBUTES psa;
  202. lstrcpyA(szCounterName, "shell.");
  203. StrCatBuffA(szCounterName, szName, ARRAYSIZE(szCounterName));
  204. psa = CreateAllAccessSecurityAttributes(NULL, NULL, NULL);
  205. hSem = CreateSemaphoreA(psa, lInitialValue, 0x7FFFFFFF, szCounterName);
  206. if (!hSem)
  207. hSem = OpenSemaphoreA(SEMAPHORE_MODIFY_STATE | SYNCHRONIZE, FALSE, szCounterName);
  208. return hSem;
  209. }
  210. //
  211. // This lets the user pass an UNICODE String as the name of the global counter, as well as an inital value
  212. //
  213. STDAPI_(HANDLE) SHGlobalCounterCreateNamedW(LPCWSTR szName, LONG lInitialValue)
  214. {
  215. CHAR szCounterName[MAX_PATH];
  216. SHUnicodeToAnsi(szName, szCounterName, ARRAYSIZE(szCounterName));
  217. return SHGlobalCounterCreateNamedA(szCounterName, lInitialValue);
  218. }
  219. //
  220. // This lets the user pass a GUID. The name of the global counter will be "shell.{guid}",
  221. // and its initial value will be zero.
  222. //
  223. STDAPI_(HANDLE) SHGlobalCounterCreate(REFGUID rguid)
  224. {
  225. CHAR szGUIDString[GUIDSTR_MAX];
  226. SHStringFromGUIDA(rguid, szGUIDString, ARRAYSIZE(szGUIDString));
  227. return SHGlobalCounterCreateNamedA(szGUIDString, 0);
  228. }
  229. // returns current value of the global counter
  230. // Note: The result is not thread-safe in the sense that if two threads
  231. // look at the value at the same time, one of them might read the wrong
  232. // value.
  233. STDAPI_(long) SHGlobalCounterGetValue(HANDLE hCounter)
  234. {
  235. long lPreviousValue = 0;
  236. DWORD dwRet;
  237. ReleaseSemaphore(hCounter, 1, &lPreviousValue); // poll and bump the count
  238. dwRet = WaitForSingleObject(hCounter, GLOBAL_COUNTER_WAIT_TIMEOUT); // reduce the count
  239. // this shouldnt happen since we just bumped up the count above
  240. ASSERT(dwRet != WAIT_TIMEOUT);
  241. return lPreviousValue;
  242. }
  243. // returns new value
  244. // Note: this _is_ thread safe
  245. STDAPI_(long) SHGlobalCounterIncrement(HANDLE hCounter)
  246. {
  247. long lPreviousValue = 0;
  248. ReleaseSemaphore(hCounter, 1, &lPreviousValue); // bump the count
  249. return lPreviousValue + 1;
  250. }
  251. // returns new value
  252. // Note: The result is not thread-safe in the sense that if two threads
  253. // try to decrement the value at the same time, whacky stuff can happen.
  254. STDAPI_(long) SHGlobalCounterDecrement(HANDLE hCounter)
  255. {
  256. DWORD dwRet;
  257. long lCurrentValue = SHGlobalCounterGetValue(hCounter);
  258. #ifdef DEBUG
  259. // extra sanity check
  260. if (lCurrentValue == 0)
  261. {
  262. ASSERTMSG(FALSE, "SHGlobalCounterDecrement called on a counter that was already equal to 0 !!");
  263. return 0;
  264. }
  265. #endif
  266. dwRet = WaitForSingleObject(hCounter, GLOBAL_COUNTER_WAIT_TIMEOUT); // reduce the count
  267. ASSERT(dwRet != WAIT_TIMEOUT);
  268. return lCurrentValue - 1;
  269. }