[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / fsrtl / notify.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/fsrtl/notify.c
5 * PURPOSE: Change Notifications and Sync for File System Drivers
6 * PROGRAMMERS: Pierre Schweitzer
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* PRIVATE FUNCTIONS *********************************************************/
16
17 PNOTIFY_CHANGE
18 FsRtlIsNotifyOnList(IN PLIST_ENTRY NotifyList,
19 IN PVOID FsContext)
20 {
21 PLIST_ENTRY NextEntry;
22 PNOTIFY_CHANGE NotifyChange;
23
24 if (!IsListEmpty(NotifyList))
25 {
26 /* Browse the notifications list to find the matching entry */
27 for (NextEntry = NotifyList->Flink;
28 NextEntry != NotifyList;
29 NextEntry = NextEntry->Flink)
30 {
31 NotifyChange = CONTAINING_RECORD(NextEntry, NOTIFY_CHANGE, NotifyList);
32 /* If the current record matches with the given context, it's the good one */
33 if (NotifyChange->FsContext == FsContext)
34 {
35 return NotifyChange;
36 }
37 }
38 }
39 return NULL;
40 }
41
42 VOID
43 FORCEINLINE
44 FsRtlNotifyAcquireFastMutex(IN PREAL_NOTIFY_SYNC RealNotifySync)
45 {
46 ULONG_PTR CurrentThread = (ULONG_PTR)KeGetCurrentThread();
47
48 /* Only acquire fast mutex if it's not already acquired by the current thread */
49 if (RealNotifySync->OwningThread != CurrentThread)
50 {
51 ExAcquireFastMutexUnsafe(&(RealNotifySync->FastMutex));
52 RealNotifySync->OwningThread = CurrentThread;
53 }
54 /* Whatever the case, keep trace of the attempt to acquire fast mutex */
55 RealNotifySync->OwnerCount++;
56 }
57
58 VOID
59 FsRtlNotifyCompleteIrpList(IN PNOTIFY_CHANGE NotifyChange,
60 IN NTSTATUS Status)
61 {
62 }
63
64 VOID
65 FORCEINLINE
66 FsRtlNotifyReleaseFastMutex(IN PREAL_NOTIFY_SYNC RealNotifySync)
67 {
68 RealNotifySync->OwnerCount--;
69 /* Release the fast mutex only if no other instance needs it */
70 if (!RealNotifySync->OwnerCount)
71 {
72 ExReleaseFastMutexUnsafe(&(RealNotifySync->FastMutex));
73 RealNotifySync->OwningThread = (ULONG_PTR)0;
74 }
75 }
76
77 /* PUBLIC FUNCTIONS **********************************************************/
78
79 /*++
80 * @name FsRtlNotifyChangeDirectory
81 * @implemented
82 *
83 * Lets FSD know if changes occures in the specified directory.
84 * Directory will be reenumerated.
85 *
86 * @param NotifySync
87 * Synchronization object pointer
88 *
89 * @param FsContext
90 * Used to identify the notify structure
91 *
92 * @param FullDirectoryName
93 * String (A or W) containing the full directory name
94 *
95 * @param NotifyList
96 * Notify list pointer (to head)
97 *
98 * @param WatchTree
99 * True to notify changes in subdirectories too
100 *
101 * @param CompletionFilter
102 * Used to define types of changes to notify
103 *
104 * @param NotifyIrp
105 * IRP pointer to complete notify operation. It can be null
106 *
107 * @return None
108 *
109 * @remarks This function only redirects to FsRtlNotifyFilterChangeDirectory.
110 *
111 *--*/
112 VOID
113 NTAPI
114 FsRtlNotifyChangeDirectory(IN PNOTIFY_SYNC NotifySync,
115 IN PVOID FsContext,
116 IN PSTRING FullDirectoryName,
117 IN PLIST_ENTRY NotifyList,
118 IN BOOLEAN WatchTree,
119 IN ULONG CompletionFilter,
120 IN PIRP NotifyIrp)
121 {
122 FsRtlNotifyFilterChangeDirectory(NotifySync,
123 NotifyList,
124 FsContext,
125 FullDirectoryName,
126 WatchTree,
127 TRUE,
128 CompletionFilter,
129 NotifyIrp,
130 NULL,
131 NULL,
132 NULL);
133 }
134
135 /*++
136 * @name FsRtlNotifyCleanup
137 * @implemented
138 *
139 * Called by FSD when all handles to FileObject (identified by FsContext) are closed
140 *
141 * @param NotifySync
142 * Synchronization object pointer
143 *
144 * @param NotifyList
145 * Notify list pointer (to head)
146 *
147 * @param FsContext
148 * Used to identify the notify structure
149 *
150 * @return None
151 *
152 * @remarks None
153 *
154 *--*/
155 VOID
156 NTAPI
157 FsRtlNotifyCleanup(IN PNOTIFY_SYNC NotifySync,
158 IN PLIST_ENTRY NotifyList,
159 IN PVOID FsContext)
160 {
161 PNOTIFY_CHANGE NotifyChange;
162 PREAL_NOTIFY_SYNC RealNotifySync;
163 PSECURITY_SUBJECT_CONTEXT SubjectContext = NULL;
164
165 /* Get real structure hidden behind the opaque pointer */
166 RealNotifySync = (PREAL_NOTIFY_SYNC)NotifySync;
167
168 /* Acquire the fast mutex */
169 FsRtlNotifyAcquireFastMutex(RealNotifySync);
170
171 _SEH2_TRY
172 {
173 /* Find if there's a matching notification with the FsContext */
174 NotifyChange = FsRtlIsNotifyOnList(NotifyList, FsContext);
175 if (NotifyChange)
176 {
177 /* Mark it as to know that cleanup is in process */
178 NotifyChange->Flags |= CLEANUP_IN_PROCESS;
179
180 /* If there are pending IRPs, complete them using the STATUS_NOTIFY_CLEANUP status */
181 if (!IsListEmpty(&NotifyChange->NotifyIrps))
182 {
183 FsRtlNotifyCompleteIrpList(NotifyChange, STATUS_NOTIFY_CLEANUP);
184 }
185 /* Remove from the list */
186 RemoveEntryList(&NotifyChange->NotifyList);
187
188 /* Downcrease reference number and if 0 is reached, it's time to do complete cleanup */
189 if (!InterlockedDecrement((PLONG)&(NotifyChange->ReferenceCount)))
190 {
191 /* In case there was an allocated buffer, free it */
192 if (NotifyChange->AllocatedBuffer)
193 {
194 PsReturnProcessPagedPoolQuota(NotifyChange->OwningProcess, NotifyChange->ThisBufferLength);
195 ExFreePool(NotifyChange->AllocatedBuffer);
196 }
197
198 /* In case there the string was set, get the captured subject security context */
199 if (NotifyChange->FullDirectoryName)
200 {
201 SubjectContext = NotifyChange->SubjectContext;
202 }
203
204 /* Finally, free the notification, as it's not needed anymore */
205 ExFreePool(NotifyChange);
206 }
207 }
208 }
209 _SEH2_FINALLY
210 {
211 /* Release fast mutex */
212 FsRtlNotifyReleaseFastMutex(RealNotifySync);
213
214 /* If the subject security context was captured, release and free it */
215 if (SubjectContext)
216 {
217 SeReleaseSubjectContext(SubjectContext);
218 ExFreePool(SubjectContext);
219 }
220 }
221 _SEH2_END;
222 }
223
224 /*++
225 * @name FsRtlNotifyFilterChangeDirectory
226 * @unimplemented
227 *
228 * FILLME
229 *
230 * @param NotifySync
231 * FILLME
232 *
233 * @param NotifyList
234 * FILLME
235 *
236 * @param FsContext
237 * FILLME
238 *
239 * @param FullDirectoryName
240 * FILLME
241 *
242 * @param WatchTree
243 * FILLME
244 *
245 * @param IgnoreBuffer
246 * FILLME
247 *
248 * @param CompletionFilter
249 * FILLME
250 *
251 * @param NotifyIrp
252 * FILLME
253 *
254 * @param TraverseCallback
255 * FILLME
256 *
257 * @param SubjectContext
258 * FILLME
259 *
260 * @param FilterCallback
261 * FILLME
262 *
263 * @return None
264 *
265 * @remarks None
266 *
267 *--*/
268 VOID
269 NTAPI
270 FsRtlNotifyFilterChangeDirectory(IN PNOTIFY_SYNC NotifySync,
271 IN PLIST_ENTRY NotifyList,
272 IN PVOID FsContext,
273 IN PSTRING FullDirectoryName,
274 IN BOOLEAN WatchTree,
275 IN BOOLEAN IgnoreBuffer,
276 IN ULONG CompletionFilter,
277 IN PIRP NotifyIrp,
278 IN PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback OPTIONAL,
279 IN PSECURITY_SUBJECT_CONTEXT SubjectContext OPTIONAL,
280 IN PFILTER_REPORT_CHANGE FilterCallback OPTIONAL)
281 {
282 KeBugCheck(FILE_SYSTEM);
283 }
284
285 /*++
286 * @name FsRtlNotifyFilterReportChange
287 * @unimplemented
288 *
289 * FILLME
290 *
291 * @param NotifySync
292 * FILLME
293 *
294 * @param NotifyList
295 * FILLME
296 *
297 * @param FullTargetName
298 * FILLME
299 *
300 * @param TargetNameOffset
301 * FILLME
302 *
303 * @param StreamName
304 * FILLME
305 *
306 * @param NormalizedParentName
307 * FILLME
308 *
309 * @param FilterMatch
310 * FILLME
311 *
312 * @param Action
313 * FILLME
314 *
315 * @param TargetContext
316 * FILLME
317 *
318 * @param FilterContext
319 * FILLME
320 *
321 * @return None
322 *
323 * @remarks None
324 *
325 *--*/
326 VOID
327 NTAPI
328 FsRtlNotifyFilterReportChange(IN PNOTIFY_SYNC NotifySync,
329 IN PLIST_ENTRY NotifyList,
330 IN PSTRING FullTargetName,
331 IN USHORT TargetNameOffset,
332 IN PSTRING StreamName OPTIONAL,
333 IN PSTRING NormalizedParentName OPTIONAL,
334 IN ULONG FilterMatch,
335 IN ULONG Action,
336 IN PVOID TargetContext,
337 IN PVOID FilterContext)
338 {
339 KeBugCheck(FILE_SYSTEM);
340 }
341
342 /*++
343 * @name FsRtlNotifyFullChangeDirectory
344 * @implemented
345 *
346 * Lets FSD know if changes occures in the specified directory.
347 *
348 * @param NotifySync
349 * Synchronization object pointer
350 *
351 * @param NotifyList
352 * Notify list pointer (to head)
353 *
354 * @param FsContext
355 * Used to identify the notify structure
356 *
357 * @param FullDirectoryName
358 * String (A or W) containing the full directory name
359 *
360 * @param WatchTree
361 * True to notify changes in subdirectories too
362 *
363 * @param IgnoreBuffer
364 * True to reenumerate directory. It's ignored it NotifyIrp is null
365 *
366 * @param CompletionFilter
367 * Used to define types of changes to notify
368 *
369 * @param NotifyIrp
370 * IRP pointer to complete notify operation. It can be null
371 *
372 * @param TraverseCallback
373 * Pointer to a callback function. It's called each time a change is
374 * done in a subdirectory of the main directory. It's ignored it NotifyIrp
375 * is null
376 *
377 * @param SubjectContext
378 * Pointer to pass to SubjectContext member of TraverseCallback.
379 * It's freed after use. It's ignored it NotifyIrp is null
380 *
381 * @return None
382 *
383 * @remarks This function only redirects to FsRtlNotifyFilterChangeDirectory.
384 *
385 *--*/
386 VOID
387 NTAPI
388 FsRtlNotifyFullChangeDirectory(IN PNOTIFY_SYNC NotifySync,
389 IN PLIST_ENTRY NotifyList,
390 IN PVOID FsContext,
391 IN PSTRING FullDirectoryName,
392 IN BOOLEAN WatchTree,
393 IN BOOLEAN IgnoreBuffer,
394 IN ULONG CompletionFilter,
395 IN PIRP NotifyIrp,
396 IN PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback OPTIONAL,
397 IN PSECURITY_SUBJECT_CONTEXT SubjectContext OPTIONAL)
398 {
399 FsRtlNotifyFilterChangeDirectory(NotifySync,
400 NotifyList,
401 FsContext,
402 FullDirectoryName,
403 WatchTree,
404 IgnoreBuffer,
405 CompletionFilter,
406 NotifyIrp,
407 TraverseCallback,
408 SubjectContext,
409 NULL);
410 }
411
412 /*++
413 * @name FsRtlNotifyFullReportChange
414 * @implemented
415 *
416 * Complets the pending notify IRPs.
417 *
418 * @param NotifySync
419 * Synchronization object pointer
420 *
421 * @param NotifyList
422 * Notify list pointer (to head)
423 *
424 * @param FullTargetName
425 * String (A or W) containing the full directory name that changed
426 *
427 * @param TargetNameOffset
428 * Offset, in FullTargetName, of the final component that is in the changed directory
429 *
430 * @param StreamName
431 * String (A or W) containing a stream name
432 *
433 * @param NormalizedParentName
434 * String (A or W) containing the full directory name that changed with long names
435 *
436 * @param FilterMatch
437 * Flags that will be compared to the completion filter
438 *
439 * @param Action
440 * Action code to store in user's buffer
441 *
442 * @param TargetContext
443 * Pointer to a callback function. It's called each time a change is
444 * done in a subdirectory of the main directory.
445 *
446 * @return None
447 *
448 * @remarks This function only redirects to FsRtlNotifyFilterReportChange.
449 *
450 *--*/
451 VOID
452 NTAPI
453 FsRtlNotifyFullReportChange(IN PNOTIFY_SYNC NotifySync,
454 IN PLIST_ENTRY NotifyList,
455 IN PSTRING FullTargetName,
456 IN USHORT TargetNameOffset,
457 IN PSTRING StreamName OPTIONAL,
458 IN PSTRING NormalizedParentName OPTIONAL,
459 IN ULONG FilterMatch,
460 IN ULONG Action,
461 IN PVOID TargetContext)
462 {
463 FsRtlNotifyFilterReportChange(NotifySync,
464 NotifyList,
465 FullTargetName,
466 TargetNameOffset,
467 StreamName,
468 NormalizedParentName,
469 FilterMatch,
470 Action,
471 TargetContext,
472 NULL);
473 }
474
475 /*++
476 * @name FsRtlNotifyInitializeSync
477 * @implemented
478 *
479 * Allocates the internal structure associated with notifications.
480 *
481 * @param NotifySync
482 * Opaque pointer. It will receive the address of the allocated internal structure.
483 *
484 * @return None
485 *
486 * @remarks This function raise an exception in case of a failure.
487 *
488 *--*/
489 VOID
490 NTAPI
491 FsRtlNotifyInitializeSync(IN PNOTIFY_SYNC *NotifySync)
492 {
493 PREAL_NOTIFY_SYNC RealNotifySync;
494
495 *NotifySync = NULL;
496
497 RealNotifySync = ExAllocatePoolWithTag(NonPagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE,
498 sizeof(REAL_NOTIFY_SYNC), 'FSNS');
499 ExInitializeFastMutex(&(RealNotifySync->FastMutex));
500 RealNotifySync->OwningThread = 0;
501 RealNotifySync->OwnerCount = 0;
502
503 *NotifySync = RealNotifySync;
504 }
505
506 /*++
507 * @name FsRtlNotifyReportChange
508 * @implemented
509 *
510 * Complets the pending notify IRPs.
511 *
512 * @param NotifySync
513 * Synchronization object pointer
514 *
515 * @param NotifyList
516 * Notify list pointer (to head)
517 *
518 * @param FullTargetName
519 * String (A or W) containing the full directory name that changed
520 *
521 * @param FileNamePartLength
522 * Length of the final component that is in the changed directory
523 *
524 * @param FilterMatch
525 * Flags that will be compared to the completion filter
526 *
527 * @return None
528 *
529 * @remarks This function only redirects to FsRtlNotifyFilterReportChange.
530 *
531 *--*/
532 VOID
533 NTAPI
534 FsRtlNotifyReportChange(IN PNOTIFY_SYNC NotifySync,
535 IN PLIST_ENTRY NotifyList,
536 IN PSTRING FullTargetName,
537 IN PUSHORT FileNamePartLength,
538 IN ULONG FilterMatch)
539 {
540 FsRtlNotifyFilterReportChange(NotifySync,
541 NotifyList,
542 FullTargetName,
543 FullTargetName->Length - *FileNamePartLength,
544 NULL,
545 NULL,
546 FilterMatch,
547 0,
548 NULL,
549 NULL);
550 }
551
552 /*++
553 * @name FsRtlNotifyUninitializeSync
554 * @implemented
555 *
556 * Uninitialize a NOTIFY_SYNC object
557 *
558 * @param NotifySync
559 * Address of a pointer to a PNOTIFY_SYNC object previously
560 * initialized by FsRtlNotifyInitializeSync()
561 *
562 * @return None
563 *
564 * @remarks None
565 *
566 *--*/
567 VOID
568 NTAPI
569 FsRtlNotifyUninitializeSync(IN PNOTIFY_SYNC *NotifySync)
570 {
571 if (*NotifySync)
572 {
573 ExFreePoolWithTag(*NotifySync, 'FSNS');
574 *NotifySync = NULL;
575 }
576 }
577