/*++

Copyright (c) 1998  Microsoft Corporation

Module Name:

    regcopy.c

Abstract:

    This is for supporting copying and munging the registry files.

Author:

    Sean Selitrennikoff - 4/5/98

Revision History:

--*/


#include "precomp.h"
#pragma hdrstop

typedef BOOL (*PFNGETPROFILESDIRECTORYW)(LPWSTR lpProfile, LPDWORD dwSize);

PWSTR HivePath;
HKEY HiveRoot;
PWSTR HiveName;
REG_CONTEXT RegistryContext;

PWSTR MachineName;
PWSTR HiveFileName;
PWSTR HiveRootName;



DWORD
DoFullRegBackup(
    PWCHAR MirrorRoot
    )

/*++

Routine Description:

    This routine copies all the registries to the given server path.

Arguments:

    None.

Return Value:

    NO_ERROR if everything was backed up properly, else the appropriate error code.

--*/

{
    PWSTR w;
    LONG Error;
    HKEY HiveListKey;
    PWSTR KeyName;
    PWSTR FileName;
    PWSTR Name;
    DWORD ValueIndex;
    DWORD ValueType;
    DWORD ValueNameLength;
    DWORD ValueDataLength;
    WCHAR ConfigPath[ MAX_PATH ];
    WCHAR HiveName[ MAX_PATH ];
    WCHAR HivePath[ MAX_PATH ];
    WCHAR FilePath[ MAX_PATH ];
    HANDLE hInstDll;
    PFNGETPROFILESDIRECTORYW pfnGetProfilesDirectory;
    NTSTATUS Status;
    BOOLEAN savedBackup;

    //
    // First try and give ourselves enough priviledge
    //
    if (!RTEnableBackupRestorePrivilege()) {
        return(GetLastError());
    }

    //
    // Now attach to the registry
    //
    Error = RTConnectToRegistry(MachineName,
                                HiveFileName,
                                HiveRootName,
                                NULL,
                                &RegistryContext
                               );

    if (Error != NO_ERROR) {
        RTDisableBackupRestorePrivilege();
        return Error;
    }

    //
    // Get handle to hivelist key
    //
    KeyName = L"HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Control\\Hivelist";
    Error = RTOpenKey(&RegistryContext,
                      NULL,
                      KeyName,
                      MAXIMUM_ALLOWED,
                      0,
                      &HiveListKey
                     );

    if (Error != NO_ERROR) {
        RTDisconnectFromRegistry(&RegistryContext);
        return Error;
    }

    //
    // get path data for system hive, which will allow us to compute
    // path name to config dir in form that hivelist uses.
    // (an NT internal form of path)  this is NOT the way the path to
    // the config directory should generally be computed.
    //

    ValueDataLength = sizeof(ConfigPath);
    Error = RTQueryValueKey(&RegistryContext,
                            HiveListKey,
                            L"\\Registry\\Machine\\System",
                            &ValueType,
                            &ValueDataLength,
                            ConfigPath
                           );
    if (Error != NO_ERROR) {
        RTDisconnectFromRegistry(&RegistryContext);
        return Error;
    }
    w = wcsrchr(ConfigPath, L'\\');
    *w = UNICODE_NULL;


    //
    // ennumerate entries in hivelist.  for each entry, find it's hive file
    // path then save it.
    //
    for (ValueIndex = 0; TRUE; ValueIndex++) {

        savedBackup = FALSE;
        ValueType = REG_NONE;
        ValueNameLength = ARRAYSIZE( HiveName );
        ValueDataLength = sizeof( HivePath );

        Error = RTEnumerateValueKey(&RegistryContext,
                                    HiveListKey,
                                    ValueIndex,
                                    &ValueType,
                                    &ValueNameLength,
                                    HiveName,
                                    &ValueDataLength,
                                    HivePath
                                   );
        if (Error == ERROR_NO_MORE_ITEMS) {
            break;
        } else if (Error != NO_ERROR) {
            RTDisconnectFromRegistry(&RegistryContext);
            return Error;
        }

        if ((ValueType == REG_SZ) && (ValueDataLength > sizeof(UNICODE_NULL))) {
            //
            // there's a file, compute it's path, hive branch, etc
            //

            if (w = wcsrchr( HivePath, L'\\' )) {
                *w++ = UNICODE_NULL;
            }
            FileName = w;

            if (w = wcsrchr( HiveName, L'\\' )) {
                *w++ = UNICODE_NULL;
            }
            Name = w;

            HiveRoot = NULL;
            if (w = wcsrchr( HiveName, L'\\' )) {
                w += 1;
                if (!_wcsicmp( w, L"MACHINE" )) {
                    HiveRoot = HKEY_LOCAL_MACHINE;
                } else if (!_wcsicmp( w, L"USER" )) {
                    HiveRoot = HKEY_USERS;
                } else {

                    Status = IMirrorRegSaveError(w, ERROR_PATH_NOT_FOUND);

                    if (Status == STATUS_RETRY) {
                        continue;
                    }

                    if (!NT_SUCCESS(Status)) {
                        return Error;
                    }

                }

            }

            if (FileName != NULL && Name != NULL && HiveRoot != NULL) {

                //
                // Extract the path name from HivePath
                //
                if (_wcsicmp(HivePath, L"\\Device")) {

                    w = HivePath + 1;
                    w = wcsstr(w, L"\\");
                    w++;
                    w = wcsstr(w, L"\\");
                    w++;

                } else if (*(HivePath + 1) == L':') {

                    w = HivePath + 2;

                } else {

                    Status = IMirrorRegSaveError(HivePath, ERROR_PATH_NOT_FOUND);

                    if (Status == STATUS_RETRY) {
                        continue;
                    }

                    if (!NT_SUCCESS(Status)) {
                        return Error;
                    }

                }

                //
                // Do the save
                //

                swprintf( FilePath, L"%ws\\UserData\\%ws\\%ws", MirrorRoot, w, FileName );

                IMirrorNowDoing(CopyRegistry, FileName);

                //
                //  if the file already exists, rename it to a backup name
                //  so that if it fails, we'll restore it.
                //

                lstrcpyW( (PWCHAR) TmpBuffer, FilePath );
                lstrcatW( (PWCHAR) TmpBuffer, L".old" );
                if (MoveFileEx( FilePath, (PWCHAR) TmpBuffer, MOVEFILE_REPLACE_EXISTING)) {

                    savedBackup = TRUE;
                }

RetrySave:
                Error = DoSpecificRegBackup(FilePath,
                                            HiveRoot,
                                            Name
                                           );

                if (Error != NO_ERROR) {

                    Status = IMirrorRegSaveError(Name, Error);

                    if (Status == STATUS_RETRY) {
                        goto RetrySave;
                    }

                    if (!NT_SUCCESS(Status)) {

                        if (savedBackup) {

                            if (MoveFileEx( (PWCHAR) TmpBuffer, FilePath, MOVEFILE_REPLACE_EXISTING)) {

                                savedBackup = FALSE;
                            }
                        }
                        return Error;
                    }
                }
            }
        }
    }

    RTDisconnectFromRegistry(&RegistryContext);
    return NO_ERROR;
}

DWORD
DoSpecificRegBackup(
    PWSTR HivePath,
    HKEY HiveRoot,
    PWSTR HiveName
    )


/*++

Routine Description:

    This routine copies all the registries to the given server path.

Arguments:

    HivePath - file name to pass directly to OS

    HiveRoot - HKEY_LOCAL_MACHINE or HKEY_USERS

    HiveName - 1st level subkey under machine or users

Return Value:

    NO_ERROR if everything was backed up properly, else the appropriate error code.

--*/

{
    HKEY HiveKey;
    ULONG Disposition;
    LONG Error;
    char *Reason;

    //
    // get a handle to the hive.  use special create call what will
    // use privileges
    //

    Reason = "accessing";
    Error = RTCreateKey(&RegistryContext,
                        HiveRoot,
                        HiveName,
                        KEY_READ,
                        REG_OPTION_BACKUP_RESTORE,
                        NULL,
                        &HiveKey,
                        &Disposition
                       );
    if (Error == NO_ERROR) {
        Reason = "saving";
        Error = RegSaveKey(HiveKey, HivePath, NULL);
        RTCloseKey(&RegistryContext, HiveKey);
    }

    return Error;
}

DWORD
GetRegistryFileList(
    PLIST_ENTRY ListHead
    )

/*++

Routine Description:

    This routine stores all registry file names to a list.

Arguments:

    None.

Return Value:

    NO_ERROR if everything was backed up properly, else the appropriate error code.

--*/

{
    LONG Error;
    HKEY HiveListKey;
    PWSTR KeyName;
    PWSTR FileName;
    PWSTR Name;
    DWORD ValueIndex;
    DWORD ValueType;
    DWORD ValueNameLength;
    DWORD ValueDataLength;
    WCHAR HiveName[ MAX_PATH ];
    WCHAR HivePath[ MAX_PATH ];
    NTSTATUS Status;
    PIMIRROR_IGNORE_FILE_LIST entry;

    //
    // enter all hardcoded files that we don't want to mirror here...
    //

    FileName = L"System Volume Information\\tracking.log";

    entry = IMirrorAllocMem(sizeof(IMIRROR_IGNORE_FILE_LIST) +
                            ((lstrlenW(FileName) + 1) * sizeof(WCHAR)));

    if (entry != NULL) {

        lstrcpyW( &entry->FileName[0], FileName );
        entry->FileNameLength = (USHORT) lstrlenW( FileName );
        InsertHeadList( ListHead, &entry->ListEntry );
    }

    //
    // Now attach to the registry
    //
    Error = RTConnectToRegistry(MachineName,
                                HiveFileName,
                                HiveRootName,
                                NULL,
                                &RegistryContext
                               );

    if (Error != NO_ERROR) {
        return Error;
    }

    //
    // Get handle to hivelist key
    //
    KeyName = L"HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Control\\Hivelist";
    Error = RTOpenKey(&RegistryContext,
                      NULL,
                      KeyName,
                      MAXIMUM_ALLOWED,
                      0,
                      &HiveListKey
                     );

    if (Error != NO_ERROR) {
        RTDisconnectFromRegistry(&RegistryContext);
        return Error;
    }

    //
    // ennumerate entries in hivelist.  for each entry, find it's hive file
    // path then save it.
    //
    for (ValueIndex = 0; TRUE; ValueIndex++) {

        ValueType = REG_NONE;
        ValueNameLength = ARRAYSIZE( HiveName );
        ValueDataLength = sizeof( HivePath );

        Error = RTEnumerateValueKey(&RegistryContext,
                                    HiveListKey,
                                    ValueIndex,
                                    &ValueType,
                                    &ValueNameLength,
                                    HiveName,
                                    &ValueDataLength,
                                    HivePath
                                   );
        if (Error != NO_ERROR) {
            if (Error == ERROR_NO_MORE_ITEMS) {
                Error = NO_ERROR;
            }
            break;
        }

        if ((ValueType == REG_SZ) && (ValueDataLength > sizeof(UNICODE_NULL))) {

            //
            // Extract the path name from HivePath
            //
            if (_wcsicmp(HivePath, L"\\Device")) {

                FileName = HivePath + 1;
                FileName = wcsstr(FileName, L"\\");
                FileName++;
                FileName = wcsstr(FileName, L"\\");
                FileName++;     // now points to L"\winnt\system32\config\sam"

            } else if (*(HivePath + 1) == L':') {

                FileName = HivePath + 2;

            } else {

                FileName = HivePath;
            }

            entry = IMirrorAllocMem(sizeof(IMIRROR_IGNORE_FILE_LIST) +
                                    ((lstrlenW(FileName) + 1) * sizeof(WCHAR)));

            if (entry != NULL) {

                lstrcpyW( &entry->FileName[0], FileName );
                entry->FileNameLength = (USHORT) lstrlenW( FileName );
                InsertHeadList( ListHead, &entry->ListEntry );
            }
        }
    }

    RTDisconnectFromRegistry(&RegistryContext);
    return Error;
}