Windows2000/private/ntos/lfs/write.c
2020-09-30 17:12:32 +02:00

564 lines
14 KiB
C

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
Write.c
Abstract:
This module implements the user routines which write log records into
or flush portions of the log file.
Author:
Brian Andrew [BrianAn] 20-June-1991
Revision History:
--*/
#include "lfsprocs.h"
// The debug trace level
#define Dbg (DEBUG_TRACE_WRITE)
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, LfsCheckWriteRange)
#pragma alloc_text(PAGE, LfsFlushToLsn)
#pragma alloc_text(PAGE, LfsForceWrite)
#pragma alloc_text(PAGE, LfsWrite)
#endif
BOOLEAN
LfsWrite (
IN LFS_LOG_HANDLE LogHandle,
IN ULONG NumberOfWriteEntries,
IN PLFS_WRITE_ENTRY WriteEntries,
IN LFS_RECORD_TYPE RecordType,
IN TRANSACTION_ID *TransactionId OPTIONAL,
IN LSN UndoNextLsn,
IN LSN PreviousLsn,
IN LONG UndoRequirement,
OUT PLSN Lsn
)
/*++
Routine Description:
This routine is called by a client to write a log record to the log file.
The log record is lazy written and is not guaranteed to be on the disk
until a subsequent LfsForceWrie or LfsWriteRestartArea or until
an LfsFlushtoLsn is issued withan Lsn greater-than or equal to the Lsn
returned from this service.
Arguments:
LogHandle - Pointer to private Lfs structure used to identify this
client.
NumberOfWriteEntries - Number of components of the log record.
WriteEntries - Pointer to an array of write entries.
RecordType - Lfs defined type for this log record.
TransactionId - Id value used to group log records by complete transaction.
UndoNextLsn - Lsn of a previous log record which needs to be undone in
the event of a client restart.
PreviousLsn - Lsn of the immediately previous log record for this client.
Lsn - Lsn to be associated with this log record.
Return Value:
BOOLEAN - Advisory, TRUE indicates that less than 1/4 of the log file is
available.
--*/
{
volatile NTSTATUS Status = STATUS_SUCCESS;
BOOLEAN LogFileFull = FALSE;
PLCH Lch;
PLFCB Lfcb;
PAGED_CODE();
DebugTrace( +1, Dbg, "LfsWrite: Entered\n", 0 );
DebugTrace( 0, Dbg, "Log Handle -> %08lx\n", LogHandle );
DebugTrace( 0, Dbg, "NumberOfWriteEntries -> %08lx\n", NumberOfWriteEntries );
DebugTrace( 0, Dbg, "WriteEntries -> %08lx\n", WriteEntries );
DebugTrace( 0, Dbg, "Record Type -> %08lx\n", RecordType );
DebugTrace( 0, Dbg, "Transaction Id -> %08lx\n", TransactionId );
DebugTrace( 0, Dbg, "UndoNextLsn (Low) -> %08lx\n", UndoNextLsn.LowPart );
DebugTrace( 0, Dbg, "UndoNextLsn (High) -> %08lx\n", UndoNextLsn.HighPart );
DebugTrace( 0, Dbg, "PreviousLsn (Low) -> %08lx\n", PreviousLsn.LowPart );
DebugTrace( 0, Dbg, "PreviousLsn (High) -> %08lx\n", PreviousLsn.HighPart );
Lch = (PLCH) LogHandle;
// Check that the structure is a valid log handle structure.
LfsValidateLch( Lch );
// Use a try-except to catch errors.
try {
// Use a try-finally to facilitate cleanup.
try {
// Acquire the log file control block for this log file.
LfsAcquireLch( Lch );
Lfcb = Lch->Lfcb;
// If the Log file has been closed then refuse access.
if (Lfcb == NULL) {
ExRaiseStatus( STATUS_ACCESS_DENIED );
}
// Check that the client Id is valid.
LfsValidateClientId( Lfcb, Lch );
// Write the log record.
LogFileFull = LfsWriteLogRecordIntoLogPage( Lfcb,
Lch,
NumberOfWriteEntries,
WriteEntries,
RecordType,
TransactionId,
UndoNextLsn,
PreviousLsn,
UndoRequirement,
FALSE,
Lsn );
} finally {
DebugUnwind( LfsWrite );
// Release the log file control block if held.
LfsReleaseLch( Lch );
DebugTrace( 0, Dbg, "Lsn (Low) -> %08lx\n", Lsn->LowPart );
DebugTrace( 0, Dbg, "Lsn (High) -> %08lx\n", Lsn->HighPart );
DebugTrace( -1, Dbg, "LfsWrite: Exit\n", 0 );
}
} except (LfsExceptionFilter( GetExceptionInformation() )) {
Status = GetExceptionCode();
}
if (Status != STATUS_SUCCESS) {
ExRaiseStatus( Status );
}
return LogFileFull;
}
BOOLEAN
LfsForceWrite (
IN LFS_LOG_HANDLE LogHandle,
IN ULONG NumberOfWriteEntries,
IN PLFS_WRITE_ENTRY WriteEntries,
IN LFS_RECORD_TYPE RecordType,
IN TRANSACTION_ID *TransactionId,
IN LSN UndoNextLsn,
IN LSN PreviousLsn,
IN LONG UndoRequirement,
OUT PLSN Lsn
)
/*++
Routine Description:
This routine is called by a client to write a log record to the log file.
This is idendical to LfsWrite except that on return the log record is
guaranteed to be on disk.
Arguments:
LogHandle - Pointer to private Lfs structure used to identify this
client.
NumberOfWriteEntries - Number of components of the log record.
WriteEntries - Pointer to an array of write entries.
RecordType - Lfs defined type for this log record.
TransactionId - Id value used to group log records by complete transaction.
UndoNextLsn - Lsn of a previous log record which needs to be undone in
the event of a client restart.
PreviousLsn - Lsn of the immediately previous log record for this client.
Lsn - Lsn to be associated with this log record.
Return Value:
BOOLEAN - Advisory, TRUE indicates that less than 1/4 of the log file is
available.
--*/
{
volatile NTSTATUS Status = STATUS_SUCCESS;
PLCH Lch;
PLFCB Lfcb;
BOOLEAN LogFileFull = FALSE;
PAGED_CODE();
DebugTrace( +1, Dbg, "LfsForceWrite: Entered\n", 0 );
DebugTrace( 0, Dbg, "Log Handle -> %08lx\n", LogHandle );
DebugTrace( 0, Dbg, "NumberOfWriteEntries -> %08lx\n", NumberOfWriteEntries );
DebugTrace( 0, Dbg, "WriteEntries -> %08lx\n", WriteEntries );
DebugTrace( 0, Dbg, "Record Type -> %08lx\n", RecordType );
DebugTrace( 0, Dbg, "Transaction Id -> %08lx\n", TransactionId );
DebugTrace( 0, Dbg, "UndoNextLsn (Low) -> %08lx\n", UndoNextLsn.LowPart );
DebugTrace( 0, Dbg, "UndoNextLsn (High) -> %08lx\n", UndoNextLsn.HighPart );
DebugTrace( 0, Dbg, "PreviousLsn (Low) -> %08lx\n", PreviousLsn.LowPart );
DebugTrace( 0, Dbg, "PreviousLsn (High) -> %08lx\n", PreviousLsn.HighPart );
Lch = (PLCH) LogHandle;
// Check that the structure is a valid log handle structure.
LfsValidateLch( Lch );
// Use a try-except to catch errors.
try {
// Use a try-finally to facilitate cleanup.
try {
// Acquire the log file control block for this log file.
LfsAcquireLch( Lch );
Lfcb = Lch->Lfcb;
// If the Log file has been closed then refuse access.
if (Lfcb == NULL) {
ExRaiseStatus( STATUS_ACCESS_DENIED );
}
// Check that the client Id is valid.
LfsValidateClientId( Lfcb, Lch );
// Write the log record.
LogFileFull = LfsWriteLogRecordIntoLogPage( Lfcb,
Lch,
NumberOfWriteEntries,
WriteEntries,
RecordType,
TransactionId,
UndoNextLsn,
PreviousLsn,
UndoRequirement,
TRUE,
Lsn );
// The call to add this lbcb to the workque is guaranteed to release
// the Lfcb if this thread may do the Io.
LfsFlushToLsnPriv( Lfcb, *Lsn );
} finally {
DebugUnwind( LfsForceWrite );
// Release the log file control block if held.
LfsReleaseLch( Lch );
DebugTrace( 0, Dbg, "Lsn (Low) -> %08lx\n", Lsn->LowPart );
DebugTrace( 0, Dbg, "Lsn (High) -> %08lx\n", Lsn->HighPart );
DebugTrace( -1, Dbg, "LfsForceWrite: Exit\n", 0 );
}
} except (LfsExceptionFilter( GetExceptionInformation() )) {
Status = GetExceptionCode();
}
if (Status != STATUS_SUCCESS) {
ExRaiseStatus( Status );
}
return LogFileFull;
}
VOID
LfsFlushToLsn (
IN LFS_LOG_HANDLE LogHandle,
IN LSN Lsn
)
/*++
Routine Description:
This routine is called by a client to insure that all log records
to a certain point have been flushed to the file. This is done by
checking if the desired Lsn has even been written at all. If so we
check if it has been flushed to the file. If not, we simply write
the current restart area to the disk.
Arguments:
LogHandle - Pointer to private Lfs structure used to identify this
client.
Lsn - This is the Lsn that must be on the disk on return from this
routine.
Return Value:
None
--*/
{
volatile NTSTATUS Status = STATUS_SUCCESS;
PLCH Lch;
PLFCB Lfcb;
PAGED_CODE();
DebugTrace( +1, Dbg, "LfsFlushToLsn: Entered\n", 0 );
DebugTrace( 0, Dbg, "Log Handle -> %08lx\n", LogHandle );
DebugTrace( 0, Dbg, "Lsn (Low) -> %08lx\n", Lsn.LowPart );
DebugTrace( 0, Dbg, "Lsn (High) -> %08lx\n", Lsn.HighPart );
Lch = (PLCH) LogHandle;
// Check that the structure is a valid log handle structure.
LfsValidateLch( Lch );
// Use a try-except to catch errors.
try {
// Use a try-finally to facilitate cleanup.
try {
// Acquire the log file control block for this log file.
LfsAcquireLch( Lch );
Lfcb = Lch->Lfcb;
// If the log file has been closed we will assume the Lsn has been flushed.
if (Lfcb != NULL) {
// Check that the client Id is valid.
LfsValidateClientId( Lfcb, Lch );
// Call our common routine to perform the work.
LfsFlushToLsnPriv( Lfcb, Lsn );
}
} finally {
DebugUnwind( LfsFlushToLsn );
// Release the log file control block if held.
LfsReleaseLch( Lch );
DebugTrace( -1, Dbg, "LfsFlushToLsn: Exit\n", 0 );
}
} except (LfsExceptionFilter( GetExceptionInformation() )) {
Status = GetExceptionCode();
}
if (Status != STATUS_SUCCESS) {
ExRaiseStatus( Status );
}
return;
}
VOID
LfsCheckWriteRange (
IN PLFS_WRITE_DATA WriteData,
IN OUT PLONGLONG FlushOffset,
IN OUT PULONG FlushLength
)
/*++
Routine Description:
This routine is called Ntfs to Lfs when a flush occurs. This will give Lfs a chance
to trim the amount of the flush. Lfs can then use a 4K log record page size
for all systems (Intel and Alpha).
This routine will trim the size of the IO request to the value stored in the
Lfcb for this volume. We will also redirty the second half of the page if
we have begun writing log records into it.
Arguments:
WriteData - This is the data in the user's data structure which is maintained
by Lfs to describe the current writes.
FlushOffset - On input this is the start of the flush passed to Ntfs from MM.
On output this is the start of the actual range to flush.
FlushLength - On input this is the length of the flush from the given FlushOffset.
On output this is the length of the flush from the possibly modified FlushOffset.
Return Value:
None
--*/
{
PLIST_ENTRY Links;
PLFCB Lfcb;
PLFCB NextLfcb;
PAGED_CODE();
// Find the correct Lfcb for this request.
Lfcb = WriteData->Lfcb;
// Trim the write if not a system page size.
if (PAGE_SIZE != Lfcb->SystemPageSize) {
// Check if we are trimming before the write.
if (*FlushOffset < WriteData->FileOffset) {
*FlushLength -= (ULONG) (WriteData->FileOffset - *FlushOffset);
*FlushOffset = WriteData->FileOffset;
}
// Check that we aren't flushing too much.
if (*FlushOffset + *FlushLength > WriteData->FileOffset + WriteData->Length) {
*FlushLength = (ULONG) (WriteData->FileOffset + WriteData->Length - *FlushOffset);
}
// Finally check if we have to redirty a page.
if ((Lfcb->PageToDirty != NULL) &&
(*FlushLength + *FlushOffset == Lfcb->PageToDirty->FileOffset)) {
*((PULONG) (Lfcb->PageToDirty->PageHeader)) = LFS_SIGNATURE_RECORD_PAGE_ULONG;
}
}
return;
}