[EXT2]
[reactos.git] / reactos / drivers / filesystems / ext2 / src / memory.c
index da91dc1..60055a6 100644 (file)
@@ -31,7 +31,8 @@ extern PEXT2_GLOBAL Ext2Global;
 #pragma alloc_text(PAGE, Ext2DestroyVcb)
 #pragma alloc_text(PAGE, Ext2SyncUninitializeCacheMap)
 #pragma alloc_text(PAGE, Ext2ReaperThread)
-#pragma alloc_text(PAGE, Ext2StartReaperThread)
+#pragma alloc_text(PAGE, Ext2StartReaper)
+#pragma alloc_text(PAGE, Ext2StopReaper)
 #endif
 
 PEXT2_IRP_CONTEXT
@@ -1404,7 +1405,7 @@ Ext2AllocateMcb (
 
     /* need wake the reaper thread if there are many Mcb allocated */
     if (Ext2Global->PerfStat.Current.Mcb > (((ULONG)Ext2Global->MaxDepth) * 4)) {
-        KeSetEvent(&Ext2Global->Reaper.Wait, 0, FALSE);
+        KeSetEvent(&Ext2Global->McbReaper.Wait, 0, FALSE);
     }
 
     /* allocate Mcb from LookasideList */
@@ -1505,6 +1506,7 @@ Ext2FreeMcb (IN PEXT2_VCB Vcb, IN PEXT2_MCB Mcb)
 #ifndef __REACTOS__
     PEXT2_MCB   Parent = Mcb->Parent;
 #endif
+
     ASSERT(Mcb != NULL);
 
     ASSERT((Mcb->Identifier.Type == EXT2MCB) &&
@@ -1839,6 +1841,7 @@ Ext2CheckSetBlock(PEXT2_IRP_CONTEXT IrpContext, PEXT2_VCB Vcb, LONGLONG Block)
         return FALSE;
     }
 
+
     RtlInitializeBitMap(&bitmap, (PULONG)bh->b_data, Length);
 
     if (RtlCheckBit(&bitmap, dwBlk) == 0) {
@@ -1982,7 +1985,7 @@ errorout:
 VOID
 Ext2ParseRegistryVolumeParams(
     IN  PUNICODE_STRING         Params,
-    OUT PEXT2_VOLUME_PROPERTY2  Property
+    OUT PEXT2_VOLUME_PROPERTY3  Property
 )
 {
     WCHAR       Codepage[CODEPAGE_MAXLEN];
@@ -1990,11 +1993,15 @@ Ext2ParseRegistryVolumeParams(
     WCHAR       Suffix[HIDINGPAT_LEN];
     USHORT      MountPoint[4];
     UCHAR       DrvLetter[4];
+    WCHAR       wUID[8], wGID[8], wEUID[8], wEGID[8];
+    CHAR        sUID[8], sGID[8], sEUID[8], sEGID[8];
 
     BOOLEAN     bWriteSupport = FALSE,
                 bCheckBitmap = FALSE,
                 bCodeName = FALSE,
                 bMountPoint = FALSE;
+    BOOLEAN     bUID = 0, bGID = 0, bEUID = 0, bEGID = 0;
+
     struct {
         PWCHAR   Name;      /* parameters name */
         PBOOLEAN bExist;    /* is it contained in params */
@@ -2020,6 +2027,11 @@ Ext2ParseRegistryVolumeParams(
         {MOUNT_POINT, &bMountPoint, 4,
          &MountPoint[0], &DrvLetter[0]},
 
+        {UID,  &bUID,  8, &wUID[0],  &sUID[0],},
+        {GID,  &bGID,  8, &wGID[0],  &sGID[0]},
+        {EUID, &bEUID, 8, &wEUID[0], &sEUID[0]},
+        {EGID, &bEGID, 8, &wEGID[0], &sEGID[0]},
+
         /* end */
         {NULL, NULL, 0, NULL}
     };
@@ -2032,9 +2044,9 @@ Ext2ParseRegistryVolumeParams(
     RtlZeroMemory(MountPoint, sizeof(USHORT) * 4);
     RtlZeroMemory(DrvLetter, sizeof(CHAR) * 4);
 
-    RtlZeroMemory(Property, sizeof(EXT2_VOLUME_PROPERTY2));
+    RtlZeroMemory(Property, sizeof(EXT2_VOLUME_PROPERTY3));
     Property->Magic = EXT2_VOLUME_PROPERTY_MAGIC;
-    Property->Command = APP_CMD_SET_PROPERTY2;
+    Property->Command = APP_CMD_SET_PROPERTY3;
 
     for (i=0; ParamPattern[i].Name != NULL; i++) {
 
@@ -2095,6 +2107,22 @@ Ext2ParseRegistryVolumeParams(
         Property->DrvLetter = DrvLetter[0];
         Property->DrvLetter |= 0x80;
     }
+
+    if (bUID && bGID) {
+        SetFlag(Property->Flags2, EXT2_VPROP3_USERIDS);
+        sUID[7] = sGID[7] = sEUID[7] = sEGID[7] = 0;
+        Property->uid = (USHORT)atoi(sUID);
+        Property->gid = (USHORT)atoi(sGID);
+        if (bEUID) {
+            Property->euid = (USHORT)atoi(sEUID);
+            Property->egid = (USHORT)atoi(sEGID);
+            Property->EIDS = TRUE;
+        } else {
+            Property->EIDS = FALSE;
+        }
+    } else {
+        ClearFlag(Property->Flags2, EXT2_VPROP3_USERIDS);
+    }
 }
 
 NTSTATUS
@@ -2107,7 +2135,7 @@ Ext2PerformRegistryVolumeParams(IN PEXT2_VCB Vcb)
     if (NT_SUCCESS(Status)) {
 
         /* set Vcb settings from registery */
-        EXT2_VOLUME_PROPERTY2  Property;
+        EXT2_VOLUME_PROPERTY3  Property;
         Ext2ParseRegistryVolumeParams(&VolumeParams, &Property);
         Ext2ProcessVolumeProperty(Vcb, &Property, sizeof(Property));
 
@@ -2141,7 +2169,7 @@ Ext2PerformRegistryVolumeParams(IN PEXT2_VCB Vcb)
         memcpy(Vcb->Codepage.AnsiName, Ext2Global->Codepage.AnsiName, CODEPAGE_MAXLEN);
         Vcb->Codepage.PageTable = Ext2Global->Codepage.PageTable;
 
-        if ((Vcb->bHidingPrefix = Ext2Global->bHidingPrefix) != 0) {
+        if (Vcb->bHidingPrefix == Ext2Global->bHidingPrefix) {
             RtlCopyMemory( Vcb->sHidingPrefix,
                            Ext2Global->sHidingPrefix,
                            HIDINGPAT_LEN);
@@ -2150,7 +2178,7 @@ Ext2PerformRegistryVolumeParams(IN PEXT2_VCB Vcb)
                            HIDINGPAT_LEN);
         }
 
-        if ((Vcb->bHidingSuffix = Ext2Global->bHidingSuffix) != 0) {
+        if (Vcb->bHidingSuffix == Ext2Global->bHidingSuffix) {
             RtlCopyMemory( Vcb->sHidingSuffix,
                            Ext2Global->sHidingSuffix,
                            HIDINGPAT_LEN);
@@ -2247,6 +2275,7 @@ Ext2InitializeVcb( IN PEXT2_IRP_CONTEXT IrpContext,
     BOOLEAN                     NotifySyncInitialized = FALSE;
     BOOLEAN                     ExtentsInitialized = FALSE;
     BOOLEAN                     InodeLookasideInitialized = FALSE;
+    BOOLEAN                     GroupLoaded = FALSE;
 
     _SEH2_TRY {
 
@@ -2293,8 +2322,10 @@ Ext2InitializeVcb( IN PEXT2_IRP_CONTEXT IrpContext,
         /* initialize eresources */
         ExInitializeResourceLite(&Vcb->MainResource);
         ExInitializeResourceLite(&Vcb->PagingIoResource);
-        ExInitializeResourceLite(&Vcb->MetaLock);
+        ExInitializeResourceLite(&Vcb->MetaInode);
+        ExInitializeResourceLite(&Vcb->MetaBlock);
         ExInitializeResourceLite(&Vcb->McbLock);
+        ExInitializeResourceLite(&Vcb->sbi.s_gd_lock);
 #ifndef _WIN2K_TARGET_
         ExInitializeFastMutex(&Vcb->Mutex);
         FsRtlSetupAdvancedHeader(&Vcb->Header,  &Vcb->Mutex);
@@ -2367,15 +2398,7 @@ Ext2InitializeVcb( IN PEXT2_IRP_CONTEXT IrpContext,
         /* initialize UUID and serial number */
         if (Ext2IsNullUuid(sb->s_uuid)) {
             ExUuidCreate((UUID *)sb->s_uuid);
-        } else {
-            /* query parameters from registry */
-            if (!NT_SUCCESS(Ext2PerformRegistryVolumeParams(Vcb))) {
-                /* don't mount this volume */
-                Status = STATUS_UNRECOGNIZED_VOLUME;
-                _SEH2_LEAVE;
-            }
         }
-
         Vpb->SerialNumber = ((ULONG*)sb->s_uuid)[0] +
                             ((ULONG*)sb->s_uuid)[1] +
                             ((ULONG*)sb->s_uuid)[2] +
@@ -2471,7 +2494,10 @@ Ext2InitializeVcb( IN PEXT2_IRP_CONTEXT IrpContext,
         Vcb->bd.bd_volume = Vcb->Volume;
         Vcb->bd.bd_priv = (void *) Vcb;
         memset(&Vcb->bd.bd_bh_root, 0, sizeof(struct rb_root));
-        spin_lock_init(&Vcb->bd.bd_bh_lock);
+        InitializeListHead(&Vcb->bd.bd_bh_free);
+        ExInitializeResourceLite(&Vcb->bd.bd_bh_lock);
+        KeInitializeEvent(&Vcb->bd.bd_bh_notify,
+                           NotificationEvent, TRUE);
         Vcb->bd.bd_bh_cache = kmem_cache_create("bd_bh_buffer",
                                                 Vcb->BlockSize, 0, 0, NULL);
         if (!Vcb->bd.bd_bh_cache) {
@@ -2600,6 +2626,7 @@ Ext2InitializeVcb( IN PEXT2_IRP_CONTEXT IrpContext,
             Status = STATUS_UNSUCCESSFUL;
             _SEH2_LEAVE;
         }
+        GroupLoaded = TRUE;
 
         /* recovery journal since it's ext3 */
         if (Vcb->IsExt3fs) {
@@ -2656,6 +2683,10 @@ Ext2InitializeVcb( IN PEXT2_IRP_CONTEXT IrpContext,
 
         /* get anything doen, then refer target device */
         ObReferenceObject(Vcb->TargetDeviceObject);
+
+        /* query parameters from registry */
+        Ext2PerformRegistryVolumeParams(Vcb);
+
         SetLongFlag(Vcb->Flags, VCB_INITIALIZED);
 
     } _SEH2_FINALLY {
@@ -2671,9 +2702,11 @@ Ext2InitializeVcb( IN PEXT2_IRP_CONTEXT IrpContext,
             }
 
             if (ExtentsInitialized) {
-                Ext2DropGroup(Vcb);
-                if (Vcb->bd.bd_bh_cache)
+                if (Vcb->bd.bd_bh_cache) {
+                    if (GroupLoaded)
+                        Ext2PutGroup(Vcb);
                     kmem_cache_destroy(Vcb->bd.bd_bh_cache);
+                }
                 FsRtlUninitializeLargeMcb(&(Vcb->Extents));
             }
 
@@ -2690,7 +2723,9 @@ Ext2InitializeVcb( IN PEXT2_IRP_CONTEXT IrpContext,
 
             if (VcbResourceInitialized) {
                 ExDeleteResourceLite(&Vcb->McbLock);
-                ExDeleteResourceLite(&Vcb->MetaLock);
+                ExDeleteResourceLite(&Vcb->MetaInode);
+                ExDeleteResourceLite(&Vcb->MetaBlock);
+                ExDeleteResourceLite(&Vcb->sbi.s_gd_lock);
                 ExDeleteResourceLite(&Vcb->MainResource);
                 ExDeleteResourceLite(&Vcb->PagingIoResource);
             }
@@ -2752,6 +2787,7 @@ Ext2DestroyVcb (IN PEXT2_VCB Vcb)
 
     if (Vcb->bd.bd_bh_cache)
         kmem_cache_destroy(Vcb->bd.bd_bh_cache);
+    ExDeleteResourceLite(&Vcb->bd.bd_bh_lock);
 
     if (Vcb->SuperBlock) {
         Ext2FreePool(Vcb->SuperBlock, EXT2_SB_MAGIC);
@@ -2770,7 +2806,9 @@ Ext2DestroyVcb (IN PEXT2_VCB Vcb)
 
     ExDeleteNPagedLookasideList(&(Vcb->InodeLookasideList));
     ExDeleteResourceLite(&Vcb->McbLock);
-    ExDeleteResourceLite(&Vcb->MetaLock);
+    ExDeleteResourceLite(&Vcb->MetaInode);
+    ExDeleteResourceLite(&Vcb->MetaBlock);
+    ExDeleteResourceLite(&Vcb->sbi.s_gd_lock);
     ExDeleteResourceLite(&Vcb->PagingIoResource);
     ExDeleteResourceLite(&Vcb->MainResource);
 
@@ -2935,16 +2973,11 @@ Ext2FirstUnusedMcb(PEXT2_VCB Vcb, BOOLEAN Wait, ULONG Number)
 
 /* Reaper thread to release unused Mcb blocks */
 VOID NTAPI
-Ext2ReaperThread(
+Ext2McbReaperThread(
     PVOID   Context
 )
 {
-    BOOLEAN         GlobalAcquired = FALSE;
-
-    BOOLEAN         DidNothing = TRUE;
-    BOOLEAN         LastState  = TRUE;
-    BOOLEAN         WaitLock;
-
+    PEXT2_REAPER    Reaper = Context;
     PLIST_ENTRY     List = NULL;
     LARGE_INTEGER   Timeout;
 
@@ -2953,13 +2986,19 @@ Ext2ReaperThread(
 
     ULONG           i, NumOfMcbs;
 
+    BOOLEAN         GlobalAcquired = FALSE;
+
+    BOOLEAN         DidNothing = TRUE;
+    BOOLEAN         LastState  = TRUE;
+    BOOLEAN         WaitLock;
+
     _SEH2_TRY {
 
         /* wake up DirverEntry */
-        KeSetEvent(&Ext2Global->Reaper.Engine, 0, FALSE);
+        KeSetEvent(&Reaper->Engine, 0, FALSE);
 
         /* now process looping */
-        while (TRUE) {
+        while (!IsFlagOn(Reaper->Flags, EXT2_REAPER_FLAG_STOP)) {
 
             WaitLock = FALSE;
 
@@ -3002,13 +3041,16 @@ Ext2ReaperThread(
 
             /* wait until it is waken or it times out */
             KeWaitForSingleObject(
-                &(Ext2Global->Reaper.Wait),
+                &Reaper->Wait,
                 Executive,
                 KernelMode,
                 FALSE,
                 &Timeout
             );
 
+            if (IsFlagOn(Reaper->Flags, EXT2_REAPER_FLAG_STOP))
+                break;
+
             DidNothing = TRUE;
 
             /* acquire global exclusive lock */
@@ -3047,22 +3089,157 @@ Ext2ReaperThread(
         if (GlobalAcquired) {
             ExReleaseResourceLite(&Ext2Global->Resource);
         }
+
+        KeSetEvent(&Reaper->Engine, 0, FALSE);
     } _SEH2_END;
 
     PsTerminateSystemThread(STATUS_SUCCESS);
 }
 
 
+/* get the first Mcb record in Vcb->McbList */
+
+BOOLEAN
+Ext2QueryUnusedBH(PEXT2_VCB Vcb, PLIST_ENTRY head)
+{
+    struct buffer_head *bh = NULL;
+    PLIST_ENTRY list = NULL;
+    LARGE_INTEGER now;
+    BOOLEAN       wake = FALSE;
+
+    KeQuerySystemTime(&now);
+
+    ExAcquireResourceExclusiveLite(&Vcb->bd.bd_bh_lock, TRUE);
+    while (!IsListEmpty(&Vcb->bd.bd_bh_free)) {
+        list = RemoveHeadList(&Vcb->bd.bd_bh_free);
+        bh = CONTAINING_RECORD(list, struct buffer_head, b_link);
+        if (atomic_read(&bh->b_count)) {
+            InitializeListHead(&bh->b_link);
+            continue;
+        }
+
+        if ( IsFlagOn(Vcb->Flags, VCB_BEING_DROPPED) ||
+            (bh->b_ts_drop.QuadPart + (LONGLONG)10*1000*1000*15) > now.QuadPart ||
+            (bh->b_ts_creat.QuadPart + (LONGLONG)10*1000*1000*180) > now.QuadPart) {
+            InsertTailList(head, &bh->b_link);
+        } else {
+            InsertHeadList(&Vcb->bd.bd_bh_free, &bh->b_link);
+            break;
+        }
+    }
+    wake = IsListEmpty(&Vcb->bd.bd_bh_free);
+    ExReleaseResourceLite(&Vcb->bd.bd_bh_lock);
+
+    if (wake)
+        KeSetEvent(&Vcb->bd.bd_bh_notify, 0, FALSE);
+
+    return IsFlagOn(Vcb->Flags, VCB_BEING_DROPPED);
+}
+
+
+/* Reaper thread to release unused buffer heads */
+VOID NTAPI
+Ext2bhReaperThread(
+    PVOID   Context
+)
+{
+    PEXT2_REAPER    Reaper = Context;
+    PEXT2_VCB       Vcb = NULL;
+    LIST_ENTRY      List, *Link;
+    LARGE_INTEGER   Timeout;
+
+    BOOLEAN         GlobalAcquired = FALSE;
+    BOOLEAN         DidNothing = FALSE;
+    BOOLEAN         NonWait = FALSE;
+
+    _SEH2_TRY {
+
+        /* wake up DirverEntry */
+        KeSetEvent(&Reaper->Engine, 0, FALSE);
+
+        /* now process looping */
+        while (!IsFlagOn(Reaper->Flags, EXT2_REAPER_FLAG_STOP)) {
+
+            /* wait until it is waken or it times out */
+            if (NonWait) {
+                Timeout.QuadPart = (LONGLONG)-10*1000*10;
+                NonWait = FALSE;
+            } else if (DidNothing) {
+                Timeout.QuadPart = Timeout.QuadPart * 2;
+            } else {
+                Timeout.QuadPart = (LONGLONG)-10*1000*1000*10; /* 10 seconds */
+            }
+            KeWaitForSingleObject(
+                &Reaper->Wait,
+                Executive,
+                KernelMode,
+                FALSE,
+                &Timeout
+            );
+
+            if (IsFlagOn(Reaper->Flags, EXT2_REAPER_FLAG_STOP))
+                break;
+
+            InitializeListHead(&List);
+
+            /* acquire global exclusive lock */
+            ExAcquireResourceSharedLite(&Ext2Global->Resource, TRUE);
+            GlobalAcquired = TRUE;
+            /* search all Vcb to get unused resources freed to system */
+            for (Link = Ext2Global->VcbList.Flink;
+                 Link != &(Ext2Global->VcbList);
+                 Link = Link->Flink ) {
+
+                Vcb = CONTAINING_RECORD(Link, EXT2_VCB, Next);
+                if (Ext2QueryUnusedBH(Vcb, &List))
+                    NonWait = TRUE;
+            }
+            if (GlobalAcquired) {
+                ExReleaseResourceLite(&Ext2Global->Resource);
+                GlobalAcquired = FALSE;
+            }
+
+            DidNothing = IsListEmpty(&List);
+            while (!IsListEmpty(&List)) {
+                struct buffer_head *bh;
+                Link = RemoveHeadList(&List);
+                bh = CONTAINING_RECORD(Link, struct buffer_head, b_link);
+                ASSERT(0 == atomic_read(&bh->b_count));
+                free_buffer_head(bh);
+            }
+        }
+
+    } _SEH2_FINALLY {
+
+        if (GlobalAcquired) {
+            ExReleaseResourceLite(&Ext2Global->Resource);
+        }
+
+        KeSetEvent(&Reaper->Engine, 0, FALSE);
+    } _SEH2_END;
+
+    PsTerminateSystemThread(STATUS_SUCCESS);
+}
+
 NTSTATUS
-Ext2StartReaperThread()
+Ext2StartReaper(PEXT2_REAPER Reaper, EXT2_REAPER_RELEASE Free)
 {
     NTSTATUS status = STATUS_SUCCESS;
     OBJECT_ATTRIBUTES  oa;
     HANDLE   handle = 0;
+    LARGE_INTEGER timeout;
+
+    Reaper->Free = Free;
 
     /* initialize wait event */
     KeInitializeEvent(
-        &Ext2Global->Reaper.Wait,
+        &Reaper->Wait,
+        SynchronizationEvent, FALSE
+    );
+
+    /* Reaper thread engine event */
+    KeInitializeEvent(
+        &Reaper->Engine,
         SynchronizationEvent, FALSE
     );
 
@@ -3083,13 +3260,45 @@ Ext2StartReaperThread()
                  &oa,
                  NULL,
                  NULL,
-                 Ext2ReaperThread,
-                 NULL
+                 Free,
+                 (PVOID)Reaper
              );
 
     if (NT_SUCCESS(status)) {
         ZwClose(handle);
+
+        /* make sure Reaperthread is started */
+        timeout.QuadPart = (LONGLONG)-10*1000*1000*2; /* 2 seconds */
+        status = KeWaitForSingleObject(
+                     &Reaper->Engine,
+                     Executive,
+                     KernelMode,
+                     FALSE,
+                     &timeout
+                 );
+        if (status != STATUS_SUCCESS) {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+        }
     }
 
     return status;
 }
+
+
+VOID NTAPI
+Ext2StopReaper(PEXT2_REAPER Reaper)
+{
+    LARGE_INTEGER timeout;
+
+    Reaper->Flags |= EXT2_REAPER_FLAG_STOP;
+    KeSetEvent(&Reaper->Wait, 0, FALSE);
+
+    /* make sure Reaperthread is started */
+    timeout.QuadPart = (LONGLONG)-10*1000*1000*2; /* 2 seconds */
+    KeWaitForSingleObject(
+                     &Reaper->Engine,
+                     Executive,
+                     KernelMode,
+                     FALSE,
+                     &timeout);
+}