/*++

Copyright (c) 1991  Microsoft Corporation

Module Name:

    chutil.h

Abstract:

    Definitions of the internals of the changelog.

    Currently only included sparingly.

Author:

    Cliff Van Dyke (cliffv) 07-May-1992

Environment:

    User mode only.
    Contains NT-specific code.
    Requires ANSI C extensions: slash-slash comments, long external names.

Revision History:

    02-Jan-1992 (madana)
        added support for builtin/multidomain replication.

--*/

#if ( _MSC_VER >= 800 )
#pragma warning ( 3 : 4100 ) // enable "Unreferenced formal parameter"
#pragma warning ( 3 : 4219 ) // enable "trailing ',' used for variable argument list"
#endif

//
// chutil.c will #include this file with CHUTIL_ALLOCATE defined.
// That will cause each of these variables to be allocated.
//
#ifdef CHUTIL_ALLOCATE
#define EXTERN
#else
#define EXTERN extern
#endif


/////////////////////////////////////////////////////////////////////////////
//
// Structures and variables describing the Change Log.
//
/////////////////////////////////////////////////////////////////////////////

//
// All of the following data is private to changelg.c and nltest1.c
//

//
// change log file name
//

#define CHANGELOG_FILE_PREFIX         L"\\NETLOGON"

#define CHANGELOG_FILE_POSTFIX_LENGTH 4         // Length of all the following postfixes
#define CHANGELOG_FILE_POSTFIX        L".CHG"
#define BACKUP_CHANGELOG_FILE_POSTFIX L".BKP"
#define REDO_FILE_POSTFIX             L".RDO"

//
// Initial size and size increment of a redo log.
//
#define REDO_LOG_INITIAL_SIZE 1024
#define REDO_LOG_INCREMENT    1024

//
// Signature at front of changelog file
//

#define CHANGELOG_SIG_V3 "NT CHANGELOG 3"
#define CHANGELOG_SIG    "NT CHANGELOG 4"

//
// Change log block state
//

typedef enum _CHANGELOG_BLOCK_STATE {
    BlockFree = 1,
    BlockUsed,
    BlockHole
} CHANGELOG_BLOCK_STATE, *PCHANGELOG_BLOCK_STATE;

//
// change log memory block header
//

typedef struct _CHANGELOG_BLOCK_HEADER {
    DWORD BlockSize;
    CHANGELOG_BLOCK_STATE BlockState;
} CHANGELOG_BLOCK_HEADER, *PCHANGELOG_BLOCK_HEADER;

typedef struct _CHANGELOG_BLOCK_TRAILER {
    DWORD BlockSize;
} CHANGELOG_BLOCK_TRAILER, *PCHANGELOG_BLOCK_TRAILER;

//
// Macro to find a trailer (given a header)
//

#define ChangeLogBlockTrailer( _Header ) ( (PCHANGELOG_BLOCK_TRAILER)(\
    ((LPBYTE)(_Header)) + \
    (_Header)->BlockSize - \
    sizeof(CHANGELOG_BLOCK_TRAILER) ))

//
// Macro to find if the change log describe be a particular
// changelog descriptor is empty.
//
//

#define ChangeLogIsEmpty( _Desc ) \
( \
    (_Desc)->FirstBlock == NULL || \
    ((_Desc)->FirstBlock->BlockState == BlockFree && \
     (_Desc)->FirstBlock->BlockSize >= \
        (DWORD)((_Desc)->BufferEnd - (LPBYTE)(_Desc)->FirstBlock) ) \
)

//
// Macro to initialize a changelog desriptor.
//

#define InitChangeLogDesc( _Desc ) \
    RtlZeroMemory( (_Desc), sizeof( *(_Desc) ) ); \
    (_Desc)->FileHandle = INVALID_HANDLE_VALUE;

//
// Macro to determine if the serial number on the change log entry matches
// the serial number specified.
//
// The serial numbers match if there is an exact match or
// if the changelog entry contains the serial number at the instant of promotion and the
// requested serial number is the corresponding pre-promotion value.
//

#define IsSerialNumberEqual( _ChangeLogDesc, _ChangeLogEntry, _SerialNumber ) \
( \
    (_ChangeLogEntry)->SerialNumber.QuadPart == (_SerialNumber)->QuadPart || \
   (((_ChangeLogEntry)->Flags & CHANGELOG_PDC_PROMOTION) && \
    (_ChangeLogEntry)->SerialNumber.QuadPart == \
        (_SerialNumber)->QuadPart + NlGlobalChangeLogPromotionIncrement.QuadPart ) \
)


//
// variables describing the change log
//

typedef struct _CHANGELOG_DESCRIPTOR {

    //
    // Start and end of the allocated block.
    //
    LPBYTE Buffer;      // Cache of the changelog contents
    ULONG BufferSize;   // Size (in bytes) of the buffer
    LPBYTE BufferEnd;   // Address of first byte beyond the end of the buffer

    //
    // Offset of the first and last dirty bytes
    //

    ULONG FirstDirtyByte;
    ULONG LastDirtyByte;

    //
    // Address of the first physical block in the change log
    //
    PCHANGELOG_BLOCK_HEADER FirstBlock; // where delta buffer starts

    //
    // Description of the circular list of change log entries.
    //
    PCHANGELOG_BLOCK_HEADER Head;       // start reading logs from here
    PCHANGELOG_BLOCK_HEADER Tail;       // where next log is written

    //
    // Serial Number of each database.
    //
    // Access is serialized via NlGlobalChangeLogCritSect
    //

    LARGE_INTEGER SerialNumber[NUM_DBS];

    //
    // Number of change log entries in the log for the specified database
    //

    DWORD EntryCount[NUM_DBS];

    //
    // Handle to file acting as backing store for the buffer.
    //

    HANDLE FileHandle;                  // handle for change log file

    //
    // Version 3: True to indicate this is a version 3 buffer.
    //

    BOOLEAN Version3;

    //
    // True if this is a re-do log and not a change log
    //

    BOOLEAN RedoLog;

} CHANGELOG_DESCRIPTOR, *PCHANGELOG_DESCRIPTOR;

//
// The change log is a log of ALL changes made to the SAM/LSA databases.  The
// change log is maintained in serial number order.
//
// The redo log is a log of changes that need to be re-applied to a BDC.  The
// redo log is not maintained in any particular order.
//
EXTERN CHANGELOG_DESCRIPTOR NlGlobalChangeLogDesc;
EXTERN CHANGELOG_DESCRIPTOR NlGlobalRedoLogDesc;
EXTERN WCHAR NlGlobalChangeLogFilePrefix[PATHLEN+1]; // Changelog file name. (w/o postfix)


//
// Tables of related delta types
//

//
// Table of delete delta types.
//  Index into the table with a delta type,
//  the entry is the delta type that is used to delete the object.
//
// There are some objects that can't be deleted.  In that case, this table
// contains a delta type that uniquely identifies the object.  That allows
// this table to be used to see if two deltas describe the same object type.
//

EXTERN const NETLOGON_DELTA_TYPE NlGlobalDeleteDeltaType[]
#ifdef CHUTIL_ALLOCATE
= {
    AddOrChangeDomain,     //   0 is an invalid delta type
    AddOrChangeDomain,     //   AddOrChangeDomain,
    DeleteGroup,           //   AddOrChangeGroup,
    DeleteGroup,           //   DeleteGroup,
    DeleteGroup,           //   RenameGroup,
    DeleteUser,            //   AddOrChangeUser,
    DeleteUser,            //   DeleteUser,
    DeleteUser,            //   RenameUser,
    DeleteGroup,           //   ChangeGroupMembership,
    DeleteAlias,           //   AddOrChangeAlias,
    DeleteAlias,           //   DeleteAlias,
    DeleteAlias,           //   RenameAlias,
    DeleteAlias,           //   ChangeAliasMembership,
    AddOrChangeLsaPolicy,  //   AddOrChangeLsaPolicy,
    DeleteLsaTDomain,      //   AddOrChangeLsaTDomain,
    DeleteLsaTDomain,      //   DeleteLsaTDomain,
    DeleteLsaAccount,      //   AddOrChangeLsaAccount,
    DeleteLsaAccount,      //   DeleteLsaAccount,
    DeleteLsaSecret,       //   AddOrChangeLsaSecret,
    DeleteLsaSecret,       //   DeleteLsaSecret,
    DeleteGroup,           //   DeleteGroupByName,
    DeleteUser,            //   DeleteUserByName,
    SerialNumberSkip,      //   SerialNumberSkip,
    DummyChangeLogEntry    //   DummyChangeLogEntry
}
#endif // CHUTIL_ALLOCATE
;

#define MAX_DELETE_DELTA \
    (sizeof(NlGlobalDeleteDeltaType)/sizeof(NlGlobalDeleteDeltaType[0]))


//
// Table of add delta types.
//  Index into the table with a delta type,
//  the entry is the delta type that is used to add the object.
//
// There are some objects that can't be added.  In that case, this table
// contains a delta type that uniquely identifies the object.  That allows
// this table to be used to see if two deltas describe the same object type.
//
// In the table, Groups and Aliases are represented as renames.  This causes
// NlPackSingleDelta to return both the group attributes and the group
// membership.
//

EXTERN const NETLOGON_DELTA_TYPE NlGlobalAddDeltaType[]
#ifdef CHUTIL_ALLOCATE
= {
    AddOrChangeDomain,     //   0 is an invalid delta type
    AddOrChangeDomain,     //   AddOrChangeDomain,
    RenameGroup,           //   AddOrChangeGroup,
    RenameGroup,           //   DeleteGroup,
    RenameGroup,           //   RenameGroup,
    AddOrChangeUser,       //   AddOrChangeUser,
    AddOrChangeUser,       //   DeleteUser,
    AddOrChangeUser,       //   RenameUser,
    RenameGroup,           //   ChangeGroupMembership,
    RenameAlias,           //   AddOrChangeAlias,
    RenameAlias,           //   DeleteAlias,
    RenameAlias,           //   RenameAlias,
    RenameAlias,           //   ChangeAliasMembership,
    AddOrChangeLsaPolicy,  //   AddOrChangeLsaPolicy,
    AddOrChangeLsaTDomain, //   AddOrChangeLsaTDomain,
    AddOrChangeLsaTDomain, //   DeleteLsaTDomain,
    AddOrChangeLsaAccount, //   AddOrChangeLsaAccount,
    AddOrChangeLsaAccount, //   DeleteLsaAccount,
    AddOrChangeLsaSecret,  //   AddOrChangeLsaSecret,
    AddOrChangeLsaSecret,  //   DeleteLsaSecret,
    RenameGroup,           //   DeleteGroupByName,
    AddOrChangeUser,       //   DeleteUserByName,
    SerialNumberSkip,      //   SerialNumberSkip,
    DummyChangeLogEntry    //   DummyChangeLogEntry
}
#endif // CHUTIL_ALLOCATE
;

#define MAX_ADD_DELTA DummyChangeLogEntry



//
// Table of Status Codes indicating the object doesn't exist.
//  Index into the table with a delta type.
//
// Map to STATUS_SUCCESS for the invalid cases to explicitly avoid other error
// codes.

EXTERN const NTSTATUS NlGlobalObjectNotFoundStatus[]
#ifdef CHUTIL_ALLOCATE
= {
    STATUS_SUCCESS,               //   0 is an invalid delta type
    STATUS_NO_SUCH_DOMAIN,        //   AddOrChangeDomain,
    STATUS_NO_SUCH_GROUP,         //   AddOrChangeGroup,
    STATUS_NO_SUCH_GROUP,         //   DeleteGroup,
    STATUS_NO_SUCH_GROUP,         //   RenameGroup,
    STATUS_NO_SUCH_USER,          //   AddOrChangeUser,
    STATUS_NO_SUCH_USER,          //   DeleteUser,
    STATUS_NO_SUCH_USER,          //   RenameUser,
    STATUS_NO_SUCH_GROUP,         //   ChangeGroupMembership,
    STATUS_NO_SUCH_ALIAS,         //   AddOrChangeAlias,
    STATUS_NO_SUCH_ALIAS,         //   DeleteAlias,
    STATUS_NO_SUCH_ALIAS,         //   RenameAlias,
    STATUS_NO_SUCH_ALIAS,         //   ChangeAliasMembership,
    STATUS_SUCCESS,               //   AddOrChangeLsaPolicy,
    STATUS_OBJECT_NAME_NOT_FOUND, //   AddOrChangeLsaTDomain,
    STATUS_OBJECT_NAME_NOT_FOUND, //   DeleteLsaTDomain,
    STATUS_OBJECT_NAME_NOT_FOUND, //   AddOrChangeLsaAccount,
    STATUS_OBJECT_NAME_NOT_FOUND, //   DeleteLsaAccount,
    STATUS_OBJECT_NAME_NOT_FOUND, //   AddOrChangeLsaSecret,
    STATUS_OBJECT_NAME_NOT_FOUND, //   DeleteLsaSecret,
    STATUS_NO_SUCH_GROUP,         //   DeleteGroupByName,
    STATUS_NO_SUCH_USER,          //   DeleteUserByName,
    STATUS_SUCCESS,               //   SerialNumberSkip,
    STATUS_SUCCESS                //   DummyChangeLogEntry
}
#endif // CHUTIL_ALLOCATE
;

#define MAX_OBJECT_NOT_FOUND_STATUS DummyChangeLogEntry

#define IsObjectNotFoundStatus( _DeltaType, _NtStatus ) \
    (((ULONG)(_DeltaType) > MAX_OBJECT_NOT_FOUND_STATUS ) ? \
    FALSE : \
    (NlGlobalObjectNotFoundStatus[ (_DeltaType) ] == (_NtStatus)) )

//
// chutil.c
//

NTSTATUS
NlCreateChangeLogFile(
    IN PCHANGELOG_DESCRIPTOR ChangeLogDesc
    );

NTSTATUS
NlFlushChangeLog(
    IN PCHANGELOG_DESCRIPTOR ChangeLogDesc
    );

PCHANGELOG_ENTRY
NlMoveToNextChangeLogEntry(
    IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
    IN PCHANGELOG_ENTRY ChangeLogEntry
    );

VOID
PrintChangeLogEntry(
    IN PCHANGELOG_ENTRY ChangeLogEntry
    );

NTSTATUS
NlResetChangeLog(
    IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
    IN DWORD NewChangeLogSize
    );

NTSTATUS
NlOpenChangeLogFile(
    IN LPWSTR ChangeLogFileName,
    OUT PCHANGELOG_DESCRIPTOR ChangeLogDesc,
    IN BOOLEAN ReadOnly
    );

VOID
NlCloseChangeLogFile(
    IN PCHANGELOG_DESCRIPTOR ChangeLogDesc
);

NTSTATUS
NlResizeChangeLogFile(
    IN OUT PCHANGELOG_DESCRIPTOR ChangeLogDesc,
    IN DWORD NewChangeLogSize
);

NTSTATUS
NlWriteChangeLogEntry(
    IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
    IN PCHANGELOG_ENTRY ChangeLogEntry,
    IN PSID ObjectSid,
    IN PUNICODE_STRING ObjectName,
    IN BOOLEAN FlushIt
    );

NTSTATUS
NlWriteDeltaToChangeLog(
    IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
    IN PNETLOGON_DELTA_ENUM Delta,
    IN ULONG DBIndex,
    IN OUT PLARGE_INTEGER SerialNumber
    );

PCHANGELOG_ENTRY
NlGetNextDownlevelChangeLogEntry(
    ULONG DownlevelSerialNumber
    );

PCHANGELOG_ENTRY
NlFindPromotionChangeLogEntry(
    IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
    IN LARGE_INTEGER SerialNumber,
    IN DWORD DBIndex
    );

PCHANGELOG_ENTRY
NlGetNextChangeLogEntry(
    IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
    IN LARGE_INTEGER SerialNumber,
    IN DWORD DBIndex,
    OUT LPDWORD ChangeLogEntrySize OPTIONAL
    );

PCHANGELOG_ENTRY
NlGetNextUniqueChangeLogEntry(
    IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
    IN LARGE_INTEGER SerialNumber,
    IN DWORD DBIndex,
    OUT LPDWORD ChangeLogEntrySize OPTIONAL
    );

BOOL
NlRecoverChangeLog(
    PCHANGELOG_ENTRY ChangeLogEntry
    );

VOID
NlDeleteChangeLogEntry(
    IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
    IN DWORD DBIndex,
    IN LARGE_INTEGER SerialNumber
    );

BOOLEAN
NlFixChangeLog(
    IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
    IN DWORD DBIndex,
    IN LARGE_INTEGER SerialNumber,
    IN BOOLEAN CopyEntriesToRedoLog
    );

BOOL
NlValidateChangeLogEntry(
    IN PCHANGELOG_ENTRY ChangeLogEntry,
    IN DWORD ChangeLogEntrySize
    );

#undef EXTERN