xref: /reactos/ntoskrnl/fsrtl/filtrctx.c (revision 50cf16b3)
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
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
64 FsRtlIsPagingFile(IN PFILE_OBJECT FileObject)
65 {
66     return MmIsFileObjectAPagingFile(FileObject);
67 }
68 
69 /*
70  * @implemented
71  */
72 PFSRTL_PER_FILEOBJECT_CONTEXT
73 NTAPI
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
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
175 FsRtlInsertPerFileObjectContext(IN PFILE_OBJECT FileObject,
176                                 IN PFSRTL_PER_FILEOBJECT_CONTEXT Ptr)
177 {
178     PFILE_OBJECT_FILTER_CONTEXTS FOContext = NULL;
179 
180     if (!FileObject)
181     {
182         return STATUS_INVALID_PARAMETER;
183     }
184 
185     if (!(FileObject->Flags & FO_FILE_OBJECT_HAS_EXTENSION))
186     {
187         return STATUS_INVALID_DEVICE_REQUEST;
188     }
189 
190     /* Get filter contexts */
191     FOContext = IoGetFileObjectFilterContext(FileObject);
192     if (!FOContext)
193     {
194         /* If there's none, allocate new structure */
195         FOContext = ExAllocatePoolWithTag(NonPagedPool, sizeof(FILE_OBJECT_FILTER_CONTEXTS), 'FOCX');
196         if (!FOContext)
197         {
198             return STATUS_INSUFFICIENT_RESOURCES;
199         }
200 
201         /* Initialize it */
202         ExInitializeFastMutex(&(FOContext->FilterContextsMutex));
203         InitializeListHead(&(FOContext->FilterContexts));
204 
205         /* Set it */
206         if (!IoChangeFileObjectFilterContext(FileObject, FOContext, TRUE))
207         {
208             /* If it fails, it means that someone else has set it in the meanwhile */
209             ExFreePoolWithTag(FOContext, 'FOCX');
210 
211             /* So, we can get it */
212             FOContext = IoGetFileObjectFilterContext(FileObject);
213             if (!FOContext)
214             {
215                 /* If we fall down here, something went very bad. This shouldn't happen */
216                 ASSERT(FALSE);
217                 return STATUS_UNSUCCESSFUL;
218             }
219         }
220     }
221 
222     /* Finally, insert */
223     ExAcquireFastMutex(&(FOContext->FilterContextsMutex));
224     InsertHeadList(&(FOContext->FilterContexts), &(Ptr->Links));
225     ExReleaseFastMutex(&(FOContext->FilterContextsMutex));
226 
227     return STATUS_SUCCESS;
228 }
229 
230 /*
231  * @implemented
232  */
233 NTSTATUS
234 NTAPI
235 FsRtlInsertPerStreamContext(IN PFSRTL_ADVANCED_FCB_HEADER AdvFcbHeader,
236                             IN PFSRTL_PER_STREAM_CONTEXT PerStreamContext)
237 {
238     if (!(AdvFcbHeader) || !(AdvFcbHeader->Flags2 & FSRTL_FLAG2_SUPPORTS_FILTER_CONTEXTS))
239     {
240         return STATUS_INVALID_DEVICE_REQUEST;
241     }
242 
243     ExAcquireFastMutex(AdvFcbHeader->FastMutex);
244     InsertHeadList(&(AdvFcbHeader->FilterContexts), &(PerStreamContext->Links));
245     ExReleaseFastMutex(AdvFcbHeader->FastMutex);
246     return STATUS_SUCCESS;
247 }
248 
249 /*
250  * @implemented
251  */
252 PFSRTL_PER_FILEOBJECT_CONTEXT
253 NTAPI
254 FsRtlRemovePerFileObjectContext(IN PFILE_OBJECT FileObject,
255                                 IN PVOID OwnerId OPTIONAL,
256                                 IN PVOID InstanceId OPTIONAL)
257 {
258     PLIST_ENTRY NextEntry;
259     PFILE_OBJECT_FILTER_CONTEXTS FOContext = NULL;
260     PFSRTL_PER_FILEOBJECT_CONTEXT TmpPerFOContext, PerFOContext = NULL;
261 
262     if (!FileObject || !(FOContext = IoGetFileObjectFilterContext(FileObject)))
263     {
264         return NULL;
265     }
266 
267     ExAcquireFastMutex(&(FOContext->FilterContextsMutex));
268 
269     /* If list is empty, no need to browse it */
270     if (!IsListEmpty(&(FOContext->FilterContexts)))
271     {
272         for (NextEntry = FOContext->FilterContexts.Flink;
273              NextEntry != &(FOContext->FilterContexts);
274              NextEntry = NextEntry->Flink)
275         {
276             /* If we don't have any criteria for search, first entry will be enough */
277             if (!OwnerId && !InstanceId)
278             {
279                 PerFOContext = (PFSRTL_PER_FILEOBJECT_CONTEXT)NextEntry;
280                 break;
281             }
282             /* Else, we've to find something that matches with the parameters. */
283             else
284             {
285                 TmpPerFOContext = CONTAINING_RECORD(NextEntry, FSRTL_PER_FILEOBJECT_CONTEXT, Links);
286                 if ((InstanceId && TmpPerFOContext->InstanceId == InstanceId && TmpPerFOContext->OwnerId == OwnerId) ||
287                     (OwnerId && TmpPerFOContext->OwnerId == OwnerId))
288                 {
289                     PerFOContext = TmpPerFOContext;
290                     break;
291                 }
292             }
293         }
294 
295         /* Finally remove entry from list */
296         if (PerFOContext)
297         {
298             RemoveEntryList(&(PerFOContext->Links));
299         }
300     }
301 
302     ExReleaseFastMutex(&(FOContext->FilterContextsMutex));
303 
304     return PerFOContext;
305 }
306 
307 /*
308  * @implemented
309  */
310 PFSRTL_PER_STREAM_CONTEXT
311 NTAPI
312 FsRtlRemovePerStreamContext(IN PFSRTL_ADVANCED_FCB_HEADER AdvFcbHeader,
313                             IN PVOID OwnerId OPTIONAL,
314                             IN PVOID InstanceId OPTIONAL)
315 {
316     PLIST_ENTRY NextEntry;
317     PFSRTL_PER_STREAM_CONTEXT TmpPerStreamContext, PerStreamContext = NULL;
318 
319     if (!(AdvFcbHeader) || !(AdvFcbHeader->Flags2 & FSRTL_FLAG2_SUPPORTS_FILTER_CONTEXTS))
320     {
321         return NULL;
322     }
323 
324     ExAcquireFastMutex(AdvFcbHeader->FastMutex);
325     /* If list is empty, no need to browse it */
326     if (!IsListEmpty(&(AdvFcbHeader->FilterContexts)))
327     {
328         for (NextEntry = AdvFcbHeader->FilterContexts.Flink;
329              NextEntry != &(AdvFcbHeader->FilterContexts);
330              NextEntry = NextEntry->Flink)
331         {
332             /* If we don't have any criteria for search, first entry will be enough */
333             if (!OwnerId && !InstanceId)
334             {
335                 PerStreamContext = (PFSRTL_PER_STREAM_CONTEXT)NextEntry;
336                 break;
337             }
338             /* Else, we've to find something that matches with the parameters. */
339             else
340             {
341                 TmpPerStreamContext = CONTAINING_RECORD(NextEntry, FSRTL_PER_STREAM_CONTEXT, Links);
342                 if ((InstanceId && TmpPerStreamContext->InstanceId == InstanceId && TmpPerStreamContext->OwnerId == OwnerId) ||
343                     (OwnerId && TmpPerStreamContext->OwnerId == OwnerId))
344                 {
345                     PerStreamContext = TmpPerStreamContext;
346                     break;
347                 }
348             }
349         }
350 
351         /* Finally remove entry from list */
352         if (PerStreamContext)
353         {
354             RemoveEntryList(&(PerStreamContext->Links));
355         }
356     }
357     ExReleaseFastMutex(AdvFcbHeader->FastMutex);
358 
359     return PerStreamContext;
360 
361 }
362 
363 /*
364  * @implemented
365  */
366 VOID
367 NTAPI
368 FsRtlTeardownPerStreamContexts(IN PFSRTL_ADVANCED_FCB_HEADER AdvFcbHeader)
369 {
370     PLIST_ENTRY NextEntry;
371     volatile BOOLEAN IsMutexLocked = FALSE;
372     PFSRTL_PER_STREAM_CONTEXT PerStreamContext;
373 
374     _SEH2_TRY
375     {
376         /* Acquire mutex to deal with the list */
377         ExAcquireFastMutex(AdvFcbHeader->FastMutex);
378         IsMutexLocked = TRUE;
379 
380         /* While there are items... */
381         while (!IsListEmpty(&(AdvFcbHeader->FilterContexts)))
382         {
383             /* ...remove one */
384             NextEntry = RemoveHeadList(&(AdvFcbHeader->FilterContexts));
385             PerStreamContext = CONTAINING_RECORD(NextEntry, FSRTL_PER_STREAM_CONTEXT, Links);
386 
387             /* Release mutex before calling callback */
388             ExReleaseFastMutex(AdvFcbHeader->FastMutex);
389             IsMutexLocked = FALSE;
390 
391             /* Call the callback */
392             ASSERT(PerStreamContext->FreeCallback);
393             (*PerStreamContext->FreeCallback)(PerStreamContext);
394 
395             /* Relock the list to continue */
396             ExAcquireFastMutex(AdvFcbHeader->FastMutex);
397             IsMutexLocked = TRUE;
398         }
399     }
400     _SEH2_FINALLY
401     {
402         /* If mutex was locked, release */
403         if (IsMutexLocked)
404         {
405             ExReleaseFastMutex(AdvFcbHeader->FastMutex);
406         }
407     }
408     _SEH2_END;
409 }
410