[NTOS:CM] Implement more support for force-unloading registry hives.
[reactos.git] / ntoskrnl / config / cmapi.c
index 3b476d8..84e0f11 100644 (file)
@@ -2221,7 +2221,7 @@ CmUnloadKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
     {
         if (Flags != REG_FORCE_UNLOAD)
         {
-            if (CmCountOpenSubKeys(Kcb, FALSE) != 0)
+            if (CmpEnumerateOpenSubKeys(Kcb, FALSE, FALSE) != 0)
             {
                 /* There are open subkeys but we don't force hive unloading, fail */
                 Hive->HiveFlags &= ~HIVE_IS_UNLOADING;
@@ -2230,7 +2230,13 @@ CmUnloadKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
         }
         else
         {
-            DPRINT1("CmUnloadKey: Force unloading is UNIMPLEMENTED, expect dangling KCBs problems!\n");
+            DPRINT1("CmUnloadKey: Force unloading is HALF-IMPLEMENTED, expect dangling KCBs problems!\n");
+            if (CmpEnumerateOpenSubKeys(Kcb, TRUE, TRUE) != 0)
+            {
+                /* There are open subkeys that we cannot force to unload, fail */
+                Hive->HiveFlags &= ~HIVE_IS_UNLOADING;
+                return STATUS_CANNOT_DELETE;
+            }
         }
     }
 
@@ -2247,6 +2253,10 @@ CmUnloadKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
+    /* Flush any notifications if we force hive unloading */
+    if (Flags == REG_FORCE_UNLOAD)
+        CmpFlushNotifiesOnKeyBodyList(Kcb, TRUE); // Lock is already held
+
     /* Clean up information we have on the subkey */
     CmpCleanUpSubKeyInfo(Kcb->ParentKcb);
 
@@ -2272,6 +2282,10 @@ CmUnloadKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
     /* Remove the hive from the hive file list */
     CmpRemoveFromHiveFileList(CmHive);
 
+/**
+ ** NOTE:
+ ** The following code is mostly equivalent to what we "call" CmpDestroyHive()
+ **/
     /* Destroy the security descriptor cache */
     CmpDestroySecurityCache(CmHive);
 
@@ -2296,8 +2310,10 @@ CmUnloadKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
 
 ULONG
 NTAPI
-CmCountOpenSubKeys(IN PCM_KEY_CONTROL_BLOCK RootKcb,
-                   IN BOOLEAN RemoveEmptyCacheEntries)
+CmpEnumerateOpenSubKeys(
+    IN PCM_KEY_CONTROL_BLOCK RootKcb,
+    IN BOOLEAN RemoveEmptyCacheEntries,
+    IN BOOLEAN DereferenceOpenedEntries)
 {
     PCM_KEY_HASH Entry;
     PCM_KEY_CONTROL_BLOCK CachedKcb;
@@ -2306,9 +2322,9 @@ CmCountOpenSubKeys(IN PCM_KEY_CONTROL_BLOCK RootKcb,
     ULONG i, j;
     ULONG SubKeys = 0;
 
-    DPRINT("CmCountOpenSubKeys() called\n");
+    DPRINT("CmpEnumerateOpenSubKeys() called\n");
 
-    /* The root key is the only referenced key. There are no refereced sub keys. */
+    /* The root key is the only referenced key. There are no referenced sub keys. */
     if (RootKcb->RefCount == 1)
     {
         DPRINT("Open sub keys: 0\n");
@@ -2347,10 +2363,43 @@ CmCountOpenSubKeys(IN PCM_KEY_CONTROL_BLOCK RootKcb,
 
                     if (CachedKcb->RefCount > 0)
                     {
+                        DPRINT1("Found a sub key pointing to '%.*s', RefCount = %u\n",
+                                CachedKcb->NameBlock->NameLength, CachedKcb->NameBlock->Name,
+                                CachedKcb->RefCount);
+
+                        /* If we dereference opened KCBs, don't touch read-only keys */
+                        if (DereferenceOpenedEntries &&
+                            !(CachedKcb->ExtFlags & CM_KCB_READ_ONLY_KEY))
+                        {
+                            /* Registry needs to be locked down */
+                            CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK();
+
+                            /* Flush any notifications */
+                            CmpFlushNotifiesOnKeyBodyList(CachedKcb, TRUE); // Lock is already held
+
+                            /* Clean up information we have on the subkey */
+                            CmpCleanUpSubKeyInfo(CachedKcb->ParentKcb);
+
+                            /* Get and cache the next cache entry */
+                            // Entry = Entry->NextHash;
+                            Entry = CachedKcb->NextHash;
+
+                            /* Set the KCB in delete mode and remove it */
+                            CachedKcb->Delete = TRUE;
+                            CmpRemoveKeyControlBlock(CachedKcb);
+
+                            /* Clear the cell */
+                            CachedKcb->KeyCell = HCELL_NIL;
+
+                            /* Restart with the next cache entry */
+                            continue;
+                        }
+                        /* Else, the key cannot be dereferenced, and we count it as in use */
+
                         /* Count the current hash entry if it is in use */
                         SubKeys++;
                     }
-                    else if ((CachedKcb->RefCount == 0) && (RemoveEmptyCacheEntries != FALSE))
+                    else if ((CachedKcb->RefCount == 0) && RemoveEmptyCacheEntries)
                     {
                         /* Remove the current key from the delayed close list */
                         CmpRemoveFromDelayedClose(CachedKcb);
@@ -2370,7 +2419,9 @@ CmCountOpenSubKeys(IN PCM_KEY_CONTROL_BLOCK RootKcb,
         }
     }
 
-    DPRINT("Open sub keys: %u\n", SubKeys);
+    if (SubKeys > 0)
+        DPRINT1("Open sub keys: %u\n", SubKeys);
+
     return SubKeys;
 }