[NTOS] Configuration Manager fixes.
[reactos.git] / ntoskrnl / config / cmlazy.c
index 295acf1..8093c61 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * PROJECT:         ReactOS Kernel
  * LICENSE:         GPL - See COPYING in the top level directory
- * FILE:            ntoskrnl/config/cmapi.c
+ * FILE:            ntoskrnl/config/cmlazy.c
  * PURPOSE:         Configuration Manager - Internal Registry APIs
  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
  */
@@ -23,7 +23,7 @@ BOOLEAN CmpLazyFlushPending;
 BOOLEAN CmpForceForceFlush;
 BOOLEAN CmpHoldLazyFlush = TRUE;
 ULONG CmpLazyFlushIntervalInSeconds = 5;
-ULONG CmpLazyFlushHiveCount = 7;
+static ULONG CmpLazyFlushHiveCount = 7;
 ULONG CmpLazyFlushCount = 1;
 LONG CmpFlushStarveWriters;
 
@@ -31,9 +31,9 @@ LONG CmpFlushStarveWriters;
 
 BOOLEAN
 NTAPI
-CmpDoFlushNextHive(IN BOOLEAN ForceFlush,
-                   OUT PBOOLEAN Error,
-                   OUT PULONG DirtyCount)
+CmpDoFlushNextHive(_In_  BOOLEAN ForceFlush,
+                   _Out_ PBOOLEAN Error,
+                   _Out_ PULONG DirtyCount)
 {
     NTSTATUS Status;
     PLIST_ENTRY NextEntry;
@@ -51,13 +51,10 @@ CmpDoFlushNextHive(IN BOOLEAN ForceFlush,
     /* Make sure we have to flush at least one hive */
     if (!HiveCount) HiveCount = 1;
 
-    /* Don't force flush */
-    CmpForceForceFlush = FALSE;
-
     /* Acquire the list lock and loop */
     ExAcquirePushLockShared(&CmpHiveListHeadLock);
     NextEntry = CmpHiveListHead.Flink;
-    while (NextEntry != &CmpHiveListHead)
+    while ((NextEntry != &CmpHiveListHead) && HiveCount)
     {
         /* Get the hive and check if we should flush it */
         CmHive = CONTAINING_RECORD(NextEntry, CMHIVE, HiveList);
@@ -67,25 +64,33 @@ CmpDoFlushNextHive(IN BOOLEAN ForceFlush,
             /* Great sucess! */
             Result = TRUE;
 
-            /* Ignore clean or volatile hves */
-            if (!(CmHive->Hive.DirtyCount) ||
+            /* One less to flush */
+            HiveCount--;
+
+            /* Ignore clean or volatile hives */
+            if ((!CmHive->Hive.DirtyCount && !ForceFlush) ||
                 (CmHive->Hive.HiveFlags & HIVE_VOLATILE))
             {
                 /* Don't do anything but do update the count */
                 CmHive->FlushCount = CmpLazyFlushCount;
+                DPRINT("Hive %wZ is clean.\n", &CmHive->FileFullPath);
             }
             else
             {
                 /* Do the sync */
-                DPRINT1("Flushing: %wZ\n", CmHive->FileFullPath);
-                DPRINT1("Handle: %lx\n", CmHive->FileHandles[HFILE_TYPE_PRIMARY]);
+                DPRINT("Flushing: %wZ\n", &CmHive->FileFullPath);
+                DPRINT("Handle: %p\n", CmHive->FileHandles[HFILE_TYPE_PRIMARY]);
                 Status = HvSyncHive(&CmHive->Hive);
                 if(!NT_SUCCESS(Status))
                 {
                     /* Let them know we failed */
+                    DPRINT1("Failed to flush %wZ on handle %p (status 0x%08lx)\n",
+                        &CmHive->FileFullPath,  CmHive->FileHandles[HFILE_TYPE_PRIMARY], Status);
                     *Error = TRUE;
                     Result = FALSE;
+                    break;
                 }
+                CmHive->FlushCount = CmpLazyFlushCount;
             }
         }
         else if ((CmHive->Hive.DirtyCount) &&
@@ -95,6 +100,7 @@ CmpDoFlushNextHive(IN BOOLEAN ForceFlush,
             /* Use another lazy flusher for this hive */
             ASSERT(CmHive->FlushCount == CmpLazyFlushCount);
             *DirtyCount += CmHive->Hive.DirtyCount;
+            DPRINT("CmHive %wZ already uptodate.\n", &CmHive->FileFullPath);
         }
 
         /* Try the next one */
@@ -139,6 +145,7 @@ CmpLazyFlushDpcRoutine(IN PKDPC Dpc,
                        IN PVOID SystemArgument2)
 {
     /* Check if we should queue the lazy flush worker */
+    DPRINT("Flush pending: %s, Holding lazy flush: %s.\n", CmpLazyFlushPending ? "yes" : "no", CmpHoldLazyFlush ? "yes" : "no");
     if ((!CmpLazyFlushPending) && (!CmpHoldLazyFlush))
     {
         CmpLazyFlushPending = TRUE;
@@ -173,20 +180,27 @@ CmpLazyFlushWorker(IN PVOID Parameter)
     PAGED_CODE();
 
     /* Don't do anything if lazy flushing isn't enabled yet */
-    if (CmpHoldLazyFlush) return;
+    if (CmpHoldLazyFlush)
+    {
+        DPRINT1("Lazy flush held. Bye bye.\n");
+        CmpLazyFlushPending = FALSE;
+        return;
+    }
 
     /* Check if we are forcing a flush */
     ForceFlush = CmpForceForceFlush;
     if (ForceFlush)
     {
+        DPRINT("Forcing flush.\n");
         /* Lock the registry exclusively */
         CmpLockRegistryExclusive();
     }
     else
     {
-        /* Do a normal lock */
-        CmpLockRegistry();
+        DPRINT("Not forcing flush.\n");
+        /* Starve writers before locking */
         InterlockedIncrement(&CmpFlushStarveWriters);
+        CmpLockRegistry();
     }
 
     /* Flush the next hive */
@@ -198,14 +212,21 @@ CmpLazyFlushWorker(IN PVOID Parameter)
     }
 
     /* Check if we have starved writers */
-    if (!ForceFlush) InterlockedDecrement(&CmpFlushStarveWriters);
+    if (!ForceFlush)
+        InterlockedDecrement(&CmpFlushStarveWriters);
 
     /* Not pending anymore, release the registry lock */
     CmpLazyFlushPending = FALSE;
     CmpUnlockRegistry();
 
-    /* Check if we need to flush another hive */
-    if ((MoreWork) || (DirtyCount)) CmpLazyFlush();
+    DPRINT("Lazy flush done. More work to be done: %s. Entries still dirty: %u.\n",
+        MoreWork ? "Yes" : "No", DirtyCount);
+
+    if (MoreWork)
+    {
+        /* Relaunch the flush timer, so the remaining hives get flushed */
+        CmpLazyFlush();
+    }
 }
 
 VOID
@@ -241,8 +262,9 @@ CmpCmdInit(IN BOOLEAN SetupBoot)
     /* Testing: Force Lazy Flushing */
     CmpHoldLazyFlush = FALSE;
 
-    /* Setup the hive list */
-    CmpInitializeHiveList(SetupBoot);
+    /* Setup the hive list if this is not a Setup boot */
+    if (!SetupBoot)
+        CmpInitializeHiveList();
 }
 
 NTSTATUS
@@ -253,13 +275,97 @@ CmpCmdHiveOpen(IN POBJECT_ATTRIBUTES FileAttributes,
                OUT PCMHIVE *NewHive,
                IN ULONG CheckFlags)
 {
-    PUNICODE_STRING FileName;
     NTSTATUS Status;
+    UNICODE_STRING FileName;
+    PWCHAR FilePath;
+    UCHAR Buffer[sizeof(OBJECT_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR)];
+    ULONG Length = sizeof(Buffer);
+    POBJECT_NAME_INFORMATION FileNameInfo = (POBJECT_NAME_INFORMATION)Buffer;
+
     PAGED_CODE();
 
+    if (FileAttributes->RootDirectory)
+    {
+        /*
+         * Validity check: The ObjectName is relative to RootDirectory,
+         * therefore it must not start with a path separator.
+         */
+        if (FileAttributes->ObjectName && FileAttributes->ObjectName->Buffer &&
+            FileAttributes->ObjectName->Length >= sizeof(WCHAR) &&
+            *FileAttributes->ObjectName->Buffer == OBJ_NAME_PATH_SEPARATOR)
+        {
+            return STATUS_OBJECT_PATH_SYNTAX_BAD;
+        }
+
+        /* Try to get the value */
+        Status = ZwQueryObject(FileAttributes->RootDirectory,
+                               ObjectNameInformation,
+                               FileNameInfo,
+                               Length,
+                               &Length);
+        if (!NT_SUCCESS(Status))
+        {
+            /* Fail */
+            DPRINT1("CmpCmdHiveOpen(): Root directory handle object name query failed, Status = 0x%08lx\n", Status);
+            return Status;
+        }
+
+        /* Null-terminate and add the length of the terminator */
+        Length -= sizeof(OBJECT_NAME_INFORMATION);
+        FilePath = FileNameInfo->Name.Buffer;
+        FilePath[Length / sizeof(WCHAR)] = UNICODE_NULL;
+        Length += sizeof(UNICODE_NULL);
+
+        /* Compute the size of the full path; Length already counts the terminating NULL */
+        Length = Length + sizeof(WCHAR) + FileAttributes->ObjectName->Length;
+        if (Length > MAXUSHORT)
+        {
+            /* Name size too long, bail out */
+            return STATUS_OBJECT_PATH_INVALID;
+        }
+
+        /* Build the full path */
+        RtlInitEmptyUnicodeString(&FileName, NULL, 0);
+        FileName.Buffer = ExAllocatePoolWithTag(PagedPool, Length, TAG_CM);
+        if (!FileName.Buffer)
+        {
+            /* Fail */
+            DPRINT1("CmpCmdHiveOpen(): Unable to allocate memory\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        FileName.MaximumLength = Length;
+        RtlCopyUnicodeString(&FileName, &FileNameInfo->Name);
+
+        /*
+         * Append a path terminator if needed (we have already accounted
+         * for a possible extra one when allocating the buffer).
+         */
+        if (/* FileAttributes->ObjectName->Buffer[0] != OBJ_NAME_PATH_SEPARATOR && */ // We excluded ObjectName starting with a path separator above.
+            FileName.Length > 0 && FileName.Buffer[FileName.Length / sizeof(WCHAR) - 1] != OBJ_NAME_PATH_SEPARATOR)
+        {
+            /* ObjectName does not start with '\' and PathBuffer does not end with '\' */
+            FileName.Buffer[FileName.Length / sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
+            FileName.Length += sizeof(WCHAR);
+            FileName.Buffer[FileName.Length / sizeof(WCHAR)] = UNICODE_NULL;
+        }
+
+        /* Append the object name */
+        Status = RtlAppendUnicodeStringToString(&FileName, FileAttributes->ObjectName);
+        if (!NT_SUCCESS(Status))
+        {
+            /* Fail */
+            DPRINT1("CmpCmdHiveOpen(): RtlAppendUnicodeStringToString() failed, Status = 0x%08lx\n", Status);
+            ExFreePoolWithTag(FileName.Buffer, TAG_CM);
+            return Status;
+        }
+    }
+    else
+    {
+        FileName = *FileAttributes->ObjectName;
+    }
+
     /* Open the file in the current security context */
-    FileName = FileAttributes->ObjectName;
-    Status = CmpInitHiveFromFile(FileName,
+    Status = CmpInitHiveFromFile(&FileName,
                                  0,
                                  NewHive,
                                  Allocate,
@@ -277,7 +383,7 @@ CmpCmdHiveOpen(IN POBJECT_ATTRIBUTES FileAttributes,
         if (NT_SUCCESS(Status))
         {
             /* Now try again */
-            Status = CmpInitHiveFromFile(FileName,
+            Status = CmpInitHiveFromFile(&FileName,
                                          0,
                                          NewHive,
                                          Allocate,
@@ -288,6 +394,11 @@ CmpCmdHiveOpen(IN POBJECT_ATTRIBUTES FileAttributes,
         }
     }
 
+    if (FileAttributes->RootDirectory)
+    {
+        ExFreePoolWithTag(FileName.Buffer, TAG_CM);
+    }
+
     /* Return status of open attempt */
     return Status;
 }