/*++ BUILD Version: 0000    // Increment this if a change has global effects

Copyright (c) 1989  Microsoft Corporation

Module Name:

    LfsProcs.h

Abstract:

    This module defines all of the globally used procedures in the Log
    File Service.

Author:

    Brian Andrew    [BrianAn]   20-June-1991

Revision History:

--*/

#ifndef _LFSPROCS_
#define _LFSPROCS_

#include <ntifs.h>
#include <string.h>
#include <lfs.h>

#include "nodetype.h"
#include "LfsDisk.h"
#include "LfsStruc.h"
#include "LfsData.h"

//
//  Tag all of our allocations if tagging is turned on
//

#undef FsRtlAllocatePool
#undef FsRtlAllocatePoolWithQuota

#define FsRtlAllocatePool(a,b) FsRtlAllocatePoolWithTag(a,b,' sfL')
#define FsRtlAllocatePoolWithQuota(a,b) FsRtlAllocatePoolWithQuotaTag(a,b,' sfL')

#define LfsAllocatePoolNoRaise(a,b)         ExAllocatePoolWithTag((a),(b),MODULE_POOL_TAG)
#define LfsAllocatePool(a,b)                ExAllocatePoolWithTag(((a) | POOL_RAISE_IF_ALLOCATION_FAILURE),(b),MODULE_POOL_TAG)
#define LfsFreePool(pv)                     ExFreePool(pv)

#ifndef INLINE
// definition of inline
#define INLINE __inline
#endif


//
//  The following routines provide an interface with the cache package.
//  They are contained in 'CacheSup.c'.
//

NTSTATUS
LfsPinOrMapData (
    IN PLFCB Lfcb,
    IN LONGLONG FileOffset,
    IN ULONG Length,
    IN BOOLEAN PinData,
    IN BOOLEAN AllowErrors,
    IN BOOLEAN IgnoreUsaErrors,
    OUT PBOOLEAN UsaError,
    OUT PVOID *Buffer,
    OUT PBCB *Bcb
    );

//
//  VOID
//  LfsPreparePinWriteData (
//      IN PLFCB Lfcb,
//      IN LONGLONG FileOffset,
//      IN ULONG Length,
//      IN LOGICAL ReadFromDisk,
//      OUT PVOID *Buffer,
//      OUT PBCB *Bcb
//      );
//

#ifdef LFS_CLUSTER_CHECK
#define LfsPreparePinWriteData(L,FO,LEN,R,BUF,B) {          \
    LONGLONG _LocalFileOffset = (FO);                       \
    CcPinRead( (L)->FileObject,                             \
               (PLARGE_INTEGER)&_LocalFileOffset,           \
               (LEN),                                       \
               TRUE,                                        \
               (B),                                         \
               (BUF) );                                     \
}
#else
#define LfsPreparePinWriteData(L,FO,LEN,R,BUF,B) {              \
    LONGLONG _LocalFileOffset = (FO);                           \
    if (R) {                                                    \
        CcPinRead( (L)->FileObject,                             \
                   (PLARGE_INTEGER)&_LocalFileOffset,           \
                   (LEN),                                       \
                   TRUE,                                        \
                   (B),                                         \
                   (BUF) );                                     \
    } else {                                                    \
        CcPreparePinWrite( (L)->FileObject,                     \
                           (PLARGE_INTEGER)&_LocalFileOffset,   \
                           (LEN),                               \
                           FALSE,                               \
                           TRUE,                                \
                           (B),                                 \
                           (BUF) );                             \
    }                                                           \
}
#endif

VOID
LfsPinOrMapLogRecordHeader (
    IN PLFCB Lfcb,
    IN LSN Lsn,
    IN BOOLEAN PinData,
    IN BOOLEAN IgnoreUsaErrors,
    OUT PBOOLEAN UsaError,
    OUT PLFS_RECORD_HEADER *RecordHeader,
    OUT PBCB *Bcb
    );

VOID
LfsCopyReadLogRecord (
    IN PLFCB Lfcb,
    IN PLFS_RECORD_HEADER RecordHeader,
    OUT PVOID Buffer
    );

VOID
LfsFlushLfcb (
    IN PLFCB Lfcb,
    IN PLBCB Lbcb
    );

BOOLEAN
LfsReadRestart (
    IN PLFCB Lfcb,
    IN LONGLONG FileSize,
    IN BOOLEAN FirstRestart,
    OUT PLONGLONG RestartPageOffset,
    OUT PLFS_RESTART_PAGE_HEADER *RestartPage,
    OUT PBCB *RestartPageBcb,
    OUT PBOOLEAN ChkdskWasRun,
    OUT PBOOLEAN ValidPage,
    OUT PBOOLEAN UninitializedFile,
    OUT PBOOLEAN LogPacked,
    OUT PLSN LastLsn
    );


//
//  The following routines manipulate buffer control blocks.  They are
//  contained in 'LbcbSup.c'
//

VOID
LfsFlushLbcb (
    IN PLFCB Lfcb,
    IN PLBCB Lbcb
    );

VOID
LfsFlushToLsnPriv (
    IN PLFCB Lfcb,
    IN LSN Lsn
    );

PLBCB
LfsGetLbcb (
    IN PLFCB Lfcb
    );


//
//  The following routines are in LfsData.c
//

LONG
LfsExceptionFilter (
    IN PEXCEPTION_POINTERS ExceptionPointer
    );


//
//  Log page support routines.  The following routines manipulate and
//  modify log pages.  They are contained in 'LogPgSup.c'
//

//
//  VOID
//  LfsTruncateOffsetToLogPage (
//      IN PLFCB Lfcb,
//      IN LONGLONG LargeInt,
//      OUT PLONGLONG Result
//      );
//
//  ULONG
//  LfsLogPageOffset (
//      IN PLFCB Lfcb,
//      IN ULONG Integer
//      );
//

#define LfsTruncateOffsetToLogPage(LFCB,LI,OUTLI)       \
    *(OUTLI) = LI;                                      \
    *((PULONG)(OUTLI)) &= (LFCB)->LogPageInverseMask

#define LfsLogPageOffset(LFCB,INT)                      \
    (INT & (LFCB)->LogPageMask)

VOID
LfsNextLogPageOffset (
    IN PLFCB Lfcb,
    IN LONGLONG CurrentLogPageOffset,
    OUT PLONGLONG NextLogPageOffset,
    OUT PBOOLEAN Wrapped
    );

PVOID
LfsAllocateSpanningBuffer (
    IN PLFCB Lfcb,
    IN ULONG Length
    );

VOID
LfsFreeSpanningBuffer (
    IN PVOID Buffer
    );


//
//  The following routines provide support for dealing with log records.  They
//  are contained in 'LogRcSup.c'
//

BOOLEAN
LfsWriteLogRecordIntoLogPage (
    IN PLFCB Lfcb,
    IN PLCH Lch,
    IN ULONG NumberOfWriteEntries,
    IN PLFS_WRITE_ENTRY WriteEntries,
    IN LFS_RECORD_TYPE RecordType,
    IN TRANSACTION_ID *TransactionId OPTIONAL,
    IN LSN ClientUndoNextLsn OPTIONAL,
    IN LSN ClientPreviousLsn OPTIONAL,
    IN LONG UndoRequirement,
    IN BOOLEAN ForceToDisk,
    OUT PLSN Lsn
    );


//
//  Lsn support routines.  The following routines provide support for
//  manipulating Lsn values.  They are contained in 'LsnSup.c'
//

//
//  LSN
//  LfsFileOffsetToLsn (
//      IN PLFCB Lfcb,
//      IN LONGLONG FileOffset,
//      IN LONGLONG SequenceNumber
//      );
//
//  BOOLEAN
//  LfsIsLsnInFile (
//      IN PLFCB Lfcb,
//      IN LSN Lsn
//      );
//
//  LSN
//  LfsComputeLsnFromLbcb (
//      IN PLFCB Lfcb,
//      IN PLBCB Lbcb
//      );
//
//  VOID
//  LfsTruncateLsnToLogPage (
//      IN PLFCB Lfcb,
//      IN LSN Lsn,
//      OUT PLONGLONG FileOffset
//      );
//
//  LONGLONG
//  LfsLsnToFileOffset (
//      IN PLFCB Lfcb,
//      IN LSN Lsn
//      );
//
//  LONGLONG
//  LfsLsnToSeqNumber (
//      IN PLFCB Lfcb,
//      IN LSN Lsn
//      );
//
//  ULONG
//  LfsLsnToPageOffset (
//      IN PLFCB Lfcb,
//      IN LSN Lsn
//      );
//

#define LfsFileOffsetToLsn(LFCB,FO,SN) (                                        \
    (((ULONGLONG)(FO)) >> 3) + Int64ShllMod32((SN), (LFCB)->FileDataBits)                                \
)

#define LfsIsLsnInFile(LFCB,LSN)                                                \
    (/*xxGeq*/( (LSN).QuadPart >= ((LFCB)->OldestLsn).QuadPart )                                          \
     && /*xxLeq*/( (LSN).QuadPart <= ((LFCB)->RestartArea->CurrentLsn).QuadPart ))

#define LfsComputeLsnFromLbcb(LFCB,LBCB) (                                              \
    LfsFileOffsetToLsn( LFCB,                                                           \
                        (LBCB)->FileOffset + (LBCB)->BufferOffset,    \
                        (LBCB)->SeqNumber )                                    \
)

#define LfsTruncateLsnToLogPage(LFCB,LSN,FO) {                                  \
    *(FO) = LfsLsnToFileOffset( LFCB, LSN );                                    \
    *((PULONG)(FO)) &= (LFCB)->LogPageInverseMask;                                \
}

#define LfsLsnToFileOffset(LFCB,LSN)                                            \
    /*xxShr*/( ((ULONGLONG)/*xxShl*/( (LSN).QuadPart << (LFCB)->SeqNumberBits )) >> ((LFCB)->SeqNumberBits - 3) )

#define LfsLsnToSeqNumber(LFCB,LSN)                                             \
    /*xxShr*/Int64ShrlMod32( ((ULONGLONG)(LSN).QuadPart), (LFCB)->FileDataBits )

#define LfsLsnToPageOffset(LFCB,LSN)                                            \
    LfsLogPageOffset( LFCB, (LSN).LowPart << 3 )

VOID
LfsLsnFinalOffset (
    IN PLFCB Lfcb,
    IN LSN Lsn,
    IN ULONG DataLength,
    OUT PLONGLONG FinalOffset
    );

BOOLEAN
LfsFindNextLsn (
    IN PLFCB Lfcb,
    IN PLFS_RECORD_HEADER RecordHeader,
    OUT PLSN Lsn
    );


//
//  The following routines support the Lfs restart areas.  They are contained
//  in 'RstrtSup.c'
//

VOID
LfsWriteLfsRestart (
    IN PLFCB Lfcb,
    IN ULONG ThisRestartSize,
    IN BOOLEAN WaitForIo
    );

VOID
LfsFindOldestClientLsn (
    IN PLFS_RESTART_AREA RestartArea,
    IN PLFS_CLIENT_RECORD ClientArray,
    OUT PLSN OldestLsn
    );


//
//  The following routines are used for managing the structures allocated
//  by us.  They are contained in 'StrucSup.c'
//

PLFCB
LfsAllocateLfcb (
    IN ULONG LogPageSize,
    IN LONGLONG FileSize
    );


VOID
LfsDeallocateLfcb (
    IN PLFCB Lfcb,
    IN BOOLEAN CompleteTeardown
    );

VOID
LfsAllocateLbcb (
    IN PLFCB Lfcb,
    OUT PLBCB *Lbcb
    );

VOID
LfsDeallocateLbcb (
    IN PLFCB Lfcb,
    IN PLBCB Lbcb
    );

VOID
LfsAllocateLeb (
    IN PLFCB Lfcb,
    OUT PLEB *NewLeb
    );

VOID
LfsDeallocateLeb (
    IN PLFCB Lfcb,
    IN PLEB Leb
    );

VOID
LfsReadPage (
    IN PLFCB Lfcb,
    IN PLARGE_INTEGER Offset,
    OUT PMDL *Mdl,
    OUT PVOID *Buffer
    );

//
//  VOID
//  LfsInitializeLeb (
//      IN PLEB Leb,
//      IN LFS_CLIENT_ID ClientId,
//      IN LFS_CONTEXT_MODE ContextMode
//      );
//
//
//  VOID
//  LfsAllocateLch (
//      OUT PLCH *Lch
//      );
//
//  VOID
//  LfsDeallocateLch (
//      IN PLCH Lch
//      );
//
//  VOID
//  LfsAllocateRestartArea (
//      OUT PLFS_RESTART_AREA *RestartArea,
//      ULONG Size
//      );
//
//  VOID
//  LfsDeallocateRestartArea (
//      IN PLFS_RESTART_AREA RestartArea
//      );
//
//  BOOLEAN
//  LfsLbcbIsRestart (
//      IN PLBCB Lbcb
//      );
//

#define LfsInitializeLeb(LEB,ID,MODE)                           \
    (LEB)->ClientId = ID;                                       \
    (LEB)->ContextMode = MODE


#define LfsAllocateLch(NEW)     {                               \
    *(NEW) = FsRtlAllocatePool( PagedPool, sizeof( LCH ));      \
    RtlZeroMemory( (*NEW), sizeof( LCH ));                      \
    (*(NEW))->NodeTypeCode = LFS_NTC_LCH;                       \
    (*(NEW))->NodeByteSize = sizeof( LCH );                     \
}

#define LfsDeallocateLch(LCH)                                   \
    ExFreePool( LCH )

#define LfsAllocateRestartArea(RS,SIZE)                         \
    *(RS) = FsRtlAllocatePool( PagedPool, (SIZE) );             \
    RtlZeroMemory( *(RS), (SIZE) )

#define LfsDeallocateRestartArea(RS)                            \
    ExFreePool( RS )

#define LfsLbcbIsRestart(LBCB)                                  \
    (FlagOn( (LBCB)->LbcbFlags, LBCB_RESTART_LBCB ))


//
//  The following routines provide synchronization support for the Lfs
//  shared structures.  They are contained in 'SyncSup.c'
//

//
//  VOID
//  LfsAcquireLfsData (
//      );
//
//  VOID
//  LfsReleaseLfsData (
//      );
//
//  VOID
//  LfsAcquireLfcb (
//      IN PLFCB Lfcb
//      );
//
//  VOID
//  LfsReleaseLfcb (
//      IN PLFCB Lfcb
//      );
//
//  VOID
//  LfsAcquireLch (
//      IN PLCH Lch
//      );
//
//  VOID
//  LfsReleaseLfcb (
//      IN PLCH Lch
//      );
//

#define LfsAcquireLfsData()                                 \
    ExAcquireFastMutex( &LfsData.LfsDataLock )

#define LfsReleaseLfsData()                                 \
    ExReleaseFastMutex( &LfsData.LfsDataLock )

#define LfsAcquireBufferLock()                              \
    ExAcquireFastMutex( &LfsData.BufferLock )

#define LfsReleaseBufferLock()                              \
    ExReleaseFastMutex( &LfsData.BufferLock )

#define LfsWaitForBufferNotification()                      \
    KeWaitForSingleObject( &LfsData.BufferNotification,     \
                           Executive,                       \
                           KernelMode,                      \
                           FALSE,                           \
                           NULL )

#define LfsNotifyBufferWaiters()                            \
    KeSetEvent( &LfsData.BufferNotification, 0, FALSE )

#define LfsBlockBufferWaiters()                             \
    KeClearEvent( &LfsData.BufferNotification )

INLINE
VOID
LfsAcquireLfcb (
    IN PLFCB Lfcb
    )
{
    ExAcquireResourceExclusiveLite( &Lfcb->Sync->Resource, TRUE );
}

INLINE
VOID
LfsReleaseLfcb (
    IN PLFCB Lfcb
    )
{
    if (Lfcb->Sync->Resource.OwnerThreads[0].OwnerThread == ExGetCurrentResourceThread()) {
        ExReleaseResourceLite( &Lfcb->Sync->Resource );
    }
}

INLINE
VOID
LfsAcquireLch (
    IN PLCH Lch
    )
{
    ExAcquireResourceExclusiveLite( &(Lch->Sync->Resource), TRUE );
}

INLINE
VOID
LfsReleaseLch (
    IN PLCH Lch
    )
{
    if (Lch->Sync->Resource.OwnerThreads[0].OwnerThread == ExGetCurrentResourceThread()) {
        ExReleaseResourceLite( &Lch->Sync->Resource );
    }
}



//
//  The following routines are used to check various structures for validity
//  and comparability.  They are contained in 'VerfySup.c'.
//

VOID
LfsCurrentAvailSpace (
    IN PLFCB Lfcb,
    OUT PLONGLONG CurrentAvailSpace,
    OUT PULONG CurrentPageBytes
    );

BOOLEAN
LfsVerifyLogSpaceAvail (
    IN PLFCB Lfcb,
    IN PLCH Lch,
    IN ULONG RemainingLogBytes,
    IN LONG UndoRequirement,
    IN BOOLEAN ForceToDisk
    );

VOID
LfsFindCurrentAvail (
    IN PLFCB Lfcb
    );

BOOLEAN
LfsCheckSubsequentLogPage (
    IN PLFCB Lfcb,
    IN PLFS_RECORD_PAGE_HEADER RecordPageHeader,
    IN LONGLONG LogFileOffset,
    IN LONGLONG SequenceNumber
    );


//
//  VOID
//  LfsValidateLch (
//      IN PLCH Lch
//      );
//
//  VOID
//  LfsValidateClientId (
//      IN PLFCB Lfcb,
//      IN PLCH Lch
//      );
//
//  BOOLEAN
//  LfsVerifyClientLsnInRange (
//      IN PLFCB Lfcb,
//      IN PLFS_CLIENT_RECORD ClientRecord,
//      IN LSN Lsn
//      );
//
//  BOOLEAN
//  LfsClientIdMatch (
//      IN PLFS_CLIENT_ID ClientA,
//      IN PLFS_CLIENT_ID ClientB
//      )
//
//  VOID
//  LfsValidateLeb (
//      IN PLFS_CONTEXT_BLOCK Leb,
//      IN PLCH Lch
//      )
//

#define LfsValidateLch(LCH)                                     \
    if ((LCH) == NULL                                           \
        || (LCH)->NodeTypeCode != LFS_NTC_LCH                   \
        || ((LCH)->Lfcb != NULL                                 \
            && (LCH)->Lfcb->NodeTypeCode != LFS_NTC_LFCB)) {    \
                                                                \
        ExRaiseStatus( STATUS_ACCESS_DENIED );                  \
    }

#define LfsValidateClientId(LFCB,LCH)                                   \
    if ((LCH)->ClientId.ClientIndex >= (LFCB)->RestartArea->LogClients  \
        || (LCH)->ClientId.SeqNumber                                    \
           != Add2Ptr( Lfcb->ClientArray,                               \
                       (LCH)->ClientArrayByteOffset,                    \
                       PLFS_CLIENT_RECORD )->SeqNumber) {               \
        ExRaiseStatus( STATUS_ACCESS_DENIED );                          \
    }

#define LfsVerifyClientLsnInRange(LFCB,CLIENT,LSN)                      \
    (/*xxGeq*/( (LSN).QuadPart >= ((CLIENT)->OldestLsn).QuadPart )                                  \
     && /*xxLeq*/( (LSN).QuadPart <= ((LFCB)->RestartArea->CurrentLsn).QuadPart )                   \
     && /*xxNeqZero*/( (LSN).QuadPart != 0 ))

#define LfsClientIdMatch(CLIENT_A,CLIENT_B)                             \
    ((BOOLEAN) ((CLIENT_A)->SeqNumber == (CLIENT_B)->SeqNumber          \
                && (CLIENT_A)->ClientIndex == (CLIENT_B)->ClientIndex))

#define LfsValidateLeb(LEB,LCH)                                         \
    if (LEB == NULL                                                     \
        || (LEB)->NodeTypeCode != LFS_NTC_LEB                           \
        || !LfsClientIdMatch( &(LEB)->ClientId, &(LCH)->ClientId )) {   \
        ExRaiseStatus( STATUS_ACCESS_DENIED );                          \
    }


//
//  Miscellaneous support routines
//

//
//      ULONG
//      FlagOn (
//          IN ULONG Flags,
//          IN ULONG SingleFlag
//          );
//
//      BOOLEAN
//      BooleanFlagOn (
//          IN ULONG Flags,
//          IN ULONG SingleFlag
//          );
//
//      VOID
//      SetFlag (
//          IN ULONG Flags,
//          IN ULONG SingleFlag
//          );
//
//      VOID
//      ClearFlag (
//          IN ULONG Flags,
//          IN ULONG SingleFlag
//          );
//

//#ifndef BooleanFlagOn
//#define BooleanFlagOn(F,SF) (    \
//    (BOOLEAN)(((F) & (SF)) != 0) \
//)
//#endif

//#ifndef SetFlag
//#define SetFlag(Flags,SingleFlag) { \
//    (Flags) |= (SingleFlag);        \
//}
//#endif

//#ifndef ClearFlag
//#define ClearFlag(Flags,SingleFlag) { \
//    (Flags) &= ~(SingleFlag);         \
//}
//#endif

//
//  This macro takes a pointer (or ulong) and returns its rounded up word
//  value
//

#define WordAlign(Ptr) (                \
    ((((ULONG)(Ptr)) + 1) & 0xfffffffe) \
    )

//
//  This macro takes a pointer (or ulong) and returns its rounded up longword
//  value
//

#define LongAlign(Ptr) (                \
    ((((ULONG)(Ptr)) + 3) & 0xfffffffc) \
    )

//
//  This macro takes a pointer (or ulong) and returns its rounded up quadword
//  value
//

#define QuadAlign(Ptr) (                \
    ((((ULONG)(Ptr)) + 7) & 0xfffffff8) \
    )

//
//  This macro will up a 64 bit value to the next quad align boundary.
//

#define LiQuadAlign(LI,OUT)   {         \
    *(OUT) = /*xxAdd*/( (LI) + 7 );       \
    *((PULONG)(OUT)) &= 0xfffffff8;       \
}

//
//      CAST
//      Add2Ptr (
//          IN PVOID Pointer,
//          IN ULONG Increment
//          IN (CAST)
//          );
//
//      ULONG
//      PtrOffset (
//          IN PVOID BasePtr,
//          IN PVOID OffsetPtr
//          );
//

#define Add2Ptr(PTR,INC,CAST) ((CAST)((PUCHAR)(PTR) + (INC)))

#define PtrOffset(BASE,OFFSET) ((ULONG)((ULONG_PTR)(OFFSET) - (ULONG_PTR)(BASE)))


//
//  The following macros are used to establish the semantics needed
//  to do a return from within a try-finally clause.  As a rule every
//  try clause must end with a label call try_exit.  For example,
//
//      try {
//              :
//              :
//
//      try_exit: NOTHING;
//      } finally {
//
//              :
//              :
//      }
//
//  Every return statement executed inside of a try clause should use the
//  try_return macro.  If the compiler fully supports the try-finally construct
//  then the macro should be
//
//      #define try_return(S)  { return(S); }
//
//  If the compiler does not support the try-finally construct then the macro
//  should be
//
//      #define try_return(S)  { S; goto try_exit; }
//

#define try_return(S) { S; goto try_exit; }

#endif // _LFSPROCS_
