* Sync up to trunk head (r60691).
[reactos.git] / ntoskrnl / fsrtl / filtrctx.c
index 24d88c4..9bd616f 100644 (file)
@@ -3,7 +3,7 @@
  * LICENSE:         GPL - See COPYING in the top level directory
  * FILE:            ntoskrnl/fsrtl/filtrctx.c
  * PURPOSE:         File Stream Filter Context support for File System Drivers
- * PROGRAMMERS:     None.
+ * PROGRAMMERS:     Pierre Schweitzer (pierre.schweitzer@reactos.org)
  */
 
 /* INCLUDES ******************************************************************/
 #define NDEBUG
 #include <debug.h>
 
+/* PRIVATE FUNCTIONS *********************************************************/
+
+typedef struct _FILE_OBJECT_FILTER_CONTEXTS
+{
+    FAST_MUTEX FilterContextsMutex;
+    LIST_ENTRY FilterContexts;
+} FILE_OBJECT_FILTER_CONTEXTS, *PFILE_OBJECT_FILTER_CONTEXTS;
+
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+FsRtlPTeardownPerFileObjectContexts(IN PFILE_OBJECT FileObject)
+{
+    PFILE_OBJECT_FILTER_CONTEXTS FOContext = NULL;
+
+    ASSERT(FileObject);
+
+    if (!(FOContext = IoGetFileObjectFilterContext(FileObject)))
+    {
+        return;
+    }
+
+    ASSERT(IoChangeFileObjectFilterContext(FileObject, FOContext, FALSE) == STATUS_SUCCESS);
+    ASSERT(IsListEmpty(&(FOContext->FilterContexts)));
+
+    ExFreePoolWithTag(FOContext, 'FOCX');
+}
+
+
 /* PUBLIC FUNCTIONS **********************************************************/
 
 /*++
  * @name FsRtlIsPagingFile
- * @implemented NT 4.0
+ * @implemented NT 5.2
  *
  *     The FsRtlIsPagingFile routine checks if the FileObject is a Paging File.
  *
@@ -32,25 +63,11 @@ LOGICAL
 NTAPI
 FsRtlIsPagingFile(IN PFILE_OBJECT FileObject)
 {
-    KeBugCheck(FILE_SYSTEM);
-    return FALSE;
+    return MmIsFileObjectAPagingFile(FileObject);
 }
 
 /*
- * @unimplemented
- */
-PFSRTL_PER_STREAM_CONTEXT
-NTAPI
-FsRtlLookupPerStreamContextInternal(IN PFSRTL_ADVANCED_FCB_HEADER StreamContext,
-                                    IN PVOID OwnerId OPTIONAL,
-                                    IN PVOID InstanceId OPTIONAL)
-{
-    KeBugCheck(FILE_SYSTEM);
-    return FALSE;
-}
-
-/*
- * @unimplemented
+ * @implemented
  */
 PFSRTL_PER_FILEOBJECT_CONTEXT
 NTAPI
@@ -58,67 +75,335 @@ FsRtlLookupPerFileObjectContext(IN PFILE_OBJECT FileObject,
                                 IN PVOID OwnerId OPTIONAL,
                                 IN PVOID InstanceId OPTIONAL)
 {
-    KeBugCheck(FILE_SYSTEM);
-    return FALSE;
+    PLIST_ENTRY NextEntry;
+    PFILE_OBJECT_FILTER_CONTEXTS FOContext = NULL;
+    PFSRTL_PER_FILEOBJECT_CONTEXT TmpPerFOContext, PerFOContext = NULL;
+
+    if (!FileObject || !(FOContext = IoGetFileObjectFilterContext(FileObject)))
+    {
+        return NULL;
+    }
+
+    ExAcquireFastMutex(&(FOContext->FilterContextsMutex));
+
+    /* If list is empty, no need to browse it */
+    if (!IsListEmpty(&(FOContext->FilterContexts)))
+    {
+        for (NextEntry = FOContext->FilterContexts.Flink;
+             NextEntry != &(FOContext->FilterContexts);
+             NextEntry = NextEntry->Flink)
+        {
+            /* If we don't have any criteria for search, first entry will be enough */
+            if (!OwnerId && !InstanceId)
+            {
+                PerFOContext = (PFSRTL_PER_FILEOBJECT_CONTEXT)NextEntry;
+                break;
+            }
+            /* Else, we've to find something that matches with the parameters. */
+            else
+            {
+                TmpPerFOContext = CONTAINING_RECORD(NextEntry, FSRTL_PER_FILEOBJECT_CONTEXT, Links);
+                if ((InstanceId && TmpPerFOContext->InstanceId == InstanceId && TmpPerFOContext->OwnerId == OwnerId) ||
+                    (OwnerId && TmpPerFOContext->OwnerId == OwnerId))
+                {
+                    PerFOContext = TmpPerFOContext;
+                    break;
+                }
+            }
+        }
+    }
+
+    ExReleaseFastMutex(&(FOContext->FilterContextsMutex));
+
+    return PerFOContext;
 }
 
 /*
- * @unimplemented
+ * @implemented
  */
-NTSTATUS
+PFSRTL_PER_STREAM_CONTEXT
 NTAPI
-FsRtlInsertPerStreamContext(IN PFSRTL_ADVANCED_FCB_HEADER PerStreamContext,
-                            IN PFSRTL_PER_STREAM_CONTEXT Ptr)
+FsRtlLookupPerStreamContextInternal(IN PFSRTL_ADVANCED_FCB_HEADER AdvFcbHeader,
+                                    IN PVOID OwnerId OPTIONAL,
+                                    IN PVOID InstanceId OPTIONAL)
 {
-    KeBugCheck(FILE_SYSTEM);
-    return STATUS_NOT_IMPLEMENTED;
+    PLIST_ENTRY NextEntry;
+    PFSRTL_PER_STREAM_CONTEXT TmpPerStreamContext, PerStreamContext = NULL;
+
+    ASSERT(AdvFcbHeader);
+    ASSERT(FlagOn(AdvFcbHeader->Flags2, FSRTL_FLAG2_SUPPORTS_FILTER_CONTEXTS));
+
+    ExAcquireFastMutex(AdvFcbHeader->FastMutex);
+
+    /* If list is empty, no need to browse it */
+    if (!IsListEmpty(&(AdvFcbHeader->FilterContexts)))
+    {
+        for (NextEntry = AdvFcbHeader->FilterContexts.Flink;
+             NextEntry != &(AdvFcbHeader->FilterContexts);
+             NextEntry = NextEntry->Flink)
+        {
+            /* If we don't have any criteria for search, first entry will be enough */
+            if (!OwnerId && !InstanceId)
+            {
+                PerStreamContext = (PFSRTL_PER_STREAM_CONTEXT)NextEntry;
+                break;
+            }
+            /* Else, we've to find something that matches with the parameters. */
+            else
+            {
+                TmpPerStreamContext = CONTAINING_RECORD(NextEntry, FSRTL_PER_STREAM_CONTEXT, Links);
+                if ((InstanceId && TmpPerStreamContext->InstanceId == InstanceId && TmpPerStreamContext->OwnerId == OwnerId) ||
+                    (OwnerId && TmpPerStreamContext->OwnerId == OwnerId))
+                {
+                    PerStreamContext = TmpPerStreamContext;
+                    break;
+                }
+            }
+        }
+    }
+
+    ExReleaseFastMutex(AdvFcbHeader->FastMutex);
+
+    return PerStreamContext;
 }
 
 /*
- * @unimplemented
+ * @implemented
  */
-PFSRTL_PER_STREAM_CONTEXT
+NTSTATUS
 NTAPI
-FsRtlRemovePerStreamContext(IN PFSRTL_ADVANCED_FCB_HEADER StreamContext,
-                            IN PVOID OwnerId OPTIONAL,
-                            IN PVOID InstanceId OPTIONAL)
+FsRtlInsertPerFileObjectContext(IN PFILE_OBJECT FileObject,
+                                IN PFSRTL_PER_FILEOBJECT_CONTEXT Ptr)
 {
-    KeBugCheck(FILE_SYSTEM);
-    return NULL;
+    PFILE_OBJECT_FILTER_CONTEXTS FOContext = NULL;
+
+    if (!FileObject)
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    if (!(FileObject->Flags & FO_FILE_OBJECT_HAS_EXTENSION))
+    {
+        return STATUS_INVALID_DEVICE_REQUEST;
+    }
+
+    /* Get filter contexts */
+    FOContext = IoGetFileObjectFilterContext(FileObject);
+    if (!FOContext)
+    {
+        /* If there's none, allocate new structure */
+        FOContext = ExAllocatePoolWithTag(NonPagedPool, sizeof(FILE_OBJECT_FILTER_CONTEXTS), 'FOCX');
+        if (!FOContext)
+        {
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        /* Initialize it */
+        ExInitializeFastMutex(&(FOContext->FilterContextsMutex));
+        InitializeListHead(&(FOContext->FilterContexts));
+
+        /* Set it */
+        if (!IoChangeFileObjectFilterContext(FileObject, FOContext, TRUE))
+        {
+            /* If it fails, it means that someone else has set it in the meanwhile */
+            ExFreePoolWithTag(FOContext, 'FOCX');
+
+            /* So, we can get it */
+            FOContext = IoGetFileObjectFilterContext(FileObject);
+            if (!FOContext)
+            {
+                /* If we fall down here, something went very bad. This shouldn't happen */
+                ASSERT(FALSE);
+                return STATUS_UNSUCCESSFUL;
+            }
+        }
+    }
+
+    /* Finally, insert */
+    ExAcquireFastMutex(&(FOContext->FilterContextsMutex));
+    InsertHeadList(&(FOContext->FilterContexts), &(Ptr->Links));
+    ExReleaseFastMutex(&(FOContext->FilterContextsMutex));
+
+    return STATUS_SUCCESS;
 }
 
 /*
- * @unimplemented
+ * @implemented
  */
 NTSTATUS
 NTAPI
-FsRtlInsertPerFileObjectContext(IN PFILE_OBJECT FileObject,
-                                IN PFSRTL_PER_FILEOBJECT_CONTEXT Ptr)
+FsRtlInsertPerStreamContext(IN PFSRTL_ADVANCED_FCB_HEADER AdvFcbHeader,
+                            IN PFSRTL_PER_STREAM_CONTEXT PerStreamContext)
 {
-    KeBugCheck(FILE_SYSTEM);
-    return STATUS_NOT_IMPLEMENTED;
+    if (!(AdvFcbHeader) || !(AdvFcbHeader->Flags2 & FSRTL_FLAG2_SUPPORTS_FILTER_CONTEXTS))
+    {
+        return STATUS_INVALID_DEVICE_REQUEST;
+    }
+
+    ExAcquireFastMutex(AdvFcbHeader->FastMutex);
+    InsertHeadList(&(AdvFcbHeader->FilterContexts), &(PerStreamContext->Links));
+    ExReleaseFastMutex(AdvFcbHeader->FastMutex);
+    return STATUS_SUCCESS;
 }
 
 /*
- * @unimplemented
+ * @implemented
  */
 PFSRTL_PER_FILEOBJECT_CONTEXT
 NTAPI
-FsRtlRemovePerFileObjectContext(IN PFILE_OBJECT PerFileObjectContext,
+FsRtlRemovePerFileObjectContext(IN PFILE_OBJECT FileObject,
                                 IN PVOID OwnerId OPTIONAL,
                                 IN PVOID InstanceId OPTIONAL)
 {
-    KeBugCheck(FILE_SYSTEM);
-    return NULL;
+    PLIST_ENTRY NextEntry;
+    PFILE_OBJECT_FILTER_CONTEXTS FOContext = NULL;
+    PFSRTL_PER_FILEOBJECT_CONTEXT TmpPerFOContext, PerFOContext = NULL;
+
+    if (!FileObject || !(FOContext = IoGetFileObjectFilterContext(FileObject)))
+    {
+        return NULL;
+    }
+
+    ExAcquireFastMutex(&(FOContext->FilterContextsMutex));
+
+    /* If list is empty, no need to browse it */
+    if (!IsListEmpty(&(FOContext->FilterContexts)))
+    {
+        for (NextEntry = FOContext->FilterContexts.Flink;
+             NextEntry != &(FOContext->FilterContexts);
+             NextEntry = NextEntry->Flink)
+        {
+            /* If we don't have any criteria for search, first entry will be enough */
+            if (!OwnerId && !InstanceId)
+            {
+                PerFOContext = (PFSRTL_PER_FILEOBJECT_CONTEXT)NextEntry;
+                break;
+            }
+            /* Else, we've to find something that matches with the parameters. */
+            else
+            {
+                TmpPerFOContext = CONTAINING_RECORD(NextEntry, FSRTL_PER_FILEOBJECT_CONTEXT, Links);
+                if ((InstanceId && TmpPerFOContext->InstanceId == InstanceId && TmpPerFOContext->OwnerId == OwnerId) ||
+                    (OwnerId && TmpPerFOContext->OwnerId == OwnerId))
+                {
+                    PerFOContext = TmpPerFOContext;
+                    break;
+                }
+            }
+        }
+
+        /* Finally remove entry from list */
+        if (PerFOContext)
+        {
+            RemoveEntryList(&(PerFOContext->Links));
+        }
+    }
+
+    ExReleaseFastMutex(&(FOContext->FilterContextsMutex));
+
+    return PerFOContext;
+}
+
+/*
+ * @implemented
+ */
+PFSRTL_PER_STREAM_CONTEXT
+NTAPI
+FsRtlRemovePerStreamContext(IN PFSRTL_ADVANCED_FCB_HEADER AdvFcbHeader,
+                            IN PVOID OwnerId OPTIONAL,
+                            IN PVOID InstanceId OPTIONAL)
+{
+    PLIST_ENTRY NextEntry;
+    PFSRTL_PER_STREAM_CONTEXT TmpPerStreamContext, PerStreamContext = NULL;
+
+    if (!(AdvFcbHeader) || !(AdvFcbHeader->Flags2 & FSRTL_FLAG2_SUPPORTS_FILTER_CONTEXTS))
+    {
+        return NULL;
+    }
+
+    ExAcquireFastMutex(AdvFcbHeader->FastMutex);
+    /* If list is empty, no need to browse it */
+    if (!IsListEmpty(&(AdvFcbHeader->FilterContexts)))
+    {
+        for (NextEntry = AdvFcbHeader->FilterContexts.Flink;
+             NextEntry != &(AdvFcbHeader->FilterContexts);
+             NextEntry = NextEntry->Flink)
+        {
+            /* If we don't have any criteria for search, first entry will be enough */
+            if (!OwnerId && !InstanceId)
+            {
+                PerStreamContext = (PFSRTL_PER_STREAM_CONTEXT)NextEntry;
+                break;
+            }
+            /* Else, we've to find something that matches with the parameters. */
+            else
+            {
+                TmpPerStreamContext = CONTAINING_RECORD(NextEntry, FSRTL_PER_STREAM_CONTEXT, Links);
+                if ((InstanceId && TmpPerStreamContext->InstanceId == InstanceId && TmpPerStreamContext->OwnerId == OwnerId) ||
+                    (OwnerId && TmpPerStreamContext->OwnerId == OwnerId))
+                {
+                    PerStreamContext = TmpPerStreamContext;
+                    break;
+                }
+            }
+        }
+
+        /* Finally remove entry from list */
+        if (PerStreamContext)
+        {
+            RemoveEntryList(&(PerStreamContext->Links));
+        }
+    }
+    ExReleaseFastMutex(AdvFcbHeader->FastMutex);
+
+    return PerStreamContext;
+
 }
 
 /*
- * @unimplemented
+ * @implemented
  */
 VOID
 NTAPI
-FsRtlTeardownPerStreamContexts(IN PFSRTL_ADVANCED_FCB_HEADER AdvancedHeader)
+FsRtlTeardownPerStreamContexts(IN PFSRTL_ADVANCED_FCB_HEADER AdvFcbHeader)
 {
-    KeBugCheck(FILE_SYSTEM);
-}
+    PLIST_ENTRY NextEntry;
+    volatile BOOLEAN IsMutexLocked = FALSE;
+    PFSRTL_PER_STREAM_CONTEXT PerStreamContext;
 
+    _SEH2_TRY
+    {
+        /* Acquire mutex to deal with the list */
+        ExAcquireFastMutex(AdvFcbHeader->FastMutex);
+        IsMutexLocked = TRUE;
+
+        /* While there are items... */
+        while (!IsListEmpty(&(AdvFcbHeader->FilterContexts)))
+        {
+            /* ...remove one */
+            NextEntry = RemoveHeadList(&(AdvFcbHeader->FilterContexts));
+            PerStreamContext = CONTAINING_RECORD(NextEntry, FSRTL_PER_STREAM_CONTEXT, Links);
+
+            /* Release mutex before calling callback */
+            ExReleaseFastMutex(AdvFcbHeader->FastMutex);
+            IsMutexLocked = FALSE;
+
+            /* Call the callback */
+            ASSERT(PerStreamContext->FreeCallback);
+            (*PerStreamContext->FreeCallback)(PerStreamContext);
+
+            /* Relock the list to continue */
+            ExAcquireFastMutex(AdvFcbHeader->FastMutex);
+            IsMutexLocked = TRUE;
+        }
+    }
+    _SEH2_FINALLY
+    {
+        /* If mutex was locked, release */
+        if (IsMutexLocked)
+        {
+            ExReleaseFastMutex(AdvFcbHeader->FastMutex);
+        }
+    }
+    _SEH2_END;
+}