xref: /reactos/ntoskrnl/fsrtl/filtrctx.c (revision c59e2d20)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/fsrtl/filtrctx.c
5  * PURPOSE:         File Stream Filter Context support for File System Drivers
6  * PROGRAMMERS:     Pierre Schweitzer (pierre.schweitzer@reactos.org)
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 /* PRIVATE FUNCTIONS *********************************************************/
16 
17 typedef struct _FILE_OBJECT_FILTER_CONTEXTS
18 {
19     FAST_MUTEX FilterContextsMutex;
20     LIST_ENTRY FilterContexts;
21 } FILE_OBJECT_FILTER_CONTEXTS, *PFILE_OBJECT_FILTER_CONTEXTS;
22 
23 /*
24  * @implemented
25  */
26 VOID
27 NTAPI
FsRtlPTeardownPerFileObjectContexts(IN PFILE_OBJECT FileObject)28 FsRtlPTeardownPerFileObjectContexts(IN PFILE_OBJECT FileObject)
29 {
30     PFILE_OBJECT_FILTER_CONTEXTS FOContext = NULL;
31 
32     ASSERT(FileObject);
33 
34     if (!(FOContext = IoGetFileObjectFilterContext(FileObject)))
35     {
36         return;
37     }
38 
39     ASSERT(IoChangeFileObjectFilterContext(FileObject, FOContext, FALSE) == STATUS_SUCCESS);
40     ASSERT(IsListEmpty(&(FOContext->FilterContexts)));
41 
42     ExFreePoolWithTag(FOContext, 'FOCX');
43 }
44 
45 
46 /* PUBLIC FUNCTIONS **********************************************************/
47 
48 /*++
49  * @name FsRtlIsPagingFile
50  * @implemented NT 5.2
51  *
52  *     The FsRtlIsPagingFile routine checks if the FileObject is a Paging File.
53  *
54  * @param FileObject
55  *        A pointer to the File Object to be tested.
56  *
57  * @return TRUE if the File is a Paging File, FALSE otherwise.
58  *
59  * @remarks None.
60  *
61  *--*/
62 LOGICAL
63 NTAPI
FsRtlIsPagingFile(IN PFILE_OBJECT FileObject)64 FsRtlIsPagingFile(IN PFILE_OBJECT FileObject)
65 {
66     return MmIsFileObjectAPagingFile(FileObject);
67 }
68 
69 /*
70  * @implemented
71  */
72 PFSRTL_PER_FILEOBJECT_CONTEXT
73 NTAPI
FsRtlLookupPerFileObjectContext(IN PFILE_OBJECT FileObject,IN PVOID OwnerId OPTIONAL,IN PVOID InstanceId OPTIONAL)74 FsRtlLookupPerFileObjectContext(IN PFILE_OBJECT FileObject,
75                                 IN PVOID OwnerId OPTIONAL,
76                                 IN PVOID InstanceId OPTIONAL)
77 {
78     PLIST_ENTRY NextEntry;
79     PFILE_OBJECT_FILTER_CONTEXTS FOContext = NULL;
80     PFSRTL_PER_FILEOBJECT_CONTEXT TmpPerFOContext, PerFOContext = NULL;
81 
82     if (!FileObject || !(FOContext = IoGetFileObjectFilterContext(FileObject)))
83     {
84         return NULL;
85     }
86 
87     ExAcquireFastMutex(&(FOContext->FilterContextsMutex));
88 
89     /* If list is empty, no need to browse it */
90     if (!IsListEmpty(&(FOContext->FilterContexts)))
91     {
92         for (NextEntry = FOContext->FilterContexts.Flink;
93              NextEntry != &(FOContext->FilterContexts);
94              NextEntry = NextEntry->Flink)
95         {
96             /* If we don't have any criteria for search, first entry will be enough */
97             if (!OwnerId && !InstanceId)
98             {
99                 PerFOContext = (PFSRTL_PER_FILEOBJECT_CONTEXT)NextEntry;
100                 break;
101             }
102             /* Else, we've to find something that matches with the parameters. */
103             else
104             {
105                 TmpPerFOContext = CONTAINING_RECORD(NextEntry, FSRTL_PER_FILEOBJECT_CONTEXT, Links);
106                 if ((InstanceId && TmpPerFOContext->InstanceId == InstanceId && TmpPerFOContext->OwnerId == OwnerId) ||
107                     (OwnerId && TmpPerFOContext->OwnerId == OwnerId))
108                 {
109                     PerFOContext = TmpPerFOContext;
110                     break;
111                 }
112             }
113         }
114     }
115 
116     ExReleaseFastMutex(&(FOContext->FilterContextsMutex));
117 
118     return PerFOContext;
119 }
120 
121 /*
122  * @implemented
123  */
124 PFSRTL_PER_STREAM_CONTEXT
125 NTAPI
FsRtlLookupPerStreamContextInternal(IN PFSRTL_ADVANCED_FCB_HEADER AdvFcbHeader,IN PVOID OwnerId OPTIONAL,IN PVOID InstanceId OPTIONAL)126 FsRtlLookupPerStreamContextInternal(IN PFSRTL_ADVANCED_FCB_HEADER AdvFcbHeader,
127                                     IN PVOID OwnerId OPTIONAL,
128                                     IN PVOID InstanceId OPTIONAL)
129 {
130     PLIST_ENTRY NextEntry;
131     PFSRTL_PER_STREAM_CONTEXT TmpPerStreamContext, PerStreamContext = NULL;
132 
133     ASSERT(AdvFcbHeader);
134     ASSERT(FlagOn(AdvFcbHeader->Flags2, FSRTL_FLAG2_SUPPORTS_FILTER_CONTEXTS));
135 
136     ExAcquireFastMutex(AdvFcbHeader->FastMutex);
137 
138     /* If list is empty, no need to browse it */
139     if (!IsListEmpty(&(AdvFcbHeader->FilterContexts)))
140     {
141         for (NextEntry = AdvFcbHeader->FilterContexts.Flink;
142              NextEntry != &(AdvFcbHeader->FilterContexts);
143              NextEntry = NextEntry->Flink)
144         {
145             /* If we don't have any criteria for search, first entry will be enough */
146             if (!OwnerId && !InstanceId)
147             {
148                 PerStreamContext = (PFSRTL_PER_STREAM_CONTEXT)NextEntry;
149                 break;
150             }
151             /* Else, we've to find something that matches with the parameters. */
152             else
153             {
154                 TmpPerStreamContext = CONTAINING_RECORD(NextEntry, FSRTL_PER_STREAM_CONTEXT, Links);
155                 if ((InstanceId && TmpPerStreamContext->InstanceId == InstanceId && TmpPerStreamContext->OwnerId == OwnerId) ||
156                     (OwnerId && TmpPerStreamContext->OwnerId == OwnerId))
157                 {
158                     PerStreamContext = TmpPerStreamContext;
159                     break;
160                 }
161             }
162         }
163     }
164 
165     ExReleaseFastMutex(AdvFcbHeader->FastMutex);
166 
167     return PerStreamContext;
168 }
169 
170 /*
171  * @implemented
172  */
173 NTSTATUS
174 NTAPI
FsRtlInsertPerFileObjectContext(IN PFILE_OBJECT FileObject,IN PFSRTL_PER_FILEOBJECT_CONTEXT Ptr)175 FsRtlInsertPerFileObjectContext(IN PFILE_OBJECT FileObject,
176                                 IN PFSRTL_PER_FILEOBJECT_CONTEXT Ptr)
177 {
178     PFILE_OBJECT_FILTER_CONTEXTS FOContext = NULL;
179     NTSTATUS Status;
180 
181     if (!FileObject)
182     {
183         return STATUS_INVALID_PARAMETER;
184     }
185 
186     if (!(FileObject->Flags & FO_FILE_OBJECT_HAS_EXTENSION))
187     {
188         return STATUS_INVALID_DEVICE_REQUEST;
189     }
190 
191     /* Get filter contexts */
192     FOContext = IoGetFileObjectFilterContext(FileObject);
193     if (!FOContext)
194     {
195         /* If there's none, allocate new structure */
196         FOContext = ExAllocatePoolWithTag(NonPagedPool, sizeof(FILE_OBJECT_FILTER_CONTEXTS), 'FOCX');
197         if (!FOContext)
198         {
199             return STATUS_INSUFFICIENT_RESOURCES;
200         }
201 
202         /* Initialize it */
203         ExInitializeFastMutex(&(FOContext->FilterContextsMutex));
204         InitializeListHead(&(FOContext->FilterContexts));
205 
206         /* Set it */
207         Status = IoChangeFileObjectFilterContext(FileObject, FOContext, TRUE);
208         if (!NT_SUCCESS(Status))
209         {
210             /* If it fails, it means that someone else has set it in the meanwhile */
211             ExFreePoolWithTag(FOContext, 'FOCX');
212 
213             /* So, we can get it */
214             FOContext = IoGetFileObjectFilterContext(FileObject);
215             if (!FOContext)
216             {
217                 /* If we fall down here, something went very bad. This shouldn't happen */
218                 ASSERT(FALSE);
219                 return STATUS_UNSUCCESSFUL;
220             }
221         }
222     }
223 
224     /* Finally, insert */
225     ExAcquireFastMutex(&(FOContext->FilterContextsMutex));
226     InsertHeadList(&(FOContext->FilterContexts), &(Ptr->Links));
227     ExReleaseFastMutex(&(FOContext->FilterContextsMutex));
228 
229     return STATUS_SUCCESS;
230 }
231 
232 /*
233  * @implemented
234  */
235 NTSTATUS
236 NTAPI
FsRtlInsertPerStreamContext(IN PFSRTL_ADVANCED_FCB_HEADER AdvFcbHeader,IN PFSRTL_PER_STREAM_CONTEXT PerStreamContext)237 FsRtlInsertPerStreamContext(IN PFSRTL_ADVANCED_FCB_HEADER AdvFcbHeader,
238                             IN PFSRTL_PER_STREAM_CONTEXT PerStreamContext)
239 {
240     if (!(AdvFcbHeader) || !(AdvFcbHeader->Flags2 & FSRTL_FLAG2_SUPPORTS_FILTER_CONTEXTS))
241     {
242         return STATUS_INVALID_DEVICE_REQUEST;
243     }
244 
245     ExAcquireFastMutex(AdvFcbHeader->FastMutex);
246     InsertHeadList(&(AdvFcbHeader->FilterContexts), &(PerStreamContext->Links));
247     ExReleaseFastMutex(AdvFcbHeader->FastMutex);
248     return STATUS_SUCCESS;
249 }
250 
251 /*
252  * @implemented
253  */
254 PFSRTL_PER_FILEOBJECT_CONTEXT
255 NTAPI
FsRtlRemovePerFileObjectContext(IN PFILE_OBJECT FileObject,IN PVOID OwnerId OPTIONAL,IN PVOID InstanceId OPTIONAL)256 FsRtlRemovePerFileObjectContext(IN PFILE_OBJECT FileObject,
257                                 IN PVOID OwnerId OPTIONAL,
258                                 IN PVOID InstanceId OPTIONAL)
259 {
260     PLIST_ENTRY NextEntry;
261     PFILE_OBJECT_FILTER_CONTEXTS FOContext = NULL;
262     PFSRTL_PER_FILEOBJECT_CONTEXT TmpPerFOContext, PerFOContext = NULL;
263 
264     if (!FileObject || !(FOContext = IoGetFileObjectFilterContext(FileObject)))
265     {
266         return NULL;
267     }
268 
269     ExAcquireFastMutex(&(FOContext->FilterContextsMutex));
270 
271     /* If list is empty, no need to browse it */
272     if (!IsListEmpty(&(FOContext->FilterContexts)))
273     {
274         for (NextEntry = FOContext->FilterContexts.Flink;
275              NextEntry != &(FOContext->FilterContexts);
276              NextEntry = NextEntry->Flink)
277         {
278             /* If we don't have any criteria for search, first entry will be enough */
279             if (!OwnerId && !InstanceId)
280             {
281                 PerFOContext = (PFSRTL_PER_FILEOBJECT_CONTEXT)NextEntry;
282                 break;
283             }
284             /* Else, we've to find something that matches with the parameters. */
285             else
286             {
287                 TmpPerFOContext = CONTAINING_RECORD(NextEntry, FSRTL_PER_FILEOBJECT_CONTEXT, Links);
288                 if ((InstanceId && TmpPerFOContext->InstanceId == InstanceId && TmpPerFOContext->OwnerId == OwnerId) ||
289                     (OwnerId && TmpPerFOContext->OwnerId == OwnerId))
290                 {
291                     PerFOContext = TmpPerFOContext;
292                     break;
293                 }
294             }
295         }
296 
297         /* Finally remove entry from list */
298         if (PerFOContext)
299         {
300             RemoveEntryList(&(PerFOContext->Links));
301         }
302     }
303 
304     ExReleaseFastMutex(&(FOContext->FilterContextsMutex));
305 
306     return PerFOContext;
307 }
308 
309 /*
310  * @implemented
311  */
312 PFSRTL_PER_STREAM_CONTEXT
313 NTAPI
FsRtlRemovePerStreamContext(IN PFSRTL_ADVANCED_FCB_HEADER AdvFcbHeader,IN PVOID OwnerId OPTIONAL,IN PVOID InstanceId OPTIONAL)314 FsRtlRemovePerStreamContext(IN PFSRTL_ADVANCED_FCB_HEADER AdvFcbHeader,
315                             IN PVOID OwnerId OPTIONAL,
316                             IN PVOID InstanceId OPTIONAL)
317 {
318     PLIST_ENTRY NextEntry;
319     PFSRTL_PER_STREAM_CONTEXT TmpPerStreamContext, PerStreamContext = NULL;
320 
321     if (!(AdvFcbHeader) || !(AdvFcbHeader->Flags2 & FSRTL_FLAG2_SUPPORTS_FILTER_CONTEXTS))
322     {
323         return NULL;
324     }
325 
326     ExAcquireFastMutex(AdvFcbHeader->FastMutex);
327     /* If list is empty, no need to browse it */
328     if (!IsListEmpty(&(AdvFcbHeader->FilterContexts)))
329     {
330         for (NextEntry = AdvFcbHeader->FilterContexts.Flink;
331              NextEntry != &(AdvFcbHeader->FilterContexts);
332              NextEntry = NextEntry->Flink)
333         {
334             /* If we don't have any criteria for search, first entry will be enough */
335             if (!OwnerId && !InstanceId)
336             {
337                 PerStreamContext = (PFSRTL_PER_STREAM_CONTEXT)NextEntry;
338                 break;
339             }
340             /* Else, we've to find something that matches with the parameters. */
341             else
342             {
343                 TmpPerStreamContext = CONTAINING_RECORD(NextEntry, FSRTL_PER_STREAM_CONTEXT, Links);
344                 if ((InstanceId && TmpPerStreamContext->InstanceId == InstanceId && TmpPerStreamContext->OwnerId == OwnerId) ||
345                     (OwnerId && TmpPerStreamContext->OwnerId == OwnerId))
346                 {
347                     PerStreamContext = TmpPerStreamContext;
348                     break;
349                 }
350             }
351         }
352 
353         /* Finally remove entry from list */
354         if (PerStreamContext)
355         {
356             RemoveEntryList(&(PerStreamContext->Links));
357         }
358     }
359     ExReleaseFastMutex(AdvFcbHeader->FastMutex);
360 
361     return PerStreamContext;
362 
363 }
364 
365 /*
366  * @implemented
367  */
368 VOID
369 NTAPI
FsRtlTeardownPerStreamContexts(IN PFSRTL_ADVANCED_FCB_HEADER AdvFcbHeader)370 FsRtlTeardownPerStreamContexts(IN PFSRTL_ADVANCED_FCB_HEADER AdvFcbHeader)
371 {
372     PLIST_ENTRY NextEntry;
373     volatile BOOLEAN IsMutexLocked = FALSE;
374     PFSRTL_PER_STREAM_CONTEXT PerStreamContext;
375 
376     _SEH2_TRY
377     {
378         /* Acquire mutex to deal with the list */
379         ExAcquireFastMutex(AdvFcbHeader->FastMutex);
380         IsMutexLocked = TRUE;
381 
382         /* While there are items... */
383         while (!IsListEmpty(&(AdvFcbHeader->FilterContexts)))
384         {
385             /* ...remove one */
386             NextEntry = RemoveHeadList(&(AdvFcbHeader->FilterContexts));
387             PerStreamContext = CONTAINING_RECORD(NextEntry, FSRTL_PER_STREAM_CONTEXT, Links);
388 
389             /* Release mutex before calling callback */
390             ExReleaseFastMutex(AdvFcbHeader->FastMutex);
391             IsMutexLocked = FALSE;
392 
393             /* Call the callback */
394             ASSERT(PerStreamContext->FreeCallback);
395             (*PerStreamContext->FreeCallback)(PerStreamContext);
396 
397             /* Relock the list to continue */
398             ExAcquireFastMutex(AdvFcbHeader->FastMutex);
399             IsMutexLocked = TRUE;
400         }
401     }
402     _SEH2_FINALLY
403     {
404         /* If mutex was locked, release */
405         if (IsMutexLocked)
406         {
407             ExReleaseFastMutex(AdvFcbHeader->FastMutex);
408         }
409     }
410     _SEH2_END;
411 }
412