/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

    win31io.c

Abstract:

    This file contains Win 3.1 Inter-Operability functions.

Author:

    Steve Wood (stevewo) 21-Feb-1993

Revision History:

--*/

#include "advapi.h"
#include <stdio.h>

#include <winbasep.h>
#include "win31io.h"

#define EVENTLOG_SOURCE "Windows 3.1 Migration"

BOOL
WaitForEventLogToStart( VOID );

#define SIZE_OF_TOKEN_INFORMATION                   \
    sizeof( TOKEN_USER )                            \
    + sizeof( SID )                                 \
    + sizeof( ULONG ) * SID_MAX_SUB_AUTHORITIES

typedef struct _WIN31IO_STATE {
    HANDLE EventLog;
    HANDLE SoftwareRoot;
    HANDLE UserRoot;
    HANDLE Win31IOKey;
    BOOL QueryOnly;
    PWSTR WindowsPath;
    PWSTR FileNamePart;
    PWSTR WorkBuffer;
    ULONG cchWorkBuffer;
    PWIN31IO_STATUS_CALLBACK StatusCallback;
    PVOID CallbackParameter;
    WCHAR szWindowsDirectory[ MAX_PATH ];
    PSID UserSid;
    UCHAR TokenInformation[ SIZE_OF_TOKEN_INFORMATION ];
    WCHAR QueryBuffer[ 64 ];
} WIN31IO_STATE, *PWIN31IO_STATE;


HANDLE
OpenCreateKey(
    IN HANDLE Root,
    IN PWSTR Path,
    IN BOOL WriteAccess
    );

BOOL
SnapShotWin31IniFilesToRegistry(
    IN OUT PWIN31IO_STATE State
    );

BOOL
SnapShotWin31GroupsToRegistry(
    IN OUT PWIN31IO_STATE State
    );

BOOL
SnapShotWin31RegDatToRegistry(
    IN OUT PWIN31IO_STATE State
    );

BOOL
InitializeWin31State(
    IN WIN31IO_EVENT EventType,
    OUT PWIN31IO_STATE State
    );


VOID
TerminateWin31State(
    IN WIN31IO_EVENT EventType,
    IN OUT PWIN31IO_STATE State
    );

BOOL
InitializeWin31State(
    IN WIN31IO_EVENT EventType,
    OUT PWIN31IO_STATE State
    )
{
    NTSTATUS Status;
    ULONG cch;
    HANDLE TokenHandle;
    ULONG ReturnLength;

    memset( State, 0, sizeof( *State ) );
    cch = GetWindowsDirectoryW( State->szWindowsDirectory,
                                sizeof( State->szWindowsDirectory ) / sizeof (WCHAR)
                              );
    State->WindowsPath = State->szWindowsDirectory;
    State->FileNamePart = State->WindowsPath + cch;
    *State->FileNamePart++ = OBJ_NAME_PATH_SEPARATOR;

    if (EventType == Win31SystemStartEvent) {
        State->SoftwareRoot = OpenCreateKey( NULL,
                                             L"\\Registry\\Machine\\Software",
                                             FALSE
                                           );
        if (State->SoftwareRoot == NULL) {
            return FALSE;
            }
        }
    else {
        Status = RtlOpenCurrentUser( GENERIC_READ, &State->UserRoot );
        if (!NT_SUCCESS( Status )) {
            BaseSetLastNTError( Status );
            return FALSE;
            }

        if (OpenThreadToken( GetCurrentThread(),
                             TOKEN_READ,
                             TRUE,
                             &TokenHandle
                           ) ||
            OpenProcessToken( GetCurrentProcess(),
                              TOKEN_READ,
                              &TokenHandle
                            )
           ) {
            if (GetTokenInformation( TokenHandle,
                                     TokenUser,
                                     &State->TokenInformation,
                                     sizeof( State->TokenInformation ),
                                     &ReturnLength
                                   )
               ) {
                PTOKEN_USER UserToken = (PTOKEN_USER)&State->TokenInformation;

                State->UserSid = UserToken->User.Sid;
                }

            CloseHandle( TokenHandle );
            }
        }

    State->Win31IOKey = OpenCreateKey( EventType == Win31SystemStartEvent ?
                                            State->SoftwareRoot : State->UserRoot,
                                       L"Windows 3.1 Migration Status",
                                       TRUE
                                     );
    if (State->Win31IOKey == NULL) {
        if (State->SoftwareRoot != NULL) {
            NtClose( State->SoftwareRoot );
            }

        if (State->UserRoot != NULL) {
            NtClose( State->UserRoot );
            }

        if (State->EventLog != NULL) {
            DeregisterEventSource( State->EventLog );
            }

        return FALSE;
        }
    else {
        return TRUE;
        }

    return TRUE;
}


VOID
TerminateWin31State(
    IN WIN31IO_EVENT EventType,
    IN OUT PWIN31IO_STATE State
    )
{
    if (State->Win31IOKey != NULL) {
        NtClose( State->Win31IOKey );
        }

    if (State->SoftwareRoot != NULL) {
        NtClose( State->SoftwareRoot );
        }

    if (State->UserRoot != NULL) {
        NtClose( State->UserRoot );
        }

    if (State->EventLog != NULL && State->EventLog != INVALID_HANDLE_VALUE) {
        DeregisterEventSource( State->EventLog );
        }

    return;
}

#define MAX_EVENT_STRINGS 8

VOID
ReportWin31IOEvent(
    IN PWIN31IO_STATE State,
    IN WORD EventType,
    IN DWORD EventId,
    IN DWORD SizeOfRawData,
    IN PVOID RawData,
    IN DWORD NumberOfStrings,
    ...
    )
{
    va_list arglist;
    ULONG i;
    PWSTR Strings[ MAX_EVENT_STRINGS ];

    va_start( arglist, NumberOfStrings );

    if (NumberOfStrings > MAX_EVENT_STRINGS) {
        NumberOfStrings = MAX_EVENT_STRINGS;
        }

    for (i=0; i<NumberOfStrings; i++) {
        Strings[ i ] = va_arg( arglist, PWSTR );
        }

    if (State->EventLog == NULL) {
        State->EventLog = RegisterEventSource( NULL, EVENTLOG_SOURCE );
        if (State->EventLog == NULL) {
            if (WaitForEventLogToStart()) {
                State->EventLog = RegisterEventSource( NULL, EVENTLOG_SOURCE );
                }

            if (State->EventLog == NULL) {
                KdPrint(( "WIN31IO: RegisterEventSource( %s ) failed - %u\n", EVENTLOG_SOURCE, GetLastError() ));
                State->EventLog = INVALID_HANDLE_VALUE;
                return;
                }
            }
        }

    if (State->EventLog != INVALID_HANDLE_VALUE) {
        if (!ReportEventW( State->EventLog,
                           EventType,
                           0,            // event category
                           EventId,
                           State->UserSid,
                           (WORD)NumberOfStrings,
                           SizeOfRawData,
                           Strings,
                           RawData
                         )
           ) {
            KdPrint(( "WIN31IO: ReportEvent( %u ) failed - %u\n", EventId, GetLastError() ));
            }
        }
}

int
Win31IOExceptionHandler(
    IN DWORD ExceptionCode,
    IN PEXCEPTION_POINTERS ExceptionInfo,
    IN OUT PWIN31IO_STATE State
    );

int
Win31IOExceptionHandler(
    IN DWORD ExceptionCode,
    IN PEXCEPTION_POINTERS ExceptionInfo,
    IN OUT PWIN31IO_STATE State
    )
{
    KdPrint(( "WIN31IO: Unexpected exception %08x at %08x referencing %08x\n",
              ExceptionInfo->ExceptionRecord->ExceptionCode,
              ExceptionInfo->ExceptionRecord->ExceptionAddress,
              ExceptionInfo->ExceptionRecord->ExceptionInformation[ 1 ]
           ));

    //
    // Unexpected exception.  Log the event with the exception record
    // so we can figure it out later.
    //

    ReportWin31IOEvent( State,
                        EVENTLOG_ERROR_TYPE,
                        WIN31IO_EVENT_EXCEPTION,
                        sizeof( *(ExceptionInfo->ExceptionRecord) ),
                        ExceptionInfo->ExceptionRecord,
                        0
                      );

    return EXCEPTION_EXECUTE_HANDLER;
}


DWORD
WINAPI
QueryWindows31FilesMigration(
    IN WIN31IO_EVENT EventType
    )
{
    DWORD Flags;
    WIN31IO_STATE State;

    if (EventType == Win31LogoffEvent) {
        return 0;
        }

    if (!InitializeWin31State( EventType, &State )) {
        return 0;
        }

    State.QueryOnly = TRUE;
    State.WorkBuffer = State.QueryBuffer;
    State.cchWorkBuffer = sizeof( State.QueryBuffer ) / sizeof( WCHAR );
    Flags = 0;

    try {
        try {
            if (EventType == Win31SystemStartEvent) {
                if (SnapShotWin31IniFilesToRegistry( &State )) {
                    Flags |= WIN31_MIGRATE_INIFILES;
                    }

                if (SnapShotWin31RegDatToRegistry( &State )) {
                    Flags |= WIN31_MIGRATE_REGDAT;
                    }
                }
            else {
                if (SnapShotWin31IniFilesToRegistry( &State )) {
                    Flags |= WIN31_MIGRATE_INIFILES;
                    }

                if (SnapShotWin31GroupsToRegistry( &State )) {
                    Flags |= WIN31_MIGRATE_GROUPS;
                    }
                }
            }
    except( Win31IOExceptionHandler( GetExceptionCode(),
                                     GetExceptionInformation(),
                                     &State
                                   )
          ) {
            BaseSetLastNTError( GetExceptionCode() );
            }
        }
    finally {
        TerminateWin31State( EventType, &State );
        }

    return Flags;
}


BOOL
WINAPI
SynchronizeWindows31FilesAndWindowsNTRegistry(
    IN WIN31IO_EVENT EventType,
    IN DWORD Flags,
    IN PWIN31IO_STATUS_CALLBACK StatusCallback,
    IN PVOID CallbackParameter
    )
{
    BOOL Result;
    VIRTUAL_BUFFER Buffer;
    ULONG cch;
    WIN31IO_STATE State;

    if (Flags == 0 || EventType == Win31LogoffEvent) {
        return TRUE;
        }

    if (!InitializeWin31State( EventType, &State )) {
        return TRUE;
        }
    State.QueryOnly = FALSE;
    State.StatusCallback = StatusCallback;
    State.CallbackParameter = CallbackParameter;

    Result = FALSE;
    try {
        try {
            try {
                cch = ((64 * 1024) / sizeof( WCHAR )) - 1;
                if (!CreateVirtualBuffer( &Buffer, cch * sizeof( WCHAR ), cch * sizeof( WCHAR ) )) {
                    leave;
                    }

                State.WorkBuffer = Buffer.Base;
                State.cchWorkBuffer = cch;

                Result = TRUE;
                if (EventType == Win31SystemStartEvent) {
                    if (Flags & WIN31_MIGRATE_INIFILES) {
                        Result &= SnapShotWin31IniFilesToRegistry( &State );
                        }

                    if (Flags & WIN31_MIGRATE_REGDAT) {
                        Result &= SnapShotWin31RegDatToRegistry( &State );
                        }
                    }
                else {
                    if (Flags & WIN31_MIGRATE_INIFILES) {
                        Result &= SnapShotWin31IniFilesToRegistry( &State );
                        }

                    if (Flags & WIN31_MIGRATE_GROUPS) {
                        Result &= SnapShotWin31GroupsToRegistry( &State );
                        }
                    }
                }
            except( VirtualBufferExceptionHandler( GetExceptionCode(),
                                                   GetExceptionInformation(),
                                                   &Buffer
                                                 )
                  ) {
                if (GetExceptionCode() == STATUS_ACCESS_VIOLATION) {
                    BaseSetLastNTError( STATUS_NO_MEMORY );
                    }
                else {
                    BaseSetLastNTError( GetExceptionCode() );
                    }

                Result = FALSE;
                }
            }
        except( Win31IOExceptionHandler( GetExceptionCode(),
                                         GetExceptionInformation(),
                                         &State
                                       )
              ) {
            BaseSetLastNTError( GetExceptionCode() );
            Result = FALSE;
            }
        }
    finally {
        TerminateWin31State( EventType, &State );
        FreeVirtualBuffer( &Buffer );
        }

    return Result;
}


BOOL
SnapShotWin31IniFileKey(
    IN OUT PWIN31IO_STATE State,
    IN PWSTR TempFileName,
    IN PWSTR FileName,
    IN PWSTR ApplicationName,
    IN PWSTR KeyName
    );

BOOL
SnapShotWin31IniFileSection(
    IN OUT PWIN31IO_STATE State,
    IN PWSTR TempFileName,
    IN PWSTR FileName,
    IN PWSTR ApplicationName
    );

BOOL
SnapShotWin31IniFileSections(
    IN OUT PWIN31IO_STATE State,
    IN PWSTR TempFileName,
    IN PWSTR FileName
    );

BOOL
SnapShotWin31IniFilesToRegistry(
    IN OUT PWIN31IO_STATE State
    )
{
    BOOL Result;
    PWSTR s, s1, FileName, ApplicationName, KeyName;
    ULONG n;
    PWSTR CurrentFileName;
    WCHAR Win31IniFileName[ MAX_PATH ];
    WCHAR TempIniFileName[ MAX_PATH ];
    HANDLE FindHandle;
    WIN32_FIND_DATAW FindFileData;
    HANDLE MigrationKey, Key;
    ULONG cchBufferUsed;

    MigrationKey = OpenCreateKey( State->Win31IOKey, L"IniFiles", !State->QueryOnly );
    if (State->QueryOnly) {
        if (MigrationKey != NULL) {
            NtClose( MigrationKey );
            return FALSE;
            }
        }
    else {
        if (MigrationKey == NULL) {
            return FALSE;
            }
        }

    Result = FALSE;
    wcscpy( State->FileNamePart, L"system.ini" );
    FindHandle = FindFirstFileW( State->WindowsPath, &FindFileData );
    if (FindHandle != INVALID_HANDLE_VALUE) {
        FindClose( FindHandle );
	if (FindFileData.nFileSizeLow > 1024) {
            Result = TRUE;
            }
        }

    if (!Result || State->QueryOnly) {
        if (MigrationKey == NULL) {
            MigrationKey = OpenCreateKey( State->Win31IOKey, L"IniFiles", TRUE );
            }

        if (MigrationKey != NULL) {
            NtClose( MigrationKey );
            }

        return Result;
        }

    cchBufferUsed = 0;
    if (State->UserRoot != NULL) {
        Result = QueryWin31IniFilesMappedToRegistry( WIN31_INIFILES_MAPPED_TO_USER,
                                                     State->WorkBuffer,
                                                     State->cchWorkBuffer,
                                                     &cchBufferUsed
                                                   );
        }
    else {
        Result = QueryWin31IniFilesMappedToRegistry( WIN31_INIFILES_MAPPED_TO_SYSTEM,
                                                     State->WorkBuffer,
                                                     State->cchWorkBuffer,
                                                     &cchBufferUsed
                                                   );
        }

    if (Result) {
        s = State->WorkBuffer;
        State->WorkBuffer += cchBufferUsed;
        State->cchWorkBuffer -= cchBufferUsed;
        CurrentFileName = NULL;
        TempIniFileName[ 0 ] = UNICODE_NULL;
        do {
            FileName = (PWSTR)((*s == UNICODE_NULL) ? NULL : s);
            while (*s++) {
                ;
                }
            ApplicationName = (PWSTR)((*s == UNICODE_NULL) ? NULL : s);
            while (*s++) {
                ;
                }
            KeyName = (PWSTR)((*s == UNICODE_NULL) ? NULL : s);
            while (*s++) {
                ;
                }

            if (FileName) {
                if (!CurrentFileName || _wcsicmp( FileName, CurrentFileName )) {
                    if (TempIniFileName[ 0 ] != UNICODE_NULL) {
                        WritePrivateProfileStringW( NULL, NULL, NULL, L"" );
                        if (!DeleteFileW( TempIniFileName )) {
                            KdPrint(( "WIN31IO: DeleteFile( %ws ) - failed (%u)\n",
                                      TempIniFileName,
                                      GetLastError()
                                   ));
                            }

                        TempIniFileName[ 0 ] = UNICODE_NULL;
                        }

                    CurrentFileName = NULL;
                    GetWindowsDirectoryW( Win31IniFileName, sizeof( Win31IniFileName ) / sizeof (WCHAR) );
                    wcscat( Win31IniFileName, L"\\" );
                    wcscat( Win31IniFileName, FileName );
                    wcscpy( TempIniFileName, Win31IniFileName );
                    _wcslwr( TempIniFileName );
                    s1 = wcsstr( TempIniFileName, L".ini" );
                    if (!s1) {
                        s1 = wcschr( TempIniFileName, UNICODE_NULL );
                        if (s1[-1] == L'.') {
                            s1--;
                            }
                        }
                    n = 0;
                    while (n < 1000) {
                        swprintf( s1, L".%03u", n++ );
                        if (CopyFileW( Win31IniFileName, TempIniFileName, TRUE )) {
                            if (State->StatusCallback != NULL) {
                                (State->StatusCallback)( FileName, State->CallbackParameter );
                                }
                            Key = OpenCreateKey( MigrationKey, FileName, TRUE );
                            if (Key != NULL) {
                                NtClose( Key );
                                }
                            CurrentFileName = FileName;
                            break;
                            }
                        else
                        if (GetLastError() != ERROR_FILE_EXISTS) {
                            if (GetLastError() != ERROR_FILE_NOT_FOUND) {
                                KdPrint(("WIN31IO: CopyFile( %ws, %ws ) failed - %u\n",
                                          Win31IniFileName,
                                          TempIniFileName,
                                          GetLastError()
                                       ));
                                }
                            break;
                            }
                        }

                    if (CurrentFileName == NULL) {
                        TempIniFileName[ 0 ] = UNICODE_NULL;
                        CurrentFileName = FileName;
                        }
                    }


                if (TempIniFileName[ 0 ] != UNICODE_NULL) {
                    if (ApplicationName) {
                        if (KeyName) {
                            if (SnapShotWin31IniFileKey( State,
                                                         TempIniFileName,
                                                         FileName,
                                                         ApplicationName,
                                                         KeyName
                                                       )
                               ) {
                                ReportWin31IOEvent( State,
                                                    EVENTLOG_INFORMATION_TYPE,
                                                    WIN31IO_EVENT_MIGRATE_INI_VARIABLE,
                                                    0,
                                                    NULL,
                                                    3,
                                                    KeyName,
                                                    ApplicationName,
                                                    FileName
                                                  );
                                }
                            else {
                                ReportWin31IOEvent( State,
                                                    EVENTLOG_WARNING_TYPE,
                                                    WIN31IO_EVENT_MIGRATE_INI_VARIABLE_FAILED,
                                                    0,
                                                    NULL,
                                                    3,
                                                    KeyName,
                                                    ApplicationName,
                                                    FileName
                                                  );
                                }

                            }
                        else {
                            if (SnapShotWin31IniFileSection( State,
                                                             TempIniFileName,
                                                             FileName,
                                                             ApplicationName
                                                           )
                               ) {
                                ReportWin31IOEvent( State,
                                                    EVENTLOG_INFORMATION_TYPE,
                                                    WIN31IO_EVENT_MIGRATE_INI_SECTION,
                                                    0,
                                                    NULL,
                                                    2,
                                                    ApplicationName,
                                                    FileName
                                                  );
                                }
                            else {
                                ReportWin31IOEvent( State,
                                                    EVENTLOG_WARNING_TYPE,
                                                    WIN31IO_EVENT_MIGRATE_INI_SECTION_FAILED,
                                                    0,
                                                    NULL,
                                                    2,
                                                    ApplicationName,
                                                    FileName
                                                  );
                                }

                            }
                        }
                    else {
                        if (SnapShotWin31IniFileSections( State,
                                                          TempIniFileName,
                                                          FileName
                                                        )
                            ) {
                            ReportWin31IOEvent( State,
                                                EVENTLOG_INFORMATION_TYPE,
                                                WIN31IO_EVENT_MIGRATE_INI_FILE,
                                                0,
                                                NULL,
                                                1,
                                                FileName
                                              );
                            }
                        else {
                            ReportWin31IOEvent( State,
                                                EVENTLOG_WARNING_TYPE,
                                                WIN31IO_EVENT_MIGRATE_INI_FILE_FAILED,
                                                0,
                                                NULL,
                                                1,
                                                FileName
                                              );
                            }
                        }
                    }
                }
            }
        while (*s != UNICODE_NULL);

        State->WorkBuffer -= cchBufferUsed;
        State->cchWorkBuffer += cchBufferUsed;
        }

    if (TempIniFileName[ 0 ] != UNICODE_NULL) {
        WritePrivateProfileStringW( NULL, NULL, NULL, L"" );
        if (!DeleteFileW( TempIniFileName )) {
            KdPrint(( "WIN31IO: DeleteFile( %ws ) - failed (%u)\n",
                      TempIniFileName,
                      GetLastError()
                   ));
            }
        }

    if (MigrationKey != NULL) {
        NtClose( MigrationKey );
        }
    return Result;
}



BOOL
SnapShotWin31IniFileKey(
    IN OUT PWIN31IO_STATE State,
    IN PWSTR TempFileName,
    IN PWSTR FileName,
    IN PWSTR ApplicationName,
    IN PWSTR KeyName
    )
{
    ULONG cch;

    cch = GetPrivateProfileStringW( ApplicationName,
                                    KeyName,
                                    NULL,
                                    State->WorkBuffer,
                                    State->cchWorkBuffer,
                                    TempFileName
                                  );
    if (cch != 0) {
        if (WritePrivateProfileStringW( ApplicationName,
                                        KeyName,
                                        State->WorkBuffer,
                                        FileName
                                      )
           ) {
            return TRUE;
            }
        else {
            KdPrint(( "WIN31IO: Copy to %ws [%ws].%ws == %ws (failed - %u)\n", FileName, ApplicationName, KeyName, State->WorkBuffer, GetLastError() ));
            return FALSE;
            }
        }
    else {
        return TRUE;
        }
}

BOOL
SnapShotWin31IniFileSection(
    IN OUT PWIN31IO_STATE State,
    IN PWSTR TempFileName,
    IN PWSTR FileName,
    IN PWSTR ApplicationName
    )
{
    BOOL Result;
    PWSTR KeyName;
    ULONG cch;

    Result = TRUE;
    cch = GetPrivateProfileStringW( ApplicationName,
                                    NULL,
                                    NULL,
                                    State->WorkBuffer,
                                    State->cchWorkBuffer,
                                    TempFileName
                                  );
    if (cch != 0) {
        KeyName = State->WorkBuffer;
        cch += 1;                   // Account for extra null
        State->WorkBuffer += cch;
        State->cchWorkBuffer -= cch;
        while (*KeyName != UNICODE_NULL) {
            Result &= SnapShotWin31IniFileKey( State,
                                               TempFileName,
                                               FileName,
                                               ApplicationName,
                                               KeyName
                                             );

            while (*KeyName++) {
                ;
                }
            }

        State->WorkBuffer -= cch;
        State->cchWorkBuffer += cch;
        }

    return Result;
}

BOOL
SnapShotWin31IniFileSections(
    IN OUT PWIN31IO_STATE State,
    IN PWSTR TempFileName,
    IN PWSTR FileName
    )
{
    BOOL Result;
    PWSTR ApplicationName;
    ULONG cch;

    Result = TRUE;
    cch = GetPrivateProfileStringW( NULL,
                                    NULL,
                                    NULL,
                                    State->WorkBuffer,
                                    State->cchWorkBuffer,
                                    TempFileName
                                  );
    if (cch != 0) {
        ApplicationName = State->WorkBuffer;
        cch += 1;                   // Account for extra null
        State->WorkBuffer += cch;
        State->cchWorkBuffer -= cch;
        while (*ApplicationName != UNICODE_NULL) {
            Result &= SnapShotWin31IniFileSection( State,
                                                   TempFileName,
                                                   FileName,
                                                   ApplicationName
                                                 );

            while (*ApplicationName++) {
                ;
                }
            }

        State->WorkBuffer -= cch;
        State->cchWorkBuffer += cch;
        }

    return Result;
}


BOOL
ConvertWindows31GroupsToRegistry(
    IN OUT PWIN31IO_STATE State,
    IN HANDLE MigrationKey,
    IN HANDLE CommonGroupsKey,
    IN HANDLE PersonalGroupsKey,
    IN PWSTR IniFileName,
    IN PWSTR GroupNames,
    IN ULONG nGroupNames
    );

BOOL
SnapShotWin31GroupsToRegistry(
    IN OUT PWIN31IO_STATE State
    )
{
    BOOL Result;
    PWSTR GroupNames, s;
    DWORD nGroupNames, cchGroupNames;
    HANDLE MigrationKey, PersonalGroupsKey, CommonGroupsKey;

    MigrationKey = OpenCreateKey( State->Win31IOKey, L"Groups", !State->QueryOnly );
    if (State->QueryOnly) {
        if (MigrationKey != NULL) {
            NtClose( MigrationKey );
            return FALSE;
            }
        }
    else {
        if (MigrationKey == NULL) {
            return FALSE;
            }
        }

    Result = FALSE;
    wcscpy( State->FileNamePart, L"progman.ini" );
    if (GetFileAttributesW( State->WindowsPath ) != 0xFFFFFFFF) {
        cchGroupNames = GetPrivateProfileStringW( L"Groups",
                                                  NULL,
                                                  L"",
                                                  State->WorkBuffer,
                                                  State->cchWorkBuffer,
                                                  State->WindowsPath
                                                );
        if (cchGroupNames != 0) {
            Result = TRUE;
            }
        }

    if (!Result || State->QueryOnly) {
        if (MigrationKey == NULL) {
            MigrationKey = OpenCreateKey( State->Win31IOKey, L"Groups", TRUE );
            }

        if (MigrationKey != NULL) {
            NtClose( MigrationKey );
            }

        return Result;
        }

    if (cchGroupNames) {
        PersonalGroupsKey = OpenCreateKey( State->UserRoot,
                                           L"UNICODE Program Groups",
                                           !State->QueryOnly
                                         );
        if (PersonalGroupsKey == NULL) {
            if (MigrationKey != NULL) {
                NtClose( MigrationKey );
                }

            return FALSE;
            }

        CommonGroupsKey = OpenCreateKey( NULL,
                                         L"\\Registry\\Machine\\Software\\Program Groups",
                                         FALSE
                                       );
        GroupNames = s = State->WorkBuffer;
        nGroupNames = 0;
        while (*s) {
            while (*s++) {
                ;
                }

            nGroupNames += 1;
            }
        State->WorkBuffer = s + 1;
        State->cchWorkBuffer -= cchGroupNames + 1;

        ConvertWindows31GroupsToRegistry( State,
                                          MigrationKey,
                                          CommonGroupsKey,
                                          PersonalGroupsKey,
                                          State->WindowsPath,
                                          GroupNames,
                                          nGroupNames
                                        );

        State->WorkBuffer = GroupNames,
        State->cchWorkBuffer += cchGroupNames + 1;

        NtClose( PersonalGroupsKey );
        if (CommonGroupsKey != NULL) {
            NtClose( CommonGroupsKey );
            }
        }

    if (MigrationKey != NULL) {
        NtClose( MigrationKey );
        }

    return TRUE;
}


BOOL
ConvertWindows31GroupsToRegistry(
    IN OUT PWIN31IO_STATE State,
    IN HANDLE MigrationKey,
    IN HANDLE CommonGroupsKey,
    IN HANDLE PersonalGroupsKey,
    IN PWSTR IniFileName,
    IN PWSTR GroupNames,
    IN ULONG nGroupNames
    )
{
    BOOL Result;
    NTSTATUS Status;
    PGROUP_DEF16 Group16;
    PGROUP_DEF Group32;
    UNICODE_STRING Group32Name;
    PWSTR Group16PathName;
    PWSTR Group16FileName;
    PWSTR s;
    ANSI_STRING AnsiString;
    ULONG NumberOfPersonalGroupNames;
    ULONG OldNumberOfPersonalGroupNames;
    HANDLE GroupNamesKey, SettingsKey, Key;

    NumberOfPersonalGroupNames = QueryNumberOfPersonalGroupNames( State->UserRoot,
                                                                  &GroupNamesKey,
                                                                  &SettingsKey
                                                                );
    OldNumberOfPersonalGroupNames = NumberOfPersonalGroupNames;

    Result = TRUE;
    while (*GroupNames) {
        //
        // Get the group file (.grp) name
        //
        if (GetPrivateProfileStringW( L"Groups",
                                      GroupNames,
                                      L"",
                                      State->WorkBuffer,
                                      State->cchWorkBuffer,
                                      IniFileName
                                    )
           ) {
            Group16PathName = State->WorkBuffer;
            Group16 = LoadGroup16( Group16PathName );
            if (Group16 != NULL) {
                Group16FileName = Group16PathName + wcslen( Group16PathName );
                while (Group16FileName > Group16PathName) {
                    if (Group16FileName[ -1 ] == OBJ_NAME_PATH_SEPARATOR) {
                        break;
                        }

                    Group16FileName -= 1;
                    }

                //
                // Get the group name.
                //
                if (Group16->pName == 0) {
                    RtlInitUnicodeString( &Group32Name, Group16FileName );
                    Status = STATUS_SUCCESS;
                    }
                else {
                    RtlInitAnsiString( &AnsiString, (PSZ)PTR( Group16, Group16->pName ) );
                    Status = RtlAnsiStringToUnicodeString( &Group32Name, &AnsiString, TRUE );
                    s = Group32Name.Buffer;
                    while (*s) {
                        if (*s == OBJ_NAME_PATH_SEPARATOR) {
                            *s = L'/';
                            }
                        s += 1;
                        }
                    }

                if (NT_SUCCESS( Status )) {
                    if (DoesExistGroup( PersonalGroupsKey, Group32Name.Buffer )) {
                        ReportWin31IOEvent( State,
                                            EVENTLOG_INFORMATION_TYPE,
                                            WIN31IO_EVENT_MIGRATE_GROUP_EXISTS,
                                            0,
                                            NULL,
                                            2,
                                            Group16PathName,
                                            Group32Name.Buffer
                                          );
                        }
                    else
                    if (DoesExistGroup( CommonGroupsKey, Group32Name.Buffer )) {
                        ReportWin31IOEvent( State,
                                            EVENTLOG_INFORMATION_TYPE,
                                            WIN31IO_EVENT_MIGRATE_GROUP_EXISTS,
                                            0,
                                            NULL,
                                            2,
                                            Group16PathName,
                                            Group32Name.Buffer
                                          );
                        }
                    else {
                        // DumpGroup16( State->WorkBuffer, Group16 );
                        Group32 = CreateGroupFromGroup16( AnsiString.Buffer, Group16 );
                        if (Group32 != NULL) {
                            if (Group32 == (PGROUP_DEF)-1) {
                                ReportWin31IOEvent( State,
                                                    EVENTLOG_WARNING_TYPE,
                                                    WIN31IO_EVENT_MIGRATE_GROUP_FAILED4,
                                                    0,
                                                    NULL,
                                                    1,
                                                    Group32Name.Buffer
                                                  );
                                Group32 = NULL;
                                }
                            else {
                                // DumpGroup( Group32Name.Buffer, Group32 );
                                if (!SaveGroup( PersonalGroupsKey,
                                                Group32Name.Buffer,
                                                Group32
                                              )
                                   ) {
                                    ReportWin31IOEvent( State,
                                                        EVENTLOG_WARNING_TYPE,
                                                        WIN31IO_EVENT_MIGRATE_GROUP_FAILED,
                                                        0,
                                                        NULL,
                                                        1,
                                                        Group32Name.Buffer
                                                      );
                                    }
                                else
                                if (!NewPersonalGroupName( GroupNamesKey,
                                                           Group32Name.Buffer,
                                                           NumberOfPersonalGroupNames+1
                                                         )
                                   ) {
                                    DeleteGroup( PersonalGroupsKey, Group32Name.Buffer );
                                    ReportWin31IOEvent( State,
                                                        EVENTLOG_WARNING_TYPE,
                                                        WIN31IO_EVENT_MIGRATE_GROUP_FAILED,
                                                        0,
                                                        NULL,
                                                        1,
                                                        Group32Name.Buffer
                                                      );
                                    }
                                else {
                                    if (State->StatusCallback != NULL) {
                                        (State->StatusCallback)( Group16FileName, State->CallbackParameter );
                                        }
                                    ReportWin31IOEvent( State,
                                                        EVENTLOG_INFORMATION_TYPE,
                                                        WIN31IO_EVENT_MIGRATE_GROUP,
                                                        0,
                                                        NULL,
                                                        1,
                                                        Group32Name.Buffer
                                                      );
                                    NumberOfPersonalGroupNames += 1;
                                    Key = OpenCreateKey( MigrationKey, Group16FileName, TRUE );
                                    if (Key != NULL) {
                                        NtClose( Key );
                                        }
                                    else {
                                        KdPrint(("WIN31IO: (3)Unable to create sub migration key for %ws (%u)\n", State->WorkBuffer, GetLastError() ));
                                        }
                                    }

                                UnloadGroup( Group32 );
                                }
                            }
                        else {
                            WCHAR ErrorCode[ 32 ];

                            _snwprintf( ErrorCode,
                                         sizeof( ErrorCode ) / sizeof( WCHAR ),
                                         L"%u",
                                         GetLastError()
                                       );
                            ReportWin31IOEvent( State,
                                                EVENTLOG_WARNING_TYPE,
                                                WIN31IO_EVENT_MIGRATE_GROUP_FAILED1,
                                                0,
                                                NULL,
                                                2,
                                                State->WorkBuffer,
                                                ErrorCode
                                              );
                            }
                        }

                    RtlFreeUnicodeString( &Group32Name );
                    }

                UnloadGroup16( Group16 );
                }
            else {
                WCHAR ErrorCode[ 32 ];

                _snwprintf( ErrorCode,
                             sizeof( ErrorCode ) / sizeof( WCHAR ),
                             L"%u",
                             GetLastError()
                           );
                ReportWin31IOEvent( State,
                                    EVENTLOG_WARNING_TYPE,
                                    WIN31IO_EVENT_MIGRATE_GROUP_FAILED1,
                                    0,
                                    NULL,
                                    2,
                                    State->WorkBuffer,
                                    ErrorCode
                                  );
                }
            }
        else {
            ReportWin31IOEvent( State,
                                EVENTLOG_WARNING_TYPE,
                                WIN31IO_EVENT_MIGRATE_GROUP_FAILED2,
                                0,
                                NULL,
                                2,
                                IniFileName,
                                GroupNames
                              );
            }

        while (*GroupNames++) {
            ;
            }
        }


    if (OldNumberOfPersonalGroupNames != NumberOfPersonalGroupNames) {
        UNICODE_STRING ValueName;
        ULONG ValueData = TRUE;

        RtlInitUnicodeString( &ValueName, L"InitialArrange" );
        Status = NtSetValueKey( SettingsKey,
                                &ValueName,
                                0,
                                REG_DWORD,
                                &ValueData,
                                sizeof( ValueData )
                              );
#if DBG
        if (!NT_SUCCESS( Status )) {
            KdPrint(( "WIN31IO: Unable to set value of %wZ - Status == %x\n", &ValueName, Status ));
            }
#endif
        }

    NtClose( SettingsKey );
    NtClose( GroupNamesKey );
    return Result;
}


BOOL
SnapShotWin31RegDatToRegistry(
    IN OUT PWIN31IO_STATE State
    )
{
    BOOL Result;
    HANDLE MigrationKey;
    PREG_HEADER16 RegDat16;

    MigrationKey = OpenCreateKey( State->Win31IOKey, L"REG.DAT", !State->QueryOnly );
    if (State->QueryOnly) {
        if (MigrationKey != NULL) {
            NtClose( MigrationKey );
            return FALSE;
            }
        }
    else {
        if (MigrationKey == NULL) {
            return FALSE;
            }
        }

    Result = FALSE;
    wcscpy( State->FileNamePart, L"reg.dat" );
    if (GetFileAttributesW( State->WindowsPath ) != 0xFFFFFFFF) {
        Result = TRUE;
        }

    if (!Result || State->QueryOnly) {
        if (MigrationKey == NULL) {
            MigrationKey = OpenCreateKey( State->Win31IOKey, L"REG.DAT", TRUE );
            }

        if (MigrationKey != NULL) {
            NtClose( MigrationKey );
            }

        return Result;
        }

    RegDat16 = LoadRegistry16( State->WindowsPath );
    if (RegDat16 != NULL) {
        if (State->StatusCallback != NULL) {
            (State->StatusCallback)( State->WindowsPath, State->CallbackParameter );
            }

        if (CreateRegistryClassesFromRegistry16( State->SoftwareRoot, RegDat16 )) {
            ReportWin31IOEvent( State,
                                EVENTLOG_INFORMATION_TYPE,
                                WIN31IO_EVENT_MIGRATE_REGDAT,
                                0,
                                NULL,
                                1,
                                State->WindowsPath
                              );
            }
        else {
            ReportWin31IOEvent( State,
                                EVENTLOG_WARNING_TYPE,
                                WIN31IO_EVENT_MIGRATE_REGDAT_FAILED,
                                0,
                                NULL,
                                1,
                                State->WindowsPath
                              );
            }
        UnloadRegistry16( RegDat16 );
        }

    if (MigrationKey != NULL) {
        NtClose( MigrationKey );
        }

    return TRUE;
}


#define SERVICE_TO_WAIT_FOR "EventLog"
#define MAX_TICKS_WAIT   90000   // ms

BOOL
WaitForEventLogToStart( VOID )
{
    BOOL bStarted = FALSE;
    DWORD StartTickCount;
    DWORD dwOldCheckPoint = (DWORD)-1;
    SC_HANDLE hScManager = NULL;
    SC_HANDLE hService = NULL;
    SERVICE_STATUS ServiceStatus;

    if ((hScManager = OpenSCManager( NULL,
                                     NULL,
                                     SC_MANAGER_CONNECT
                                   )
        ) == (SC_HANDLE) NULL
       ) {
        KdPrint(("WIN31IO: IsNetworkStarted: OpenSCManager failed, error = %d\n", GetLastError()));
        goto Exit;
    }

    //
    // OpenService
    //

    if ((hService = OpenService( hScManager,
                                 SERVICE_TO_WAIT_FOR,
                                 SERVICE_QUERY_STATUS
                               )
        ) == (SC_HANDLE) NULL
       ) {
        KdPrint(("WIN31IO: IsNetworkStarted: OpenService failed, error = %d\n", GetLastError()));
        goto Exit;
        }

    //
    // Loop until the service starts or we think it never will start
    // or we've exceeded our maximum time delay.
    //

    StartTickCount = GetTickCount();
    while (!bStarted) {

        if ((GetTickCount() - StartTickCount) > MAX_TICKS_WAIT) {
            KdPrint(("WIN31IO: Max wait exceeded waiting for service <%s> to start\n", SERVICE_TO_WAIT_FOR));
            break;
            }

        if (!QueryServiceStatus( hService, &ServiceStatus )) {
            KdPrint(("WIN31IO: IsNetworkStarted: QueryServiceStatus failed, error = %d\n", GetLastError()));
            break;
            }

        if (ServiceStatus.dwCurrentState == SERVICE_STOPPED) {
            KdPrint(("WIN31IO: Service STOPPED"));

            if (ServiceStatus.dwWin32ExitCode == ERROR_SERVICE_NEVER_STARTED) {
                KdPrint(("WIN31IO: Waiting for 3 secs"));
                Sleep(3000);
                }
            else {
                KdPrint(("WIN31IO: Service exit code = %d, returning failure\n", ServiceStatus.dwWin32ExitCode));
                break;
                }
            }
        else
        if ( (ServiceStatus.dwCurrentState == SERVICE_RUNNING) ||
             (ServiceStatus.dwCurrentState == SERVICE_CONTINUE_PENDING) ||
             (ServiceStatus.dwCurrentState == SERVICE_PAUSE_PENDING) ||
             (ServiceStatus.dwCurrentState == SERVICE_PAUSED)
           ) {
            bStarted = TRUE;
            }
        else
        if (ServiceStatus.dwCurrentState == SERVICE_START_PENDING) {

            //
            // Wait to give a chance for the network to start.
            //

            Sleep( ServiceStatus.dwWaitHint );
            }
        else {
            KdPrint(( "WIN31IO: Service in unknown state : %d\n", ServiceStatus.dwCurrentState ));
            }
        }

Exit:
    if (hScManager != NULL) {
        CloseServiceHandle( hScManager );
    }
    if (hService != NULL) {
        CloseServiceHandle( hService );
    }

    return( bStarted );
}


HANDLE
OpenCreateKey(
    IN HANDLE Root,
    IN PWSTR Path,
    IN BOOL WriteAccess
    )
{
    NTSTATUS Status;
    HANDLE Key;
    UNICODE_STRING KeyName;
    OBJECT_ATTRIBUTES ObjectAttributes;
    ULONG CreateDisposition;

    RtlInitUnicodeString( &KeyName, Path );
    InitializeObjectAttributes( &ObjectAttributes,
                                &KeyName,
                                OBJ_CASE_INSENSITIVE,
                                Root,
                                NULL
                              );
    Status = NtOpenKey( &Key,
                        WriteAccess ? (STANDARD_RIGHTS_WRITE |
                                          KEY_QUERY_VALUE |
                                          KEY_ENUMERATE_SUB_KEYS |
                                          KEY_SET_VALUE |
                                          KEY_CREATE_SUB_KEY
                                      )
                                    : GENERIC_READ,
                        &ObjectAttributes
                      );
    if (NT_SUCCESS( Status )) {
        return Key;
        }
    else
    if (!WriteAccess) {
        BaseSetLastNTError( Status );
        return NULL;
        }

    Status = NtCreateKey( &Key,
                          STANDARD_RIGHTS_WRITE |
                            KEY_QUERY_VALUE |
                            KEY_ENUMERATE_SUB_KEYS |
                            KEY_SET_VALUE |
                            KEY_CREATE_SUB_KEY,
                          &ObjectAttributes,
                          0,
                          NULL,
                          0,
                          &CreateDisposition
                        );
    if (!NT_SUCCESS( Status )) {
        BaseSetLastNTError( Status );
        return NULL;
        }
    else {
        return Key;
        }
}
