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.

358 lines
8.4 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. Regnccls.c
  5. Abstract:
  6. This file contains functions needed for handling
  7. change notifications in the classes portion of the registry
  8. Author:
  9. Adam P. Edwards (adamed) 14-Nov-1997
  10. Key Functions:
  11. BaseRegNotifyClassKey
  12. Notes:
  13. --*/
  14. #ifdef LOCAL
  15. #include <rpc.h>
  16. #include <string.h>
  17. #include <wchar.h>
  18. #include "regrpc.h"
  19. #include "localreg.h"
  20. #include "regclass.h"
  21. #include "regnccls.h"
  22. #include <malloc.h>
  23. NTSTATUS BaseRegNotifyClassKey(
  24. IN HKEY hKey,
  25. IN HANDLE hEvent,
  26. IN PIO_STATUS_BLOCK pLocalIoStatusBlock,
  27. IN DWORD dwNotifyFilter,
  28. IN BOOLEAN fWatchSubtree,
  29. IN BOOLEAN fAsynchronous)
  30. {
  31. NTSTATUS Status;
  32. HKEY hkUser;
  33. HKEY hkMachine;
  34. SKeySemantics KeyInfo;
  35. UNICODE_STRING EmptyString = {0, 0, 0};
  36. BYTE rgNameBuf[REG_MAX_CLASSKEY_LEN + REG_CHAR_SIZE + sizeof(OBJECT_NAME_INFORMATION)];
  37. OBJECT_ATTRIBUTES Obja;
  38. BOOL fAllocatedPath;
  39. //
  40. // Set buffer to store info about this key
  41. //
  42. KeyInfo._pFullPath = (PKEY_NAME_INFORMATION) rgNameBuf;
  43. KeyInfo._cbFullPath = sizeof(rgNameBuf);
  44. KeyInfo._fAllocedNameBuf = FALSE;
  45. //
  46. // get information about this key
  47. //
  48. Status = BaseRegGetKeySemantics(hKey, &EmptyString, &KeyInfo);
  49. if (!NT_SUCCESS(Status)) {
  50. return Status;
  51. }
  52. //
  53. // Initialize conditionally freed resources
  54. //
  55. hkUser = NULL;
  56. hkMachine = NULL;
  57. fAllocatedPath = FALSE;
  58. Obja.ObjectName = NULL;
  59. //
  60. // Now get handles for both user and machine versions of the key
  61. //
  62. Status = BaseRegGetUserAndMachineClass(
  63. &KeyInfo,
  64. hKey,
  65. KEY_NOTIFY,
  66. &hkUser,
  67. &hkMachine);
  68. if (!NT_SUCCESS(Status)) {
  69. goto cleanup;
  70. }
  71. if (fWatchSubtree || (hkUser && hkMachine)) {
  72. //
  73. // This will return the closest ancestor to the
  74. // nonexistent translated key -- note that it allocates memory
  75. // to the Obja.ObjectName member, so we need to free that on
  76. // success
  77. //
  78. Status = BaseRegGetBestAncestor(
  79. &KeyInfo,
  80. hkUser,
  81. hkMachine,
  82. &Obja);
  83. fAllocatedPath = Obja.ObjectName != NULL;
  84. if (!NT_SUCCESS(Status)) {
  85. goto cleanup;
  86. }
  87. //
  88. // Ask for the notify on both user and machine keys (or
  89. // the closest approximation). Note that we pass a full path --
  90. // if we used an relative path with an object handle instead, we
  91. // would never have an opportunity to close the object, so we would
  92. // leak objects
  93. //
  94. //
  95. Status = NtNotifyChangeMultipleKeys(
  96. hKey,
  97. 1,
  98. &Obja,
  99. hEvent,
  100. NULL,
  101. NULL,
  102. pLocalIoStatusBlock,
  103. dwNotifyFilter,
  104. fWatchSubtree,
  105. NULL,
  106. 0,
  107. fAsynchronous
  108. );
  109. } else {
  110. Status = NtNotifyChangeKey(
  111. hkUser ? hkUser : hkMachine,
  112. hEvent,
  113. NULL,
  114. NULL,
  115. pLocalIoStatusBlock,
  116. dwNotifyFilter,
  117. fWatchSubtree,
  118. NULL,
  119. 0,
  120. fAsynchronous
  121. );
  122. }
  123. cleanup:
  124. //if (!NT_SUCCESS(Status)) {
  125. if (hkUser && (hkUser != hKey)) {
  126. NtClose(hkUser);
  127. }
  128. if (hkMachine && (hkMachine != hKey)) {
  129. NtClose(hkMachine);
  130. }
  131. //}
  132. if (fAllocatedPath) {
  133. RegClassHeapFree(Obja.ObjectName);
  134. }
  135. return Status;
  136. }
  137. NTSTATUS BaseRegGetBestAncestor(
  138. IN SKeySemantics* pKeySemantics,
  139. IN HKEY hkUser,
  140. IN HKEY hkMachine,
  141. IN POBJECT_ATTRIBUTES pObja)
  142. /*++
  143. Routine Description:
  144. Finds a full object path for the closest ancestor for a key
  145. described by a key semantics structure
  146. Arguments:
  147. pKeySemantics - contains information about a registry key
  148. hkUser - handle to a user class version of the key above
  149. hkMachine - handle to a machine class version of the key above
  150. pObja - Object Attributes structure to initialize with a full
  151. object path for the closest ancestor -- not that memory
  152. is allocated for the ObjectName member of the structure
  153. which must be freed by the caller -- caller should
  154. check this member to see if it's non-NULL, regardless
  155. of success code returned by function
  156. Return Value:
  157. Returns ERROR_SUCCESS (0) for success; error-code for failure.
  158. --*/
  159. {
  160. USHORT PrefixLen;
  161. NTSTATUS Status;
  162. PUNICODE_STRING pKeyPath;
  163. USHORT uMaxLen;
  164. //
  165. // Allocate memory for the Obja's ObjectName member
  166. //
  167. uMaxLen = (USHORT) pKeySemantics->_pFullPath->NameLength + REG_CLASSES_SUBTREE_PADDING;
  168. pKeyPath = RegClassHeapAlloc(uMaxLen + sizeof(*pKeyPath));
  169. if (!(pKeyPath)) {
  170. return STATUS_NO_MEMORY;
  171. }
  172. //
  173. // Now initialize the structure
  174. //
  175. pKeyPath->MaximumLength = uMaxLen;
  176. pKeyPath->Buffer = (WCHAR*) (((PBYTE) pKeyPath) + sizeof(*pKeyPath));
  177. //
  178. // Now form a version of this key path in the opposite tree
  179. //
  180. if (pKeySemantics->_fUser) {
  181. Status = BaseRegTranslateToMachineClassKey(
  182. pKeySemantics,
  183. pKeyPath,
  184. &PrefixLen);
  185. } else {
  186. Status = BaseRegTranslateToUserClassKey(
  187. pKeySemantics,
  188. pKeyPath,
  189. &PrefixLen);
  190. }
  191. //
  192. // Make sure the caller has a reference to allocated memory
  193. //
  194. pObja->ObjectName = pKeyPath;
  195. if (!NT_SUCCESS(Status)) {
  196. goto cleanup;
  197. }
  198. //
  199. // Set up the object attributes with this translated key so
  200. // we can use the structure to notify keys
  201. //
  202. InitializeObjectAttributes(
  203. pObja,
  204. pKeyPath,
  205. OBJ_CASE_INSENSITIVE,
  206. NULL, // using absolute path, no hkey
  207. NULL);
  208. //
  209. // If we were supplied both keys, then they both exist,
  210. // so we can simply use the translated path above
  211. //
  212. if (hkUser && hkMachine) {
  213. goto cleanup;
  214. }
  215. //
  216. // At this point, we know the translated path doesn't exist,
  217. // since we only have a handle for one of the paths. Therefore
  218. // we will attempt to find an approximation. Note that the
  219. // manipulation of KeyPath below affects the Obja passed in since
  220. // the Obja struct references KeyPath
  221. //
  222. do
  223. {
  224. WCHAR* pBufferEnd;
  225. HKEY hkExistingKey;
  226. //
  227. // Find the last pathsep in the current key path
  228. //
  229. pBufferEnd = wcsrchr(pKeyPath->Buffer, L'\\');
  230. //
  231. // We should never get NULL here, because all keys
  232. // have the ancestory \Registry\User or \Registry\Machine,
  233. // each which have two pathseps to spare -- the loop
  234. // terminates once that path is shorter than those prefixes,
  235. // so we should never encounter this situation
  236. //
  237. ASSERT(pBufferEnd);
  238. //
  239. // Now truncate the string
  240. //
  241. *pBufferEnd = L'\0';
  242. //
  243. // Adjust the unicode string structure to conform
  244. // to the truncated string
  245. //
  246. RtlInitUnicodeString(pKeyPath, pKeyPath->Buffer);
  247. //
  248. // Now attempt to open with this truncated path
  249. //
  250. Status = NtOpenKey(
  251. &hkExistingKey,
  252. KEY_NOTIFY,
  253. pObja);
  254. //
  255. // If we do open it, we will close it and not pass this object
  256. // since we want our obja to use a full path and not a relative
  257. // path off a kernel object
  258. //
  259. if (NT_SUCCESS(Status)) {
  260. NtClose(hkExistingKey);
  261. break;
  262. }
  263. //
  264. // If we get any error besides a key not found error, then our reason
  265. // for failing the open is not because the key did not exist, but because
  266. // of some other error, most likely access denied.
  267. //
  268. if (STATUS_OBJECT_NAME_NOT_FOUND != Status) {
  269. break;
  270. }
  271. } while (pKeyPath->Length > PrefixLen);
  272. cleanup:
  273. return Status;
  274. }
  275. #endif // defined ( LOCAL )