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.

1257 lines
38 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. dirwatch.c
  5. Abstract:
  6. Implementation of directory watcher and file list manipulation.
  7. Author:
  8. Wesley Witt (wesw) 18-Dec-1998
  9. Revision History:
  10. Andrew Ritz (andrewr) 6-Jul-1999 : added comments
  11. --*/
  12. #include "sfcp.h"
  13. #pragma hdrstop
  14. #include <ntrpcp.h>
  15. #include "sfcapi.h"
  16. #include "sxsapi.h"
  17. //
  18. // List of directories being watched. The assumption is that we are
  19. // protecting many files in few directories, so a linked list of directories
  20. // is fine, while something a bit more heavy duty is necessary to traverse the
  21. // number of files we are watching
  22. //
  23. LIST_ENTRY SfcWatchDirectoryList;
  24. //
  25. // count of directories being watched
  26. //
  27. ULONG WatchDirectoryListCount;
  28. //
  29. // b-tree of filenames for quick sorting
  30. //
  31. NAME_TREE FileTree;
  32. //
  33. // handle to the thread that watches directories for changes
  34. //
  35. HANDLE WatcherThread;
  36. //
  37. // Instance of the WinSxS that we are providing protection for.
  38. //
  39. HMODULE SxsDllInstance = NULL;
  40. //
  41. // This function gets called back when a change is noticed in the SXS
  42. // protected functions.
  43. //
  44. PSXS_PROTECT_NOTIFICATION SxsNotification = NULL;
  45. //
  46. // This function is called once to let SXS offer a list of protected
  47. // directories.
  48. //
  49. PSXS_PROTECT_RETRIEVELISTS SxsGatherLists = NULL;
  50. //
  51. // Notification functions from sfcp.h
  52. //
  53. PSXS_PROTECT_LOGIN_EVENT SxsLogonEvent = NULL;
  54. PSXS_PROTECT_LOGIN_EVENT SxsLogoffEvent = NULL;
  55. PSXS_PROTECT_SCAN_ONCE SxsScanForcedFunc = NULL;
  56. VOID
  57. SfcShutdownSxsProtection(
  58. void
  59. )
  60. {
  61. SxsNotification = NULL;
  62. SxsGatherLists = NULL;
  63. SxsLogonEvent = NULL;
  64. SxsLogoffEvent = NULL;
  65. SxsScanForcedFunc = NULL;
  66. if ( NULL != SxsDllInstance )
  67. {
  68. FreeLibrary( SxsDllInstance );
  69. SxsDllInstance = NULL;
  70. }
  71. }
  72. BOOL
  73. SfcLoadSxsProtection(
  74. void
  75. )
  76. /*++
  77. Routine Description:
  78. Loads and initializes the SxS protection system into the overall list of
  79. directory entries to watch.
  80. Arguments:
  81. None.
  82. Return Value:
  83. NTSTATUS indicating whether or not the entire SxS watching system was
  84. initialized or not. Failure of this function is not necesarily a complete
  85. failure of the SFC functionality, but it should be logged somewhere.
  86. --*/
  87. {
  88. SIZE_T cProtectList = 0;
  89. SIZE_T iIndex;
  90. SIZE_T cbDirectory;
  91. DWORD dwLastError = 0;
  92. HANDLE hDirectory;
  93. PSXS_PROTECT_DIRECTORY pProtectList = NULL;
  94. PSFC_REGISTRY_VALUE pDirectory;
  95. PSXS_PROTECT_DIRECTORY pSxsItem;
  96. BOOL bOk = FALSE;
  97. const static WCHAR cwszFailMessage[] = L"Failed to load SxS.DLL: %ls";
  98. // If someone else has already loaded us, we don't really need to go and
  99. // load sxs again.
  100. if ( SxsDllInstance != NULL ) {
  101. DebugPrint1( LVL_MINIMAL, L"SFC:%s - SxS.DLL is already loaded.", __FUNCTION__ );
  102. bOk = TRUE;
  103. goto Exit;
  104. }
  105. ASSERT( NULL == SxsDllInstance );
  106. ASSERT( NULL == SxsNotification );
  107. ASSERT( NULL == SxsGatherLists );
  108. if ( NULL == ( SxsDllInstance = LoadLibraryW( L"sxs.dll" ) ) ) {
  109. DebugPrint1( LVL_MINIMAL, cwszFailMessage, L"LoadLibrary" );
  110. goto Exit;
  111. }
  112. if ( NULL == ( SxsNotification = (PSXS_PROTECT_NOTIFICATION)GetProcAddress( SxsDllInstance, PFN_NAME_PROTECTION_NOTIFY_CHANGE_W ) ) ) {
  113. DebugPrint1( LVL_MINIMAL, cwszFailMessage, L"GetProcAddress(SxsNotification)" );
  114. goto Exit;
  115. }
  116. if ( NULL == ( SxsGatherLists = (PSXS_PROTECT_RETRIEVELISTS)GetProcAddress( SxsDllInstance, PFN_NAME_PROTECTION_GATHER_LISTS_W ) ) ) {
  117. DebugPrint1( LVL_MINIMAL, cwszFailMessage, L"GetProcAddress(SxsGatherLists)" );
  118. goto Exit;
  119. }
  120. if ( NULL == ( SxsLogonEvent = (PSXS_PROTECT_LOGIN_EVENT)GetProcAddress( SxsDllInstance, PFN_NAME_PROTECTION_NOTIFY_LOGON ) ) ) {
  121. DebugPrint1( LVL_MINIMAL, cwszFailMessage, L"GetProcAddress(SxsLogonEvent)" );
  122. goto Exit;
  123. }
  124. if ( NULL == ( SxsLogoffEvent = (PSXS_PROTECT_LOGIN_EVENT)GetProcAddress( SxsDllInstance, PFN_NAME_PROTECTION_NOTIFY_LOGOFF ) ) ) {
  125. DebugPrint1( LVL_MINIMAL, cwszFailMessage, L"GetProcAddress(SxsLogoffEvent)" );
  126. goto Exit;
  127. }
  128. if ( NULL == ( SxsScanForcedFunc = (PSXS_PROTECT_SCAN_ONCE)GetProcAddress( SxsDllInstance, PFN_NAME_PROTECTION_SCAN_ONCE ) ) ) {
  129. DebugPrint1( LVL_MINIMAL, cwszFailMessage, L"GetProcAddress(SxsScanForcedFunc)" );
  130. goto Exit;
  131. }
  132. //
  133. // Ensure that all is OK - something bad happened if this is true.
  134. //
  135. ASSERT( ( NULL != SxsDllInstance ) && ( NULL != SxsNotification ) && ( NULL != SxsGatherLists ) );
  136. if ( !SxsGatherLists( &pProtectList, &cProtectList ) ) {
  137. DebugPrint1( LVL_MINIMAL, cwszFailMessage, L"SxsGatherLists" );
  138. goto Exit;
  139. }
  140. //
  141. // Loop across all the entries in the returned list of items, adding them to our
  142. // protection list as we go.
  143. //
  144. for ( iIndex = 0; iIndex < cProtectList; iIndex++ ) {
  145. //
  146. // Create a new holder for the list entry
  147. //
  148. pSxsItem = &pProtectList[iIndex];
  149. cbDirectory = sizeof( SFC_REGISTRY_VALUE ) + MAX_PATH;
  150. pDirectory = (PSFC_REGISTRY_VALUE)MemAlloc( cbDirectory );
  151. if ( NULL == pDirectory ) {
  152. DebugPrint( LVL_MINIMAL, L"SfcLoadSxsProtection: Out of memory allocating new watch bucket" );
  153. goto Exit;
  154. }
  155. //
  156. // Set up string
  157. //
  158. ZeroMemory( pDirectory, cbDirectory );
  159. pDirectory->DirName.Length = (USHORT)wcslen( pSxsItem->pwszDirectory );
  160. pDirectory->DirName.MaximumLength = MAX_PATH;
  161. pDirectory->DirName.Buffer = (PWSTR)((PUCHAR)pDirectory + sizeof(SFC_REGISTRY_VALUE));
  162. //
  163. // Move the all-important SxS cookies to the watch list.
  164. //
  165. pDirectory->pvWinSxsCookie = pSxsItem->pvCookie;
  166. pDirectory->dwWinSxsFlags = pSxsItem->ulRecursiveFlag;
  167. //
  168. // Copy string
  169. //
  170. RtlCopyMemory( pDirectory->DirName.Buffer, pSxsItem->pwszDirectory, pDirectory->DirName.Length );
  171. //
  172. // If we're at a point where the protected directory exists, then
  173. // we should create the handle to it and go forth. Otherwise, we
  174. // might want to not do this at all.. but that would be odd that
  175. // the directory is toast at this point.
  176. //
  177. MakeDirectory( pSxsItem->pwszDirectory );
  178. hDirectory = SfcOpenDir( TRUE, FALSE, pSxsItem->pwszDirectory );
  179. if ( NULL != hDirectory ) {
  180. InsertTailList( &SfcWatchDirectoryList, &pDirectory->Entry );
  181. pDirectory->DirHandle = hDirectory;
  182. WatchDirectoryListCount += 1;
  183. } else {
  184. DebugPrint1( LVL_MINIMAL, L"SfcLoadSxsProtection: Failed adding item %ls to the watch list.", pSxsItem->pwszDirectory );
  185. MemFree( pDirectory );
  186. }
  187. }
  188. bOk = TRUE;
  189. Exit:
  190. if ( !bOk )
  191. {
  192. if ( !SfcReportEvent( MSG_SXS_INITIALIZATION_FAILED, NULL, NULL, GetLastError() ) )
  193. {
  194. DebugPrint( LVL_MINIMAL, L"It's not our day - reporting that sxs initialization failed." );
  195. }
  196. SfcShutdownSxsProtection();
  197. }
  198. return bOk;
  199. }
  200. PVOID
  201. SfcFindProtectedFile(
  202. IN PCWSTR FileName,
  203. IN ULONG FileNameLength
  204. )
  205. /*++
  206. Routine Description:
  207. Routine to find a given file in our protected list.
  208. Arguments:
  209. FileName - name of file to look for. Note that this shoud be a fully
  210. qualified file path that has already been lowercased by the
  211. caller for performance reasons
  212. FileNameLength - length of the file buffer in bytes
  213. Return Value:
  214. a pointer to the files NAME_NODE if it is in the list, else NULL if the
  215. file is not in the list.
  216. --*/
  217. {
  218. ASSERT((FileName != NULL) && (FileNameLength > 0));
  219. return ((PVOID)BtreeFind( &FileTree, (PWSTR)FileName, FileNameLength ));
  220. }
  221. BOOL
  222. SfcBuildDirectoryWatchList(
  223. void
  224. )
  225. /*++
  226. Routine Description:
  227. Routine that builds up the list of directories to watch
  228. Arguments:
  229. None.
  230. Return Value:
  231. TRUE for success, FALSE if the list failed to be built for any reason.
  232. --*/
  233. {
  234. NTSTATUS Status;
  235. PSFC_REGISTRY_VALUE p;
  236. PSFC_REGISTRY_VALUE RegVal;
  237. ULONG i;
  238. ULONG Size;
  239. HANDLE DirHandle;
  240. HANDLE hFile;
  241. PLIST_ENTRY Entry;
  242. BOOLEAN Found;
  243. PNAME_NODE Node;
  244. //
  245. // initialize our lists
  246. //
  247. InitializeListHead( &SfcWatchDirectoryList );
  248. BtreeInit( &FileTree );
  249. SfcExceptionInfoInit();
  250. for (i=0; i<SfcProtectedDllCount; i++) {
  251. RegVal = &SfcProtectedDllsList[i];
  252. //
  253. // add the file to the btree if it's not already there
  254. //
  255. if (!SfcFindProtectedFile( RegVal->FullPathName.Buffer, RegVal->FullPathName.Length )) {
  256. Node = BtreeInsert( &FileTree, RegVal->FullPathName.Buffer, RegVal->FullPathName.Length );
  257. if (Node) {
  258. Node->Context = RegVal;
  259. } else {
  260. DebugPrint2( LVL_MINIMAL, L"failed to insert file %ws into btree, ec = %x", RegVal->FullPathName.Buffer, GetLastError() );
  261. }
  262. } else {
  263. DebugPrint1( LVL_VERBOSE, L"file %ws is protected more than once", RegVal->FullPathName.Buffer );
  264. }
  265. //
  266. // add the directory to the list of directories to watch
  267. // but do not add a duplicate. we must search the existing list
  268. // for duplicates first.
  269. //
  270. Entry = SfcWatchDirectoryList.Flink;
  271. Found = FALSE;
  272. while (Entry != &SfcWatchDirectoryList) {
  273. p = CONTAINING_RECORD( Entry, SFC_REGISTRY_VALUE, Entry );
  274. Entry = Entry->Flink;
  275. if (_wcsicmp( p->DirName.Buffer, RegVal->DirName.Buffer ) == 0) {
  276. Found = TRUE;
  277. break;
  278. }
  279. }
  280. if (Found) {
  281. ASSERT( p->DirHandle != NULL );
  282. RegVal->DirHandle = p->DirHandle;
  283. } else {
  284. //
  285. // go ahead and add it to the list
  286. //
  287. Size = sizeof(SFC_REGISTRY_VALUE) + RegVal->DirName.MaximumLength;
  288. p = (PSFC_REGISTRY_VALUE) MemAlloc( Size );
  289. if (p == NULL) {
  290. DebugPrint1( LVL_VERBOSE, L"failed to allocate %x bytes for new directory", Size );
  291. return(FALSE);
  292. }
  293. ZeroMemory(p, Size);
  294. p->DirName.Length = RegVal->DirName.Length;
  295. p->DirName.MaximumLength = RegVal->DirName.MaximumLength;
  296. //
  297. // point string buffer at end of registry value structure
  298. //
  299. p->DirName.Buffer = (PWSTR)((PUCHAR)p + sizeof(SFC_REGISTRY_VALUE));
  300. //
  301. // copy the directory name into the buffer
  302. //
  303. RtlCopyMemory( p->DirName.Buffer, RegVal->DirName.Buffer, RegVal->DirName.Length );
  304. //
  305. // Make sure the directory exists before we start protecting it.
  306. //
  307. //
  308. // NTRAID#97842-2000/03/29-andrewr
  309. // This isn't such a great solution since it creates
  310. // directories that the user might not want on the system
  311. //
  312. MakeDirectory( p->DirName.Buffer );
  313. DirHandle = SfcOpenDir( TRUE, FALSE, p->DirName.Buffer );
  314. if (DirHandle) {
  315. InsertTailList( &SfcWatchDirectoryList, &p->Entry );
  316. RegVal->DirHandle = DirHandle;
  317. p->DirHandle = DirHandle;
  318. WatchDirectoryListCount += 1;
  319. DebugPrint1( LVL_MINIMAL, L"Adding watch directory %ws", RegVal->DirName.Buffer );
  320. } else {
  321. DebugPrint1( LVL_MINIMAL, L"failed to add watch directory %ws", RegVal->DirName.Buffer );
  322. MemFree( p );
  323. }
  324. }
  325. //
  326. // special case: ntoskrnl and hal, which are both renamed from multiple
  327. // sources; we're not sure what source file name should be. To work
  328. // around this, we look in the version resource in these files for the
  329. // original install name, which gives us what we're looking for
  330. //
  331. if (_wcsicmp( RegVal->FileName.Buffer, L"ntoskrnl.exe" ) == 0 ||
  332. _wcsicmp( RegVal->FileName.Buffer, L"ntkrnlpa.exe" ) == 0 ||
  333. _wcsicmp( RegVal->FileName.Buffer, L"hal.dll" ) == 0)
  334. {
  335. Status = SfcOpenFile( &RegVal->FileName, RegVal->DirHandle, SHARE_ALL, &hFile );
  336. if (NT_SUCCESS(Status) ) {
  337. SfcGetFileVersion( hFile, NULL, NULL, RegVal->OriginalFileName );
  338. NtClose( hFile );
  339. }
  340. }
  341. }
  342. //
  343. // Ask WinSxs for anything that they want to watch.
  344. //
  345. if ( SfcLoadSxsProtection() ) {
  346. DebugPrint( LVL_MINIMAL, L"Loaded SXS protection lists entirely." );
  347. } else {
  348. DebugPrint( LVL_MINIMAL, L"Failed to load SXS protection! Assemblies will not be safe." );
  349. }
  350. return(TRUE);
  351. }
  352. BOOL
  353. SfcStartDirWatch(
  354. IN PDIRECTORY_WATCH_DATA dwd
  355. )
  356. /*++
  357. Routine Description:
  358. Routine that starts up the directory watch for the specified directory. We
  359. take our open directory handle to each of the directories and ask for
  360. pending change notifications.
  361. Arguments:
  362. dwd - pointer to the DIRECTORY_WATCH_DATA for the specified directory
  363. Return Value:
  364. TRUE for success, FALSE if the pending notification failed to be setup.
  365. --*/
  366. {
  367. NTSTATUS Status;
  368. BOOLEAN bWatchTree;
  369. ASSERT(dwd != NULL);
  370. ASSERT(dwd->DirHandle != NULL);
  371. ASSERT(dwd->DirEvent != NULL);
  372. //
  373. // If the watch directory is an SxS watched directory, then see if they want to
  374. // watch the directory recursively or not.
  375. //
  376. if ( ( dwd->WatchDirectory ) && ( NULL != dwd->WatchDirectory->pvWinSxsCookie ) ) {
  377. bWatchTree = ( ( dwd->WatchDirectory->dwWinSxsFlags & SXS_PROTECT_RECURSIVE ) == SXS_PROTECT_RECURSIVE );
  378. } else {
  379. bWatchTree = FALSE;
  380. }
  381. Status = NtNotifyChangeDirectoryFile(
  382. dwd->DirHandle, // Directory handle
  383. dwd->DirEvent, // Event
  384. NULL, // ApcRoutine
  385. NULL, // ApcContext
  386. &dwd->Iosb, // IoStatusBlock
  387. dwd->WatchBuffer, // Buffer
  388. WATCH_BUFFER_SIZE, // Buffer Size
  389. FILE_NOTIFY_FLAGS, // Flags
  390. bWatchTree // WatchTree
  391. );
  392. if (!NT_SUCCESS(Status)) {
  393. //
  394. // if we fail with STATUS_PENDING error code, try to wait for our
  395. // specified event to be signalled. otherwise something else is hosed.
  396. //
  397. if (Status == STATUS_PENDING) {
  398. Status = NtWaitForSingleObject(dwd->DirEvent,TRUE,NULL);
  399. if (!NT_SUCCESS(Status)) {
  400. DebugPrint1( LVL_MINIMAL, L"Wait for notify change STATUS_PENDING failed, ec=0x%08x", Status );
  401. return(FALSE);
  402. }
  403. } else {
  404. DebugPrint2( LVL_MINIMAL, L"Could not start watch on %ws - %x", dwd->WatchDirectory->DirName.Buffer, Status );
  405. return(FALSE);
  406. }
  407. }
  408. return(TRUE);
  409. }
  410. BOOL
  411. SfcCreateWatchDataEntry(
  412. IN PSFC_REGISTRY_VALUE WatchDirectory,
  413. OUT PDIRECTORY_WATCH_DATA dwd
  414. )
  415. /*++
  416. Routine Description:
  417. Routine takes our internal structure for directories and builds up a
  418. structure for asking for change notifications. We then start waiting
  419. for notifications.
  420. Arguments:
  421. WatchDirectory - pointer to SFC_REGISTRY_VALUE describing directory we want
  422. to begin watching
  423. dwd - pointer to DIRECTORY_WATCH_DATA for the specified data
  424. Return Value:
  425. TRUE for success, FALSE if the structure failed to be setup.
  426. --*/
  427. {
  428. NTSTATUS Status;
  429. ASSERT((WatchDirectory != NULL) && (dwd != NULL));
  430. ASSERT(WatchDirectory->DirHandle != NULL);
  431. //
  432. // the watch directory and directory handle are already created
  433. //
  434. dwd->WatchDirectory = WatchDirectory;
  435. dwd->DirHandle = WatchDirectory->DirHandle;
  436. //
  437. // we have to create the watch buffer
  438. //
  439. dwd->WatchBuffer = MemAlloc( WATCH_BUFFER_SIZE );
  440. if (dwd->WatchBuffer == NULL) {
  441. DebugPrint1( LVL_MINIMAL, L"SfcCreateWatchDataEntry: MemAlloc(%x) failed", WATCH_BUFFER_SIZE );
  442. goto err_exit;
  443. }
  444. RtlZeroMemory( dwd->WatchBuffer, WATCH_BUFFER_SIZE );
  445. //
  446. // we have to create an event that is signalled when something changes in
  447. // the directory
  448. //
  449. Status = NtCreateEvent(
  450. &dwd->DirEvent,
  451. EVENT_ALL_ACCESS,
  452. NULL,
  453. NotificationEvent,
  454. FALSE
  455. );
  456. if (!NT_SUCCESS(Status)) {
  457. DebugPrint1( LVL_MINIMAL, L"Unable to create dir event, ec=0x%08x", Status );
  458. goto err_exit;
  459. }
  460. //
  461. // now that the DIRECTORY_WATCH_DATA is built up, start watching for
  462. // changes
  463. //
  464. if (!SfcStartDirWatch(dwd)) {
  465. goto err_exit;
  466. }
  467. DebugPrint2( LVL_MINIMAL, L"Watching [%ws] with handle %x", WatchDirectory->DirName.Buffer, dwd->DirEvent );
  468. return(TRUE);
  469. err_exit:
  470. if (dwd->WatchBuffer) {
  471. MemFree( dwd->WatchBuffer );
  472. dwd->WatchBuffer = NULL;
  473. }
  474. if (dwd->DirEvent) {
  475. NtClose(dwd->DirEvent);
  476. dwd->DirEvent = NULL;
  477. }
  478. return(FALSE);
  479. }
  480. NTSTATUS
  481. SfcWatchProtectedDirectoriesWorkerThread(
  482. IN PWATCH_THREAD_PARAMS WatchParams
  483. )
  484. /*++
  485. Routine Description:
  486. Worker thread for SfcWatchProtectedDirectoriesThread. This routine
  487. watches the supplied handles for notification, then enqueues a verification
  488. request to the verification thread if necessary.
  489. Note that the code in between the wait being satisfied and watching
  490. for changes again must be as quick as possible. The time this code takes
  491. to run is a window here where we are NOT watching for changes in that
  492. directory.
  493. Arguments:
  494. WatchParams - pointer to a WATCH_THREAD_PARAMS structure which supplies
  495. the list of handles to be watched, etc.
  496. Return Value:
  497. NTSTATUS code indicating outcome.
  498. --*/
  499. {
  500. #if DBG
  501. #define EVENT_OFFSET 2
  502. #else
  503. #define EVENT_OFFSET 1
  504. #endif
  505. UNICODE_STRING FileName;
  506. //
  507. // if the list of notfications changes in sfcp.h, this list must also change!
  508. //
  509. DWORD am[] = { 0, SFC_ACTION_ADDED, SFC_ACTION_REMOVED, SFC_ACTION_MODIFIED, SFC_ACTION_RENAMED_OLD_NAME, SFC_ACTION_RENAMED_NEW_NAME };
  510. PLARGE_INTEGER pTimeout = NULL;
  511. BOOL IgnoreChanges = FALSE;
  512. PFILE_NOTIFY_INFORMATION fni = NULL;
  513. PDIRECTORY_WATCH_DATA dwd = WatchParams->DirectoryWatchList;
  514. PSFC_REGISTRY_VALUE RegVal;
  515. PNAME_NODE Node;
  516. PWSTR FullPathName = NULL;
  517. ULONG Len,tmp;
  518. DebugPrint( LVL_MINIMAL, L"Entering SfcWatchProtectedDirectoriesWorkerThread" );
  519. DebugPrint2( LVL_VERBOSE, L"watching %d events at %x ", WatchParams->HandleCount, WatchParams->HandleList );
  520. //
  521. // allocate a big scratch buffer for our notifications to get copied into
  522. //
  523. FullPathName = MemAlloc( (MAX_PATH * 2)*sizeof(WCHAR) );
  524. if (FullPathName == NULL) {
  525. DebugPrint( LVL_MINIMAL, L"Unable to allocate full path buffer" );
  526. goto exit;
  527. }
  528. RtlZeroMemory(FullPathName, (MAX_PATH * 2)*sizeof(WCHAR) );
  529. while (TRUE) {
  530. NTSTATUS WaitStatus;
  531. //
  532. // Wait for a change
  533. //
  534. WaitStatus = NtWaitForMultipleObjects(
  535. WatchParams->HandleCount, // Count
  536. WatchParams->HandleList, // Handles
  537. WaitAny, // WaitType
  538. TRUE, // Alertable
  539. pTimeout // Timeout
  540. );
  541. if (!NT_SUCCESS( WaitStatus )) {
  542. DebugPrint1( LVL_MINIMAL, L"WaitForMultipleObjects failed returning %x", WaitStatus );
  543. break;
  544. }
  545. if (WaitStatus == 0) {
  546. //
  547. // WatchTermEvent was signalled, exit loop
  548. //
  549. goto exit;
  550. }
  551. if (WaitStatus == STATUS_TIMEOUT) {
  552. //
  553. // we timed out
  554. //
  555. ASSERT(FALSE && "we should never get here since we never specified a timeout");
  556. IgnoreChanges = FALSE;
  557. pTimeout = NULL;
  558. continue;
  559. }
  560. #if DBG
  561. if (WaitStatus == 1) {
  562. DebugBreak();
  563. continue;
  564. }
  565. #endif
  566. if ((ULONG)WaitStatus >= WatchParams->HandleCount) {
  567. DebugPrint1( LVL_MINIMAL, L"Unknown success code for WaitForMultipleObjects",WaitStatus );
  568. goto exit;
  569. }
  570. // DebugPrint( LVL_MINIMAL, L"Wake up!!!" );
  571. //
  572. // one of the directories hit a notification, so we cycle
  573. // through the list of files that have changed in that directory
  574. //
  575. if (!IgnoreChanges) {
  576. //
  577. // check the io buffer for the list of files that changed
  578. //
  579. //
  580. // note that we have to offset the waitstatus by the EVENT_OFFSET
  581. // to get the correct offset into the DIRECTORY_WATCH_DATA array
  582. //
  583. ASSERT((INT)(WaitStatus-EVENT_OFFSET) >=0);
  584. fni = (PFILE_NOTIFY_INFORMATION) dwd[WaitStatus-EVENT_OFFSET].WatchBuffer;
  585. while (TRUE) {
  586. ULONG c;
  587. RtlZeroMemory(FullPathName, (MAX_PATH * 2)*sizeof(WCHAR) );
  588. //
  589. // We can short-circuit a large amount of this by checking to see
  590. // if the change is from a SxS-protected directory and notifying
  591. // them immediately.
  592. //
  593. if ( NULL != dwd[WaitStatus-EVENT_OFFSET].WatchDirectory->pvWinSxsCookie ) {
  594. ASSERT( SxsNotification != NULL );
  595. if ( SxsNotification ) {
  596. SxsNotification(
  597. dwd[WaitStatus-EVENT_OFFSET].WatchDirectory->pvWinSxsCookie,
  598. fni->FileName,
  599. fni->FileNameLength / sizeof( fni->FileName[0] ),
  600. fni->Action
  601. );
  602. DebugPrint( LVL_MINIMAL, L"Notified SxS about a change in their directory" );
  603. }
  604. goto LoopAgain;
  605. }
  606. wcscpy( FullPathName, dwd[WaitStatus-EVENT_OFFSET].WatchDirectory->DirName.Buffer );
  607. ASSERT(fni->FileName != NULL);
  608. //
  609. // FILE_NOTIFY_INFORMATION->FileName is not always a null
  610. // terminated string, so we copy the string using memmove.
  611. // the buffer already zero'ed out so the string will now be
  612. // NULL terminated
  613. //
  614. c = wcslen(FullPathName);
  615. if (FullPathName[c-1] != L'\\') {
  616. FullPathName[c] = L'\\';
  617. FullPathName[c+1] = L'\0';
  618. c++;
  619. }
  620. RtlMoveMemory( &FullPathName[c], fni->FileName, fni->FileNameLength);
  621. // DebugPrint3( LVL_VERBOSE, L"received a notification in directory %ws (%x) for %ws",
  622. //dwd[WaitStatus-EVENT_OFFSET].WatchDirectory->DirName.Buffer,
  623. //WatchParams->DirectoryWatchList[WaitStatus-EVENT_OFFSET].DirEvent,
  624. //FullPathName);
  625. Len = wcslen(FullPathName);
  626. MyLowerString( FullPathName, Len );
  627. // DebugPrint1( LVL_VERBOSE, L"Is %ws a protected file?", FullPathName );
  628. //
  629. // see if we found a protected file
  630. //
  631. Node = SfcFindProtectedFile( FullPathName, Len*sizeof(WCHAR) );
  632. if (Node) {
  633. RegVal = (PSFC_REGISTRY_VALUE)Node->Context;
  634. ASSERT(RegVal != NULL);
  635. #if DBG
  636. {
  637. PWSTR ActionString[] = { NULL, L"Added(1)", L"Removed(2)", L"Modified(3)", L"Rename-Old(4)", L"Rename-New(5)" };
  638. FileName.Buffer = FullPathName;
  639. FileName.Length = (USHORT)(Len*sizeof(WCHAR));
  640. FileName.MaximumLength = (USHORT)(FileName.Length
  641. + sizeof(UNICODE_NULL));
  642. DebugPrint2( LVL_MINIMAL,
  643. L"[%wZ] file changed (%ws)",
  644. &FileName,
  645. ActionString[fni->Action] );
  646. }
  647. #endif
  648. //
  649. // check if we're supposed to ignore this change
  650. // notification because someone exempted it
  651. //
  652. RtlEnterCriticalSection( &ErrorCs );
  653. tmp = SfcGetExemptionFlags(RegVal);
  654. RtlLeaveCriticalSection( &ErrorCs );
  655. if((tmp & am[fni->Action]) != 0 && SfcAreExemptionFlagsValid(FALSE)) {
  656. DebugPrint2( LVL_MINIMAL,
  657. L"[%wZ] f i (0x%x)",
  658. &FileName,
  659. tmp );
  660. } else {
  661. //
  662. // a protected file has changed so we queue up a
  663. // request to see if the file is still valid
  664. //
  665. SfcQueueValidationRequest( (PSFC_REGISTRY_VALUE)Node->Context, fni->Action );
  666. }
  667. }
  668. LoopAgain:
  669. //
  670. // point to the next file in the directory that has changed
  671. //
  672. if (fni->NextEntryOffset == 0) {
  673. break;
  674. }
  675. fni = (PFILE_NOTIFY_INFORMATION) ((ULONG_PTR)fni + fni->NextEntryOffset);
  676. }
  677. }
  678. //
  679. // Restart the notify for this directory now that we've cleared out
  680. // all of the changes.
  681. //
  682. if (!SfcStartDirWatch(&dwd[WaitStatus-EVENT_OFFSET])) {
  683. goto exit;
  684. }
  685. }
  686. exit:
  687. if (FullPathName) {
  688. MemFree( FullPathName );
  689. }
  690. return(STATUS_SUCCESS);
  691. }
  692. NTSTATUS
  693. SfcWatchProtectedDirectoriesThread(
  694. IN PVOID NotUsed
  695. )
  696. /*++
  697. Routine Description:
  698. Thread routine that performs watch/update loop. This routine opens
  699. up directory watch handles for each directory we're watching.
  700. Depending on the amount of directories (handles) we're watching, we require
  701. one or more worker threads that do the actual directory watching.
  702. Arguments:
  703. Unreferenced Parameter.
  704. Return Value:
  705. NTSTATUS code of any fatal error.
  706. --*/
  707. {
  708. #if DBG
  709. #define EVENT_OFFSET 2
  710. #else
  711. #define EVENT_OFFSET 1
  712. #endif
  713. PLIST_ENTRY Entry;
  714. ULONG i,j;
  715. PDIRECTORY_WATCH_DATA dwd = NULL;
  716. PSFC_REGISTRY_VALUE WatchDirectory = NULL;
  717. PHANDLE *HandlesArray;
  718. ULONG TotalHandleCount,CurrentHandleCount;
  719. ULONG TotalHandleThreads,CurrentHandleList;
  720. ULONG TotalHandleCountWithEvents;
  721. ULONG WatchCount = 0;
  722. PLARGE_INTEGER pTimeout = NULL;
  723. PWATCH_THREAD_PARAMS WorkerThreadParams;
  724. PHANDLE ThreadHandles;
  725. NTSTATUS WaitStatus,Status;
  726. UNREFERENCED_PARAMETER( NotUsed );
  727. //
  728. // now start protecting each of the directories in the system
  729. //
  730. DebugPrint1( LVL_MINIMAL, L"%d watch directories", WatchDirectoryListCount );
  731. //
  732. // allocate array of DIRECTORY_WATCH_DATA structures
  733. //
  734. i = sizeof(DIRECTORY_WATCH_DATA) * (WatchDirectoryListCount);
  735. dwd = MemAlloc( i );
  736. if (dwd == NULL) {
  737. DebugPrint1( LVL_MINIMAL, L"Unable to allocate directory watch table (%x bytes", i );
  738. SfcReportEvent( MSG_INITIALIZATION_FAILED, 0, NULL, ERROR_NOT_ENOUGH_MEMORY );
  739. return(STATUS_NO_MEMORY);
  740. }
  741. RtlZeroMemory(dwd,i);
  742. //
  743. // we can have more than MAXIMUM_WAIT_OBJECTS directory handles to watch
  744. // so we create an array of handle arrays, each of which contain at most
  745. // MAXIMUM_WAIT_OBJECTS handles to be watched
  746. //
  747. TotalHandleCount = WatchDirectoryListCount;
  748. CurrentHandleCount = 0;
  749. TotalHandleCountWithEvents = 0;
  750. TotalHandleThreads = 0;
  751. //
  752. // find out how many lists of handle's we'll need
  753. //
  754. while (CurrentHandleCount < TotalHandleCount) {
  755. if (CurrentHandleCount + (MAXIMUM_WAIT_OBJECTS - EVENT_OFFSET) < TotalHandleCount) {
  756. CurrentHandleCount += (MAXIMUM_WAIT_OBJECTS-EVENT_OFFSET);
  757. DebugPrint2( LVL_VERBOSE, L"incremented currenthandlecount (%d) by %d ", CurrentHandleCount, (MAXIMUM_WAIT_OBJECTS-EVENT_OFFSET) );
  758. } else {
  759. CurrentHandleCount += (TotalHandleCount-CurrentHandleCount);
  760. DebugPrint1( LVL_VERBOSE, L"incremented currenthandlecount (%d) ", CurrentHandleCount );
  761. }
  762. TotalHandleThreads += 1;
  763. }
  764. DebugPrint1( LVL_MINIMAL, L"we need %d worker threads", TotalHandleThreads );
  765. //
  766. // allocates space for each handle list pointer
  767. //
  768. HandlesArray = MemAlloc( sizeof(HANDLE *) * TotalHandleThreads );
  769. if (!HandlesArray) {
  770. MemFree(dwd);
  771. DebugPrint( LVL_MINIMAL, L"Unable to allocate HandlesArray" );
  772. SfcReportEvent( MSG_INITIALIZATION_FAILED, 0, NULL, ERROR_NOT_ENOUGH_MEMORY );
  773. return(STATUS_NO_MEMORY);
  774. }
  775. WorkerThreadParams = MemAlloc( sizeof(WATCH_THREAD_PARAMS) * TotalHandleThreads );
  776. if (!WorkerThreadParams) {
  777. MemFree(dwd);
  778. MemFree(HandlesArray);
  779. DebugPrint( LVL_MINIMAL, L"Unable to allocate WorkerThreadParams" );
  780. SfcReportEvent( MSG_INITIALIZATION_FAILED, 0, NULL, ERROR_NOT_ENOUGH_MEMORY );
  781. return(STATUS_NO_MEMORY);
  782. }
  783. ThreadHandles = MemAlloc( sizeof(HANDLE) * TotalHandleThreads );
  784. if (!ThreadHandles) {
  785. DebugPrint( LVL_MINIMAL, L"Unable to allocate ThreadHandles" );
  786. MemFree(dwd);
  787. MemFree(WorkerThreadParams);
  788. MemFree(HandlesArray);
  789. SfcReportEvent( MSG_INITIALIZATION_FAILED, 0, NULL, ERROR_NOT_ENOUGH_MEMORY );
  790. return(STATUS_NO_MEMORY);
  791. }
  792. //
  793. // now create a handle list at each element
  794. //
  795. CurrentHandleCount = 0;
  796. TotalHandleThreads = 0;
  797. while (CurrentHandleCount < TotalHandleCount) {
  798. if (CurrentHandleCount + (MAXIMUM_WAIT_OBJECTS - EVENT_OFFSET) < TotalHandleCount) {
  799. DebugPrint1( LVL_VERBOSE, L"current thread will have %d handles ", (MAXIMUM_WAIT_OBJECTS) );
  800. i = sizeof(HANDLE) * MAXIMUM_WAIT_OBJECTS;
  801. } else {
  802. DebugPrint1( LVL_VERBOSE, L"current thread will have %d handles", EVENT_OFFSET + (TotalHandleCount-CurrentHandleCount) );
  803. i = sizeof(HANDLE) * (EVENT_OFFSET + (TotalHandleCount-CurrentHandleCount));
  804. ASSERT((i/sizeof(HANDLE)) <= MAXIMUM_WAIT_OBJECTS);
  805. }
  806. HandlesArray[TotalHandleThreads] = MemAlloc( i );
  807. CurrentHandleCount += (i/sizeof(HANDLE))-EVENT_OFFSET;
  808. DebugPrint2( LVL_VERBOSE, L"CurrentHandlecount (%d) was incremented by %d ", CurrentHandleCount, (i/sizeof(HANDLE))-EVENT_OFFSET );
  809. //
  810. // if we failed the allocation, bail out
  811. //
  812. if (!HandlesArray[TotalHandleThreads]) {
  813. j = 0;
  814. while (j < TotalHandleThreads) {
  815. MemFree( HandlesArray[j] );
  816. j++;
  817. }
  818. MemFree(dwd);
  819. MemFree(ThreadHandles);
  820. MemFree(WorkerThreadParams);
  821. MemFree(HandlesArray);
  822. SfcReportEvent( MSG_INITIALIZATION_FAILED, 0, NULL, ERROR_NOT_ENOUGH_MEMORY );
  823. return(STATUS_NO_MEMORY);
  824. }
  825. //
  826. // each list of handles has these two events at the start of their list
  827. //
  828. HandlesArray[TotalHandleThreads][0] = WatchTermEvent;
  829. #if DBG
  830. HandlesArray[TotalHandleThreads][1] = SfcDebugBreakEvent;
  831. #endif
  832. //
  833. // save off the current handle list for the worker thread along with
  834. // the number of handles that the worker thread will be watching
  835. //
  836. WorkerThreadParams[TotalHandleThreads].HandleList = HandlesArray[TotalHandleThreads];
  837. WorkerThreadParams[TotalHandleThreads].HandleCount = (i / sizeof(HANDLE));
  838. //
  839. // save off the directory watch list structure for the worker thread,
  840. // remembering that each thread can have at most
  841. // (MAXIMUM_WAIT_OBJECTS-EVENT_OFFSET) directory watch elements
  842. //
  843. WorkerThreadParams[TotalHandleThreads].DirectoryWatchList = &dwd[(TotalHandleThreads*(MAXIMUM_WAIT_OBJECTS-EVENT_OFFSET))];
  844. //
  845. // save off the total number of events we're watching
  846. //
  847. TotalHandleCountWithEvents += WorkerThreadParams[TotalHandleThreads].HandleCount;
  848. TotalHandleThreads += 1;
  849. }
  850. //
  851. // Open the protected directories and start a watch on each, inserting
  852. // the handle into the proper handle list
  853. //
  854. CurrentHandleCount = 0;
  855. CurrentHandleList = 0;
  856. WatchCount = 0;
  857. Entry = SfcWatchDirectoryList.Flink;
  858. while (Entry != &SfcWatchDirectoryList) {
  859. WatchDirectory = CONTAINING_RECORD( Entry, SFC_REGISTRY_VALUE, Entry );
  860. if (SfcCreateWatchDataEntry(WatchDirectory,&dwd[WatchCount])) {
  861. //
  862. // save off a pointer to the directory we're watching into the
  863. // handles array, remembering that the start of each
  864. // handles list contains EVENT_OFFSET events that we don't want
  865. // to overwrite
  866. //
  867. HandlesArray[CurrentHandleList][CurrentHandleCount+EVENT_OFFSET] = dwd[WatchCount].DirEvent;
  868. WatchCount += 1;
  869. CurrentHandleCount += 1;
  870. if (CurrentHandleCount + EVENT_OFFSET > MAXIMUM_WAIT_OBJECTS - 1) {
  871. CurrentHandleList += 1;
  872. CurrentHandleCount = 0;
  873. }
  874. }
  875. Entry = Entry->Flink;
  876. }
  877. DebugPrint1( LVL_MINIMAL, L"%d directories being watched", WatchCount );
  878. if (WatchCount != WatchDirectoryListCount) {
  879. DebugPrint2( LVL_MINIMAL,
  880. L"The number of directories to be watched (%d) does not match the actual number of directories being watched (%d)",
  881. WatchDirectoryListCount,
  882. WatchCount );
  883. }
  884. //
  885. // we're ready to start watching directories, so now initialize the rpc
  886. // server
  887. //
  888. RpcpInitRpcServer();
  889. Status = RpcpStartRpcServer( L"SfcApi", SfcSrv_sfcapi_ServerIfHandle );
  890. if (! NT_SUCCESS(Status)) {
  891. DebugPrint1( LVL_MINIMAL,
  892. L"Start Rpc Server failed, ec = 0x%08x\n",
  893. Status
  894. );
  895. goto exit;
  896. }
  897. //
  898. // create a worker thread to monitor each of the handle lists
  899. //
  900. for (CurrentHandleList = 0,CurrentHandleCount = 0; CurrentHandleList < TotalHandleThreads; CurrentHandleList++) {
  901. ThreadHandles[CurrentHandleList] = CreateThread(
  902. NULL,
  903. 0,
  904. SfcWatchProtectedDirectoriesWorkerThread,
  905. &WorkerThreadParams[CurrentHandleList],
  906. 0,
  907. NULL
  908. );
  909. if (!ThreadHandles[CurrentHandleList]) {
  910. DebugPrint1( LVL_MINIMAL,
  911. L"Failed to create SfcWatchProtectedDirectoriesWorkerThread, ec = %x",
  912. GetLastError() );
  913. Status = STATUS_UNSUCCESSFUL;
  914. goto exit;
  915. }
  916. }
  917. //
  918. // wait for the worker threads to all exit
  919. //
  920. WaitStatus = NtWaitForMultipleObjects(
  921. TotalHandleThreads, // Count
  922. ThreadHandles, // Handles
  923. WaitAll, // WaitType
  924. TRUE, // Alertable
  925. pTimeout // Timeout
  926. );
  927. if (!NT_SUCCESS(WaitStatus)) {
  928. SfcReportEvent( MSG_INITIALIZATION_FAILED, 0, NULL, ERROR_INVALID_PARAMETER );
  929. DebugPrint1( LVL_MINIMAL, L"WaitForMultipleObjects failed returning %x", WaitStatus );
  930. goto exit;
  931. }
  932. DebugPrint( LVL_MINIMAL, L"all worker threads have signalled their exit" );
  933. Status = STATUS_SUCCESS;
  934. exit:
  935. //
  936. // cleanup and return
  937. //
  938. if (HandlesArray) {
  939. j=0;
  940. while (j < TotalHandleThreads) {
  941. MemFree( HandlesArray[j] );
  942. NtClose(ThreadHandles[j]);
  943. j++;
  944. }
  945. MemFree( HandlesArray );
  946. MemFree(WorkerThreadParams);
  947. }
  948. if (dwd) {
  949. for (i=0; i<WatchDirectoryListCount; i++) {
  950. NtClose( dwd[i].DirHandle );
  951. NtClose( dwd[i].DirEvent );
  952. MemFree( dwd[i].WatchBuffer );
  953. }
  954. MemFree( dwd );
  955. //
  956. // now clean out any references to these directory handles in the
  957. // protected dll list
  958. //
  959. for (i=0;i<SfcProtectedDllCount;i++) {
  960. PSFC_REGISTRY_VALUE RegVal;
  961. RegVal = &SfcProtectedDllsList[i];
  962. RegVal->DirHandle = NULL;
  963. }
  964. }
  965. if (SfcProtectedDllFileDirectory) {
  966. NtClose( SfcProtectedDllFileDirectory );
  967. }
  968. DebugPrint( LVL_MINIMAL, L"SfcWatchProtectedDirectoriesThread terminating" );
  969. return(Status);
  970. }
  971. NTSTATUS
  972. SfcStartProtectedDirectoryWatch(
  973. void
  974. )
  975. /*++
  976. Routine Description:
  977. Create asynchronous directory notifications on SYSTEM32 and SYSTEM32\DRIVERS
  978. to look for notifications. Create a thread that waits on changes from either.
  979. Arguments:
  980. None.
  981. Return Value:
  982. NTSTATUS code indicating outcome.
  983. --*/
  984. {
  985. //
  986. // Create watcher thread
  987. //
  988. WatcherThread = CreateThread(
  989. NULL,
  990. 0,
  991. (LPTHREAD_START_ROUTINE)SfcWatchProtectedDirectoriesThread,
  992. 0,
  993. 0,
  994. NULL
  995. );
  996. if (WatcherThread == NULL) {
  997. DebugPrint1( LVL_MINIMAL, L"Unable to create watcher thread, ec=%d", GetLastError() );
  998. return(STATUS_UNSUCCESSFUL);
  999. }
  1000. return(STATUS_SUCCESS);
  1001. }