//+----------------------------------------------------------------------------
//
//  Copyright (C) 2000, Microsoft Corporation
//
//  File:       DfsRegistryRootFolder.cxx
//
//  Contents:   the Root DFS Folder class for Registry Store
//
//  Classes:    DfsRegistryRootFolder
//
//  History:    Dec. 8 2000,   Author: udayh
//
//-----------------------------------------------------------------------------


#include "DfsRegistryRootFolder.hxx"
#include "DfsReplica.hxx"

#include "lmdfs.h"
#include "DfsClusterSupport.hxx"
//
// logging specific includes
//
#include "DfsRegistryRootFolder.tmh" 

//+----------------------------------------------------------------------------
//
//  Class:      DfsRegistryRootFolder
//
//  Synopsis:   This class implements The Dfs Registry root folder.
//
//-----------------------------------------------------------------------------


//+-------------------------------------------------------------------------
//
//  Function:   DfsRegistryRootFolder - constructor
//
//  Arguments:    NameContext -  the dfs name context
//                pLogicalShare -  the logical share
//                pParentStore -  the parent store for this root.
//                pStatus - the return status
//
//  Returns:    NONE
//
//  Description: This routine initializes a RegistryRootFolder instance
//
//--------------------------------------------------------------------------
DfsRegistryRootFolder::DfsRegistryRootFolder(
    LPWSTR NameContext,
    LPWSTR pRootRegKeyNameString,
    PUNICODE_STRING pLogicalShare,
    PUNICODE_STRING pPhysicalShare,
    DfsRegistryStore *pParentStore,
    DFSSTATUS *pStatus ) :  DfsRootFolder ( NameContext,
                                            pRootRegKeyNameString,
                                            pLogicalShare,
                                            pPhysicalShare,
                                            DFS_OBJECT_TYPE_REGISTRY_ROOT_FOLDER,
                                            pStatus )
{
    DFSSTATUS Status = *pStatus;

    _pStore = pParentStore;
    if (_pStore != NULL)
    {
        _pStore->AcquireReference();
    }
    _RootFlavor = DFS_VOLUME_FLAVOR_STANDALONE;

    //
    // If the namecontext that we are passed is an emptry string,
    // then we are dealing with the referral server running on the root
    // itself. We are required to ignore the name context for such
    // roots during lookups, so that aliasing works. (Aliasing is where
    // someone may access the root with an aliased machine name or ip
    // address)
    //
    if (IsEmptyString(NameContext) == TRUE)
    {
        SetIgnoreNameContext();
        _LocalCreate = TRUE;
    }

    //
    // dfsdev: If this is cluster resource, we should set the visible name
    // to virtual server name of this resource.
    //
    // The constructor for DfsRootFolder will be called before we
    // get here, and pstatus will be initialized
    //
    if (Status == ERROR_SUCCESS) 
    {
        if (DfsIsMachineCluster())
        {
            DFSSTATUS ClusterStatus;

            ClusterStatus = GetRootClusterInformation( pLogicalShare, 
                                                       &_DfsVisibleContext);
            if (ClusterStatus != ERROR_SUCCESS)
            {
                RtlInitUnicodeString(&_DfsVisibleContext, NULL);
            }
        }
        if (IsEmptyString(_DfsVisibleContext.Buffer))
        {
            Status = DfsGetMachineName( &_DfsVisibleContext );
        }
    }

    *pStatus = Status;
}


//+-------------------------------------------------------------------------
//
//  Function:   Synchronize
//
//  Arguments:    None
//
//  Returns:    Status: Success or Error status code
//
//  Description: This routine synchronizes the children folders
//               of this root.
//
//--------------------------------------------------------------------------

DFSSTATUS
DfsRegistryRootFolder::Synchronize( BOOLEAN fForceSynch, BOOLEAN CalledByApi )
{

    DFSSTATUS Status = ERROR_SUCCESS;
    HKEY RootKey = NULL;
    ULONG ChildNum = 0;
    DWORD CchMaxName = 0;
    DWORD CchChildName = 0;
    LPWSTR ChildName = NULL;
    FILETIME LastModifiedTime;
    
    UNICODE_STRING NewClusterName;

    UNREFERENCED_PARAMETER(fForceSynch);

    
    DFS_TRACE_NORM(REFERRAL_SERVER, "Synchronize for %p\n", this);


    RtlInitUnicodeString(&NewClusterName, NULL);

    if (DfsIsMachineCluster())
    {
        DFSSTATUS IgnoreStatus;

        IgnoreStatus = GetRootClusterInformation( GetLogicalShare(),
                                              &NewClusterName);
        ASSERT((IgnoreStatus == ERROR_SUCCESS) ||
                (NewClusterName.Buffer == NULL));
    }


    Status = AcquireRootLock();
    if (Status != ERROR_SUCCESS)
    {
        return Status;
    }

    if (NewClusterName.Length != 0)
    {
        DFSSTATUS ClusterNameStatus;
        BOOLEAN Changed = FALSE;
        ClusterNameStatus = SetVisibleContext( &NewClusterName, &Changed );

        //
        // if the cluster information has changed, get rid of cached
        // referral.
        //
        if (ClusterNameStatus == ERROR_SUCCESS)
        {
            if (Changed)
            {
                RemoveReferralData(NULL, NULL);
            }
        }
        DfsFreeUnicodeString(&NewClusterName);

        DFS_TRACE_LOW(REFERRAL_SERVER, "SetVisbleContext, status %x\n", 
                      ClusterNameStatus);
    }

    if (CheckRootFolderSkipSynchronize() == TRUE)
    {
        ReleaseRootLock();
        return ERROR_SUCCESS;
    }


    //
    // now acquire the root share directory. If this
    // fails, we continue our operation: we can continue
    // with synchonize and not create directories.
    // dfsdev:we need to post a eventlog or something when
    // we run into this.
    //
    do 
    {
        DFSSTATUS RootStatus = AcquireRootShareDirectory();

        if(CalledByApi && (RootStatus != ERROR_SUCCESS))
        {
            DFS_TRACE_ERROR_LOW(RootStatus, REFERRAL_SERVER, "Recognize Dfs: Root folder for %p, validate status %x\n",
                                this, RootStatus );
            Status = RootStatus;
            break;
        }

        //
        // if we are in a standby mode, we dont synchronize, till we obtain 
        // ownership again.
        //

        Status = GetMetadataKey( &RootKey );

        if ( Status == ERROR_SUCCESS )
        {

            {
                DFS_METADATA_HANDLE DfsHandle;

                DfsHandle = CreateMetadataHandle(RootKey);

                UpdateLinkInformation( DfsHandle, NULL );

                DestroyMetadataHandle(DfsHandle );
            }

            //
            // Iterate over all our folders to make sure they actually
            // exist in the registry.
            //
            CheckPreSynchronize( RootKey );

            //
            // First find the length of the longest subkey 
            // and allocate a buffer big enough for it.
            //
            Status = RegQueryInfoKey( RootKey,       // Key
                                      NULL,         // Class string
                                      NULL,         // Size of class string
                                      NULL,         // Reserved
                                      NULL,         // # of subkeys
                                      &CchMaxName,  // max size of subkey name in TCHARs
                                      NULL,         // max size of class name
                                      NULL,         // # of values
                                      NULL,         // max size of value name
                                      NULL,    // max size of value data,
                                      NULL,         // security descriptor
                                      NULL );       // Last write time
            if (Status == ERROR_SUCCESS)
            {
                // Space for the NULL terminator.
                CchMaxName++; 

                ChildName = (LPWSTR) new WCHAR [CchMaxName];
                if (ChildName == NULL)
                {
                    Status = ERROR_NOT_ENOUGH_MEMORY;
                }
            }

            if (Status == ERROR_SUCCESS)
            {

                do
                {
                    //
                    // For each child, get the child name.
                    //

                    CchChildName = CchMaxName;

                    Status = RegEnumKeyEx( RootKey,
                                           ChildNum,
                                           ChildName,
                                           &CchChildName,
                                           NULL,
                                           NULL,
                                           NULL,
                                           &LastModifiedTime );

                    ChildNum++;

                    //
                    // Call update on the child. This either adds a new folder
                    // or if it exists, ensure the child folder is upto date.
                    //
                    if ( Status == ERROR_SUCCESS )
                    {
                        DFS_METADATA_HANDLE DfsHandle;

                        DfsHandle = CreateMetadataHandle(RootKey);

                        Status = UpdateLinkInformation( DfsHandle,
                                                        ChildName );

                        DestroyMetadataHandle(DfsHandle );
                    }

                } while ( Status == ERROR_SUCCESS );

                delete [] ChildName;
            }

            if ( Status == ERROR_NO_MORE_ITEMS )
            {
                Status = ERROR_SUCCESS;
            }


            //
            // We are done with synchronize.
            // update the Root folder, so that this root folder may be made 
            // either available or unavailable, as the case may be.
            //
            if (Status == ERROR_SUCCESS)
            {
                SetRootFolderSynchronized();
            }
            else
            {
                ClearRootFolderSynchronized();
            }

            //
            // Now release the Root metadata key.
            //
            ReleaseMetadataKey( RootKey );
        }


        if(CalledByApi && (Status != ERROR_SUCCESS))
        {

          DFS_TRACE_ERROR_LOW(Status, REFERRAL_SERVER, "Recognize Dfs: Registry Synchronize Root folder for %p, validate status %x\n",
                              this, Status );
          (void) ReleaseRootShareDirectory();
        }

    } while (0);


    DFS_TRACE_NORM(REFERRAL_SERVER, "Synchronize for %p, Status %x\n", this, Status);

    ReleaseRootLock();

    return Status;
}