14
14
15
15
/* PRIVATE FUNCTIONS *********************************************************/
16
16
17
+ VOID
18
+ FsRtlNotifyCompleteIrpList (IN PNOTIFY_CHANGE NotifyChange ,
19
+ IN NTSTATUS Status );
20
+
21
+ BOOLEAN
22
+ FsRtlNotifySetCancelRoutine (IN PIRP Irp ,
23
+ IN PNOTIFY_CHANGE NotifyChange OPTIONAL );
24
+
25
+ VOID
26
+ NTAPI
27
+ FsRtlCancelNotify (IN PDEVICE_OBJECT DeviceObject ,
28
+ IN PIRP Irp )
29
+ {
30
+ IoReleaseCancelSpinLock (Irp -> CancelIrql );
31
+ UNIMPLEMENTED ;
32
+ }
33
+
34
+ /*
35
+ * @implemented
36
+ */
37
+ VOID
38
+ FsRtlCheckNotifyForDelete (IN PLIST_ENTRY NotifyList ,
39
+ IN PVOID FsContext )
40
+ {
41
+ PLIST_ENTRY NextEntry ;
42
+ PNOTIFY_CHANGE NotifyChange ;
43
+
44
+ if (!IsListEmpty (NotifyList ))
45
+ {
46
+ /* Browse the notifications list to find the matching entry */
47
+ for (NextEntry = NotifyList -> Flink ;
48
+ NextEntry != NotifyList ;
49
+ NextEntry = NextEntry -> Flink )
50
+ {
51
+ NotifyChange = CONTAINING_RECORD (NextEntry , NOTIFY_CHANGE , NotifyList );
52
+ /* If the current record matches with the given context, it's the good one */
53
+ if (NotifyChange -> FsContext == FsContext && !IsListEmpty (& (NotifyChange -> NotifyIrps )))
54
+ {
55
+ FsRtlNotifyCompleteIrpList (NotifyChange , STATUS_DELETE_PENDING );
56
+ }
57
+ }
58
+ }
59
+ }
60
+
17
61
PNOTIFY_CHANGE
18
62
FsRtlIsNotifyOnList (IN PLIST_ENTRY NotifyList ,
19
63
IN PVOID FsContext )
@@ -55,10 +99,138 @@ FsRtlNotifyAcquireFastMutex(IN PREAL_NOTIFY_SYNC RealNotifySync)
55
99
RealNotifySync -> OwnerCount ++ ;
56
100
}
57
101
102
+ /*
103
+ * @implemented
104
+ */
105
+ VOID
106
+ FsRtlNotifyCompleteIrp (IN PIRP Irp ,
107
+ IN PNOTIFY_CHANGE NotifyChange ,
108
+ IN ULONG DataLength ,
109
+ IN NTSTATUS Status ,
110
+ IN BOOLEAN SkipCompletion )
111
+ {
112
+ PVOID Buffer ;
113
+ PIO_STACK_LOCATION Stack ;
114
+
115
+ PAGED_CODE ();
116
+
117
+ /* Check if we need to complete */
118
+ if (!FsRtlNotifySetCancelRoutine (Irp , NotifyChange ) && SkipCompletion )
119
+ {
120
+ return ;
121
+ }
122
+
123
+ /* No succes => no data to return just complete */
124
+ if (Status != STATUS_SUCCESS )
125
+ {
126
+ goto Completion ;
127
+ }
128
+
129
+ /* Ensure there's something to return */
130
+ Stack = IoGetCurrentIrpStackLocation (Irp );
131
+ if (!DataLength || Stack -> Parameters .NotifyDirectory .Length < DataLength )
132
+ {
133
+ Status = STATUS_NOTIFY_ENUM_DIR ;
134
+ goto Completion ;
135
+ }
136
+
137
+ /* Ensture there's a buffer where to find data */
138
+ if (!NotifyChange -> AllocatedBuffer )
139
+ {
140
+ Irp -> IoStatus .Information = DataLength ;
141
+ NotifyChange -> Buffer = NULL ;
142
+ goto Completion ;
143
+ }
144
+
145
+ /* Now, browse all the way to return data
146
+ * and find the one that will work. We will
147
+ * return data whatever happens
148
+ */
149
+ if (Irp -> AssociatedIrp .SystemBuffer )
150
+ {
151
+ Buffer = Irp -> AssociatedIrp .SystemBuffer ;
152
+ goto CopyAndComplete ;
153
+ }
154
+
155
+ if (Irp -> MdlAddress )
156
+ {
157
+ Buffer = MmGetSystemAddressForMdl (Irp -> MdlAddress );
158
+ goto CopyAndComplete ;
159
+ }
160
+
161
+ if (!(Stack -> Control & SL_PENDING_RETURNED ))
162
+ {
163
+ Buffer = Irp -> UserBuffer ;
164
+ goto CopyAndComplete ;
165
+ }
166
+
167
+ Irp -> Flags |= (IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER | IRP_SYNCHRONOUS_PAGING_IO );
168
+ Irp -> AssociatedIrp .SystemBuffer = NotifyChange -> AllocatedBuffer ;
169
+ /* Nothing to copy */
170
+ goto ReleaseAndComplete ;
171
+
172
+ CopyAndComplete :
173
+ _SEH2_TRY
174
+ {
175
+ RtlCopyMemory (Buffer , NotifyChange -> AllocatedBuffer , DataLength );
176
+ }
177
+ _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER )
178
+ {
179
+ /* Do nothing */
180
+ }
181
+ _SEH2_END ;
182
+
183
+ ReleaseAndComplete :
184
+ PsReturnProcessPagedPoolQuota (NotifyChange -> OwningProcess , NotifyChange -> ThisBufferLength );
185
+
186
+ /* Release buffer UNLESS it's used */
187
+ if (NotifyChange -> AllocatedBuffer != Irp -> AssociatedIrp .SystemBuffer &&
188
+ NotifyChange -> AllocatedBuffer )
189
+ {
190
+ ExFreePoolWithTag (NotifyChange -> AllocatedBuffer , 0 );
191
+ }
192
+
193
+ /* Prepare for return */
194
+ NotifyChange -> AllocatedBuffer = 0 ;
195
+ NotifyChange -> ThisBufferLength = 0 ;
196
+ Irp -> IoStatus .Information = DataLength ;
197
+ NotifyChange -> Buffer = NULL ;
198
+
199
+ /* Finally complete */
200
+ Completion :
201
+ IoMarkIrpPending (Irp );
202
+ Irp -> IoStatus .Status = Status ;
203
+ IofCompleteRequest (Irp , EVENT_INCREMENT );
204
+ }
205
+
206
+ /*
207
+ * @implemented
208
+ */
58
209
VOID
59
210
FsRtlNotifyCompleteIrpList (IN PNOTIFY_CHANGE NotifyChange ,
60
211
IN NTSTATUS Status )
61
212
{
213
+ PIRP Irp ;
214
+ ULONG DataLength ;
215
+ PLIST_ENTRY NextEntry ;
216
+
217
+ DataLength = NotifyChange -> DataLength ;
218
+
219
+ NotifyChange -> Flags &= (INVALIDATE_BUFFERS | WATCH_TREE );
220
+ NotifyChange -> DataLength = 0 ;
221
+ NotifyChange -> LastEntry = 0 ;
222
+
223
+ while (!IsListEmpty (& (NotifyChange -> NotifyIrps )))
224
+ {
225
+ /* We take the first entry */
226
+ NextEntry = RemoveHeadList (& (NotifyChange -> NotifyIrps ));
227
+ Irp = CONTAINING_RECORD (NextEntry , IRP , Tail .Overlay .ListEntry );
228
+ /* We complete it */
229
+ FsRtlNotifyCompleteIrp (Irp , NotifyChange , DataLength , Status , TRUE);
230
+ /* If we're notifying success, just notify first one */
231
+ if (Status == STATUS_SUCCESS )
232
+ break ;
233
+ }
62
234
}
63
235
64
236
VOID
@@ -74,6 +246,55 @@ FsRtlNotifyReleaseFastMutex(IN PREAL_NOTIFY_SYNC RealNotifySync)
74
246
}
75
247
}
76
248
249
+ /*
250
+ * @implemented
251
+ */
252
+ BOOLEAN
253
+ FsRtlNotifySetCancelRoutine (IN PIRP Irp ,
254
+ IN PNOTIFY_CHANGE NotifyChange OPTIONAL )
255
+ {
256
+ PDRIVER_CANCEL CancelRoutine ;
257
+
258
+ /* Acquire cancel lock */
259
+ IoAcquireCancelSpinLock (& Irp -> CancelIrql );
260
+
261
+ /* If NotifyChange was given */
262
+ if (NotifyChange )
263
+ {
264
+ /* First get cancel routine */
265
+ CancelRoutine = IoSetCancelRoutine (Irp , NULL );
266
+ Irp -> IoStatus .Information = 0 ;
267
+ /* Release cancel lock */
268
+ IoReleaseCancelSpinLock (Irp -> CancelIrql );
269
+ /* If there was a cancel routine */
270
+ if (CancelRoutine )
271
+ {
272
+ /* Decrease reference count */
273
+ InterlockedDecrement ((PLONG )& NotifyChange -> ReferenceCount );
274
+ /* Notify that we removed cancel routine */
275
+ return TRUE;
276
+ }
277
+ }
278
+ else
279
+ {
280
+ /* If IRP is cancel, call FsRtl cancel routine */
281
+ if (Irp -> Cancel )
282
+ {
283
+ FsRtlCancelNotify (NULL , Irp );
284
+ }
285
+ else
286
+ {
287
+ /* Otherwise, define FsRtl cancel routine as IRP cancel routine */
288
+ IoSetCancelRoutine (Irp , FsRtlCancelNotify );
289
+ /* Release lock */
290
+ IoReleaseCancelSpinLock (Irp -> CancelIrql );
291
+ }
292
+ }
293
+
294
+ /* Return that we didn't removed cancel routine */
295
+ return FALSE;
296
+ }
297
+
77
298
/* PUBLIC FUNCTIONS **********************************************************/
78
299
79
300
/*++
0 commit comments