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.

3214 lines
95 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. Notify.c
  5. Abstract:
  6. The Notify package provides support to filesystems which implement
  7. NotifyChangeDirectory. This package will manage a queue of notify
  8. blocks which are attached to some filesystem structure (i.e. Vcb
  9. in Fat, HPFS). The filesystems will allocate a fast mutex to be used
  10. by this package to synchronize access to the notify queue.
  11. The following routines are provided by this package:
  12. o FsRtlNotifyInitializeSync - Create and initializes the
  13. synchronization object.
  14. o FsrtlNotifyUninitializeSync - Deallocates the synchronization
  15. object.
  16. o FsRtlNotifyChangeDirectory - This routine is called whenever the
  17. filesystems receive a NotifyChangeDirectoryFile call. This
  18. routine allocates any neccessary structures and places the
  19. Irp in the NotifyQueue (or possibly completes or cancels it
  20. immediately).
  21. o FsRtlNotifyFullChangeDirectory - This routine is called whenever the
  22. filesystems receive a NotifyChangeDirectoryFile call. This differs
  23. from the FsRtlNotifyChangeDirectory in that it expects to return
  24. the notify information in the user's buffer.
  25. o FsRtlNotifyFilterChangeDirectory - This routine is called whenever the
  26. filesystems receive a NotifyChangeDirectoryFile call. This differs
  27. from the FsRtlNotifyFullChangeDirectory in that it accepts a
  28. FilterRoutine Callback.
  29. o FsRtlNotifyReportChange - This routine is called by the
  30. filesystems whenever they perform some operation that could
  31. cause the completion of a notify operation. This routine will
  32. walk through the notify queue to see if any Irps are affected
  33. by the indicated operation.
  34. o FsRtlNotifyFullReportChange - This routine is called by the
  35. filesystems whenever they perform some operation that could
  36. cause the completion of a notify operation. This routine differs
  37. from the FsRtlNotifyReportChange call in that it returns more
  38. detailed information in the caller's buffer if present.
  39. o FsRtlNotifyFilterReportChange - This routine is called by the
  40. filesystems whenever they perform some operation that could
  41. cause the completion of a notify operation. This routine differs
  42. from the FsRtlNotifyFullReportChange call in that it accepts a
  43. FilterContext parameter for notifyees who specified a FilterRoutine.
  44. o FsRtlNotifyCleanup - This routine is called to remove any
  45. references to a particular FsContext structure from the notify
  46. queue. If the matching FsContext structure is found in the
  47. queue, then all associated Irps are completed.
  48. Author:
  49. Brian Andrew [BrianAn] 9-19-1991
  50. Revision History:
  51. --*/
  52. #include "FsRtlP.h"
  53. //
  54. // Trace level for the module
  55. //
  56. #define Dbg (0x04000000)
  57. //
  58. // This is the synchronization object for the notify package. The caller
  59. // given a pointer to this structure.
  60. //
  61. typedef struct _REAL_NOTIFY_SYNC {
  62. FAST_MUTEX FastMutex;
  63. ERESOURCE_THREAD OwningThread;
  64. ULONG OwnerCount;
  65. } REAL_NOTIFY_SYNC, *PREAL_NOTIFY_SYNC;
  66. //
  67. // A list of the following structures is used to store the NotifyChange
  68. // requests. They are linked to a filesystem-defined list head.
  69. //
  70. typedef struct _NOTIFY_CHANGE {
  71. //
  72. // Fast Mutex. This fast mutex is used to access the list containing this
  73. // structure.
  74. //
  75. PREAL_NOTIFY_SYNC NotifySync;
  76. //
  77. // FsContext. This value is given by the filesystems to uniquely
  78. // identify this structure. The identification is on a
  79. // per-user file object basis. The expected value is the Ccb address
  80. // for this user file object.
  81. //
  82. PVOID FsContext;
  83. //
  84. // StreamID. This value matches the FsContext field in the file object for
  85. // the directory being watched. This is used to identify the directory stream
  86. // when the directory is being deleted.
  87. //
  88. PVOID StreamID;
  89. //
  90. // TraverseAccessCallback. This is the filesystem-supplied routine used
  91. // to call back into the filesystem to check whether the caller has traverse
  92. // access when watching a sub-directory. Only applies when watching a
  93. // sub-directory.
  94. //
  95. PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback;
  96. //
  97. // SubjectContext. If the caller specifies a traverse callback routine
  98. // we will need to pass the Security Context from the thread which
  99. // originated this call. The notify package will free this structure
  100. // on tearing down the notify package. We don't expect to need this
  101. // structure often.
  102. //
  103. PSECURITY_SUBJECT_CONTEXT SubjectContext;
  104. //
  105. // Full Directory Name. The following string is the full directory
  106. // name of the directory being watched. It is used during watch tree
  107. // operations to check whether this directory is an ancestor of
  108. // the modified file. The string could be in ANSI or UNICODE form.
  109. //
  110. PSTRING FullDirectoryName;
  111. //
  112. // Notify List. The following field links the notify structures for
  113. // a particular volume.
  114. //
  115. LIST_ENTRY NotifyList;
  116. //
  117. // Notify Irps. The following field links the Irps associated with
  118. //
  119. //
  120. LIST_ENTRY NotifyIrps;
  121. //
  122. // FilterCallback. This is the filesystem-supplied routine used
  123. // to call back into the filesystem to check whether a Notify block
  124. // should see the change. (Initially added for TxfNtfs development
  125. // as part of a strategy to control when, not if, transactions see
  126. // changes from other transactions.)
  127. //
  128. PFILTER_REPORT_CHANGE FilterCallback;
  129. //
  130. // Flags. State of the notify for this volume.
  131. //
  132. USHORT Flags;
  133. //
  134. // Character size. Larger size indicates unicode characters.
  135. // unicode names.
  136. //
  137. UCHAR CharacterSize;
  138. //
  139. // Completion Filter. This field is used to mask the modification
  140. // actions to determine whether to complete the notify irp.
  141. //
  142. ULONG CompletionFilter;
  143. //
  144. // The following values are used to manage a buffer if there is no current
  145. // Irp to complete. The fields have the following meaning:
  146. //
  147. // AllocatedBuffer - Buffer we need to allocate
  148. // Buffer - Buffer to store data in
  149. // BufferLength - Length of original user buffer
  150. // ThisBufferLength - Length of the buffer we are using
  151. // DataLength - Current length of the data in the buffer
  152. // LastEntry - Offset of previous entry in the buffer
  153. //
  154. PVOID AllocatedBuffer;
  155. PVOID Buffer;
  156. ULONG BufferLength;
  157. ULONG ThisBufferLength;
  158. ULONG DataLength;
  159. ULONG LastEntry;
  160. //
  161. // Reference count which keeps the notify structure around. Such references include
  162. //
  163. // - Lifetime reference. Count set to one initially and removed on cleanup
  164. // - Cancel reference. Reference the notify struct when storing the cancel routine
  165. // in the Irp. The routine which actually clears the routine will decrement
  166. // this value.
  167. //
  168. ULONG ReferenceCount;
  169. //
  170. // This is the process on whose behalf the structure was allocated. We
  171. // charge any quota to this process.
  172. //
  173. PEPROCESS OwningProcess;
  174. } NOTIFY_CHANGE, *PNOTIFY_CHANGE;
  175. #define NOTIFY_WATCH_TREE (0x0001)
  176. #define NOTIFY_IMMEDIATE_NOTIFY (0x0002)
  177. #define NOTIFY_CLEANUP_CALLED (0x0004)
  178. #define NOTIFY_DEFER_NOTIFY (0x0008)
  179. #define NOTIFY_DIR_IS_ROOT (0x0010)
  180. #define NOTIFY_STREAM_IS_DELETED (0x0020)
  181. //
  182. // CAST
  183. // Add2Ptr (
  184. // IN PVOID Pointer,
  185. // IN ULONG Increment
  186. // IN (CAST)
  187. // );
  188. //
  189. // ULONG
  190. // PtrOffset (
  191. // IN PVOID BasePtr,
  192. // IN PVOID OffsetPtr
  193. // );
  194. //
  195. #define Add2Ptr(PTR,INC,CAST) ((CAST)((PUCHAR)(PTR) + (INC)))
  196. #define PtrOffset(BASE,OFFSET) ((ULONG)((PCHAR)(OFFSET) - (PCHAR)(BASE)))
  197. //
  198. // VOID
  199. // SetFlag (
  200. // IN ULONG Flags,
  201. // IN ULONG SingleFlag
  202. // );
  203. //
  204. // VOID
  205. // ClearFlag (
  206. // IN ULONG Flags,
  207. // IN ULONG SingleFlag
  208. // );
  209. //
  210. #define SetFlag(F,SF) { \
  211. (F) |= (SF); \
  212. }
  213. #define ClearFlag(F,SF) { \
  214. (F) &= ~(SF); \
  215. }
  216. //
  217. // VOID
  218. // AcquireNotifySync (
  219. // IN PREAL_NOTIFY_SYNC NotifySync
  220. // );
  221. //
  222. // VOID
  223. // ReleaseNotifySync (
  224. // IN PREAL_NOTIFY_SYNC NotifySync
  225. // );
  226. //
  227. #define AcquireNotifySync(NS) { \
  228. ERESOURCE_THREAD _CurrentThread; \
  229. _CurrentThread = (ERESOURCE_THREAD) PsGetCurrentThread(); \
  230. if (_CurrentThread != ((PREAL_NOTIFY_SYNC) (NS))->OwningThread) { \
  231. ExAcquireFastMutexUnsafe( &((PREAL_NOTIFY_SYNC) (NS))->FastMutex ); \
  232. ((PREAL_NOTIFY_SYNC) (NS))->OwningThread = _CurrentThread; \
  233. } \
  234. ((PREAL_NOTIFY_SYNC) (NS))->OwnerCount += 1; \
  235. }
  236. #define ReleaseNotifySync(NS) { \
  237. ((PREAL_NOTIFY_SYNC) (NS))->OwnerCount -= 1; \
  238. if (((PREAL_NOTIFY_SYNC) (NS))->OwnerCount == 0) { \
  239. ((PREAL_NOTIFY_SYNC) (NS))->OwningThread = (ERESOURCE_THREAD) 0; \
  240. ExReleaseFastMutexUnsafe(&((PREAL_NOTIFY_SYNC) (NS))->FastMutex); \
  241. } \
  242. }
  243. //
  244. // Define a tag for general pool allocations from this module
  245. //
  246. #undef MODULE_POOL_TAG
  247. #define MODULE_POOL_TAG ('NrSF')
  248. //
  249. // Local support routines
  250. //
  251. PNOTIFY_CHANGE
  252. FsRtlIsNotifyOnList (
  253. IN PLIST_ENTRY NotifyListHead,
  254. IN PVOID FsContext
  255. );
  256. VOID
  257. FsRtlNotifyCompleteIrp (
  258. IN PIRP NotifyIrp,
  259. IN PNOTIFY_CHANGE Notify,
  260. IN ULONG DataLength,
  261. IN NTSTATUS Status,
  262. IN ULONG CheckCancel
  263. );
  264. BOOLEAN
  265. FsRtlNotifySetCancelRoutine (
  266. IN PIRP NotifyIrp,
  267. IN PNOTIFY_CHANGE Notify OPTIONAL
  268. );
  269. BOOLEAN
  270. FsRtlNotifyUpdateBuffer (
  271. IN PFILE_NOTIFY_INFORMATION NotifyInfo,
  272. IN ULONG FileAction,
  273. IN PSTRING ParentName,
  274. IN PSTRING TargetName,
  275. IN PSTRING StreamName OPTIONAL,
  276. IN BOOLEAN UnicodeName,
  277. IN ULONG SizeOfEntry
  278. );
  279. VOID
  280. FsRtlNotifyCompleteIrpList (
  281. IN PNOTIFY_CHANGE Notify,
  282. IN NTSTATUS Status
  283. );
  284. VOID
  285. FsRtlCancelNotify (
  286. IN PDEVICE_OBJECT DeviceObject,
  287. IN PIRP ThisIrp
  288. );
  289. VOID
  290. FsRtlCheckNotifyForDelete (
  291. IN PLIST_ENTRY NotifyListHead,
  292. IN PVOID FsContext
  293. );
  294. #ifdef ALLOC_PRAGMA
  295. #pragma alloc_text(PAGE, FsRtlNotifyInitializeSync)
  296. #pragma alloc_text(PAGE, FsRtlNotifyUninitializeSync)
  297. #pragma alloc_text(PAGE, FsRtlNotifyFullChangeDirectory)
  298. #pragma alloc_text(PAGE, FsRtlNotifyFullReportChange)
  299. #pragma alloc_text(PAGE, FsRtlNotifyFilterChangeDirectory)
  300. #pragma alloc_text(PAGE, FsRtlNotifyFilterReportChange)
  301. #pragma alloc_text(PAGE, FsRtlIsNotifyOnList)
  302. #pragma alloc_text(PAGE, FsRtlNotifyChangeDirectory)
  303. #pragma alloc_text(PAGE, FsRtlNotifyCleanup)
  304. #pragma alloc_text(PAGE, FsRtlNotifyCompleteIrp)
  305. #pragma alloc_text(PAGE, FsRtlNotifyReportChange)
  306. #pragma alloc_text(PAGE, FsRtlNotifyUpdateBuffer)
  307. #pragma alloc_text(PAGE, FsRtlCheckNotifyForDelete)
  308. #pragma alloc_text(PAGE, FsRtlNotifyCompleteIrpList)
  309. #endif
  310. NTKERNELAPI
  311. VOID
  312. FsRtlNotifyInitializeSync (
  313. IN PNOTIFY_SYNC *NotifySync
  314. )
  315. /*++
  316. Routine Description:
  317. This routine is called to allocate and initialize the synchronization object
  318. for this notify list.
  319. Arguments:
  320. NotifySync - This is the address to store the structure we allocate.
  321. Return Value:
  322. None.
  323. --*/
  324. {
  325. PREAL_NOTIFY_SYNC RealSync;
  326. PAGED_CODE();
  327. DebugTrace( +1, Dbg, "FsRtlNotifyInitializeSync: Entered\n", 0 );
  328. //
  329. // Clear the pointer and then attempt to allocate a non-paged
  330. // structure.
  331. //
  332. *NotifySync = NULL;
  333. RealSync = (PREAL_NOTIFY_SYNC) FsRtlpAllocatePool( NonPagedPool,
  334. sizeof( REAL_NOTIFY_SYNC ));
  335. //
  336. // Initialize the structure.
  337. //
  338. ExInitializeFastMutex( &RealSync->FastMutex );
  339. RealSync->OwningThread = (ERESOURCE_THREAD) 0;
  340. RealSync->OwnerCount = 0;
  341. *NotifySync = (PNOTIFY_SYNC) RealSync;
  342. DebugTrace( -1, Dbg, "FsRtlNotifyInitializeSync: Exit\n", 0 );
  343. return;
  344. }
  345. NTKERNELAPI
  346. VOID
  347. FsRtlNotifyUninitializeSync (
  348. IN PNOTIFY_SYNC *NotifySync
  349. )
  350. /*++
  351. Routine Description:
  352. This routine is called to uninitialize the synchronization object
  353. for this notify list.
  354. Arguments:
  355. NotifySync - This is the address containing the pointer to our synchronization
  356. object.
  357. Return Value:
  358. None.
  359. --*/
  360. {
  361. PAGED_CODE();
  362. DebugTrace( +1, Dbg, "FsRtlNotifyUninitializeSync: Entered\n", 0 );
  363. //
  364. // Free the structure if present and clear the pointer.
  365. //
  366. if (*NotifySync != NULL) {
  367. ExFreePool( *NotifySync );
  368. *NotifySync = NULL;
  369. }
  370. DebugTrace( -1, Dbg, "FsRtlNotifyUninitializeSync: Exit\n", 0 );
  371. return;
  372. }
  373. VOID
  374. FsRtlNotifyChangeDirectory (
  375. IN PNOTIFY_SYNC NotifySync,
  376. IN PVOID FsContext,
  377. IN PSTRING FullDirectoryName,
  378. IN PLIST_ENTRY NotifyList,
  379. IN BOOLEAN WatchTree,
  380. IN ULONG CompletionFilter,
  381. IN PIRP NotifyIrp
  382. )
  383. /*++
  384. Routine Description:
  385. This routine is called by a file system which has received a NotifyChange
  386. request. This routine checks if there is already a notify structure and
  387. inserts one if not present. With a notify structure in hand, we check
  388. whether we already have a pending notify and report it if so. If there
  389. is no pending notify, we check if this Irp has already been cancelled and
  390. completes it if so. Otherwise we add this to the list of Irps waiting
  391. for notification.
  392. Arguments:
  393. NotifySync - This is the controlling fast mutex for this notify list.
  394. It is stored here so that it can be found for an Irp which is being
  395. cancelled.
  396. FsContext - This is supplied by the file system so that this notify
  397. structure can be uniquely identified.
  398. FullDirectoryName - Points to the full name for the directory associated
  399. with this notify structure.
  400. NotifyList - This is the start of the notify list to add this
  401. structure to.
  402. WatchTree - This indicates whether all subdirectories for this directory
  403. should be watched, or just the directory itself.
  404. CompletionFilter - This provides the mask to determine which operations
  405. will trigger the notify operations.
  406. NotifyIrp - This is the Irp to complete on notify change.
  407. Return Value:
  408. None.
  409. --*/
  410. {
  411. PAGED_CODE();
  412. DebugTrace( +1, Dbg, "FsRtlNotifyChangeDirectory: Entered\n", 0 );
  413. //
  414. // We will simply call the full notify routine to do the real work.
  415. //
  416. FsRtlNotifyFilterChangeDirectory( NotifySync,
  417. NotifyList,
  418. FsContext,
  419. FullDirectoryName,
  420. WatchTree,
  421. TRUE,
  422. CompletionFilter,
  423. NotifyIrp,
  424. NULL,
  425. NULL,
  426. NULL );
  427. DebugTrace( -1, Dbg, "FsRtlNotifyChangeDirectory: Exit\n", 0 );
  428. return;
  429. }
  430. VOID
  431. FsRtlNotifyFullChangeDirectory (
  432. IN PNOTIFY_SYNC NotifySync,
  433. IN PLIST_ENTRY NotifyList,
  434. IN PVOID FsContext,
  435. IN PSTRING FullDirectoryName,
  436. IN BOOLEAN WatchTree,
  437. IN BOOLEAN IgnoreBuffer,
  438. IN ULONG CompletionFilter,
  439. IN PIRP NotifyIrp,
  440. IN PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback OPTIONAL,
  441. IN PSECURITY_SUBJECT_CONTEXT SubjectContext OPTIONAL
  442. )
  443. /*++
  444. Routine Description:
  445. This routine is called by a file system which has received a NotifyChange
  446. request. This routine checks if there is already a notify structure and
  447. inserts one if not present. With a notify structure in hand, we check
  448. whether we already have a pending notify and report it if so. If there
  449. is no pending notify, we check if this Irp has already been cancelled and
  450. completes it if so. Otherwise we add this to the list of Irps waiting
  451. for notification.
  452. This is the version of this routine which understands about the user's
  453. buffer and will fill it in on a reported change.
  454. Arguments:
  455. NotifySync - This is the controlling fast mutex for this notify list.
  456. It is stored here so that it can be found for an Irp which is being
  457. cancelled.
  458. NotifyList - This is the start of the notify list to add this
  459. structure to.
  460. FsContext - This is supplied by the file system so that this notify
  461. structure can be uniquely identified. If the NotifyIrp is not specified
  462. then this is used to identify the stream and it will match the FsContext
  463. field in the file object of a stream being deleted.
  464. FullDirectoryName - Points to the full name for the directory associated
  465. with this notify structure. Ignored if the NotifyIrp is not specified.
  466. WatchTree - This indicates whether all subdirectories for this directory
  467. should be watched, or just the directory itself. Ignored if the
  468. NotifyIrp is not specified.
  469. IgnoreBuffer - Indicates whether we will always ignore any user buffer
  470. and force the directory to be reenumerated. This will speed up the
  471. operation. Ignored if the NotifyIrp is not specified.
  472. CompletionFilter - This provides the mask to determine which operations
  473. will trigger the notify operations. Ignored if the NotifyIrp is not
  474. specified.
  475. NotifyIrp - This is the Irp to complete on notify change. If this irp is
  476. not specified it means that the stream represented by this file object
  477. is being deleted.
  478. TraverseCallback - If specified we must call this routine when a change
  479. has occurred in a subdirectory being watched in a tree. This will
  480. let the filesystem check if the watcher has traverse access to that
  481. directory. Ignored if the NotifyIrp is not specified.
  482. SubjectContext - If there is a traverse callback routine then we will
  483. pass this subject context as a parameter to the call. We will release
  484. the context and free the structure when done with it. Ignored if the
  485. NotifyIrp is not specified, NULL in these cases.
  486. Return Value:
  487. None.
  488. --*/
  489. {
  490. PAGED_CODE();
  491. DebugTrace( +1, Dbg, "FsRtlNotifyFullChangeDirectory: Entered\n", 0 );
  492. //
  493. // We will simply call the full notify routine to do the real work.
  494. //
  495. FsRtlNotifyFilterChangeDirectory( NotifySync,
  496. NotifyList,
  497. FsContext,
  498. FullDirectoryName,
  499. WatchTree,
  500. IgnoreBuffer,
  501. CompletionFilter,
  502. NotifyIrp,
  503. TraverseCallback,
  504. SubjectContext,
  505. NULL );
  506. DebugTrace( -1, Dbg, "FsRtlNotifyFullChangeDirectory: Exit\n", 0 );
  507. return;
  508. }
  509. VOID
  510. FsRtlNotifyFilterChangeDirectory (
  511. IN PNOTIFY_SYNC NotifySync,
  512. IN PLIST_ENTRY NotifyList,
  513. IN PVOID FsContext,
  514. IN PSTRING FullDirectoryName,
  515. IN BOOLEAN WatchTree,
  516. IN BOOLEAN IgnoreBuffer,
  517. IN ULONG CompletionFilter,
  518. IN PIRP NotifyIrp,
  519. IN PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback OPTIONAL,
  520. IN PSECURITY_SUBJECT_CONTEXT SubjectContext OPTIONAL,
  521. IN PFILTER_REPORT_CHANGE FilterCallback OPTIONAL
  522. )
  523. /*++
  524. Routine Description:
  525. This routine is called by a file system which has received a NotifyChange
  526. request. This routine checks if there is already a notify structure and
  527. inserts one if not present. With a notify structure in hand, we check
  528. whether we already have a pending notify and report it if so. If there
  529. is no pending notify, we check if this Irp has already been cancelled and
  530. completes it if so. Otherwise we add this to the list of Irps waiting
  531. for notification.
  532. This is the version of this routine which understands about the user's
  533. buffer and will fill it in on a reported change.
  534. Arguments:
  535. NotifySync - This is the controlling fast mutex for this notify list.
  536. It is stored here so that it can be found for an Irp which is being
  537. cancelled.
  538. NotifyList - This is the start of the notify list to add this
  539. structure to.
  540. FsContext - This is supplied by the file system so that this notify
  541. structure can be uniquely identified. If the NotifyIrp is not specified
  542. then this is used to identify the stream and it will match the FsContext
  543. field in the file object of a stream being deleted.
  544. FullDirectoryName - Points to the full name for the directory associated
  545. with this notify structure. Ignored if the NotifyIrp is not specified.
  546. WatchTree - This indicates whether all subdirectories for this directory
  547. should be watched, or just the directory itself. Ignored if the
  548. NotifyIrp is not specified.
  549. IgnoreBuffer - Indicates whether we will always ignore any user buffer
  550. and force the directory to be reenumerated. This will speed up the
  551. operation. Ignored if the NotifyIrp is not specified.
  552. CompletionFilter - This provides the mask to determine which operations
  553. will trigger the notify operations. Ignored if the NotifyIrp is not
  554. specified.
  555. NotifyIrp - This is the Irp to complete on notify change. If this irp is
  556. not specified it means that the stream represented by this file object
  557. is being deleted.
  558. TraverseCallback - If specified we must call this routine when a change
  559. has occurred in a subdirectory being watched in a tree. This will
  560. let the filesystem check if the watcher has traverse access to that
  561. directory. Ignored if the NotifyIrp is not specified.
  562. SubjectContext - If there is a traverse callback routine then we will
  563. pass this subject context as a parameter to the call. We will release
  564. the context and free the structure when done with it. Ignored if the
  565. NotifyIrp is not specified.
  566. FilterCallback - This is the filesystem-supplied routine used
  567. to call back into the filesystem to check whether this Notify block
  568. should see the change. (Initially added for TxfNtfs development
  569. as part of a strategy to control when, not if, transactions see
  570. changes from other transactions.)
  571. Return Value:
  572. None.
  573. --*/
  574. {
  575. PNOTIFY_CHANGE Notify = NULL;
  576. PIO_STACK_LOCATION IrpSp;
  577. PAGED_CODE();
  578. DebugTrace( +1, Dbg, "FsRtlNotifyFullChangeDirectory: Entered\n", 0 );
  579. //
  580. // Acquire exclusive access to the list by acquiring the mutex.
  581. //
  582. AcquireNotifySync( NotifySync );
  583. //
  584. // Use a try-finally to facilitate cleanup.
  585. //
  586. try {
  587. //
  588. // If there is no Irp then find all of the pending Irps whose file objects
  589. // refer to the same stream and complete them with STATUS_DELETE_PENDING.
  590. //
  591. if (NotifyIrp == NULL) {
  592. FsRtlCheckNotifyForDelete( NotifyList, FsContext );
  593. try_return( NOTHING );
  594. }
  595. //
  596. // Get the current Stack location
  597. //
  598. IrpSp = IoGetCurrentIrpStackLocation( NotifyIrp );
  599. //
  600. // Clear the Iosb in the Irp.
  601. //
  602. NotifyIrp->IoStatus.Status = STATUS_SUCCESS;
  603. NotifyIrp->IoStatus.Information = 0;
  604. //
  605. // If the file object has already gone through cleanup, then complete
  606. // the request immediately.
  607. //
  608. if (FlagOn( IrpSp->FileObject->Flags, FO_CLEANUP_COMPLETE )) {
  609. //
  610. // Always mark this Irp as pending returned.
  611. //
  612. IoMarkIrpPending( NotifyIrp );
  613. FsRtlCompleteRequest( NotifyIrp, STATUS_NOTIFY_CLEANUP );
  614. try_return( NOTHING );
  615. }
  616. //
  617. // If the notify structure is not already in the list, add it
  618. // now.
  619. //
  620. Notify = FsRtlIsNotifyOnList( NotifyList, FsContext );
  621. if (Notify == NULL) {
  622. //
  623. // Allocate and initialize the structure.
  624. //
  625. Notify = FsRtlpAllocatePool( PagedPool, sizeof( NOTIFY_CHANGE ));
  626. RtlZeroMemory( Notify, sizeof( NOTIFY_CHANGE ));
  627. Notify->NotifySync = (PREAL_NOTIFY_SYNC) NotifySync;
  628. Notify->FsContext = FsContext;
  629. Notify->StreamID = IrpSp->FileObject->FsContext;
  630. Notify->TraverseCallback = TraverseCallback;
  631. Notify->SubjectContext = SubjectContext;
  632. SubjectContext = NULL;
  633. Notify->FilterCallback = FilterCallback;
  634. Notify->FullDirectoryName = FullDirectoryName;
  635. InitializeListHead( &Notify->NotifyIrps );
  636. if (WatchTree) {
  637. SetFlag( Notify->Flags, NOTIFY_WATCH_TREE );
  638. }
  639. if (FullDirectoryName == NULL) {
  640. //
  641. // In the view index we aren't using this buffer to hold a
  642. // unicode string.
  643. //
  644. Notify->CharacterSize = sizeof( CHAR );
  645. } else {
  646. //
  647. // We look at the directory name to decide if we have a unicode
  648. // name.
  649. //
  650. if (FullDirectoryName->Length >= 2
  651. && FullDirectoryName->Buffer[1] == '\0') {
  652. Notify->CharacterSize = sizeof( WCHAR );
  653. } else {
  654. Notify->CharacterSize = sizeof( CHAR );
  655. }
  656. if (FullDirectoryName->Length == Notify->CharacterSize) {
  657. SetFlag( Notify->Flags, NOTIFY_DIR_IS_ROOT );
  658. }
  659. }
  660. Notify->CompletionFilter = CompletionFilter;
  661. //
  662. // If we are to return data to the user then look for the length
  663. // of the original buffer in the IrpSp.
  664. //
  665. if (!IgnoreBuffer) {
  666. Notify->BufferLength = IrpSp->Parameters.NotifyDirectory.Length;
  667. }
  668. Notify->OwningProcess = THREAD_TO_PROCESS( NotifyIrp->Tail.Overlay.Thread );
  669. InsertTailList( NotifyList, &Notify->NotifyList );
  670. Notify->ReferenceCount = 1;
  671. //
  672. // If we have already been called with cleanup then complete
  673. // the request immediately.
  674. //
  675. } else if (FlagOn( Notify->Flags, NOTIFY_CLEANUP_CALLED )) {
  676. //
  677. // Always mark this Irp as pending returned.
  678. //
  679. IoMarkIrpPending( NotifyIrp );
  680. FsRtlCompleteRequest( NotifyIrp, STATUS_NOTIFY_CLEANUP );
  681. try_return( NOTHING );
  682. //
  683. // If this file has been deleted then complete with STATUS_DELETE_PENDING.
  684. //
  685. } else if (FlagOn( Notify->Flags, NOTIFY_STREAM_IS_DELETED )) {
  686. //
  687. // Always mark this Irp as pending returned.
  688. //
  689. IoMarkIrpPending( NotifyIrp );
  690. FsRtlCompleteRequest( NotifyIrp, STATUS_DELETE_PENDING );
  691. try_return( NOTHING );
  692. //
  693. // If the notify pending flag is set or there is data in an internal buffer
  694. // we complete this Irp immediately and exit.
  695. //
  696. } else if (FlagOn( Notify->Flags, NOTIFY_IMMEDIATE_NOTIFY )
  697. && !FlagOn( Notify->Flags, NOTIFY_DEFER_NOTIFY )) {
  698. DebugTrace( 0, Dbg, "Notify has been pending\n", 0 );
  699. //
  700. // Clear the flag in our notify structure before completing the
  701. // Irp. This will prevent a caller who reposts in his completion
  702. // routine from looping in the completion routine.
  703. //
  704. ClearFlag( Notify->Flags, NOTIFY_IMMEDIATE_NOTIFY );
  705. //
  706. // Always mark this Irp as pending returned.
  707. //
  708. IoMarkIrpPending( NotifyIrp );
  709. FsRtlCompleteRequest( NotifyIrp, STATUS_NOTIFY_ENUM_DIR );
  710. try_return( NOTHING );
  711. } else if (Notify->DataLength != 0
  712. && !FlagOn( Notify->Flags, NOTIFY_DEFER_NOTIFY )) {
  713. ULONG ThisDataLength = Notify->DataLength;
  714. //
  715. // Now set our buffer pointers back to indicate an empty buffer.
  716. //
  717. Notify->DataLength = 0;
  718. Notify->LastEntry = 0;
  719. FsRtlNotifyCompleteIrp( NotifyIrp,
  720. Notify,
  721. ThisDataLength,
  722. STATUS_SUCCESS,
  723. FALSE );
  724. try_return( NOTHING );
  725. }
  726. //
  727. // Add the Irp to the tail of the notify queue.
  728. //
  729. NotifyIrp->IoStatus.Information = (ULONG_PTR) Notify;
  730. IoMarkIrpPending( NotifyIrp );
  731. InsertTailList( &Notify->NotifyIrps, &NotifyIrp->Tail.Overlay.ListEntry );
  732. //
  733. // Increment the reference count to indicate that Irp might go through cancel.
  734. //
  735. InterlockedIncrement( &Notify->ReferenceCount );
  736. //
  737. // Call the routine to set the cancel routine.
  738. //
  739. FsRtlNotifySetCancelRoutine( NotifyIrp, NULL );
  740. try_exit: NOTHING;
  741. } finally {
  742. //
  743. // Release the mutex.
  744. //
  745. ReleaseNotifySync( NotifySync );
  746. //
  747. // If there is still a subject context then release it and deallocate
  748. // the structure. Remember that if FullDirectoryName is null, it means
  749. // this is a view index, not a directory index, and the SubjectContext
  750. // is really a piece of file system context information.
  751. //
  752. if ((SubjectContext != NULL) &&
  753. ((Notify == NULL) ||
  754. (Notify->FullDirectoryName != NULL))) {
  755. SeReleaseSubjectContext( SubjectContext );
  756. ExFreePool( SubjectContext );
  757. }
  758. DebugTrace( -1, Dbg, "FsRtlNotifyFullChangeDirectory: Exit\n", 0 );
  759. }
  760. return;
  761. }
  762. VOID
  763. FsRtlNotifyReportChange (
  764. IN PNOTIFY_SYNC NotifySync,
  765. IN PLIST_ENTRY NotifyList,
  766. IN PSTRING FullTargetName,
  767. IN PSTRING TargetName,
  768. IN ULONG FilterMatch
  769. )
  770. /*++
  771. Routine Description:
  772. This routine is called by a file system when a file has been modified in
  773. such a way that it will cause a notify change Irp to complete. We walk
  774. through all the notify structures looking for those structures which
  775. would be associated with an ancestor directory of the target file name.
  776. We look for all the notify structures which have a filter match and
  777. then check that the directory name in the notify structure is a
  778. proper prefix of the full target name.
  779. If we find a notify structure which matches the above conditions, we
  780. complete all the Irps for the notify structure. If the structure has
  781. no Irps, we mark the notify pending field.
  782. Arguments:
  783. NotifySync - This is the controlling fast mutex for this notify list.
  784. It is stored here so that it can be found for an Irp which is being
  785. cancelled.
  786. NotifyList - This is the start of the notify list to add this
  787. structure to.
  788. FullTargetName - This is the full name of the file which has been
  789. changed.
  790. TargetName - This is the final component of the modified file.
  791. FilterMatch - This flag field is compared with the completion filter
  792. in the notify structure. If any of the corresponding
  793. bits in the completion filter are set, then a notify
  794. condition exists.
  795. Return Value:
  796. None.
  797. --*/
  798. {
  799. PAGED_CODE();
  800. DebugTrace( +1, Dbg, "FsRtlNotifyReportChange: Entered\n", 0 );
  801. //
  802. // Call the full notify routine to do the actual work.
  803. //
  804. FsRtlNotifyFilterReportChange( NotifySync,
  805. NotifyList,
  806. FullTargetName,
  807. (USHORT) (FullTargetName->Length - TargetName->Length),
  808. NULL,
  809. NULL,
  810. FilterMatch,
  811. 0,
  812. NULL,
  813. NULL );
  814. DebugTrace( -1, Dbg, "FsRtlNotifyReportChange: Exit\n", 0 );
  815. return;
  816. }
  817. VOID
  818. FsRtlNotifyFullReportChange (
  819. IN PNOTIFY_SYNC NotifySync,
  820. IN PLIST_ENTRY NotifyList,
  821. IN PSTRING FullTargetName,
  822. IN USHORT TargetNameOffset,
  823. IN PSTRING StreamName OPTIONAL,
  824. IN PSTRING NormalizedParentName OPTIONAL,
  825. IN ULONG FilterMatch,
  826. IN ULONG Action,
  827. IN PVOID TargetContext
  828. )
  829. /*++
  830. Routine Description:
  831. This routine is called by a file system when a file has been modified in
  832. such a way that it will cause a notify change Irp to complete. We walk
  833. through all the notify structures looking for those structures which
  834. would be associated with an ancestor directory of the target file name.
  835. We look for all the notify structures which have a filter match and
  836. then check that the directory name in the notify structure is a
  837. proper prefix of the full target name.
  838. If we find a notify structure which matches the above conditions, we
  839. complete all the Irps for the notify structure. If the structure has
  840. no Irps, we mark the notify pending field.
  841. Arguments:
  842. NotifySync - This is the controlling fast mutex for this notify list.
  843. It is stored here so that it can be found for an Irp which is being
  844. cancelled.
  845. NotifyList - This is the start of the notify list to add this
  846. structure to.
  847. FullTargetName - This is the full name of the file from the root of the volume.
  848. TargetNameOffset - This is the offset in the full name of the final component
  849. of the name.
  850. StreamName - If present then this is the stream name to store with
  851. the filename.
  852. NormalizedParentName - If present this is the same path as the parent name
  853. but the DOS-ONLY names have been replaced with the associated long name.
  854. FilterMatch - This flag field is compared with the completion filter
  855. in the notify structure. If any of the corresponding bits in the
  856. completion filter are set, then a notify condition exists.
  857. Action - This is the action code to store in the user's buffer if
  858. present.
  859. TargetContext - This is one of the context pointers to pass to the file
  860. system if performing a traverse check in the case of a tree being
  861. watched.
  862. Return Value:
  863. None.
  864. --*/
  865. {
  866. PAGED_CODE();
  867. DebugTrace( +1, Dbg, "FsRtlNotifyReportChange: Entered\n", 0 );
  868. //
  869. // Call the full notify routine to do the actual work.
  870. //
  871. FsRtlNotifyFilterReportChange( NotifySync,
  872. NotifyList,
  873. FullTargetName,
  874. TargetNameOffset,
  875. StreamName,
  876. NormalizedParentName,
  877. FilterMatch,
  878. Action,
  879. TargetContext,
  880. NULL );
  881. DebugTrace( -1, Dbg, "FsRtlNotifyReportChange: Exit\n", 0 );
  882. return;
  883. }
  884. VOID
  885. FsRtlNotifyFilterReportChange (
  886. IN PNOTIFY_SYNC NotifySync,
  887. IN PLIST_ENTRY NotifyList,
  888. IN PSTRING FullTargetName,
  889. IN USHORT TargetNameOffset,
  890. IN PSTRING StreamName OPTIONAL,
  891. IN PSTRING NormalizedParentName OPTIONAL,
  892. IN ULONG FilterMatch,
  893. IN ULONG Action,
  894. IN PVOID TargetContext,
  895. IN PVOID FilterContext
  896. )
  897. /*++
  898. Routine Description:
  899. This routine is called by a file system when a file has been modified in
  900. such a way that it will cause a notify change Irp to complete. We walk
  901. through all the notify structures looking for those structures which
  902. would be associated with an ancestor directory of the target file name.
  903. We look for all the notify structures which have a filter match and
  904. then check that the directory name in the notify structure is a
  905. proper prefix of the full target name.
  906. If we find a notify structure which matches the above conditions, we
  907. complete all the Irps for the notify structure. If the structure has
  908. no Irps, we mark the notify pending field.
  909. Arguments:
  910. NotifySync - This is the controlling fast mutex for this notify list.
  911. It is stored here so that it can be found for an Irp which is being
  912. cancelled.
  913. NotifyList - This is the start of the notify list to add this
  914. structure to.
  915. FullTargetName - This is the full name of the file from the root of the volume.
  916. TargetNameOffset - This is the offset in the full name of the final component
  917. of the name.
  918. StreamName - If present then this is the stream name to store with
  919. the filename.
  920. NormalizedParentName - If present this is the same path as the parent name
  921. but the DOS-ONLY names have been replaced with the associated long name.
  922. FilterMatch - This flag field is compared with the completion filter
  923. in the notify structure. If any of the corresponding bits in the
  924. completion filter are set, then a notify condition exists.
  925. Action - This is the action code to store in the user's buffer if
  926. present.
  927. TargetContext - This is one of the context pointers to pass to the file
  928. system if performing a traverse check in the case of a tree being
  929. watched.
  930. FilterContext - This is the filesystem-supplied routine used
  931. to call back into the filesystem to check whether each Notify block
  932. should see the change. (Initially added for TxfNtfs development
  933. as part of a strategy to control when, not if, transactions see
  934. changes from other transactions.)
  935. Return Value:
  936. None.
  937. --*/
  938. {
  939. PLIST_ENTRY NotifyLinks;
  940. STRING NormalizedParent;
  941. STRING ParentName;
  942. STRING TargetName;
  943. PNOTIFY_CHANGE Notify;
  944. STRING TargetParent;
  945. PIRP NotifyIrp;
  946. BOOLEAN NotifyIsParent;
  947. BOOLEAN ViewIndex = FALSE;
  948. UCHAR ComponentCount;
  949. ULONG SizeOfEntry;
  950. ULONG CurrentOffset;
  951. ULONG NextEntryOffset;
  952. ULONG ExceptionCode;
  953. PAGED_CODE();
  954. DebugTrace( +1, Dbg, "FsRtlNotifyFullReportChange: Entered\n", 0 );
  955. //
  956. // If this is a change to the root directory then return immediately.
  957. //
  958. if ((TargetNameOffset == 0) && (FullTargetName != NULL)) {
  959. DebugTrace( -1, Dbg, "FsRtlNotifyFullReportChange: Exit\n", 0 );
  960. return;
  961. }
  962. ParentName.Buffer = NULL;
  963. TargetName.Buffer = NULL;
  964. //
  965. // Acquire exclusive access to the list by acquiring the mutex.
  966. //
  967. AcquireNotifySync( NotifySync );
  968. //
  969. // Use a try-finally to facilitate cleanup.
  970. //
  971. try {
  972. //
  973. // Walk through all the notify blocks.
  974. //
  975. for (NotifyLinks = NotifyList->Flink;
  976. NotifyLinks != NotifyList;
  977. NotifyLinks = NotifyLinks->Flink) {
  978. //
  979. // Obtain the Notify structure from the list entry.
  980. //
  981. Notify = CONTAINING_RECORD( NotifyLinks, NOTIFY_CHANGE, NotifyList );
  982. //
  983. // The rules for deciding whether this notification applies are
  984. // different for view indices versus file name indices (directories).
  985. //
  986. if (FullTargetName == NULL) {
  987. ASSERTMSG( "Directory notify handle in view index notify list!", Notify->FullDirectoryName == NULL);
  988. //
  989. // Make sure this is the Fcb being watched.
  990. //
  991. if (TargetContext != Notify->SubjectContext) {
  992. continue;
  993. }
  994. TargetParent.Buffer = NULL;
  995. TargetParent.Length = 0;
  996. ViewIndex = TRUE;
  997. NotifyIsParent = FALSE;
  998. //
  999. // Handle the directory case.
  1000. //
  1001. } else {
  1002. ASSERTMSG( "View index notify handle in directory notify list!", Notify->FullDirectoryName != NULL);
  1003. //
  1004. // If the length of the name in the notify block is currently zero then
  1005. // someone is doing a rename and we can skip this block.
  1006. //
  1007. if (Notify->FullDirectoryName->Length == 0) {
  1008. continue;
  1009. }
  1010. //
  1011. // If this filter match is not part of the completion filter then continue.
  1012. //
  1013. if (!(FilterMatch & Notify->CompletionFilter)) {
  1014. continue;
  1015. }
  1016. //
  1017. // If there is no normalized name then set its value from the full
  1018. // file name.
  1019. //
  1020. if (!ARGUMENT_PRESENT( NormalizedParentName )) {
  1021. NormalizedParent.Buffer = FullTargetName->Buffer;
  1022. NormalizedParent.Length = TargetNameOffset;
  1023. if (NormalizedParent.Length != Notify->CharacterSize) {
  1024. NormalizedParent.Length -= Notify->CharacterSize;
  1025. }
  1026. NormalizedParent.MaximumLength = NormalizedParent.Length;
  1027. NormalizedParentName = &NormalizedParent;
  1028. }
  1029. //
  1030. // If the length of the directory being watched is longer than the
  1031. // parent of the modified file then it can't be an ancestor of the
  1032. // modified file.
  1033. //
  1034. if (Notify->FullDirectoryName->Length > NormalizedParentName->Length) {
  1035. continue;
  1036. }
  1037. //
  1038. // If the lengths match exactly then this can only be the parent of
  1039. // the modified file.
  1040. //
  1041. if (NormalizedParentName->Length == Notify->FullDirectoryName->Length) {
  1042. NotifyIsParent = TRUE;
  1043. //
  1044. // If we are not watching the subtree of this directory then continue.
  1045. //
  1046. } else if (!FlagOn( Notify->Flags, NOTIFY_WATCH_TREE )) {
  1047. continue;
  1048. //
  1049. // The watched directory can only be an ancestor of the modified
  1050. // file. Make sure that there is legal pathname separator immediately
  1051. // after the end of the watched directory name within the normalized name.
  1052. // If the watched directory is the root then we know this condition is TRUE.
  1053. //
  1054. } else {
  1055. if (!FlagOn( Notify->Flags, NOTIFY_DIR_IS_ROOT )) {
  1056. //
  1057. // Check for the character size.
  1058. //
  1059. if (Notify->CharacterSize == sizeof( CHAR )) {
  1060. if (*(Add2Ptr( NormalizedParentName->Buffer,
  1061. Notify->FullDirectoryName->Length,
  1062. PCHAR )) != '\\') {
  1063. continue;
  1064. }
  1065. } else if (*(Add2Ptr( NormalizedParentName->Buffer,
  1066. Notify->FullDirectoryName->Length,
  1067. PWCHAR )) != L'\\') {
  1068. continue;
  1069. }
  1070. }
  1071. NotifyIsParent = FALSE;
  1072. }
  1073. //
  1074. // We now have a correct match of the name lengths. Now verify that the
  1075. // characters match exactly.
  1076. //
  1077. if (!RtlEqualMemory( Notify->FullDirectoryName->Buffer,
  1078. NormalizedParentName->Buffer,
  1079. Notify->FullDirectoryName->Length )) {
  1080. continue;
  1081. }
  1082. //
  1083. // The characters are correct. Now check in the case of a non-parent
  1084. // notify that we have traverse callback.
  1085. //
  1086. if (!NotifyIsParent &&
  1087. Notify->TraverseCallback != NULL &&
  1088. !Notify->TraverseCallback( Notify->FsContext,
  1089. TargetContext,
  1090. Notify->SubjectContext )) {
  1091. continue;
  1092. }
  1093. //
  1094. // Finally, if Notify block has a FilterRoutine *and* the caller specified
  1095. // a FilterContext, then we must finally call the filter routine.
  1096. //
  1097. if ((Notify->FilterCallback != NULL) &&
  1098. ARGUMENT_PRESENT( FilterContext ) &&
  1099. !Notify->FilterCallback( Notify->FsContext, FilterContext )) {
  1100. continue;
  1101. }
  1102. }
  1103. //
  1104. // If this entry is going into a buffer then check that
  1105. // it will fit.
  1106. //
  1107. if (!FlagOn( Notify->Flags, NOTIFY_IMMEDIATE_NOTIFY )
  1108. && Notify->BufferLength != 0) {
  1109. ULONG AllocationLength;
  1110. AllocationLength = 0;
  1111. NotifyIrp = NULL;
  1112. //
  1113. // If we don't already have a buffer then check to see
  1114. // if we have any Irps in the list and use the buffer
  1115. // length in the Irp.
  1116. //
  1117. if (Notify->ThisBufferLength == 0) {
  1118. //
  1119. // If there is an entry in the list then get the length.
  1120. //
  1121. if (!IsListEmpty( &Notify->NotifyIrps )) {
  1122. PIO_STACK_LOCATION IrpSp;
  1123. NotifyIrp = CONTAINING_RECORD( Notify->NotifyIrps.Flink,
  1124. IRP,
  1125. Tail.Overlay.ListEntry );
  1126. IrpSp = IoGetCurrentIrpStackLocation( NotifyIrp );
  1127. AllocationLength = IrpSp->Parameters.NotifyDirectory.Length;
  1128. //
  1129. // Otherwise use the caller's last buffer size.
  1130. //
  1131. } else {
  1132. AllocationLength = Notify->BufferLength;
  1133. }
  1134. //
  1135. // Otherwise use the length of the current buffer.
  1136. //
  1137. } else {
  1138. AllocationLength = Notify->ThisBufferLength;
  1139. }
  1140. //
  1141. // Build the strings for the relative name. This includes
  1142. // the strings for the parent name, file name and stream
  1143. // name.
  1144. //
  1145. if (!NotifyIsParent) {
  1146. //
  1147. // We need to find the string for the ancestor of this
  1148. // file from the watched directory. If the normalized parent
  1149. // name is the same as the parent name then we can use
  1150. // the tail of the parent directly. Otherwise we need to
  1151. // count the matching name components and capture the
  1152. // final components.
  1153. //
  1154. if (!ViewIndex) {
  1155. //
  1156. // If the watched directory is the root then we just use the full
  1157. // parent name.
  1158. //
  1159. if (FlagOn( Notify->Flags, NOTIFY_DIR_IS_ROOT ) ||
  1160. NormalizedParentName->Buffer != FullTargetName->Buffer) {
  1161. //
  1162. // If we don't have a string for the parent then construct
  1163. // it now.
  1164. //
  1165. if (ParentName.Buffer == NULL) {
  1166. ParentName.Buffer = FullTargetName->Buffer;
  1167. ParentName.Length = TargetNameOffset;
  1168. if (ParentName.Length != Notify->CharacterSize) {
  1169. ParentName.Length -= Notify->CharacterSize;
  1170. }
  1171. ParentName.MaximumLength = ParentName.Length;
  1172. }
  1173. //
  1174. // Count through the components of the parent until we have
  1175. // swallowed the same number of name components as in the
  1176. // watched directory name. We have the unicode version and
  1177. // the Ansi version to watch for.
  1178. //
  1179. ComponentCount = 0;
  1180. CurrentOffset = 0;
  1181. //
  1182. // If this is the root then there is no more to do.
  1183. //
  1184. if (FlagOn( Notify->Flags, NOTIFY_DIR_IS_ROOT )) {
  1185. NOTHING;
  1186. } else {
  1187. ULONG ParentComponentCount;
  1188. ULONG ParentOffset;
  1189. ParentComponentCount = 1;
  1190. ParentOffset = 0;
  1191. if (Notify->CharacterSize == sizeof( CHAR )) {
  1192. //
  1193. // Find the number of components in the parent. We
  1194. // have to do this for each one because this name and
  1195. // the number of components could have changed.
  1196. //
  1197. while (ParentOffset < Notify->FullDirectoryName->Length) {
  1198. if (*((PCHAR) Notify->FullDirectoryName->Buffer + ParentOffset) == '\\') {
  1199. ParentComponentCount += 1;
  1200. }
  1201. ParentOffset += 1;
  1202. }
  1203. while (TRUE) {
  1204. if (*((PCHAR) ParentName.Buffer + CurrentOffset) == '\\') {
  1205. ComponentCount += 1;
  1206. if (ComponentCount == ParentComponentCount) {
  1207. break;
  1208. }
  1209. }
  1210. CurrentOffset += 1;
  1211. }
  1212. } else {
  1213. //
  1214. // Find the number of components in the parent. We
  1215. // have to do this for each one because this name and
  1216. // the number of components could have changed.
  1217. //
  1218. while (ParentOffset < Notify->FullDirectoryName->Length / sizeof( WCHAR )) {
  1219. if (*((PWCHAR) Notify->FullDirectoryName->Buffer + ParentOffset) == '\\') {
  1220. ParentComponentCount += 1;
  1221. }
  1222. ParentOffset += 1;
  1223. }
  1224. while (TRUE) {
  1225. if (*((PWCHAR) ParentName.Buffer + CurrentOffset) == L'\\') {
  1226. ComponentCount += 1;
  1227. if (ComponentCount == ParentComponentCount) {
  1228. break;
  1229. }
  1230. }
  1231. CurrentOffset += 1;
  1232. }
  1233. //
  1234. // Convert characters to bytes.
  1235. //
  1236. CurrentOffset *= Notify->CharacterSize;
  1237. }
  1238. }
  1239. //
  1240. // We now know the offset into the parent name of the separator
  1241. // immediately preceding the relative parent name. Construct the
  1242. // target parent name for the buffer.
  1243. //
  1244. CurrentOffset += Notify->CharacterSize;
  1245. TargetParent.Buffer = Add2Ptr( ParentName.Buffer,
  1246. CurrentOffset,
  1247. PCHAR );
  1248. TargetParent.MaximumLength =
  1249. TargetParent.Length = ParentName.Length - (USHORT) CurrentOffset;
  1250. //
  1251. // If the normalized is the same as the parent name use the portion
  1252. // after the match with the watched directory.
  1253. //
  1254. } else {
  1255. TargetParent.Buffer = Add2Ptr( NormalizedParentName->Buffer,
  1256. (Notify->FullDirectoryName->Length +
  1257. Notify->CharacterSize),
  1258. PCHAR );
  1259. TargetParent.MaximumLength =
  1260. TargetParent.Length = NormalizedParentName->Length -
  1261. Notify->FullDirectoryName->Length -
  1262. Notify->CharacterSize;
  1263. }
  1264. }
  1265. } else {
  1266. //
  1267. // The length of the target parent is zero.
  1268. //
  1269. TargetParent.Length = 0;
  1270. }
  1271. //
  1272. // Compute how much buffer space this report will take.
  1273. //
  1274. SizeOfEntry = FIELD_OFFSET( FILE_NOTIFY_INFORMATION, FileName );
  1275. if (ViewIndex) {
  1276. //
  1277. // In the view index case, the information to copy to the
  1278. // buffer comes to us in the stream name, and that is all
  1279. // the room we need to worry about having.
  1280. //
  1281. ASSERT(ARGUMENT_PRESENT( StreamName ));
  1282. SizeOfEntry += StreamName->Length;
  1283. } else {
  1284. //
  1285. // If there is a parent to report, find the size and include a separator
  1286. // character.
  1287. //
  1288. if (!NotifyIsParent) {
  1289. if (Notify->CharacterSize == sizeof( CHAR )) {
  1290. SizeOfEntry += RtlOemStringToCountedUnicodeSize( &TargetParent );
  1291. } else {
  1292. SizeOfEntry += TargetParent.Length;
  1293. }
  1294. //
  1295. // Include the separator. This is always a unicode character.
  1296. //
  1297. SizeOfEntry += sizeof( WCHAR );
  1298. }
  1299. //
  1300. // If we don't have the string for the target then construct it now.
  1301. //
  1302. if (TargetName.Buffer == NULL) {
  1303. TargetName.Buffer = Add2Ptr( FullTargetName->Buffer, TargetNameOffset, PCHAR );
  1304. TargetName.MaximumLength =
  1305. TargetName.Length = FullTargetName->Length - TargetNameOffset;
  1306. }
  1307. if (Notify->CharacterSize == sizeof( CHAR )) {
  1308. SizeOfEntry += RtlOemStringToCountedUnicodeSize( &TargetName );
  1309. } else {
  1310. SizeOfEntry += TargetName.Length;
  1311. }
  1312. //
  1313. // If there is a stream name then add the bytes needed
  1314. // for that.
  1315. //
  1316. if (ARGUMENT_PRESENT( StreamName )) {
  1317. //
  1318. // Add the space needed for the ':' separator.
  1319. //
  1320. if (Notify->CharacterSize == sizeof( WCHAR )) {
  1321. SizeOfEntry += (StreamName->Length + sizeof( WCHAR ));
  1322. } else {
  1323. SizeOfEntry += (RtlOemStringToCountedUnicodeSize( StreamName )
  1324. + sizeof( CHAR ));
  1325. }
  1326. }
  1327. }
  1328. //
  1329. // Remember if this report would overflow the buffer.
  1330. //
  1331. NextEntryOffset = (ULONG)LongAlign( Notify->DataLength );
  1332. if (SizeOfEntry <= AllocationLength
  1333. && (NextEntryOffset + SizeOfEntry) <= AllocationLength) {
  1334. PFILE_NOTIFY_INFORMATION NotifyInfo = NULL;
  1335. //
  1336. // If there is already a notify buffer, we append this
  1337. // data to it.
  1338. //
  1339. if (Notify->Buffer != NULL) {
  1340. NotifyInfo = Add2Ptr( Notify->Buffer,
  1341. Notify->LastEntry,
  1342. PFILE_NOTIFY_INFORMATION );
  1343. NotifyInfo->NextEntryOffset = NextEntryOffset - Notify->LastEntry;
  1344. Notify->LastEntry = NextEntryOffset;
  1345. NotifyInfo = Add2Ptr( Notify->Buffer,
  1346. Notify->LastEntry,
  1347. PFILE_NOTIFY_INFORMATION );
  1348. //
  1349. // If there is an Irp list we check whether we will need
  1350. // to allocate a new buffer.
  1351. //
  1352. } else if (NotifyIrp != NULL) {
  1353. if (NotifyIrp->AssociatedIrp.SystemBuffer != NULL) {
  1354. Notify->Buffer =
  1355. NotifyInfo = NotifyIrp->AssociatedIrp.SystemBuffer;
  1356. Notify->ThisBufferLength = AllocationLength;
  1357. } else if (NotifyIrp->MdlAddress != NULL) {
  1358. Notify->Buffer =
  1359. NotifyInfo = MmGetSystemAddressForMdl( NotifyIrp->MdlAddress );
  1360. Notify->ThisBufferLength = AllocationLength;
  1361. }
  1362. }
  1363. //
  1364. // If we need to allocate a buffer, we will charge quota
  1365. // to the original process and allocate paged pool.
  1366. //
  1367. if (Notify->Buffer == NULL) {
  1368. BOOLEAN ChargedQuota = FALSE;
  1369. try {
  1370. PsChargePoolQuota( Notify->OwningProcess,
  1371. PagedPool,
  1372. AllocationLength );
  1373. ChargedQuota = TRUE;
  1374. Notify->AllocatedBuffer =
  1375. Notify->Buffer = FsRtlpAllocatePool( PagedPool,
  1376. AllocationLength );
  1377. Notify->ThisBufferLength = AllocationLength;
  1378. NotifyInfo = Notify->Buffer;
  1379. } except(( ExceptionCode = GetExceptionCode(), FsRtlIsNtstatusExpected(ExceptionCode))
  1380. ? EXCEPTION_EXECUTE_HANDLER
  1381. : EXCEPTION_CONTINUE_SEARCH ) {
  1382. ASSERT( (ExceptionCode == STATUS_INSUFFICIENT_RESOURCES) ||
  1383. (ExceptionCode == STATUS_QUOTA_EXCEEDED) );
  1384. //
  1385. // Return quota if we allocated the buffer.
  1386. //
  1387. if (ChargedQuota) {
  1388. PsReturnProcessPagedPoolQuota( Notify->OwningProcess,
  1389. AllocationLength );
  1390. }
  1391. //
  1392. // Forget any current buffer and resort to immediate
  1393. // notify.
  1394. //
  1395. SetFlag( Notify->Flags, NOTIFY_IMMEDIATE_NOTIFY );
  1396. }
  1397. }
  1398. //
  1399. // If we have a buffer then fill in the results
  1400. // from this operation. Otherwise we remember
  1401. // to simply alert the caller.
  1402. //
  1403. if (NotifyInfo != NULL) {
  1404. //
  1405. // Update the buffer with the current data.
  1406. //
  1407. if (FsRtlNotifyUpdateBuffer( NotifyInfo,
  1408. Action,
  1409. &TargetParent,
  1410. &TargetName,
  1411. StreamName,
  1412. (BOOLEAN) (Notify->CharacterSize == sizeof( WCHAR )),
  1413. SizeOfEntry )) {
  1414. //
  1415. // Update the buffer data length.
  1416. //
  1417. Notify->DataLength = NextEntryOffset + SizeOfEntry;
  1418. //
  1419. // We couldn't copy the data into the buffer. Just
  1420. // notify without any additional information.
  1421. //
  1422. } else {
  1423. SetFlag( Notify->Flags, NOTIFY_IMMEDIATE_NOTIFY );
  1424. }
  1425. }
  1426. } else {
  1427. SetFlag( Notify->Flags, NOTIFY_IMMEDIATE_NOTIFY );
  1428. }
  1429. //
  1430. // If we have a buffer but can't use it then clear all of the
  1431. // buffer related fields. Also deallocate any buffer allocated
  1432. // by us.
  1433. //
  1434. if (FlagOn( Notify->Flags, NOTIFY_IMMEDIATE_NOTIFY )
  1435. && Notify->Buffer != NULL) {
  1436. if (Notify->AllocatedBuffer != NULL) {
  1437. PsReturnProcessPagedPoolQuota( Notify->OwningProcess,
  1438. Notify->ThisBufferLength );
  1439. ExFreePool( Notify->AllocatedBuffer );
  1440. }
  1441. Notify->AllocatedBuffer = Notify->Buffer = NULL;
  1442. Notify->ThisBufferLength = Notify->DataLength = Notify->LastEntry = 0;
  1443. }
  1444. }
  1445. //
  1446. // Complete the next entry on the list if we aren't holding
  1447. // up notification.
  1448. //
  1449. if (Action == FILE_ACTION_RENAMED_OLD_NAME) {
  1450. SetFlag( Notify->Flags, NOTIFY_DEFER_NOTIFY );
  1451. } else {
  1452. ClearFlag( Notify->Flags, NOTIFY_DEFER_NOTIFY );
  1453. if (!IsListEmpty( &Notify->NotifyIrps )) {
  1454. FsRtlNotifyCompleteIrpList( Notify, STATUS_SUCCESS );
  1455. }
  1456. }
  1457. }
  1458. } finally {
  1459. ReleaseNotifySync( NotifySync );
  1460. DebugTrace( -1, Dbg, "FsRtlNotifyFullReportChange: Exit\n", 0 );
  1461. }
  1462. return;
  1463. }
  1464. VOID
  1465. FsRtlNotifyCleanup (
  1466. IN PNOTIFY_SYNC NotifySync,
  1467. IN PLIST_ENTRY NotifyList,
  1468. IN PVOID FsContext
  1469. )
  1470. /*++
  1471. Routine Description:
  1472. This routine is called for a cleanup of a user directory handle. We
  1473. walk through our notify structures looking for a matching context field.
  1474. We complete all the pending notify Irps for this Notify structure, remove
  1475. the notify structure and deallocate it.
  1476. Arguments:
  1477. NotifySync - This is the controlling fast mutex for this notify list.
  1478. It is stored here so that it can be found for an Irp which is being
  1479. cancelled.
  1480. NotifyList - This is the start of the notify list to add this
  1481. structure to.
  1482. FsContext - This is a unique value assigned by the file system to
  1483. identify a particular notify structure.
  1484. Return Value:
  1485. None.
  1486. --*/
  1487. {
  1488. PNOTIFY_CHANGE Notify;
  1489. PSECURITY_SUBJECT_CONTEXT SubjectContext = NULL;
  1490. PAGED_CODE();
  1491. DebugTrace( +1, Dbg, "FsRtlNotifyCleanup: Entered\n", 0 );
  1492. DebugTrace( 0, Dbg, "Mutex -> %08lx\n", Mutex );
  1493. DebugTrace( 0, Dbg, "Notify List -> %08lx\n", NotifyList );
  1494. DebugTrace( 0, Dbg, "FsContext -> %08lx\n", FsContext );
  1495. //
  1496. // Acquire exclusive access to the list by acquiring the mutex.
  1497. //
  1498. AcquireNotifySync( NotifySync );
  1499. //
  1500. // Use a try-finally to facilitate cleanup.
  1501. //
  1502. try {
  1503. //
  1504. // Search for a match on the list.
  1505. //
  1506. Notify = FsRtlIsNotifyOnList( NotifyList, FsContext );
  1507. //
  1508. // If found, then complete all the Irps with STATUS_NOTIFY_CLEANUP
  1509. //
  1510. if (Notify != NULL) {
  1511. //
  1512. // Set the flag to indicate that we have been called with cleanup.
  1513. //
  1514. SetFlag( Notify->Flags, NOTIFY_CLEANUP_CALLED );
  1515. if (!IsListEmpty( &Notify->NotifyIrps )) {
  1516. FsRtlNotifyCompleteIrpList( Notify, STATUS_NOTIFY_CLEANUP );
  1517. }
  1518. //
  1519. // Cleanup can only occur once, and will always remove the notify from
  1520. // the list. Cancel will defer this work to cleanup, and cannot free
  1521. // a notify which has not gone through cleanup since we have a ref
  1522. // to ensure it.
  1523. //
  1524. RemoveEntryList( &Notify->NotifyList );
  1525. //
  1526. // We assert this in cancel routine.
  1527. //
  1528. #if DBG
  1529. Notify->NotifyList.Flink = NULL;
  1530. #endif
  1531. InterlockedDecrement( &Notify->ReferenceCount );
  1532. if (Notify->ReferenceCount == 0) {
  1533. if (Notify->AllocatedBuffer != NULL) {
  1534. PsReturnProcessPagedPoolQuota( Notify->OwningProcess,
  1535. Notify->ThisBufferLength );
  1536. ExFreePool( Notify->AllocatedBuffer );
  1537. }
  1538. if (Notify->FullDirectoryName != NULL) {
  1539. SubjectContext = Notify->SubjectContext;
  1540. }
  1541. ExFreePool( Notify );
  1542. }
  1543. }
  1544. } finally {
  1545. ReleaseNotifySync( NotifySync );
  1546. if (SubjectContext != NULL) {
  1547. SeReleaseSubjectContext( SubjectContext );
  1548. ExFreePool( SubjectContext );
  1549. }
  1550. DebugTrace( -1, Dbg, "FsRtlNotifyCleanup: Exit\n", 0 );
  1551. }
  1552. return;
  1553. }
  1554. //
  1555. // Local support routine
  1556. //
  1557. PNOTIFY_CHANGE
  1558. FsRtlIsNotifyOnList (
  1559. IN PLIST_ENTRY NotifyListHead,
  1560. IN PVOID FsContext
  1561. )
  1562. /*++
  1563. Routine Description:
  1564. This routine is called to walk through a notify list searching for
  1565. a member associated with the FsContext value.
  1566. Arguments:
  1567. NotifyListHead - This is the start of the notify list.
  1568. FsContext - This is supplied by the file system so that this notify
  1569. structure can be uniquely identified.
  1570. Return Value:
  1571. PNOTIFY_CHANGE - A pointer to the matching structure is returned. NULL
  1572. is returned if the structure isn't present.
  1573. --*/
  1574. {
  1575. PLIST_ENTRY Link;
  1576. PNOTIFY_CHANGE ThisNotify;
  1577. PNOTIFY_CHANGE Notify;
  1578. PAGED_CODE();
  1579. DebugTrace( +1, Dbg, "FsRtlIsNotifyOnList: Entered\n", 0 );
  1580. //
  1581. // Assume we won't have a match.
  1582. //
  1583. Notify = NULL;
  1584. //
  1585. // Walk through all the entries on the list looking for a match.
  1586. //
  1587. for (Link = NotifyListHead->Flink;
  1588. Link != NotifyListHead;
  1589. Link = Link->Flink) {
  1590. //
  1591. // Obtain the notify structure from the link.
  1592. //
  1593. ThisNotify = CONTAINING_RECORD( Link, NOTIFY_CHANGE, NotifyList );
  1594. //
  1595. // If the context field matches, remember this structure and
  1596. // exit.
  1597. //
  1598. if (ThisNotify->FsContext == FsContext) {
  1599. Notify = ThisNotify;
  1600. break;
  1601. }
  1602. }
  1603. DebugTrace( 0, Dbg, "Notify Structure -> %08lx\n", Notify );
  1604. DebugTrace( -1, Dbg, "FsRtlIsNotifyOnList: Exit\n", 0 );
  1605. return Notify;
  1606. }
  1607. //
  1608. // Local support routine
  1609. //
  1610. VOID
  1611. FsRtlNotifyCompleteIrp (
  1612. IN PIRP NotifyIrp,
  1613. IN PNOTIFY_CHANGE Notify,
  1614. IN ULONG DataLength,
  1615. IN NTSTATUS Status,
  1616. IN ULONG CheckCancel
  1617. )
  1618. /*++
  1619. Routine Description:
  1620. This routine is called to complete an Irp with the data in the NOTIFY_CHANGE
  1621. structure.
  1622. Arguments:
  1623. NotifyIrp - Irp to complete.
  1624. Notify - Notify structure which contains the data.
  1625. DataLength - This is the length of the data in the buffer in the notify
  1626. structure. A value of zero indicates that we should complete the
  1627. request with STATUS_NOTIFY_ENUM_DIR.
  1628. Status - Indicates the status to complete the Irp with.
  1629. CheckCancel - Indicates if we should only complete the irp if we clear the cancel routine
  1630. ourselves.
  1631. Return Value:
  1632. None.
  1633. --*/
  1634. {
  1635. PIO_STACK_LOCATION IrpSp;
  1636. PAGED_CODE();
  1637. DebugTrace( +1, Dbg, "FsRtlIsNotifyCompleteIrp: Entered\n", 0 );
  1638. //
  1639. // Attempt to clear the cancel routine. If this routine owns the cancel
  1640. // routine then it can complete the irp. Otherwise there is a cancel underway
  1641. // on this.
  1642. //
  1643. if (FsRtlNotifySetCancelRoutine( NotifyIrp, Notify ) || !CheckCancel) {
  1644. //
  1645. // We only process the buffer if the status is STATUS_SUCCESS.
  1646. //
  1647. if (Status == STATUS_SUCCESS) {
  1648. //
  1649. // Get the current Stack location
  1650. //
  1651. IrpSp = IoGetCurrentIrpStackLocation( NotifyIrp );
  1652. //
  1653. // If the data won't fit in the user's buffer or there was already a
  1654. // buffer overflow then return the alternate status code. If the data
  1655. // was already stored in the Irp buffer then we know that we won't
  1656. // take this path. Otherwise we wouldn't be cleaning up the Irp
  1657. // correctly.
  1658. //
  1659. if (DataLength == 0
  1660. || IrpSp->Parameters.NotifyDirectory.Length < DataLength) {
  1661. Status = STATUS_NOTIFY_ENUM_DIR;
  1662. //
  1663. // We have to carefully return the buffer to the user and handle all
  1664. // of the different buffer cases. If there is no allocated buffer
  1665. // in the notify structure it means that we have already used the
  1666. // caller's buffer.
  1667. //
  1668. // 1 - If the system allocated an associated system buffer we
  1669. // can simply fill that in.
  1670. //
  1671. // 2 - If there is an Mdl then we get a system address for the Mdl
  1672. // and copy the data into it.
  1673. //
  1674. // 3 - If there is only a user's buffer and pending has not been
  1675. // returned, we can fill the user's buffer in directly.
  1676. //
  1677. // 4 - If there is only a user's buffer and pending has been returned
  1678. // then we are not in the user's address space. We dress up
  1679. // the Irp with our system buffer and let the Io system
  1680. // copy the data in.
  1681. //
  1682. } else {
  1683. if (Notify->AllocatedBuffer != NULL) {
  1684. //
  1685. // Protect the copy with a try-except and ignore the buffer
  1686. // if we have some error in copying it to the buffer.
  1687. //
  1688. try {
  1689. if (NotifyIrp->AssociatedIrp.SystemBuffer != NULL) {
  1690. RtlCopyMemory( NotifyIrp->AssociatedIrp.SystemBuffer,
  1691. Notify->AllocatedBuffer,
  1692. DataLength );
  1693. } else if (NotifyIrp->MdlAddress != NULL) {
  1694. RtlCopyMemory( MmGetSystemAddressForMdl( NotifyIrp->MdlAddress ),
  1695. Notify->AllocatedBuffer,
  1696. DataLength );
  1697. } else if (!FlagOn( IrpSp->Control, SL_PENDING_RETURNED )) {
  1698. RtlCopyMemory( NotifyIrp->UserBuffer,
  1699. Notify->AllocatedBuffer,
  1700. DataLength );
  1701. } else {
  1702. NotifyIrp->Flags |= (IRP_BUFFERED_IO | IRP_INPUT_OPERATION | IRP_DEALLOCATE_BUFFER);
  1703. NotifyIrp->AssociatedIrp.SystemBuffer = Notify->AllocatedBuffer;
  1704. }
  1705. } except( EXCEPTION_EXECUTE_HANDLER ) {
  1706. Status = STATUS_NOTIFY_ENUM_DIR;
  1707. DataLength = 0;
  1708. }
  1709. //
  1710. // Return the quota and deallocate the buffer if we didn't pass it
  1711. // back via the irp.
  1712. //
  1713. PsReturnProcessPagedPoolQuota( Notify->OwningProcess, Notify->ThisBufferLength );
  1714. if (Notify->AllocatedBuffer != NotifyIrp->AssociatedIrp.SystemBuffer
  1715. && Notify->AllocatedBuffer != NULL) {
  1716. ExFreePool( Notify->AllocatedBuffer );
  1717. }
  1718. Notify->AllocatedBuffer = NULL;
  1719. Notify->ThisBufferLength = 0;
  1720. }
  1721. //
  1722. // Update the data length in the Irp.
  1723. //
  1724. NotifyIrp->IoStatus.Information = DataLength;
  1725. //
  1726. // Show that there is no buffer in the notify package
  1727. // anymore.
  1728. //
  1729. Notify->Buffer = NULL;
  1730. }
  1731. }
  1732. //
  1733. // Make sure the Irp is marked as pending returned.
  1734. //
  1735. IoMarkIrpPending( NotifyIrp );
  1736. //
  1737. // Now complete the request.
  1738. //
  1739. FsRtlCompleteRequest( NotifyIrp, Status );
  1740. }
  1741. DebugTrace( -1, Dbg, "FsRtlIsNotifyCompleteIrp: Exit\n", 0 );
  1742. return;
  1743. }
  1744. //
  1745. // Local support routine
  1746. //
  1747. BOOLEAN
  1748. FsRtlNotifySetCancelRoutine (
  1749. IN PIRP NotifyIrp,
  1750. IN PNOTIFY_CHANGE Notify OPTIONAL
  1751. )
  1752. /*++
  1753. Routine Description:
  1754. This is a separate routine because it cannot be paged.
  1755. Arguments:
  1756. NotifyIrp - Set the cancel routine in this Irp.
  1757. Notify - If NULL then we are setting the cancel routine. If not-NULL then we
  1758. are clearing the cancel routine. If the cancel routine is not-null then
  1759. we need to decrement the reference count on this Notify structure
  1760. Return Value:
  1761. BOOLEAN - Only meaningfull if Notify is specified. It indicates if this
  1762. routine cleared the cancel routine. FALSE indicates that the cancel
  1763. routine is processing the Irp.
  1764. --*/
  1765. {
  1766. BOOLEAN ClearedCancel = FALSE;
  1767. PDRIVER_CANCEL CurrentCancel;
  1768. //
  1769. // Grab the cancel spinlock and set our cancel routine in the Irp.
  1770. //
  1771. IoAcquireCancelSpinLock( &NotifyIrp->CancelIrql );
  1772. //
  1773. // If we are completing an Irp then clear the cancel routine and
  1774. // the information field.
  1775. //
  1776. if (ARGUMENT_PRESENT( Notify )) {
  1777. CurrentCancel = IoSetCancelRoutine( NotifyIrp, NULL );
  1778. NotifyIrp->IoStatus.Information = 0;
  1779. IoReleaseCancelSpinLock( NotifyIrp->CancelIrql );
  1780. //
  1781. // If the current cancel routine is non-NULL then decrement the reference count
  1782. // in the Notify.
  1783. //
  1784. if (CurrentCancel != NULL) {
  1785. InterlockedDecrement( &Notify->ReferenceCount );
  1786. ClearedCancel = TRUE;
  1787. }
  1788. //
  1789. // If the cancel flag is set, we complete the Irp with cancelled
  1790. // status and exit.
  1791. //
  1792. } else if (NotifyIrp->Cancel) {
  1793. DebugTrace( 0, Dbg, "Irp has been cancelled\n", 0 );
  1794. FsRtlCancelNotify( NULL, NotifyIrp );
  1795. } else {
  1796. //
  1797. // Set our cancel routine in the Irp.
  1798. //
  1799. IoSetCancelRoutine( NotifyIrp, FsRtlCancelNotify );
  1800. IoReleaseCancelSpinLock( NotifyIrp->CancelIrql );
  1801. }
  1802. return ClearedCancel;
  1803. }
  1804. //
  1805. // Local support routine
  1806. //
  1807. BOOLEAN
  1808. FsRtlNotifyUpdateBuffer (
  1809. IN PFILE_NOTIFY_INFORMATION NotifyInfo,
  1810. IN ULONG FileAction,
  1811. IN PSTRING ParentName,
  1812. IN PSTRING TargetName,
  1813. IN PSTRING StreamName OPTIONAL,
  1814. IN BOOLEAN UnicodeName,
  1815. IN ULONG SizeOfEntry
  1816. )
  1817. /*++
  1818. Routine Description:
  1819. This routine is called to fill in a FILE_NOTIFY_INFORMATION structure for a
  1820. notify change event. The main work is in converting an OEM string to Unicode.
  1821. Arguments:
  1822. NotifyInfo - Information structure to complete.
  1823. FileAction - Action which triggered the notification event.
  1824. ParentName - Relative path to the parent of the changed file from the
  1825. directory being watched. The length for this will be zero if the modified
  1826. file is in the watched directory.
  1827. TargetName - This is the name of the modified file.
  1828. StreamName - If present there is a stream name to append to the filename.
  1829. UnicodeName - Indicates if the above name is Unicode or Oem.
  1830. SizeOfEntry - Indicates the number of bytes to be used in the buffer.
  1831. Return Value:
  1832. BOOLEAN - TRUE if we were able to update the buffer, FALSE otherwise.
  1833. --*/
  1834. {
  1835. BOOLEAN CopiedToBuffer;
  1836. ULONG BufferOffset = 0;
  1837. PAGED_CODE();
  1838. DebugTrace( +1, Dbg, "FsRtlNotifyUpdateBuffer: Entered\n", 0 );
  1839. //
  1840. // Protect the entire call with a try-except. If we had an error
  1841. // we will assume that we have a bad buffer and we won't return
  1842. // the data in the buffer.
  1843. //
  1844. try {
  1845. //
  1846. // Update the common fields in the notify information.
  1847. //
  1848. NotifyInfo->NextEntryOffset = 0;
  1849. NotifyInfo->Action = FileAction;
  1850. NotifyInfo->FileNameLength = SizeOfEntry - FIELD_OFFSET( FILE_NOTIFY_INFORMATION, FileName );
  1851. //
  1852. // If we have a unicode name, then copy the data directly into the output buffer.
  1853. //
  1854. if (UnicodeName) {
  1855. if (ParentName->Length != 0) {
  1856. RtlCopyMemory( NotifyInfo->FileName,
  1857. ParentName->Buffer,
  1858. ParentName->Length );
  1859. *(Add2Ptr( NotifyInfo->FileName, ParentName->Length, PWCHAR )) = L'\\';
  1860. BufferOffset = ParentName->Length + sizeof( WCHAR );
  1861. }
  1862. RtlCopyMemory( Add2Ptr( NotifyInfo->FileName,
  1863. BufferOffset,
  1864. PVOID ),
  1865. TargetName->Buffer,
  1866. TargetName->Length );
  1867. if (ARGUMENT_PRESENT( StreamName )) {
  1868. BufferOffset += TargetName->Length;
  1869. *(Add2Ptr( NotifyInfo->FileName, BufferOffset, PWCHAR )) = L':';
  1870. RtlCopyMemory( Add2Ptr( NotifyInfo->FileName,
  1871. BufferOffset + sizeof( WCHAR ),
  1872. PVOID ),
  1873. StreamName->Buffer,
  1874. StreamName->Length );
  1875. }
  1876. //
  1877. // For a non-unicode name, use the conversion routines.
  1878. //
  1879. } else {
  1880. ULONG BufferLength;
  1881. if (ParentName->Length != 0) {
  1882. RtlOemToUnicodeN( NotifyInfo->FileName,
  1883. NotifyInfo->FileNameLength,
  1884. &BufferLength,
  1885. ParentName->Buffer,
  1886. ParentName->Length );
  1887. *(Add2Ptr( NotifyInfo->FileName, BufferLength, PWCHAR )) = L'\\';
  1888. BufferOffset = BufferLength + sizeof( WCHAR );
  1889. }
  1890. //
  1891. // For view indices, we do not have a parent name.
  1892. //
  1893. if (ParentName->Length == 0) {
  1894. ASSERT(ARGUMENT_PRESENT( StreamName ));
  1895. RtlCopyMemory( Add2Ptr( NotifyInfo->FileName,
  1896. BufferOffset,
  1897. PCHAR ),
  1898. StreamName->Buffer,
  1899. StreamName->Length );
  1900. } else {
  1901. RtlOemToUnicodeN( Add2Ptr( NotifyInfo->FileName,
  1902. BufferOffset,
  1903. PWCHAR ),
  1904. NotifyInfo->FileNameLength,
  1905. &BufferLength,
  1906. TargetName->Buffer,
  1907. TargetName->Length );
  1908. if (ARGUMENT_PRESENT( StreamName )) {
  1909. BufferOffset += BufferLength;
  1910. *(Add2Ptr( NotifyInfo->FileName, BufferOffset, PWCHAR )) = L':';
  1911. RtlOemToUnicodeN( Add2Ptr( NotifyInfo->FileName,
  1912. BufferOffset + sizeof( WCHAR ),
  1913. PWCHAR ),
  1914. NotifyInfo->FileNameLength,
  1915. &BufferLength,
  1916. StreamName->Buffer,
  1917. StreamName->Length );
  1918. }
  1919. }
  1920. }
  1921. CopiedToBuffer = TRUE;
  1922. } except( EXCEPTION_EXECUTE_HANDLER ) {
  1923. CopiedToBuffer = FALSE;
  1924. }
  1925. DebugTrace( -1, Dbg, "FsRtlNotifyUpdateBuffer: Exit\n", 0 );
  1926. return CopiedToBuffer;
  1927. }
  1928. //
  1929. // Local support routine
  1930. //
  1931. VOID
  1932. FsRtlNotifyCompleteIrpList (
  1933. IN OUT PNOTIFY_CHANGE Notify,
  1934. IN NTSTATUS Status
  1935. )
  1936. /*++
  1937. Routine Description:
  1938. This routine walks through the Irps for a particular notify structure
  1939. and completes the Irps with the indicated status. If the status is
  1940. STATUS_SUCCESS then we are completing an Irp because of a notification event.
  1941. In that case we look at the notify structure to decide if we can return the
  1942. data to the user.
  1943. Arguments:
  1944. Notify - This is the notify change structure.
  1945. Status - Indicates the status used to complete the request. If this status
  1946. is STATUS_SUCCESS then we only want to complete one Irp. Otherwise we
  1947. want complete all the Irps in the list.
  1948. Return Value:
  1949. None.
  1950. --*/
  1951. {
  1952. PIRP Irp;
  1953. ULONG DataLength;
  1954. DebugTrace( +1, Dbg, "FsRtlNotifyCompleteIrpList: Entered\n", 0 );
  1955. DataLength = Notify->DataLength;
  1956. //
  1957. // Clear the fields to indicate that there is no more data to return.
  1958. //
  1959. ClearFlag( Notify->Flags, NOTIFY_IMMEDIATE_NOTIFY );
  1960. Notify->DataLength = 0;
  1961. Notify->LastEntry = 0;
  1962. //
  1963. // Walk through all the Irps in the list. We are never called unless
  1964. // there is at least one irp.
  1965. //
  1966. do {
  1967. Irp = CONTAINING_RECORD( Notify->NotifyIrps.Flink, IRP, Tail.Overlay.ListEntry );
  1968. RemoveHeadList( &Notify->NotifyIrps );
  1969. //
  1970. // Call our completion routine to complete the request.
  1971. //
  1972. FsRtlNotifyCompleteIrp( Irp,
  1973. Notify,
  1974. DataLength,
  1975. Status,
  1976. TRUE );
  1977. //
  1978. // If we were only to complete one Irp then break now.
  1979. //
  1980. if (Status == STATUS_SUCCESS) {
  1981. break;
  1982. }
  1983. } while (!IsListEmpty( &Notify->NotifyIrps ));
  1984. DebugTrace( -1, Dbg, "FsRtlNotifyCompleteIrpList: Exit\n", 0 );
  1985. return;
  1986. }
  1987. //
  1988. // Local support routine
  1989. //
  1990. VOID
  1991. FsRtlCancelNotify (
  1992. IN PDEVICE_OBJECT DeviceObject,
  1993. IN PIRP ThisIrp
  1994. )
  1995. /*++
  1996. Routine Description:
  1997. This routine is for an Irp which is being cancelled. We Null the cancel
  1998. routine and then walk through the Irps for this notify structure and
  1999. complete all cancelled Irps. It is possible there is pending notify
  2000. stored in the buffer for this Irp. In this case we want to copy the
  2001. data to a system buffer if possible.
  2002. Arguments:
  2003. DeviceObject - Ignored.
  2004. ThisIrp - This is the Irp to cancel.
  2005. Return Value:
  2006. None.
  2007. --*/
  2008. {
  2009. PSECURITY_SUBJECT_CONTEXT SubjectContext = NULL;
  2010. PNOTIFY_CHANGE Notify;
  2011. PNOTIFY_SYNC NotifySync;
  2012. LONG ExceptionCode;
  2013. UNREFERENCED_PARAMETER( DeviceObject );
  2014. DebugTrace( +1, Dbg, "FsRtlCancelNotify: Entered\n", 0 );
  2015. DebugTrace( 0, Dbg, "Irp -> %08lx\n", Irp );
  2016. //
  2017. // Capture the notify structure.
  2018. //
  2019. Notify = (PNOTIFY_CHANGE) ThisIrp->IoStatus.Information;
  2020. //
  2021. // Void the cancel routine and release the cancel spinlock.
  2022. //
  2023. IoSetCancelRoutine( ThisIrp, NULL );
  2024. ThisIrp->IoStatus.Information = 0;
  2025. IoReleaseCancelSpinLock( ThisIrp->CancelIrql );
  2026. FsRtlEnterFileSystem();
  2027. //
  2028. // Grab the mutex for this structure.
  2029. //
  2030. NotifySync = Notify->NotifySync;
  2031. AcquireNotifySync( NotifySync );
  2032. //
  2033. // Use a try finally to faciltate cleanup.
  2034. //
  2035. try {
  2036. //
  2037. // Remove the Irp from the queue.
  2038. //
  2039. RemoveEntryList( &ThisIrp->Tail.Overlay.ListEntry );
  2040. IoMarkIrpPending( ThisIrp );
  2041. //
  2042. // We now have the Irp. Check to see if there is data stored
  2043. // in the buffer for this Irp.
  2044. //
  2045. if (Notify->Buffer != NULL
  2046. && Notify->AllocatedBuffer == NULL
  2047. && ((ThisIrp->MdlAddress != NULL
  2048. && MmGetSystemAddressForMdl( ThisIrp->MdlAddress ) == Notify->Buffer)
  2049. || (Notify->Buffer == ThisIrp->AssociatedIrp.SystemBuffer))) {
  2050. PIRP NextIrp;
  2051. PVOID NewBuffer;
  2052. ULONG NewBufferLength;
  2053. PIO_STACK_LOCATION IrpSp;
  2054. //
  2055. // Initialize the above values.
  2056. //
  2057. NewBuffer = NULL;
  2058. NewBufferLength = 0;
  2059. //
  2060. // Remember the next Irp on the list. Find the length of any
  2061. // buffer it might have. Also keep a pointer to the buffer
  2062. // if present.
  2063. //
  2064. if (!IsListEmpty( &Notify->NotifyIrps )) {
  2065. NextIrp = CONTAINING_RECORD( Notify->NotifyIrps.Flink,
  2066. IRP,
  2067. Tail.Overlay.ListEntry );
  2068. IrpSp = IoGetCurrentIrpStackLocation( NextIrp );
  2069. //
  2070. // If the buffer here is large enough to hold the data we
  2071. // can use that buffer.
  2072. //
  2073. if (IrpSp->Parameters.NotifyDirectory.Length >= Notify->DataLength) {
  2074. //
  2075. // If there is a system buffer or Mdl then get a new
  2076. // buffer there.
  2077. //
  2078. if (NextIrp->AssociatedIrp.SystemBuffer != NULL) {
  2079. NewBuffer = NextIrp->AssociatedIrp.SystemBuffer;
  2080. } else if (NextIrp->MdlAddress != NULL) {
  2081. NewBuffer = MmGetSystemAddressForMdl( NextIrp->MdlAddress );
  2082. }
  2083. NewBufferLength = IrpSp->Parameters.NotifyDirectory.Length;
  2084. if (NewBufferLength > Notify->BufferLength) {
  2085. NewBufferLength = Notify->BufferLength;
  2086. }
  2087. }
  2088. //
  2089. // Otherwise check if the user's original buffer is larger than
  2090. // the current buffer.
  2091. //
  2092. } else if (Notify->BufferLength >= Notify->DataLength) {
  2093. NewBufferLength = Notify->BufferLength;
  2094. }
  2095. //
  2096. // If we have a new buffer length then we either have a new
  2097. // buffer or need to allocate one. We will do this under
  2098. // the protection of a try-except in order to continue in the
  2099. // event of a failure.
  2100. //
  2101. if (NewBufferLength != 0) {
  2102. BOOLEAN ChargedQuota;
  2103. try {
  2104. ChargedQuota = FALSE;
  2105. if (NewBuffer == NULL) {
  2106. PsChargePoolQuota( Notify->OwningProcess,
  2107. PagedPool,
  2108. NewBufferLength );
  2109. ChargedQuota = TRUE;
  2110. //
  2111. // If we didn't get an error then attempt to
  2112. // allocate the pool. If there is an error
  2113. // don't forget to release the quota.
  2114. //
  2115. NewBuffer = FsRtlpAllocatePool( PagedPool,
  2116. NewBufferLength );
  2117. Notify->AllocatedBuffer = NewBuffer;
  2118. }
  2119. //
  2120. // Now copy the data over to the new buffer.
  2121. //
  2122. RtlCopyMemory( NewBuffer,
  2123. Notify->Buffer,
  2124. Notify->DataLength );
  2125. //
  2126. // It is possible that the buffer size changed.
  2127. //
  2128. Notify->ThisBufferLength = NewBufferLength;
  2129. Notify->Buffer = NewBuffer;
  2130. } except( FsRtlIsNtstatusExpected( ExceptionCode = GetExceptionCode()) ?
  2131. EXCEPTION_EXECUTE_HANDLER :
  2132. EXCEPTION_CONTINUE_SEARCH ) {
  2133. ASSERT( (ExceptionCode == STATUS_INSUFFICIENT_RESOURCES) ||
  2134. (ExceptionCode == STATUS_QUOTA_EXCEEDED) );
  2135. //
  2136. // Return quota if we allocated the buffer.
  2137. //
  2138. if (ChargedQuota) {
  2139. PsReturnProcessPagedPoolQuota( Notify->OwningProcess,
  2140. NewBufferLength );
  2141. }
  2142. //
  2143. // Forget any current buffer and resort to immediate
  2144. // notify.
  2145. //
  2146. SetFlag( Notify->Flags, NOTIFY_IMMEDIATE_NOTIFY );
  2147. }
  2148. //
  2149. // Otherwise set the immediate notify flag.
  2150. //
  2151. } else {
  2152. SetFlag( Notify->Flags, NOTIFY_IMMEDIATE_NOTIFY );
  2153. }
  2154. //
  2155. // If the immediate notify flag is set then clear the other
  2156. // values in the notify structures.
  2157. //
  2158. if (FlagOn( Notify->Flags, NOTIFY_IMMEDIATE_NOTIFY )) {
  2159. //
  2160. // Forget any current buffer and resort to immediate
  2161. // notify.
  2162. //
  2163. Notify->AllocatedBuffer = Notify->Buffer = NULL;
  2164. Notify->ThisBufferLength =
  2165. Notify->DataLength = Notify->LastEntry = 0;
  2166. }
  2167. }
  2168. //
  2169. // Complete the Irp with status cancelled.
  2170. //
  2171. FsRtlCompleteRequest( ThisIrp, STATUS_CANCELLED );
  2172. //
  2173. // Decrement the count of Irps that might go through the cancel path.
  2174. //
  2175. InterlockedDecrement( &Notify->ReferenceCount );
  2176. if (Notify->ReferenceCount == 0) {
  2177. if (Notify->AllocatedBuffer != NULL) {
  2178. PsReturnProcessPagedPoolQuota( Notify->OwningProcess,
  2179. Notify->ThisBufferLength );
  2180. ExFreePool( Notify->AllocatedBuffer );
  2181. }
  2182. if (Notify->FullDirectoryName != NULL) {
  2183. SubjectContext = Notify->SubjectContext;
  2184. }
  2185. //
  2186. // Cleanup owned one of the refcounts, and must have removed this from
  2187. // the notify list. If not, we are freeing pool with live links.
  2188. //
  2189. ASSERT( Notify->NotifyList.Flink == NULL );
  2190. ExFreePool( Notify );
  2191. Notify = NULL;
  2192. }
  2193. } finally {
  2194. //
  2195. // No matter how we exit, we release the mutex.
  2196. //
  2197. ReleaseNotifySync( NotifySync );
  2198. if (SubjectContext != NULL) {
  2199. SeReleaseSubjectContext( SubjectContext );
  2200. ExFreePool( SubjectContext );
  2201. }
  2202. FsRtlExitFileSystem();
  2203. DebugTrace( -1, Dbg, "FsRtlCancelNotify: Exit\n", 0 );
  2204. }
  2205. return;
  2206. }
  2207. //
  2208. // Local support routine
  2209. //
  2210. VOID
  2211. FsRtlCheckNotifyForDelete (
  2212. IN PLIST_ENTRY NotifyListHead,
  2213. IN PVOID StreamID
  2214. )
  2215. /*++
  2216. Routine Description:
  2217. This routine is called when a stream is being marked for delete. We will
  2218. walk through the notify structures looking for an Irp for the same stream.
  2219. We will complete these Irps with STATUS_DELETE_PENDING.
  2220. Arguments:
  2221. NotifyListHead - This is the start of the notify list.
  2222. StreamID - This is the Context ID used to identify the stream.
  2223. Return Value:
  2224. None.
  2225. --*/
  2226. {
  2227. PLIST_ENTRY Link;
  2228. PNOTIFY_CHANGE ThisNotify;
  2229. PAGED_CODE();
  2230. //
  2231. // Walk through all the entries on the list looking for a match.
  2232. //
  2233. for (Link = NotifyListHead->Flink;
  2234. Link != NotifyListHead;
  2235. Link = Link->Flink) {
  2236. //
  2237. // Obtain the notify structure from the link.
  2238. //
  2239. ThisNotify = CONTAINING_RECORD( Link, NOTIFY_CHANGE, NotifyList );
  2240. //
  2241. // If the context field matches, then complete any waiting Irps.
  2242. //
  2243. if (ThisNotify->StreamID == StreamID) {
  2244. //
  2245. // Start by marking the notify structure as file deleted.
  2246. //
  2247. SetFlag( ThisNotify->Flags, NOTIFY_STREAM_IS_DELETED );
  2248. //
  2249. // Now complete all of the Irps on this list.
  2250. //
  2251. if (!IsListEmpty( &ThisNotify->NotifyIrps )) {
  2252. FsRtlNotifyCompleteIrpList( ThisNotify, STATUS_DELETE_PENDING );
  2253. }
  2254. }
  2255. }
  2256. return;
  2257. }