Leaked source code of windows server 2003

2326 lines
65 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. watch.c
  5. Abstract:
  6. This module contains routines for watching changes to the
  7. current user's profile directory and the HKEY_CURRENT_USER key.
  8. Author:
  9. Chuck Lenzmeier (chuckl)
  10. Revision History:
  11. --*/
  12. #include "setupp.h"
  13. #pragma hdrstop
  14. //
  15. // Debugging aids.
  16. //
  17. #if WATCH_DEBUG
  18. DWORD WatchDebugLevel = 0;
  19. #define dprintf(_lvl_,_x_) if ((_lvl_) <= WatchDebugLevel) DbgPrint _x_
  20. #define DEBUG(_x_) _x_
  21. PWCH StartDirectoryName = TEXT("");
  22. PWCH StartKeyName = TEXT("");
  23. PWCH StopDirectoryName = TEXT("");
  24. PWCH StopKeyName = TEXT("");
  25. static PSZ Types[] = {"ACK!", "DIR ", "FILE ", "KEY ", "VALUE"};
  26. static PSZ States[] = {"NONE", "CHANGED", "DELETED", "NEW", "MATCHED"};
  27. #undef MyMalloc
  28. #undef MyFree
  29. #define MyMalloc MyMallocEx
  30. #define MyFree MyFreeEx
  31. PVOID
  32. MyMallocEx (
  33. IN DWORD Size
  34. );
  35. VOID
  36. MyFreeEx (
  37. IN PVOID p
  38. );
  39. VOID
  40. DumpMallocStats (
  41. PSZ Event
  42. );
  43. #else
  44. #define dprintf(_lvl_,_x_)
  45. #define DEBUG(_x_)
  46. #define DumpMallocStats(_event)
  47. #endif
  48. //
  49. // Additions to the change types in WatchEnum.
  50. //
  51. #define WATCH_NONE 0
  52. #define WATCH_MATCHED 4
  53. //
  54. // Common header for container entries (directories and keys).
  55. //
  56. typedef struct _CONTAINER_ENTRY {
  57. LIST_ENTRY SiblingListEntry;
  58. LIST_ENTRY ContainerList;
  59. LIST_ENTRY ObjectList;
  60. struct _CONTAINER_ENTRY *Parent;
  61. DWORD State;
  62. #if WATCH_DEBUG
  63. DWORD IsDirectory;
  64. #endif
  65. } CONTAINER_ENTRY, *PCONTAINER_ENTRY;
  66. //
  67. // Common header for object entries (files and values).
  68. //
  69. typedef struct _OBJECT_ENTRY {
  70. LIST_ENTRY SiblingListEntry;
  71. DWORD State;
  72. } OBJECT_ENTRY, *POBJECT_ENTRY;
  73. //
  74. // Macros for manipulating containers and objects.
  75. //
  76. #if WATCH_DEBUG
  77. #define SetIsDirectory(_container,_isdir) (_container)->IsDirectory = (_isdir)
  78. #else
  79. #define SetIsDirectory(_container,_isdir)
  80. #endif
  81. #define InitializeContainer(_container,_state,_parent,_isdir) { \
  82. InitializeListHead(&(_container)->ContainerList); \
  83. InitializeListHead(&(_container)->ObjectList); \
  84. (_container)->Parent = (PCONTAINER_ENTRY)(_parent); \
  85. (_container)->State = (_state); \
  86. SetIsDirectory((_container),(_isdir)); \
  87. }
  88. #define InitializeObject(_object,_state) (_object)->State = (_state);
  89. #define InsertContainer(_container,_subcontainer) \
  90. InsertTailList(&(_container)->ContainerList,&(_subcontainer)->SiblingListEntry)
  91. #define InsertObject(_container,_object) \
  92. InsertTailList(&(_container)->ObjectList,&(_object)->SiblingListEntry)
  93. #define RemoveObject(_object) RemoveEntryList(&(_object)->SiblingListEntry)
  94. #define RemoveContainer(_container) RemoveEntryList(&(_container)->SiblingListEntry)
  95. #define GetFirstObject(_container) \
  96. ((_container)->ObjectList.Flink != &(_container)->ObjectList ? \
  97. CONTAINING_RECORD( (_container)->ObjectList.Flink, \
  98. OBJECT_ENTRY, \
  99. SiblingListEntry ) : NULL)
  100. #define GetNextObject(_container,_object) \
  101. ((_object)->SiblingListEntry.Flink != &(_container)->ObjectList ? \
  102. CONTAINING_RECORD( (_object)->SiblingListEntry.Flink, \
  103. OBJECT_ENTRY, \
  104. SiblingListEntry ) : NULL)
  105. #define GetFirstContainer(_container) \
  106. ((_container)->ContainerList.Flink != &(_container)->ContainerList ? \
  107. CONTAINING_RECORD( (_container)->ContainerList.Flink, \
  108. CONTAINER_ENTRY, \
  109. SiblingListEntry ) : NULL)
  110. #define GetNextContainer(_container) \
  111. ((_container)->SiblingListEntry.Flink != &(_container)->Parent->ContainerList ? \
  112. CONTAINING_RECORD( (_container)->SiblingListEntry.Flink, \
  113. CONTAINER_ENTRY, \
  114. SiblingListEntry ) : NULL)
  115. #define GetParent(_container) (_container)->Parent
  116. #define GetEntryState(_entry) (_entry)->State
  117. #define SetEntryState(_entry,_state) ((_entry)->State = (_state))
  118. #if WATCH_DEBUG
  119. #define CONTAINER_NAME(_container) \
  120. (_container)->IsDirectory ? ((PDIRECTORY_ENTRY)(_container))->Name : \
  121. ((PKEY_ENTRY)(_container))->Name
  122. #define OBJECT_NAME(_container,_object) \
  123. (_container)->IsDirectory ? ((PFILE_ENTRY)(_object))->Name : \
  124. ((PVALUE_ENTRY)(_object))->Name
  125. #endif
  126. //
  127. // Structures for entries in the watch tree.
  128. //
  129. typedef struct _DIRECTORY_ENTRY {
  130. CONTAINER_ENTRY ;
  131. WCHAR Name[1];
  132. } DIRECTORY_ENTRY, *PDIRECTORY_ENTRY;
  133. typedef struct _FILE_ENTRY {
  134. OBJECT_ENTRY ;
  135. FILETIME LastWriteTime;
  136. WCHAR Name[1];
  137. } FILE_ENTRY, *PFILE_ENTRY;
  138. typedef struct _KEY_ENTRY {
  139. CONTAINER_ENTRY ;
  140. HKEY Handle;
  141. WCHAR Name[1];
  142. } KEY_ENTRY, *PKEY_ENTRY;
  143. typedef struct _VALUE_ENTRY {
  144. OBJECT_ENTRY ;
  145. DWORD Type;
  146. DWORD NameLength;
  147. DWORD ValueDataLength;
  148. WCHAR Name[1];
  149. } VALUE_ENTRY, *PVALUE_ENTRY;
  150. //
  151. // The root of the watch tree is allocated as a ROOT_ENTRY followed by
  152. // a DIRECTORY_ENTRY and a KEY_ENTRY.
  153. //
  154. typedef struct _ROOT_ENTRY {
  155. PDIRECTORY_ENTRY RootDirectoryEntry;
  156. PKEY_ENTRY RootKeyEntry;
  157. } ROOT_ENTRY, *PROOT_ENTRY;
  158. //
  159. // Macro for comparing file times.
  160. //
  161. #define TIMES_EQUAL(_a,_b) \
  162. (((_a).dwLowDateTime == (_b).dwLowDateTime) && \
  163. ((_a).dwHighDateTime == (_b).dwHighDateTime))
  164. typedef struct _KEY_ENUM_CONTEXT {
  165. PKEY_ENTRY ParentKey;
  166. PWCH CurrentPath;
  167. } KEY_ENUM_CONTEXT, *PKEY_ENUM_CONTEXT;
  168. //
  169. // Forward declaration of local subroutines.
  170. //
  171. VOID
  172. WatchFreeChildren (
  173. IN PCONTAINER_ENTRY Container
  174. );
  175. DWORD
  176. WatchDirStart (
  177. IN PROOT_ENTRY Root
  178. );
  179. DWORD
  180. WatchDirStop (
  181. IN PROOT_ENTRY Root
  182. );
  183. DWORD
  184. WatchKeyStart (
  185. IN PROOT_ENTRY Root
  186. );
  187. DWORD
  188. WatchKeyStop (
  189. IN PROOT_ENTRY Root
  190. );
  191. DWORD
  192. AddValueAtStart (
  193. IN PVOID Context,
  194. IN DWORD ValueNameLength,
  195. IN PWCH ValueName,
  196. IN DWORD ValueType,
  197. IN PVOID ValueData,
  198. IN DWORD ValueDataLength
  199. );
  200. DWORD
  201. AddKeyAtStart (
  202. IN PVOID Context,
  203. IN DWORD KeyNameLength,
  204. IN PWCH KeyName
  205. );
  206. DWORD
  207. CheckValueAtStop (
  208. IN PVOID Context,
  209. IN DWORD ValueNameLength,
  210. IN PWCH ValueName,
  211. IN DWORD ValueType,
  212. IN PVOID ValueData,
  213. IN DWORD ValueDataLength
  214. );
  215. DWORD
  216. CheckKeyAtStop (
  217. IN PVOID Context,
  218. IN DWORD KeyNameLength,
  219. IN PWCH KeyName
  220. );
  221. DWORD
  222. WatchStart (
  223. OUT PVOID *WatchHandle
  224. )
  225. /*++
  226. Routine Description:
  227. Starts watching. Captures the initial state of the Start Menu directory
  228. and HKEY_CURRENT_USER.
  229. Arguments:
  230. WatchHandle - returns a handle for calls to the other Watch routines.
  231. Return Value:
  232. DWORD - Win32 status of the operation.
  233. --*/
  234. {
  235. PROOT_ENTRY root;
  236. PDIRECTORY_ENTRY rootDirectory;
  237. PKEY_ENTRY rootKey;
  238. DWORD dirSize;
  239. DWORD keySize;
  240. DWORD size;
  241. DWORD error;
  242. //
  243. // Calculate the size of the root entry, which includes entries for the
  244. // root directory and the root key. The root directory and the root key
  245. // do not have names, so we don't have to allocate additional space.
  246. //
  247. // Allocate and initialize the root entry.
  248. //
  249. #if !WATCH_DEBUG
  250. dirSize = (sizeof(DIRECTORY_ENTRY) + 7) & ~7;
  251. keySize = (sizeof(KEY_ENTRY) + 7) & ~7;
  252. #else
  253. dirSize = (sizeof(DIRECTORY_ENTRY) + (wcslen(StartDirectoryName)*sizeof(WCHAR)) + 7) & ~7;
  254. keySize = (sizeof(KEY_ENTRY) + (wcslen(StartKeyName)*sizeof(WCHAR)) + 7) & ~7;
  255. #endif
  256. root = MyMalloc( ((sizeof(ROOT_ENTRY) + 7) & ~7) + dirSize + keySize );
  257. if ( root == NULL ) {
  258. return ERROR_NOT_ENOUGH_MEMORY;
  259. }
  260. rootDirectory = (PDIRECTORY_ENTRY)(root + 1);
  261. rootKey = (PKEY_ENTRY)((PCHAR)rootDirectory + dirSize);
  262. root->RootDirectoryEntry = rootDirectory;
  263. root->RootKeyEntry = rootKey;
  264. //
  265. // Initialize the root directory and the root key.
  266. //
  267. InitializeContainer( rootDirectory, 0, NULL, TRUE );
  268. InitializeContainer( rootKey, 0, NULL, FALSE );
  269. rootKey->Handle = NULL;
  270. #if !WATCH_DEBUG
  271. rootDirectory->Name[0] = 0;
  272. rootKey->Name[0] = 0;
  273. #else
  274. wcscpy( rootDirectory->Name, StartDirectoryName );
  275. wcscpy( rootKey->Name, StartKeyName );
  276. #endif
  277. //
  278. // Start watching the Start Menu directory and the current user key.
  279. //
  280. error = WatchDirStart( root );
  281. DumpMallocStats( "After WatchDirStart" );
  282. if ( error == NO_ERROR ) {
  283. error = WatchKeyStart( root );
  284. DumpMallocStats( "After WatchKeyStart" );
  285. }
  286. //
  287. // If an error occurred, free the root entry. Otherwise, return the
  288. // address of the root entry as the watch handle.
  289. //
  290. if ( error != NO_ERROR ) {
  291. WatchFree( root );
  292. DumpMallocStats( "After WatchFree" );
  293. } else {
  294. *WatchHandle = root;
  295. }
  296. return error;
  297. } // WatchStart
  298. DWORD
  299. WatchStop (
  300. IN PVOID WatchHandle
  301. )
  302. /*++
  303. Routine Description:
  304. Stops watching. Compares the current state of the directory and key
  305. to the initial state.
  306. Arguments:
  307. WatchHandle - supplies the handle returned by WatchStart.
  308. Return Value:
  309. DWORD - Win32 status of the operation.
  310. --*/
  311. {
  312. PROOT_ENTRY root;
  313. DWORD error;
  314. root = WatchHandle;
  315. //
  316. // Stop watching the Start Menu directory and the current user key.
  317. // Capture the differences from the initial state.
  318. //
  319. #if WATCH_DEBUG
  320. if ( (wcslen(StopDirectoryName) > wcslen(root->RootDirectoryEntry->Name)) ||
  321. (wcslen(StopKeyName) > wcslen(root->RootKeyEntry->Name)) ) {
  322. return ERROR_INVALID_PARAMETER;
  323. }
  324. wcscpy( root->RootDirectoryEntry->Name, StopDirectoryName );
  325. wcscpy( root->RootKeyEntry->Name, StopKeyName );
  326. #endif
  327. error = WatchDirStop( root );
  328. DumpMallocStats( "After WatchDirStop" );
  329. if ( error == NO_ERROR ) {
  330. error = WatchKeyStop( root );
  331. DumpMallocStats( "After WatchKeyStop" );
  332. }
  333. return error;
  334. } // WatchStop
  335. DWORD
  336. WatchEnum (
  337. IN PVOID WatchHandle,
  338. IN PVOID Context,
  339. IN PWATCH_ENUM_ROUTINE EnumRoutine
  340. )
  341. /*++
  342. Routine Description:
  343. Enumerates the new, changed, and deleted elements of the watched
  344. directory and key. Call the EnumRoutine for each such entry.
  345. Arguments:
  346. WatchHandle - handle returned by WatchStart.
  347. Context - context value to be passed to EnumRoutine.
  348. EnumRoutine - routine to call for each entry.
  349. Return Value:
  350. DWORD - Win32 status of the operation.
  351. --*/
  352. {
  353. PROOT_ENTRY root;
  354. PWCH name;
  355. PCONTAINER_ENTRY rootContainer;
  356. PCONTAINER_ENTRY currentContainer;
  357. PCONTAINER_ENTRY container;
  358. POBJECT_ENTRY object;
  359. WATCH_ENTRY enumEntry;
  360. DWORD i;
  361. DWORD containerNameOffset;
  362. DWORD objectNameOffset;
  363. DWORD containerType;
  364. DWORD objectType;
  365. DWORD error;
  366. BOOL bTooLong;
  367. WCHAR currentPath[MAX_PATH + 1];
  368. root = WatchHandle;
  369. //
  370. // Loop twice -- once for the watched directory and once for
  371. // the watched key.
  372. //
  373. for ( i = 0; i < 2; i++ ) {
  374. //
  375. // Set up for walking the appropriate tree.
  376. //
  377. if ( i == 0 ) {
  378. rootContainer = (PCONTAINER_ENTRY)root->RootDirectoryEntry;
  379. containerType = WATCH_DIRECTORY;
  380. objectType = WATCH_FILE;
  381. containerNameOffset = FIELD_OFFSET( DIRECTORY_ENTRY, Name );
  382. objectNameOffset = FIELD_OFFSET( FILE_ENTRY, Name );
  383. } else {
  384. rootContainer = (PCONTAINER_ENTRY)root->RootKeyEntry;
  385. containerType = WATCH_KEY;
  386. objectType = WATCH_VALUE;
  387. containerNameOffset = FIELD_OFFSET( KEY_ENTRY, Name );
  388. objectNameOffset = FIELD_OFFSET( VALUE_ENTRY, Name );
  389. }
  390. currentContainer = rootContainer;
  391. if( rootContainer && wcslen( (PWCH)((PCHAR)rootContainer + containerNameOffset)) > (MAX_PATH-1)) {
  392. SetupDebugPrint1( L"SETUP: : WatchEnum. Rootcontainer too long %s.", (PWCH)((PCHAR)rootContainer + containerNameOffset));
  393. continue;
  394. }
  395. wcscpy( currentPath, (PWCH)((PCHAR)rootContainer + containerNameOffset) );
  396. enumEntry.Name = currentPath;
  397. if ( wcslen(currentPath) == 0 ) {
  398. enumEntry.Name += 1; // skip leading backslash
  399. }
  400. do {
  401. //
  402. // Call the EnumRoutine for each object (file/value) in the
  403. // container (directory/key). All objects remaining in the
  404. // tree are either changed, new, or deleted.
  405. //
  406. object = GetFirstObject( currentContainer );
  407. while ( object != NULL ) {
  408. enumEntry.EntryType = objectType;
  409. enumEntry.ChangeType = GetEntryState( object );
  410. if( (wcslen( currentPath) + wcslen( L"\\") + wcslen((PWCH)((PCHAR)object + objectNameOffset)))
  411. < MAX_PATH) {
  412. wcscat( currentPath, L"\\" );
  413. wcscat( currentPath, (PWCH)((PCHAR)object + objectNameOffset) );
  414. error = EnumRoutine( Context, &enumEntry );
  415. if ( error != NO_ERROR ) {
  416. dprintf( 0, ("EnumRoutine returned %d\n", error) );
  417. return error;
  418. }
  419. *wcsrchr(currentPath, L'\\') = 0;
  420. }
  421. else {
  422. SetupDebugPrint1( L"SETUP: : WatchEnum. Object too long %s.", (PWCH)((PCHAR)object + objectNameOffset));
  423. }
  424. object = GetNextObject( currentContainer, object );
  425. }
  426. //
  427. // If the current container has subcontainers, recurse
  428. // into the first one.
  429. //
  430. container = GetFirstContainer( currentContainer );
  431. bTooLong = container && (wcslen( currentPath) + wcslen( L"\\") + wcslen((PWCH)((PCHAR)container + containerNameOffset))) >= MAX_PATH;
  432. if( bTooLong) {
  433. SetupDebugPrint1( L"SETUP: : WatchEnum. Container too long %s.", (PWCH)((PCHAR)container + containerNameOffset));
  434. }
  435. if ( container != NULL && !bTooLong) {
  436. currentContainer = container;
  437. wcscat( currentPath, L"\\" );
  438. wcscat( currentPath, (PWCH)((PCHAR)currentContainer + containerNameOffset) );
  439. } else {
  440. //
  441. // The container has no subcontainers. Walk back up the
  442. // tree looking for a sibling container to process.
  443. //
  444. while ( TRUE ) {
  445. //
  446. // If the current container is the root container, we're done.
  447. //
  448. if ( currentContainer == rootContainer ) {
  449. currentContainer = NULL;
  450. break;
  451. }
  452. //
  453. // If the current container is new or deleted, call
  454. // the EnumRoutine.
  455. //
  456. if ( GetEntryState(currentContainer) != WATCH_MATCHED ) {
  457. enumEntry.EntryType = containerType;
  458. enumEntry.ChangeType = GetEntryState( currentContainer );
  459. error = EnumRoutine( Context, &enumEntry );
  460. if ( error != NO_ERROR ) {
  461. dprintf( 0, ("EnumRoutine returned %d\n", error) );
  462. return error;
  463. }
  464. }
  465. //
  466. // Strip the name of the current container off of the path.
  467. //
  468. *wcsrchr(currentPath, L'\\') = 0;
  469. //
  470. // If the parent container has more subcontainers, recurse
  471. // into the next one. Otherwise, move up to the parent
  472. // container and try again.
  473. //
  474. container = GetNextContainer( currentContainer );
  475. bTooLong = container && (wcslen( currentPath) + wcslen(L"\\") + wcslen( (PWCH)((PCHAR)container + containerNameOffset))) >= MAX_PATH;
  476. if( bTooLong) {
  477. SetupDebugPrint1( L"SETUP: : WatchEnum. Container too long %s.", (PWCH)((PCHAR)container + containerNameOffset));
  478. }
  479. if ( container != NULL && !bTooLong) {
  480. currentContainer = container;
  481. wcscat( currentPath, L"\\" );
  482. wcscat( currentPath, (PWCH)((PCHAR)currentContainer + containerNameOffset) );
  483. break;
  484. } else {
  485. currentContainer = GetParent( currentContainer );
  486. }
  487. }
  488. }
  489. } while ( currentContainer != NULL );
  490. } // for
  491. return NO_ERROR;
  492. } // WatchEnum
  493. VOID
  494. WatchFree (
  495. IN PVOID WatchHandle
  496. )
  497. /*++
  498. Routine Description:
  499. Frees the watch data structures.
  500. Arguments:
  501. WatchHandle - supplies the handle returned by WatchStart.
  502. Return Value:
  503. DWORD - Win32 status of the operation.
  504. --*/
  505. {
  506. PROOT_ENTRY root;
  507. root = WatchHandle;
  508. //
  509. // Free the directory tree, and key tree, and the root entry.
  510. //
  511. WatchFreeChildren( (PCONTAINER_ENTRY)root->RootDirectoryEntry );
  512. WatchFreeChildren( (PCONTAINER_ENTRY)root->RootKeyEntry );
  513. MyFree( root );
  514. DumpMallocStats( "After WatchFree" );
  515. return;
  516. } // WatchFree
  517. VOID
  518. WatchFreeChildren (
  519. IN PCONTAINER_ENTRY RootContainer
  520. )
  521. /*++
  522. Routine Description:
  523. Frees the children of a container (directory or key). Note that the
  524. container itself is not freed.
  525. Arguments:
  526. RootContainer - the container whose children are to be freed.
  527. Return Value:
  528. None.
  529. --*/
  530. {
  531. PCONTAINER_ENTRY currentContainer;
  532. PCONTAINER_ENTRY container;
  533. PCONTAINER_ENTRY parent;
  534. POBJECT_ENTRY object;
  535. #if WATCH_DEBUG
  536. WCHAR currentPath[MAX_PATH + 1];
  537. #endif
  538. DEBUG( wcscpy( currentPath, CONTAINER_NAME(RootContainer) ) );
  539. //
  540. // Delete children starting at the root container.
  541. //
  542. currentContainer = RootContainer;
  543. do {
  544. //
  545. // Delete all objects (files or values) within the container.
  546. //
  547. object = GetFirstObject( currentContainer );
  548. while ( object != NULL ) {
  549. dprintf( 2, ("Deleting entry for object %ws\\%ws: %s\n", currentPath, OBJECT_NAME(currentContainer,object), States[GetEntryState(object)]) );
  550. RemoveObject( object );
  551. MyFree( object );
  552. object = GetFirstObject( currentContainer );
  553. }
  554. //
  555. // If the container has subcontainers, recurse into the first one.
  556. //
  557. container = GetFirstContainer( currentContainer );
  558. if ( container != NULL ) {
  559. currentContainer = container;
  560. DEBUG( wcscat( currentPath, L"\\" ) );
  561. DEBUG( wcscat( currentPath, CONTAINER_NAME(currentContainer) ) );
  562. } else {
  563. //
  564. // The container has no subcontainers. Walk back up the
  565. // tree looking for a sibling container to process.
  566. //
  567. while ( TRUE ) {
  568. //
  569. // If the current container is the root container, we're done.
  570. //
  571. if ( currentContainer == RootContainer ) {
  572. currentContainer = NULL;
  573. break;
  574. }
  575. DEBUG( dprintf( 2, ("Deleting entry for container %ws: %s\n", currentPath, States[GetEntryState(currentContainer)]) ) );
  576. DEBUG( *wcsrchr(currentPath, L'\\') = 0 );
  577. //
  578. // Free the current container.
  579. //
  580. parent = GetParent( currentContainer );
  581. RemoveContainer( currentContainer );
  582. MyFree( currentContainer );
  583. //
  584. // If the parent container has more subcontainers,
  585. // recurse into the first one. Otherwise, move up
  586. // to the parent container and loop back to free it.
  587. //
  588. currentContainer = GetFirstContainer( parent );
  589. if ( currentContainer != NULL ) {
  590. DEBUG( wcscat( currentPath, L"\\" ) );
  591. DEBUG( wcscat( currentPath, CONTAINER_NAME(currentContainer) ) );
  592. break;
  593. } else {
  594. currentContainer = parent;
  595. }
  596. }
  597. }
  598. } while ( currentContainer != NULL );
  599. return;
  600. } // WatchFreeChildren
  601. DWORD
  602. WatchDirStart (
  603. IN PROOT_ENTRY Root
  604. )
  605. /*++
  606. Routine Description:
  607. Starts watching the current user's profile directory. Captures the
  608. initial state of the directory tree.
  609. Arguments:
  610. Root - pointer to the ROOT_ENTRY allocated by WatchStart.
  611. Return Value:
  612. DWORD - Win32 status of the operation.
  613. --*/
  614. {
  615. PDIRECTORY_ENTRY rootDirectory;
  616. PDIRECTORY_ENTRY currentDirectory;
  617. PDIRECTORY_ENTRY newDirectory;
  618. PFILE_ENTRY newFile;
  619. WIN32_FIND_DATA fileData;
  620. HANDLE findHandle;
  621. DWORD error;
  622. BOOL ok;
  623. WCHAR currentPath[MAX_PATH + 1];
  624. //
  625. // Get the address of the root directory entry.
  626. //
  627. rootDirectory = Root->RootDirectoryEntry;
  628. currentDirectory = rootDirectory;
  629. //
  630. // Get the full path to the current user's profile directory.
  631. //
  632. ok = GetSpecialFolderPath ( CSIDL_PROGRAMS, currentPath );
  633. if ( !ok ) {
  634. return GetLastError();
  635. }
  636. DEBUG( if ( wcslen( rootDirectory->Name ) != 0 ) {
  637. wcscat( currentPath, TEXT("\\") );
  638. wcscat( currentPath, rootDirectory->Name );
  639. } )
  640. do {
  641. //
  642. // Look for files/directories in the current directory.
  643. //
  644. wcscat( currentPath, L"\\*" );
  645. dprintf( 2, ("FindFirst for %ws\n", currentPath) );
  646. findHandle = FindFirstFile( currentPath, &fileData );
  647. currentPath[wcslen(currentPath) - 2] = 0;
  648. if ( findHandle != INVALID_HANDLE_VALUE ) {
  649. do {
  650. if ( FlagOff(fileData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY) ) {
  651. //
  652. // The entry returned is for a file. Add it to the tree,
  653. // capturing the file's LastWriteTime.
  654. //
  655. dprintf( 2, (" found file %ws\\%ws\n", currentPath, fileData.cFileName) );
  656. newFile = MyMalloc( (DWORD)(sizeof(FILE_ENTRY) - sizeof(WCHAR) +
  657. ((wcslen(fileData.cFileName) + 1) * sizeof(WCHAR))) );
  658. if ( newFile == NULL ) {
  659. FindClose( findHandle );
  660. return ERROR_NOT_ENOUGH_MEMORY;
  661. }
  662. InitializeObject( newFile, 0 );
  663. wcscpy( newFile->Name, fileData.cFileName );
  664. newFile->LastWriteTime = fileData.ftLastWriteTime;
  665. InsertObject( currentDirectory, newFile );
  666. } else if ((wcscmp(fileData.cFileName,L".") != 0) &&
  667. (wcscmp(fileData.cFileName,L"..") != 0)) {
  668. //
  669. // The entry returned is for a directory. Add it to the tree.
  670. //
  671. dprintf( 2, (" found directory %ws\\%ws\n", currentPath, fileData.cFileName) );
  672. newDirectory = MyMalloc( (DWORD)(sizeof(DIRECTORY_ENTRY) - sizeof(WCHAR) +
  673. ((wcslen(fileData.cFileName) + 1) * sizeof(WCHAR))) );
  674. if ( newDirectory == NULL ) {
  675. FindClose( findHandle );
  676. return ERROR_NOT_ENOUGH_MEMORY;
  677. }
  678. InitializeContainer( newDirectory, 0, currentDirectory, TRUE );
  679. wcscpy( newDirectory->Name, fileData.cFileName );
  680. InsertContainer( currentDirectory, newDirectory );
  681. }
  682. //
  683. // Find another entry in the directory.
  684. //
  685. ok = FindNextFile( findHandle, &fileData );
  686. } while ( ok );
  687. //
  688. // All entries found. Close the find handle.
  689. //
  690. FindClose( findHandle );
  691. } // findHandle != INVALID_HANDLE_VALUE
  692. //
  693. // If the current directory has subdirectories, recurse into the
  694. // first one.
  695. //
  696. newDirectory = (PDIRECTORY_ENTRY)GetFirstContainer( currentDirectory );
  697. if ( newDirectory != NULL ) {
  698. currentDirectory = newDirectory;
  699. wcscat( currentPath, L"\\" );
  700. wcscat( currentPath, currentDirectory->Name );
  701. } else {
  702. //
  703. // The directory has no subdirectories. Walk back up the
  704. // tree looking for a sibling directory to process.
  705. //
  706. while ( TRUE ) {
  707. //
  708. // If the current directory is the root directory, we're done.
  709. //
  710. if ( currentDirectory == rootDirectory ) {
  711. currentDirectory = NULL;
  712. break;
  713. }
  714. //
  715. // Strip the name of the current directory off of the path.
  716. //
  717. *wcsrchr(currentPath, L'\\') = 0;
  718. //
  719. // If the parent directory has more subdirectories,
  720. // recurse into the next one. Otherwise, move up
  721. // to the parent directory and try again.
  722. //
  723. newDirectory = (PDIRECTORY_ENTRY)GetNextContainer( currentDirectory );
  724. if ( newDirectory != NULL ) {
  725. currentDirectory = newDirectory;
  726. wcscat( currentPath, L"\\" );
  727. wcscat( currentPath, currentDirectory->Name );
  728. break;
  729. } else {
  730. currentDirectory = (PDIRECTORY_ENTRY)GetParent( currentDirectory );
  731. }
  732. }
  733. }
  734. } while ( currentDirectory != NULL );
  735. return NO_ERROR;
  736. } // WatchDirStart
  737. DWORD
  738. WatchDirStop (
  739. IN PROOT_ENTRY Root
  740. )
  741. /*++
  742. Routine Description:
  743. Stops watching the current user's profile directory. Captures the
  744. differences between the initial state and the current state.
  745. Arguments:
  746. Root - pointer to the ROOT_ENTRY allocated by WatchStart.
  747. Return Value:
  748. DWORD - Win32 status of the operation.
  749. --*/
  750. {
  751. PDIRECTORY_ENTRY rootDirectory;
  752. PDIRECTORY_ENTRY currentDirectory;
  753. PDIRECTORY_ENTRY directory;
  754. PFILE_ENTRY file;
  755. WIN32_FIND_DATA fileData;
  756. HANDLE findHandle;
  757. DWORD error;
  758. BOOL ok;
  759. WCHAR currentPath[MAX_PATH + 1];
  760. //
  761. // Get the address of the root directory entry.
  762. //
  763. rootDirectory = Root->RootDirectoryEntry;
  764. currentDirectory = rootDirectory;
  765. //
  766. // Get the full path to the current user's directory.
  767. //
  768. ok = GetSpecialFolderPath ( CSIDL_PROGRAMS, currentPath );
  769. if ( !ok ) {
  770. return GetLastError();
  771. }
  772. DEBUG( if ( wcslen( rootDirectory->Name ) != 0 ) {
  773. wcscat( currentPath, TEXT("\\") );
  774. wcscat( currentPath, rootDirectory->Name );
  775. } )
  776. do {
  777. //
  778. // Look for files/directories in the current directory.
  779. //
  780. wcscat( currentPath, L"\\*" );
  781. dprintf( 2, ("FindFirst for %ws\n", currentPath) );
  782. findHandle = FindFirstFile( currentPath, &fileData );
  783. currentPath[wcslen(currentPath) - 2] = 0;
  784. if ( findHandle != INVALID_HANDLE_VALUE ) {
  785. do {
  786. if ( FlagOff(fileData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY) ) {
  787. //
  788. // The entry returned is for a file. Check to see if
  789. // this file existed at the start.
  790. //
  791. dprintf( 2, (" found file %ws\\%ws\n", currentPath, fileData.cFileName) );
  792. ok = FALSE;
  793. file = (PFILE_ENTRY)GetFirstObject( currentDirectory );
  794. while ( file != NULL ) {
  795. if ( _wcsicmp( file->Name, fileData.cFileName ) == 0 ) {
  796. ok = TRUE;
  797. break;
  798. }
  799. file = (PFILE_ENTRY)GetNextObject( currentDirectory, file );
  800. }
  801. if ( ok ) {
  802. //
  803. // The file existed at the start. If its LastWriteTime
  804. // hasn't changed, remove it from the watch tree.
  805. // Otherwise, mark it as changed.
  806. //
  807. if ( TIMES_EQUAL( file->LastWriteTime, fileData.ftLastWriteTime ) ) {
  808. dprintf( 2, (" Deleting entry for unchanged file %ws\\%ws\n", currentPath, file->Name) );
  809. RemoveObject( file );
  810. MyFree( file );
  811. } else {
  812. dprintf( 1, (" Marking entry for changed file %ws\\%ws\n", currentPath, file->Name) );
  813. SetEntryState( file, WATCH_CHANGED );
  814. }
  815. } else {
  816. //
  817. // The file is new. Add it to the tree.
  818. //
  819. file = MyMalloc( (DWORD)(sizeof(FILE_ENTRY) - sizeof(WCHAR) +
  820. ((wcslen(fileData.cFileName) + 1) * sizeof(WCHAR))) );
  821. if ( file == NULL ) {
  822. FindClose( findHandle );
  823. return ERROR_NOT_ENOUGH_MEMORY;
  824. }
  825. InitializeObject( file, WATCH_NEW );
  826. wcscpy( file->Name, fileData.cFileName );
  827. dprintf( 1, (" Adding entry for new file %ws\\%ws\n", currentPath, file->Name) );
  828. InsertObject( currentDirectory, file );
  829. }
  830. } else if ((wcscmp(fileData.cFileName,L".") != 0) &&
  831. (wcscmp(fileData.cFileName,L"..") != 0)) {
  832. //
  833. // The entry returned is for a directory. Check to see if
  834. // this directory existed at the start.
  835. //
  836. dprintf( 2, (" found directory %ws\\%ws\n", currentPath, fileData.cFileName) );
  837. ok = FALSE;
  838. directory = (PDIRECTORY_ENTRY)GetFirstContainer( currentDirectory );
  839. while ( directory != NULL ) {
  840. if ( _wcsicmp( directory->Name, fileData.cFileName ) == 0 ) {
  841. ok = TRUE;
  842. break;
  843. }
  844. directory = (PDIRECTORY_ENTRY)GetNextContainer( directory );
  845. }
  846. if ( ok ) {
  847. //
  848. // The directory existed at the start. Mark it as
  849. // matched. (We can't delete matched directories,
  850. // as we do files, because they need to be in the
  851. // tree for recursion.)
  852. //
  853. SetEntryState( directory, WATCH_MATCHED );
  854. } else {
  855. //
  856. // The directory is new. Add it to the tree.
  857. //
  858. directory = MyMalloc( (DWORD)(sizeof(DIRECTORY_ENTRY) - sizeof(WCHAR) +
  859. ((wcslen(fileData.cFileName) + 1) * sizeof(WCHAR))) );
  860. if ( directory == NULL ) {
  861. FindClose( findHandle );
  862. return ERROR_NOT_ENOUGH_MEMORY;
  863. }
  864. InitializeContainer( directory, WATCH_NEW, currentDirectory, TRUE );
  865. wcscpy( directory->Name, fileData.cFileName );
  866. dprintf( 1, (" Adding entry for new directory %ws\\%ws\n", currentPath, directory->Name) );
  867. InsertContainer( currentDirectory, directory );
  868. }
  869. }
  870. //
  871. // Find another entry in the directory.
  872. //
  873. ok = FindNextFile( findHandle, &fileData );
  874. } while ( ok );
  875. //
  876. // All entries found. Close the find handle.
  877. //
  878. FindClose( findHandle );
  879. } // findHandle != INVALID_HANDLE_VALUE
  880. //
  881. // Any file entries in the current directory that were not removed
  882. // (because they were matched), marked as changed (because the
  883. // file time had changed), or added (for new files) represent files
  884. // that have been deleted. Mark them as such.
  885. //
  886. file = (PFILE_ENTRY)GetFirstObject( currentDirectory );
  887. while ( file != NULL ) {
  888. if ( GetEntryState(file) == WATCH_NONE ) {
  889. dprintf( 1, (" Marking entry for deleted file %ws\\%ws\n", currentPath, file->Name) );
  890. SetEntryState( file, WATCH_DELETED );
  891. }
  892. file = (PFILE_ENTRY)GetNextObject( currentDirectory, file );
  893. }
  894. //
  895. // Any subdirectory entries in the current directory that were not
  896. // marked as matched (directory still exists) or added (new directory)
  897. // represent directories that have been deleted. Mark them as such
  898. // and delete the entries for the their children -- we don't need
  899. // these entries any more.
  900. //
  901. directory = (PDIRECTORY_ENTRY)GetFirstContainer( currentDirectory );
  902. while ( directory != NULL ) {
  903. if ( GetEntryState(directory) == WATCH_NONE ) {
  904. dprintf( 1, (" Marking entry for deleted directory %ws\\%ws\n", currentPath, directory->Name) );
  905. SetEntryState( directory, WATCH_DELETED );
  906. WatchFreeChildren( (PCONTAINER_ENTRY)directory );
  907. }
  908. directory = (PDIRECTORY_ENTRY)GetNextContainer( directory );
  909. }
  910. //
  911. // Find a subdirectory of the current directory that is marked as
  912. // matched. We don't need to walk the subtrees for new or deleted
  913. // directories.
  914. //
  915. directory = (PDIRECTORY_ENTRY)GetFirstContainer( currentDirectory );
  916. while ( directory != NULL ) {
  917. if ( GetEntryState(directory) == WATCH_MATCHED ) {
  918. break;
  919. }
  920. directory = (PDIRECTORY_ENTRY)GetNextContainer( directory );
  921. }
  922. //
  923. // If a matched subdirectory was found, recurse into it.
  924. //
  925. if ( directory != NULL ) {
  926. currentDirectory = directory;
  927. wcscat( currentPath, L"\\" );
  928. wcscat( currentPath, currentDirectory->Name );
  929. } else {
  930. //
  931. // The directory has no matched subdirectories. Walk back up the
  932. // tree looking for a sibling directory to process.
  933. //
  934. while ( TRUE ) {
  935. //
  936. // If the current directory is the root directory, we're done.
  937. //
  938. if ( currentDirectory == rootDirectory ) {
  939. currentDirectory = NULL;
  940. break;
  941. }
  942. //
  943. // Strip the name of the current directory off of the path.
  944. //
  945. *wcsrchr(currentPath, L'\\') = 0;
  946. //
  947. // If the parent directories has more matched subdirectories,
  948. // recurse into the next one. Otherwise, move up to the
  949. // parent directory and try again.
  950. //
  951. directory = (PDIRECTORY_ENTRY)GetNextContainer( currentDirectory );
  952. while ( directory != NULL ) {
  953. if ( GetEntryState(directory) == WATCH_MATCHED ) {
  954. break;
  955. }
  956. directory = (PDIRECTORY_ENTRY)GetNextContainer( directory );
  957. }
  958. if ( directory != NULL ) {
  959. currentDirectory = directory;
  960. wcscat( currentPath, L"\\" );
  961. wcscat( currentPath, currentDirectory->Name );
  962. break;
  963. } else {
  964. currentDirectory = (PDIRECTORY_ENTRY)GetParent( currentDirectory );
  965. }
  966. }
  967. }
  968. } while ( currentDirectory != NULL );
  969. return NO_ERROR;
  970. } // WatchDirStop
  971. DWORD
  972. WatchKeyStart (
  973. IN PROOT_ENTRY Root
  974. )
  975. /*++
  976. Routine Description:
  977. Starts watching the current user key. Captures the initial state of the
  978. key tree.
  979. Arguments:
  980. Root - pointer to the ROOT_ENTRY allocated by WatchStart.
  981. Return Value:
  982. DWORD - Win32 status of the operation.
  983. --*/
  984. {
  985. PKEY_ENTRY rootKey;
  986. PKEY_ENTRY currentKey;
  987. PKEY_ENTRY newKey;
  988. DWORD error;
  989. KEY_ENUM_CONTEXT context;
  990. #if WATCH_DEBUG
  991. WCHAR currentPath[MAX_PATH + 1];
  992. #endif
  993. //
  994. // Get the address of the root key entry.
  995. //
  996. rootKey = Root->RootKeyEntry;
  997. currentKey = rootKey;
  998. DEBUG( wcscpy( currentPath, rootKey->Name ) );
  999. do {
  1000. //
  1001. // Open the current key. If the current key is the root key, then
  1002. // just use the HKEY_CURRENT_USER predefined key. Otherwise, open
  1003. // the current key relative to the parent key.
  1004. //
  1005. if ( (currentKey == rootKey)
  1006. #if WATCH_DEBUG
  1007. && (wcslen(currentKey->Name) == 0)
  1008. #endif
  1009. ) {
  1010. currentKey->Handle = HKEY_CURRENT_USER;
  1011. } else {
  1012. error = RegOpenKeyEx(
  1013. #if WATCH_DEBUG
  1014. currentKey == rootKey ?
  1015. HKEY_CURRENT_USER :
  1016. #endif
  1017. ((PKEY_ENTRY)GetParent(currentKey))->Handle,
  1018. currentKey->Name,
  1019. 0,
  1020. KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
  1021. &currentKey->Handle );
  1022. if ( error != NO_ERROR ) {
  1023. goto cleanup;
  1024. }
  1025. }
  1026. //
  1027. // Enumerate the values and subkeys of the key, adding entries
  1028. // to the watch tree for each one.
  1029. //
  1030. context.ParentKey = currentKey;
  1031. DEBUG( context.CurrentPath = currentPath );
  1032. error = EnumerateKey( currentKey->Handle,
  1033. &context,
  1034. AddValueAtStart,
  1035. AddKeyAtStart );
  1036. if ( error != NO_ERROR ) {
  1037. goto cleanup;
  1038. }
  1039. //
  1040. // If the current key has subkeys, recurse into the first one.
  1041. //
  1042. newKey = (PKEY_ENTRY)GetFirstContainer( currentKey );
  1043. if ( newKey != NULL ) {
  1044. currentKey = newKey;
  1045. DEBUG( wcscat( currentPath, L"\\" ) );
  1046. DEBUG( wcscat( currentPath, currentKey->Name ) );
  1047. } else {
  1048. //
  1049. // The key has no subkeys. Walk back up the tree looking
  1050. // for a sibling key to process.
  1051. //
  1052. while ( TRUE ) {
  1053. //
  1054. // Close the handle to the key.
  1055. //
  1056. if ( currentKey->Handle != HKEY_CURRENT_USER ) {
  1057. RegCloseKey( currentKey->Handle );
  1058. }
  1059. currentKey->Handle = NULL;
  1060. //
  1061. // If the current key is the root key, we're done.
  1062. //
  1063. if ( currentKey == rootKey ) {
  1064. currentKey = NULL;
  1065. break;
  1066. }
  1067. DEBUG( *wcsrchr(currentPath, L'\\') = 0 );
  1068. //
  1069. // If the parent key has more subkeys, recurse into the next
  1070. // one. Otherwise, move up to the parent key and try again.
  1071. //
  1072. newKey = (PKEY_ENTRY)GetNextContainer( currentKey );
  1073. if ( newKey != NULL ) {
  1074. currentKey = newKey;
  1075. DEBUG( wcscat( currentPath, L"\\" ) );
  1076. DEBUG( wcscat( currentPath, currentKey->Name ) );
  1077. break;
  1078. } else {
  1079. currentKey = (PKEY_ENTRY)GetParent( currentKey );
  1080. }
  1081. }
  1082. }
  1083. } while ( currentKey != NULL );
  1084. return NO_ERROR;
  1085. cleanup:
  1086. //
  1087. // Error cleanup. Walk back up the tree closing handles.
  1088. //
  1089. do {
  1090. if ( (currentKey->Handle != NULL) && (currentKey->Handle != HKEY_CURRENT_USER) ) {
  1091. RegCloseKey( currentKey->Handle );
  1092. }
  1093. currentKey->Handle = NULL;
  1094. currentKey = (PKEY_ENTRY)GetParent( currentKey );
  1095. } while ( currentKey != NULL );
  1096. return error;
  1097. } // WatchKeyStart
  1098. DWORD
  1099. WatchKeyStop (
  1100. IN PROOT_ENTRY Root
  1101. )
  1102. /*++
  1103. Routine Description:
  1104. Stops watching the current user key. Captures the differences
  1105. between the initial state and the current state.
  1106. Arguments:
  1107. Root - pointer to the ROOT_ENTRY allocated by WatchStart.
  1108. Return Value:
  1109. DWORD - Win32 status of the operation.
  1110. --*/
  1111. {
  1112. PKEY_ENTRY rootKey;
  1113. PKEY_ENTRY currentKey;
  1114. PKEY_ENTRY key;
  1115. PVALUE_ENTRY value;
  1116. DWORD error;
  1117. KEY_ENUM_CONTEXT context;
  1118. #if WATCH_DEBUG
  1119. WCHAR currentPath[MAX_PATH + 1];
  1120. #endif
  1121. //
  1122. // Get the address of the root key entry.
  1123. //
  1124. rootKey = Root->RootKeyEntry;
  1125. currentKey = rootKey;
  1126. DEBUG( wcscpy( currentPath, rootKey->Name ) );
  1127. do {
  1128. //
  1129. // Open the current key. If the current key is the root key, then
  1130. // just use the HKEY_CURRENT_USER predefined key. Otherwise, open
  1131. // the current key relative to the parent key.
  1132. //
  1133. if ( (currentKey == rootKey)
  1134. #if WATCH_DEBUG
  1135. && (wcslen(currentKey->Name) == 0)
  1136. #endif
  1137. ) {
  1138. currentKey->Handle = HKEY_CURRENT_USER;
  1139. } else {
  1140. error = RegOpenKeyEx(
  1141. #if WATCH_DEBUG
  1142. currentKey == rootKey ?
  1143. HKEY_CURRENT_USER :
  1144. #endif
  1145. ((PKEY_ENTRY)GetParent(currentKey))->Handle,
  1146. currentKey->Name,
  1147. 0,
  1148. KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
  1149. &currentKey->Handle );
  1150. if ( error != NO_ERROR ) {
  1151. goto cleanup;
  1152. }
  1153. }
  1154. //
  1155. // Enumerate the values and subkeys of the key, checking entries
  1156. // in the watch tree for each one.
  1157. //
  1158. context.ParentKey = currentKey;
  1159. DEBUG( context.CurrentPath = currentPath );
  1160. error = EnumerateKey( currentKey->Handle,
  1161. &context,
  1162. CheckValueAtStop,
  1163. CheckKeyAtStop );
  1164. if ( error != NO_ERROR ) {
  1165. goto cleanup;
  1166. }
  1167. //
  1168. // Any value entries in the current key that were not removed
  1169. // (because they were matched), marked as changed (because the
  1170. // value data had changed), or added (for new values) represent
  1171. // values that have been deleted. Mark them as such.
  1172. //
  1173. value = (PVALUE_ENTRY)GetFirstObject( currentKey );
  1174. while ( value != NULL ) {
  1175. if ( GetEntryState(value) == WATCH_NONE ) {
  1176. dprintf( 1, (" Marking entry for deleted value %ws\\%ws\n", currentPath, value->Name) );
  1177. SetEntryState( value, WATCH_DELETED );
  1178. }
  1179. value = (PVALUE_ENTRY)GetNextObject( currentKey, value );
  1180. }
  1181. //
  1182. // Any subkey entries in the current key that were not marked as
  1183. // matched (subkey still exists) or added (new subkey) represent
  1184. // subkeys that have been deleted. Mark them as such and delete
  1185. // the entries for the their children -- we don't need these
  1186. // entries any more.
  1187. //
  1188. key = (PKEY_ENTRY)GetFirstContainer( currentKey );
  1189. while ( key != NULL ) {
  1190. if ( GetEntryState(key) == WATCH_NONE ) {
  1191. dprintf( 1, (" Marking entry for deleted key %ws\\%ws\n", currentPath, key->Name) );
  1192. SetEntryState( key, WATCH_DELETED );
  1193. WatchFreeChildren( (PCONTAINER_ENTRY)key );
  1194. }
  1195. key = (PKEY_ENTRY)GetNextContainer( key );
  1196. }
  1197. //
  1198. // Find a subkey of the current directory that is marked as matched.
  1199. // We don't need to walk the subtrees for new or deleted keys.
  1200. //
  1201. key = (PKEY_ENTRY)GetFirstContainer( currentKey );
  1202. while ( key != NULL ) {
  1203. if ( GetEntryState(key) == WATCH_MATCHED ) {
  1204. break;
  1205. }
  1206. key = (PKEY_ENTRY)GetNextContainer( key );
  1207. }
  1208. //
  1209. // If a matched subkey was found, recurse into it.
  1210. //
  1211. if ( key != NULL ) {
  1212. currentKey = key;
  1213. DEBUG( wcscat( currentPath, L"\\" ) );
  1214. DEBUG( wcscat( currentPath, currentKey->Name ) );
  1215. } else {
  1216. //
  1217. // The key has no matched subkeys. Walk back up the
  1218. // tree looking for a sibling key to process.
  1219. //
  1220. while ( TRUE ) {
  1221. //
  1222. // Close the handle to the key.
  1223. //
  1224. if ( currentKey->Handle != HKEY_CURRENT_USER ) {
  1225. RegCloseKey( currentKey->Handle );
  1226. }
  1227. currentKey->Handle = NULL;
  1228. //
  1229. // If the current key is the root key, we're done.
  1230. //
  1231. if ( currentKey == rootKey ) {
  1232. currentKey = NULL;
  1233. break;
  1234. }
  1235. DEBUG( *wcsrchr(currentPath, L'\\') = 0 );
  1236. //
  1237. // If the parent key has more matched subkeys, recurse
  1238. // into the next one. Otherwise, move up to the parent
  1239. // key and try again.
  1240. //
  1241. key = (PKEY_ENTRY)GetNextContainer( currentKey );
  1242. while ( key != NULL ) {
  1243. if ( GetEntryState(key) == WATCH_MATCHED ) {
  1244. break;
  1245. }
  1246. key = (PKEY_ENTRY)GetNextContainer( key );
  1247. }
  1248. if ( key != NULL ) {
  1249. currentKey = key;
  1250. DEBUG( wcscat( currentPath, L"\\" ) );
  1251. DEBUG( wcscat( currentPath, currentKey->Name ) );
  1252. break;
  1253. } else {
  1254. currentKey = (PKEY_ENTRY)GetParent( currentKey );
  1255. }
  1256. }
  1257. }
  1258. } while ( currentKey != NULL );
  1259. return NO_ERROR;
  1260. cleanup:
  1261. //
  1262. // Error cleanup. Walk back up the tree closing handles.
  1263. //
  1264. do {
  1265. if ( (currentKey->Handle != NULL) && (currentKey->Handle != HKEY_CURRENT_USER) ) {
  1266. RegCloseKey( currentKey->Handle );
  1267. }
  1268. currentKey->Handle = NULL;
  1269. currentKey = (PKEY_ENTRY)GetParent( currentKey );
  1270. } while ( currentKey != NULL );
  1271. return error;
  1272. } // WatchKeyStop
  1273. DWORD
  1274. EnumerateKey (
  1275. IN HKEY KeyHandle,
  1276. IN PVOID Context,
  1277. IN PVALUE_ENUM_ROUTINE ValueEnumRoutine OPTIONAL,
  1278. IN PKEY_ENUM_ROUTINE KeyEnumRoutine OPTIONAL
  1279. )
  1280. /*++
  1281. Routine Description:
  1282. Enumerates the values and subkeys in a key. Calls an EnumRoutine for
  1283. each value and subkey.
  1284. Arguments:
  1285. KeyHandle - handle to the key to be enumerated.
  1286. Context - context value to be passed to EnumRoutine.
  1287. ValueEnumRoutine - routine to call for each value. If omitted, values
  1288. are not enumerated.
  1289. KeyEnumRoutine - routine to call for each key. If omitted, keys are
  1290. not enumerated.
  1291. Return Value:
  1292. DWORD - Win32 status of the operation.
  1293. --*/
  1294. {
  1295. DWORD error;
  1296. DWORD keyCount;
  1297. DWORD valueCount;
  1298. DWORD i;
  1299. DWORD type;
  1300. DWORD nameLength;
  1301. DWORD maxKeyNameLength;
  1302. DWORD maxValueNameLength;
  1303. DWORD dataLength;
  1304. DWORD maxValueDataLength;
  1305. PWCH nameBuffer;
  1306. PVOID dataBuffer;
  1307. FILETIME time;
  1308. //
  1309. // Query information about the key that is needed to query
  1310. // its values and subkeys.
  1311. //
  1312. error = RegQueryInfoKey( KeyHandle,
  1313. NULL,
  1314. NULL,
  1315. NULL,
  1316. &keyCount,
  1317. &maxKeyNameLength,
  1318. NULL,
  1319. &valueCount,
  1320. &maxValueNameLength,
  1321. &maxValueDataLength,
  1322. NULL,
  1323. NULL );
  1324. if ( error != NO_ERROR ) {
  1325. return error;
  1326. }
  1327. if ( ValueEnumRoutine != NULL ) {
  1328. //
  1329. // Allocate a buffer large enough for the longest value name and
  1330. // another buffer large enough for the longest value data.
  1331. //
  1332. nameBuffer = MyMalloc( (maxValueNameLength + 1) * sizeof(WCHAR) );
  1333. if ( nameBuffer == NULL ) {
  1334. return ERROR_NOT_ENOUGH_MEMORY;
  1335. }
  1336. dataBuffer = MyMalloc( maxValueDataLength );
  1337. if ( dataBuffer == NULL ) {
  1338. MyFree( nameBuffer );
  1339. return ERROR_NOT_ENOUGH_MEMORY;
  1340. }
  1341. //
  1342. // Query the key's values.
  1343. //
  1344. for ( i = 0; i < valueCount; i++ ) {
  1345. nameLength = maxValueNameLength + 1;
  1346. dataLength = maxValueDataLength;
  1347. error = RegEnumValue( KeyHandle,
  1348. i,
  1349. nameBuffer,
  1350. &nameLength,
  1351. NULL,
  1352. &type,
  1353. dataBuffer,
  1354. &dataLength );
  1355. if ( error != NO_ERROR ) {
  1356. MyFree( dataBuffer );
  1357. MyFree( nameBuffer );
  1358. return error;
  1359. }
  1360. //
  1361. // Call the EnumRoutine.
  1362. //
  1363. error = ValueEnumRoutine( Context,
  1364. nameLength,
  1365. nameBuffer,
  1366. type,
  1367. dataBuffer,
  1368. dataLength );
  1369. if ( error != NO_ERROR ) {
  1370. MyFree( dataBuffer );
  1371. MyFree( nameBuffer );
  1372. return error;
  1373. }
  1374. }
  1375. //
  1376. // Free the value data and value name buffers.
  1377. //
  1378. MyFree( dataBuffer );
  1379. dataBuffer = NULL;
  1380. MyFree( nameBuffer );
  1381. }
  1382. if ( KeyEnumRoutine != NULL) {
  1383. //
  1384. // Allocate a buffer large enough for the longest subkey name.
  1385. //
  1386. nameBuffer = MyMalloc( (maxKeyNameLength + 1) * sizeof(WCHAR) );
  1387. if ( nameBuffer == NULL ) {
  1388. return ERROR_NOT_ENOUGH_MEMORY;
  1389. }
  1390. //
  1391. // Query the key's subkeys.
  1392. //
  1393. for ( i = 0; i < keyCount; i++ ) {
  1394. nameLength = maxKeyNameLength + 1;
  1395. error = RegEnumKeyEx( KeyHandle,
  1396. i,
  1397. nameBuffer,
  1398. &nameLength,
  1399. NULL,
  1400. NULL,
  1401. NULL,
  1402. &time );
  1403. if ( error != NO_ERROR ) {
  1404. MyFree( nameBuffer );
  1405. return error;
  1406. }
  1407. //
  1408. // Call the EnumRoutine.
  1409. //
  1410. error = KeyEnumRoutine( Context,
  1411. nameLength,
  1412. nameBuffer );
  1413. if ( error != NO_ERROR ) {
  1414. MyFree( nameBuffer );
  1415. return error;
  1416. }
  1417. }
  1418. //
  1419. // Free the key name buffer.
  1420. //
  1421. MyFree( nameBuffer );
  1422. }
  1423. return NO_ERROR;
  1424. } // EnumerateKey
  1425. DWORD
  1426. AddValueAtStart (
  1427. IN PVOID Context,
  1428. IN DWORD ValueNameLength,
  1429. IN PWCH ValueName,
  1430. IN DWORD ValueType,
  1431. IN PVOID ValueData,
  1432. IN DWORD ValueDataLength
  1433. )
  1434. /*++
  1435. Routine Description:
  1436. Adds a value entry to the watch tree during WatchKeyStart.
  1437. Arguments:
  1438. Context - context value passed to EnumerateKey.
  1439. ValueNameLength - length in characters of ValueName.
  1440. ValueName - pointer to name of the value.
  1441. ValueType - type of the value data.
  1442. ValueData - pointer to value data.
  1443. ValueDataLength - length in bytes of ValueData.
  1444. Return Value:
  1445. DWORD - Win32 status of the operation.
  1446. --*/
  1447. {
  1448. PKEY_ENUM_CONTEXT context = Context;
  1449. PVALUE_ENTRY newValue;
  1450. //
  1451. // Add the value to the tree, capturing the value data.
  1452. //
  1453. dprintf( 2, (" found value %ws\\%ws\n", context->CurrentPath, ValueName) );
  1454. newValue = MyMalloc( sizeof(VALUE_ENTRY) - sizeof(WCHAR) +
  1455. ((ValueNameLength + 1) * sizeof(WCHAR)) +
  1456. ValueDataLength );
  1457. if ( newValue == NULL ) {
  1458. return ERROR_NOT_ENOUGH_MEMORY;
  1459. }
  1460. InitializeObject( newValue, 0 );
  1461. wcscpy( newValue->Name, ValueName );
  1462. newValue->Type = ValueType;
  1463. newValue->NameLength = ValueNameLength;
  1464. newValue->ValueDataLength = ValueDataLength;
  1465. memcpy( &newValue->Name + ValueNameLength + 1, ValueData, ValueDataLength );
  1466. InsertObject( context->ParentKey, newValue );
  1467. return NO_ERROR;
  1468. } // AddValueAtStart
  1469. DWORD
  1470. AddKeyAtStart (
  1471. IN PVOID Context,
  1472. IN DWORD KeyNameLength,
  1473. IN PWCH KeyName
  1474. )
  1475. /*++
  1476. Routine Description:
  1477. Adds a key entry to the watch tree during WatchKeyStart.
  1478. Arguments:
  1479. Context - context value passed to EnumerateKey.
  1480. KeyNameLength - length in characters of KeyName.
  1481. KeyName - pointer to name of the key.
  1482. Return Value:
  1483. DWORD - Win32 status of the operation.
  1484. --*/
  1485. {
  1486. PKEY_ENUM_CONTEXT context = Context;
  1487. PKEY_ENTRY newKey;
  1488. //
  1489. // Add the key to the tree.
  1490. //
  1491. dprintf( 2, (" found key %ws\\%ws\n", context->CurrentPath, KeyName) );
  1492. newKey = MyMalloc( sizeof(KEY_ENTRY) - sizeof(WCHAR) +
  1493. ((KeyNameLength + 1) * sizeof(WCHAR)) );
  1494. if ( newKey == NULL ) {
  1495. return ERROR_NOT_ENOUGH_MEMORY;
  1496. }
  1497. InitializeContainer( newKey, 0, context->ParentKey, FALSE );
  1498. wcscpy( newKey->Name, KeyName );
  1499. newKey->Handle = NULL;
  1500. InsertContainer( context->ParentKey, newKey );
  1501. return NO_ERROR;
  1502. } // AddKeyAtStart
  1503. DWORD
  1504. CheckValueAtStop (
  1505. IN PVOID Context,
  1506. IN DWORD ValueNameLength,
  1507. IN PWCH ValueName,
  1508. IN DWORD ValueType,
  1509. IN PVOID ValueData,
  1510. IN DWORD ValueDataLength
  1511. )
  1512. /*++
  1513. Routine Description:
  1514. Checks the watch tree for an enumerated value during WatchKeyStop.
  1515. Arguments:
  1516. Context - context value passed to EnumerateKey.
  1517. ValueNameLength - length in characters of ValueName.
  1518. ValueName - pointer to name of the value.
  1519. ValueType - type of the value data.
  1520. ValueData - pointer to value data.
  1521. ValueDataLength - length in bytes of ValueData.
  1522. Return Value:
  1523. DWORD - Win32 status of the operation.
  1524. --*/
  1525. {
  1526. PKEY_ENUM_CONTEXT context = Context;
  1527. PVALUE_ENTRY value;
  1528. BOOL ok;
  1529. //
  1530. // Check to see if the value existed at the start.
  1531. //
  1532. dprintf( 2, (" found value %ws\\%ws\n", context->CurrentPath, ValueName) );
  1533. ok = FALSE;
  1534. value = (PVALUE_ENTRY)GetFirstObject( context->ParentKey );
  1535. while ( value != NULL ) {
  1536. if ( _wcsicmp( value->Name, ValueName ) == 0 ) {
  1537. ok = TRUE;
  1538. break;
  1539. }
  1540. value = (PVALUE_ENTRY)GetNextObject( context->ParentKey, value );
  1541. }
  1542. if ( ok ) {
  1543. //
  1544. // The value existed at the start. If its data hasn't changed,
  1545. // remove it from the tree. Otherwise, mark it as changed.
  1546. //
  1547. if ( (value->Type == ValueType) &&
  1548. (value->ValueDataLength == ValueDataLength) &&
  1549. (memcmp( &value->Name + value->NameLength + 1,
  1550. ValueData,
  1551. ValueDataLength ) == 0) ) {
  1552. dprintf( 2, ("Deleting entry for unchanged value %ws\\%ws\n", context->CurrentPath, ValueName) );
  1553. RemoveObject( value );
  1554. MyFree( value );
  1555. } else {
  1556. dprintf( 1, (" Marking entry for changed value %ws\\%ws\n", context->CurrentPath, ValueName) );
  1557. SetEntryState( value, WATCH_CHANGED );
  1558. }
  1559. } else {
  1560. //
  1561. // The value is new. Add it to the tree.
  1562. //
  1563. // Note that we do not bother to save the value's data here,
  1564. // even though we have it in hand. The routines that
  1565. // populate userdifr already have to deal with querying
  1566. // value data, so the code is simpler this way.
  1567. //
  1568. value = MyMalloc( sizeof(VALUE_ENTRY) - sizeof(WCHAR) +
  1569. ((ValueNameLength + 1) * sizeof(WCHAR)) );
  1570. if ( value == NULL ) {
  1571. return ERROR_NOT_ENOUGH_MEMORY;
  1572. }
  1573. InitializeObject( value, WATCH_NEW );
  1574. wcscpy( value->Name, ValueName );
  1575. dprintf( 1, (" Adding entry for new value %ws\\%ws\n", context->CurrentPath, ValueName) );
  1576. InsertObject( context->ParentKey, value );
  1577. }
  1578. return NO_ERROR;
  1579. } // CheckValueAtStop
  1580. DWORD
  1581. CheckKeyAtStop (
  1582. IN PVOID Context,
  1583. IN DWORD KeyNameLength,
  1584. IN PWCH KeyName
  1585. )
  1586. /*++
  1587. Routine Description:
  1588. Checks the watch tree for an enumerated key during WatchKeyStop.
  1589. Arguments:
  1590. Context - context value passed to EnumerateKey.
  1591. KeyNameLength - length in characters of KeyName.
  1592. KeyName - pointer to name of the key.
  1593. Return Value:
  1594. DWORD - Win32 status of the operation.
  1595. --*/
  1596. {
  1597. PKEY_ENUM_CONTEXT context = Context;
  1598. PKEY_ENTRY key;
  1599. BOOL ok;
  1600. //
  1601. // Check to see if the subkey existed at the start.
  1602. //
  1603. dprintf( 2, (" found key %ws\\%ws\n", context->CurrentPath, KeyName) );
  1604. ok = FALSE;
  1605. key = (PKEY_ENTRY)GetFirstContainer( context->ParentKey );
  1606. while ( key != NULL ) {
  1607. if ( _wcsicmp( key->Name, KeyName ) == 0 ) {
  1608. ok = TRUE;
  1609. break;
  1610. }
  1611. key = (PKEY_ENTRY)GetNextContainer( key );
  1612. }
  1613. if ( ok ) {
  1614. //
  1615. // The key existed at the start. Mark it as matched.
  1616. // (We can't delete matched keys, as we do values,
  1617. // because they need to be in the tree for recursion.)
  1618. //
  1619. SetEntryState( key, WATCH_MATCHED );
  1620. } else {
  1621. //
  1622. // The key is new. Add it to the tree.
  1623. //
  1624. key = MyMalloc( sizeof(KEY_ENTRY) - sizeof(WCHAR) +
  1625. ((KeyNameLength + 1) * sizeof(WCHAR)) );
  1626. if ( key == NULL ) {
  1627. return ERROR_NOT_ENOUGH_MEMORY;
  1628. }
  1629. InitializeContainer( key, WATCH_NEW, context->ParentKey, FALSE );
  1630. wcscpy( key->Name, KeyName );
  1631. dprintf( 1, (" Adding entry for new key %ws\\%ws\n", context->CurrentPath, KeyName) );
  1632. InsertContainer( context->ParentKey, key );
  1633. }
  1634. return NO_ERROR;
  1635. } // CheckKeyAtStop
  1636. //
  1637. // Debug code for tracking allocates and frees.
  1638. //
  1639. #if WATCH_DEBUG
  1640. #undef MyMalloc
  1641. #undef MyFree
  1642. DWORD TotalAllocs = 0;
  1643. DWORD TotalFrees = 0;
  1644. DWORD PeakAllocs = 0;
  1645. DWORD TotalAllocated = 0;
  1646. DWORD TotalFreed = 0;
  1647. DWORD PeakAllocated = 0;
  1648. PVOID
  1649. MyMallocEx (
  1650. DWORD Size
  1651. )
  1652. {
  1653. PVOID p = MyMalloc( Size + 8 );
  1654. if ( p == NULL ) {
  1655. dprintf( 0, ("MyMallocEx: failure allocating %d bytes\n", Size) );
  1656. DumpMallocStats("");
  1657. DbgBreakPoint();
  1658. return NULL;
  1659. }
  1660. TotalAllocs++;
  1661. if ( (TotalAllocs - TotalFrees) > PeakAllocs ) {
  1662. PeakAllocs = TotalAllocs - TotalFrees;
  1663. }
  1664. TotalAllocated += Size;
  1665. if ( (TotalAllocated - TotalFreed) > PeakAllocated ) {
  1666. PeakAllocated = TotalAllocated - TotalFreed;
  1667. }
  1668. *(PDWORD)p = Size;
  1669. return (PVOID)((PCHAR)p + 8);
  1670. }
  1671. VOID
  1672. MyFreeEx (
  1673. PVOID p
  1674. )
  1675. {
  1676. PVOID pp = (PVOID)((PCHAR)p - 8);
  1677. TotalFrees++;
  1678. TotalFreed += *(PDWORD)pp;
  1679. MyFree( pp );
  1680. }
  1681. VOID
  1682. DumpMallocStats (
  1683. PSZ Event
  1684. )
  1685. {
  1686. if ( *Event != 0 ) {
  1687. dprintf( 0, ("%s\n", Event) );
  1688. }
  1689. dprintf( 0, ("Allocations %d, frees %d, active allocs %d\n",
  1690. TotalAllocs, TotalFrees, TotalAllocs - TotalFrees) );
  1691. dprintf( 0, ("Bytes allocated %d, bytes freed %d, active bytes %d\n",
  1692. TotalAllocated, TotalFreed, TotalAllocated - TotalFreed) );
  1693. dprintf( 0, ("Peak allocs %d, peak bytes %d\n",
  1694. PeakAllocs, PeakAllocated) );
  1695. return;
  1696. }
  1697. #endif
  1698. typedef HRESULT (*PFNSHGETFOLDERPATH)(HWND hwnd, int csidl, HANDLE hToken, DWORD dwType, LPTSTR pszPath);
  1699. BOOL
  1700. GetSpecialFolderPath (
  1701. IN INT csidl,
  1702. IN LPWSTR lpPath
  1703. )
  1704. /*++
  1705. Routine Description:
  1706. Gets the path to the requested special folder.
  1707. (This function was copied from userenv.dll)
  1708. Arguments:
  1709. csid - CSIDL of the special folder
  1710. lpPath - Path to place result in assumed to be MAX_PATH in size
  1711. Return Value:
  1712. TRUE if successful
  1713. FALSE if an error occurs
  1714. --*/
  1715. {
  1716. HRESULT hResult;
  1717. HINSTANCE hInstShell32;
  1718. PFNSHGETFOLDERPATH pfnSHGetFolderPath;
  1719. //
  1720. // Load the function we need
  1721. //
  1722. hInstShell32 = LoadLibrary(L"shell32.dll");
  1723. if (!hInstShell32) {
  1724. SetupDebugPrint1( L"SETUP: GetSpecialFolderPath() failed to load shell32. Error = %d.", GetLastError() );
  1725. return FALSE;
  1726. }
  1727. pfnSHGetFolderPath = (PFNSHGETFOLDERPATH)GetProcAddress (hInstShell32, "SHGetFolderPathW");
  1728. if (!pfnSHGetFolderPath) {
  1729. SetupDebugPrint1( L"SETUP: GetSpecialFolderPath() failed to find SHGetFolderPath(). Error = %d.", GetLastError() );
  1730. FreeLibrary (hInstShell32);
  1731. return FALSE;
  1732. }
  1733. //
  1734. // Ask the shell for the folder location
  1735. //
  1736. hResult = pfnSHGetFolderPath (
  1737. NULL,
  1738. csidl | CSIDL_FLAG_CREATE,
  1739. (HANDLE) -1, // this specifies .Default
  1740. 0,
  1741. lpPath);
  1742. if (S_OK != hResult) {
  1743. SetupDebugPrint1( L"SETUP: GetSpecialFolderPath: SHGetFolderPath() returned %d.", hResult );
  1744. }
  1745. //
  1746. // Clean up
  1747. //
  1748. FreeLibrary (hInstShell32);
  1749. return (S_OK == hResult);
  1750. }