//
//  REGDKEY.C
//
//  Copyright (C) Microsoft Corporation, 1995
//
//  Implementation of RegDeleteKey and supporting functions.
//

#include "pch.h"

//
//  RgFreeDatablockStructures
//
//  Helper routine for RgDeleteKey.  Deletes the specified datablock structures.
//  The datablock is not assumed to be locked.  We don't care about the success
//  of this routine-- in the worst case, some stuff will be orphaned in the
//  file.
//

VOID
INTERNAL
RgFreeDatablockStructures(
    LPFILE_INFO lpFileInfo,
    UINT BlockIndex,
    UINT KeyRecordIndex
    )
{

    LPDATABLOCK_INFO lpDatablockInfo;
    LPKEY_RECORD lpKeyRecord;

    if (RgLockKeyRecord(lpFileInfo, BlockIndex, (BYTE) KeyRecordIndex,
        &lpKeyRecord) == ERROR_SUCCESS) {
        lpDatablockInfo = RgIndexDatablockInfoPtr(lpFileInfo, BlockIndex);
        RgFreeKeyRecord(lpDatablockInfo, lpKeyRecord);
        RgFreeKeyRecordIndex(lpDatablockInfo, KeyRecordIndex);
        RgUnlockDatablock(lpFileInfo, BlockIndex, TRUE);
    }

}

//
//  RgDeleteKey
//
//  Worker routine for VMMRegDeleteKey.  The given key handle references a key
//  that has already been validated as "deleteable".
//

int
INTERNAL
RgDeleteKey(
    HKEY hKey
    )
{

    int ErrorCode;
    LPFILE_INFO lpFileInfo;
    DWORD KeynodeIndex;
    LPKEYNODE lpKeynode;
    DWORD NextKeynodeIndex;
    LPKEYNODE lpNextKeynode;
    DWORD ReplacementKeynodeIndex;
    HKEY hTempKey;

    lpFileInfo = hKey-> lpFileInfo;

    //
    //  Stage one: unlink the keynode of the specified key from the keynode
    //  tree and free all associate file structures with the key.
    //

    if ((ErrorCode = RgLockInUseKeynode(lpFileInfo, hKey-> KeynodeIndex,
        &lpKeynode)) != ERROR_SUCCESS)
        return ErrorCode;

    KeynodeIndex = lpKeynode-> ParentIndex;
    ReplacementKeynodeIndex = lpKeynode-> NextIndex;
    RgUnlockKeynode(lpFileInfo, hKey-> KeynodeIndex, FALSE);

    //  Signal any waiting notifies on the parent that this key is about to be
    //  deleted.
    //
    //  Note that we may fail below, but NT does _exactly_ the same thing in
    //  this case: doesn't care.  If we get an error and don't actually delete
    //  this key, then we'll have sent a spurious notify.
    //
    //  Note also that we don't send any notification that the key itself has
    //  been deleted.  REG_NOTIFY_CHANGE_NAME is supposed to be for subkey
    //  changes only, not changes to the key itself.  But because of the
    //  incompatible way we must deal with subkeys of the key we're about to
    //  delete, we may well end up notifying the key if it has subkeys.
    RgSignalWaitingNotifies(lpFileInfo, KeynodeIndex, REG_NOTIFY_CHANGE_NAME);

    if ((ErrorCode = RgLockInUseKeynode(lpFileInfo, KeynodeIndex,
        &lpKeynode)) != ERROR_SUCCESS)
        return ErrorCode;

    //  The per-key cache that we use for RegEnumKey may be invalid, so it must
    //  be zapped.
    if (!IsNullPtr(hTempKey = RgFindOpenKeyHandle(lpFileInfo, KeynodeIndex)))
        hTempKey-> Flags &= ~KEYF_ENUMKEYCACHED;

    NextKeynodeIndex = lpKeynode-> ChildIndex;

    if (NextKeynodeIndex == hKey-> KeynodeIndex) {

        //  Update the cached child keynode index in the open handle on the
        //  parent.
        if (!IsNullPtr(hTempKey))
            hTempKey-> ChildKeynodeIndex = ReplacementKeynodeIndex;

        //  This is the parent of the keynode that we need to delete.  Replace
        //  it's "child" link.
        lpKeynode-> ChildIndex = ReplacementKeynodeIndex;

    }

    else {

        //  Loop through the siblings of the keynode we're trying to delete.
        do {

            RgUnlockKeynode(lpFileInfo, KeynodeIndex, FALSE);
            KeynodeIndex = NextKeynodeIndex;

            if (IsNullKeynodeIndex(KeynodeIndex)) {
                DEBUG_OUT(("RgDeleteKey: couldn't find the keynode to delete\n"));
                return ERROR_BADDB;
            }

            if ((ErrorCode = RgLockInUseKeynode(lpFileInfo, KeynodeIndex,
                &lpKeynode)) != ERROR_SUCCESS)
                return ErrorCode;

            NextKeynodeIndex = lpKeynode-> NextIndex;

        }   while (NextKeynodeIndex != hKey-> KeynodeIndex);

        //  This is the previous sibling of the keynode that we need to delete.
        //  Replace it's "next" link.
        lpKeynode-> NextIndex = ReplacementKeynodeIndex;

    }

    //  Unlock the updated "parent" or "next" of this keynode.
    RgUnlockKeynode(lpFileInfo, KeynodeIndex, TRUE);

    //  Free the structures associated with the datablock.
    RgFreeDatablockStructures(lpFileInfo, hKey-> BlockIndex, hKey->
        KeyRecordIndex);

    //  Free the structures associated with the keynode tables.
    RgFreeKeynode(lpFileInfo, hKey-> KeynodeIndex);

    //  The key is definitely toast now.
    hKey-> Flags |= KEYF_DELETED;

    //
    //  Stage two: the specified key is unlinked, but any of its subkeys now
    //  have to be freed.  Errors are ignored at this point: we won't try to
    //  undo the stuff we did in stage one.  The worst thing that can happen is
    //  that some file structures are orphaned.
    //

    NextKeynodeIndex = hKey-> ChildKeynodeIndex;

    if (IsNullKeynodeIndex(NextKeynodeIndex) || RgLockInUseKeynode(lpFileInfo,
        NextKeynodeIndex, &lpNextKeynode) != ERROR_SUCCESS)
        return ERROR_SUCCESS;

    while (!IsNullKeynodeIndex(NextKeynodeIndex)) {

        KeynodeIndex = NextKeynodeIndex;
        lpKeynode = lpNextKeynode;

        //  Check if the keynode has any children.  If it does and we can lock
        //  it down, then move to it.
        NextKeynodeIndex = lpKeynode-> ChildIndex;

        if (!IsNullKeynodeIndex(NextKeynodeIndex) &&
            RgLockInUseKeynode(lpFileInfo, NextKeynodeIndex, &lpNextKeynode) ==
            ERROR_SUCCESS) {

            ASSERT(KeynodeIndex == lpNextKeynode-> ParentIndex);

            RgYield();

            //  "Burn" the link to our child, so that on the way back out of
            //  the tree, we don't end up recursing.  Plus, if we hit any errors
            //  deep in the tree deletion, the child of the current keynode
            //  could have already been toasted, so we have to zap our link to
            //  it.
            lpKeynode-> ChildIndex = REG_NULL;
            RgUnlockKeynode(lpFileInfo, KeynodeIndex, TRUE);

            //  We've now caused a change in the subkeys of the current key.
            //  Note that we don't bother signaling notifies that are doing a
            //  subtree watch because any such notifies should have already been
            //  signaled by the above call or they've already been signaled
            //  during our recursion.  In the off chance that we have a lot of
            //  notifications registered, this will avoid a lot of unnecessary
            //  checking.
            RgSignalWaitingNotifies(lpFileInfo, KeynodeIndex,
                REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_NO_WATCH_SUBTREE);

            continue;

        }

        //  The keynode doesn't have any children.  Check for sibling keynodes.
        NextKeynodeIndex = lpKeynode-> NextIndex;

        if (IsNullKeynodeIndex(NextKeynodeIndex) ||
            RgLockInUseKeynode(lpFileInfo, NextKeynodeIndex, &lpNextKeynode) !=
            ERROR_SUCCESS) {

            //  The keynode doesn't have any siblings or we were unable to get
            //  at them.  Move back to the parent.
            NextKeynodeIndex = lpKeynode-> ParentIndex;

            //  If we wrapped back up to the top of the deleted branch or if we
            //  just can't access the parent keynode, then set next to REG_NULL
            //  and bail out on the next iteration.
            if ((NextKeynodeIndex == hKey-> KeynodeIndex) ||
                RgLockInUseKeynode(lpFileInfo, NextKeynodeIndex,
                &lpNextKeynode) != ERROR_SUCCESS)
                NextKeynodeIndex = REG_NULL;

        }

        //  If an open key refers to this file and keynode index, mark it as
        //  deleted.
        if (!IsNullPtr(hTempKey = RgFindOpenKeyHandle(lpFileInfo,
            KeynodeIndex)))
            hTempKey-> Flags |= KEYF_DELETED;

        //  Free the structures associated with the datablock.
        RgFreeDatablockStructures(lpFileInfo, lpKeynode-> BlockIndex,
            (BYTE) lpKeynode-> KeyRecordIndex);

        //  Free the structures associated with the keynode tables.
        RgUnlockKeynode(lpFileInfo, KeynodeIndex, TRUE);
        RgFreeKeynode(lpFileInfo, KeynodeIndex);

    }

    return ERROR_SUCCESS;

}

//
//  VMMRegDeleteKey
//
//  See Win32 documentation for a description of the behavior.
//
//  Although the Win32 documentation states that lpSubKey must be NULL, NT
//  actually allows this to pass through.  Win95 rejected the call, but the only
//  reason we didn't change it then was because we realized too late in the
//  product that it was different.
//

LONG
REGAPI
VMMRegDeleteKey(
    HKEY hKey,
    LPCSTR lpSubKey
    )
{

    LONG ErrorCode;
    HKEY hSubKey = 0;

    if ((ErrorCode = VMMRegOpenKey(hKey, lpSubKey, &hSubKey)) != ERROR_SUCCESS)
        return ErrorCode;

    if (!RgLockRegistry())
        ErrorCode = ERROR_LOCK_FAILED;

    else {

        if (IsKeyRootOfHive(hSubKey) || (hSubKey-> lpFileInfo-> Flags &
            FI_READONLY))
            ErrorCode = ERROR_ACCESS_DENIED;
        else
            ErrorCode = RgDeleteKey(hSubKey);

        RgUnlockRegistry();

    }

    VMMRegCloseKey(hSubKey);

    return ErrorCode;

}