You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
654 lines
16 KiB
654 lines
16 KiB
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Restart.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the routines which access the client restart
|
|
areas.
|
|
|
|
Author:
|
|
|
|
Brian Andrew [BrianAn] 20-June-1991
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "lfsprocs.h"
|
|
|
|
//
|
|
// The debug trace level
|
|
//
|
|
|
|
#define Dbg (DEBUG_TRACE_RESTART)
|
|
#define MODULE_POOL_TAG ('RsfL')
|
|
|
|
VOID
|
|
LfsSetBaseLsnPriv (
|
|
IN PLFCB Lfcb,
|
|
IN PLFS_CLIENT_RECORD ClientRecord,
|
|
IN LSN BaseLsn
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, LfsReadRestartArea)
|
|
#pragma alloc_text(PAGE, LfsSetBaseLsn)
|
|
#pragma alloc_text(PAGE, LfsSetBaseLsnPriv)
|
|
#pragma alloc_text(PAGE, LfsWriteRestartArea)
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
LfsReadRestartArea (
|
|
IN LFS_LOG_HANDLE LogHandle,
|
|
IN OUT PULONG BufferLength,
|
|
IN PVOID Buffer,
|
|
OUT PLSN Lsn
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the client when he wishes to read his restart
|
|
area in the log file.
|
|
|
|
Arguments:
|
|
|
|
LogHandle - Pointer to private Lfs structure used to identify this
|
|
client.
|
|
|
|
BufferLength - On entry it is the length of the user buffer. On exit
|
|
it is the size of the data stored in the buffer.
|
|
|
|
Buffer - Pointer to the buffer where the client restart data is to be
|
|
copied.
|
|
|
|
Lsn - This is the Lsn for client restart area.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN UsaError;
|
|
|
|
PLCH Lch;
|
|
|
|
PLFS_CLIENT_RECORD ClientRecord;
|
|
|
|
PLFS_RECORD_HEADER RecordHeader;
|
|
PBCB RecordHeaderBcb;
|
|
|
|
PLFCB Lfcb;
|
|
NTSTATUS RetStatus = STATUS_SUCCESS;
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, "LfsReadRestartArea: Entered\n", 0 );
|
|
DebugTrace( 0, Dbg, "Log Handle -> %08lx\n", LogHandle );
|
|
DebugTrace( 0, Dbg, "Buffer Length -> %08lx\n", *BufferLength );
|
|
DebugTrace( 0, Dbg, "Buffer -> %08lx\n", Buffer );
|
|
|
|
RecordHeaderBcb = NULL;
|
|
|
|
Lch = (PLCH) LogHandle;
|
|
|
|
//
|
|
// Check that the structure is a valid log handle structure.
|
|
//
|
|
|
|
LfsValidateLch( Lch );
|
|
|
|
|
|
//
|
|
// Use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// Acquire the log file control block for this log file.
|
|
//
|
|
|
|
LfsAcquireLchExclusive( 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 );
|
|
|
|
ClientRecord = Add2Ptr( Lfcb->ClientArray,
|
|
Lch->ClientArrayByteOffset,
|
|
PLFS_CLIENT_RECORD );
|
|
|
|
//
|
|
// If the client doesn't have a restart area, go ahead and exit
|
|
// now.
|
|
//
|
|
|
|
if (ClientRecord->ClientRestartLsn.QuadPart == 0) {
|
|
|
|
//
|
|
// We show there is no restart area by returning a length
|
|
// of zero. We also set the Lsn value to zero so that
|
|
// we can catch it if the user tries to use the Lsn.
|
|
//
|
|
|
|
DebugTrace( 0, Dbg, "No client restart area exists\n", 0 );
|
|
|
|
*BufferLength = 0;
|
|
*Lsn = LfsZeroLsn;
|
|
|
|
try_return( NOTHING );
|
|
}
|
|
|
|
//
|
|
// Release the Lfcb as we won't be modifying any fields in it.
|
|
//
|
|
|
|
LfsReleaseLfcb( Lfcb );
|
|
|
|
//
|
|
// Pin the log record for this Lsn.
|
|
//
|
|
|
|
LfsPinOrMapLogRecordHeader( Lfcb,
|
|
ClientRecord->ClientRestartLsn,
|
|
FALSE,
|
|
FALSE,
|
|
&UsaError,
|
|
&RecordHeader,
|
|
&RecordHeaderBcb );
|
|
|
|
//
|
|
// If the Lsn values don't match, then the disk is corrupt.
|
|
//
|
|
|
|
if (ClientRecord->ClientRestartLsn.QuadPart != RecordHeader->ThisLsn.QuadPart) {
|
|
|
|
ExRaiseStatus( STATUS_DISK_CORRUPT_ERROR );
|
|
}
|
|
|
|
|
|
//
|
|
// Check that the user's buffer is big enough to hold the restart
|
|
// data. We raise an error status for this error.
|
|
//
|
|
|
|
if (RecordHeader->ClientDataLength > *BufferLength) {
|
|
|
|
DebugTrace( 0, Dbg, "Client buffer is too small\n", 0 );
|
|
*BufferLength = RecordHeader->ClientDataLength;
|
|
*Lsn = LfsZeroLsn;
|
|
try_return( RetStatus = STATUS_BUFFER_TOO_SMALL );
|
|
}
|
|
|
|
|
|
//
|
|
// Use the cache manager to copy the data into the user's buffer.
|
|
//
|
|
|
|
LfsCopyReadLogRecord( Lfcb,
|
|
RecordHeader,
|
|
Buffer );
|
|
|
|
//
|
|
// Pass the length and the Lsn of the restart area back to the
|
|
// caller.
|
|
//
|
|
|
|
*BufferLength = RecordHeader->ClientDataLength;
|
|
*Lsn = RecordHeader->ThisLsn;
|
|
|
|
try_exit: NOTHING;
|
|
} finally {
|
|
|
|
DebugUnwind( LfsReadRestartArea );
|
|
|
|
//
|
|
// Release the log file control block if held.
|
|
//
|
|
|
|
LfsReleaseLch( Lch );
|
|
|
|
//
|
|
// Unpin the log record header for the client restart if pinned.
|
|
//
|
|
|
|
if (RecordHeaderBcb != NULL) {
|
|
|
|
CcUnpinData( RecordHeaderBcb );
|
|
}
|
|
|
|
DebugTrace( 0, Dbg, "Lsn (Low) -> %08lx\n", Lsn->LowPart );
|
|
DebugTrace( 0, Dbg, "Lsn (High) -> %08lx\n", Lsn->HighPart );
|
|
DebugTrace( 0, Dbg, "Buffer Length -> %08lx\n", *BufferLength );
|
|
DebugTrace( -1, Dbg, "LfsReadRestartArea: Exit\n", 0 );
|
|
}
|
|
|
|
return RetStatus;
|
|
}
|
|
|
|
|
|
VOID
|
|
LfsWriteRestartArea (
|
|
IN LFS_LOG_HANDLE LogHandle,
|
|
IN ULONG BufferLength,
|
|
IN PVOID Buffer,
|
|
IN LOGICAL CleanShutdown,
|
|
OUT PLSN Lsn
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the client to write a restart area to the
|
|
disk. This routine will not return to the caller until the client
|
|
restart area and all prior Lsn's have been flushed and the Lfs
|
|
restart area on the disk has been updated.
|
|
|
|
On return, all log records up to and including 'Lsn' have been flushed
|
|
to the disk.
|
|
|
|
Arguments:
|
|
|
|
LogHandle - Pointer to private Lfs structure used to identify this client.
|
|
|
|
BufferLength - On entry it is the length of the user buffer.
|
|
|
|
Buffer - Pointer to the buffer where the client restart data resides.
|
|
|
|
CleanShutdown - Logical indicating that the caller won't need to run
|
|
restart from this restart area. Lfs can set the CLEAN_SHUTDOWN flag
|
|
in its restart area as an indication for 3rd party utilities that
|
|
it is safe to party on the drive.
|
|
|
|
Lsn - This is the Lsn for this write operation. On input, this will be the
|
|
new Base Lsn for this client.
|
|
|
|
**** This was used to prevent adding an interface change to
|
|
the Beta release.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PLCH Lch;
|
|
|
|
PLFCB Lfcb;
|
|
|
|
PLFS_CLIENT_RECORD ClientRecord;
|
|
|
|
LFS_WRITE_ENTRY WriteEntry;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, "LfsWriteRestartArea: Entered\n", 0 );
|
|
DebugTrace( 0, Dbg, "Log Handle -> %08lx\n", LogHandle );
|
|
DebugTrace( 0, Dbg, "Buffer Length -> %08lx\n", BufferLength );
|
|
DebugTrace( 0, Dbg, "Buffer -> %08lx\n", Buffer );
|
|
|
|
Lch = (PLCH) LogHandle;
|
|
|
|
//
|
|
// Check that the structure is a valid log handle structure.
|
|
//
|
|
|
|
LfsValidateLch( Lch );
|
|
|
|
|
|
//
|
|
// Use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// Acquire the log file control block for this log file.
|
|
//
|
|
|
|
LfsAcquireLchExclusive( 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 );
|
|
|
|
//
|
|
// If the clean shutdown flag is currently set and this caller
|
|
// will run restart out of this record then clear the bit on
|
|
// disk first.
|
|
//
|
|
|
|
if (FlagOn( Lfcb->RestartArea->Flags, LFS_CLEAN_SHUTDOWN ) &&
|
|
!CleanShutdown) {
|
|
|
|
ClearFlag( Lfcb->RestartArea->Flags, LFS_CLEAN_SHUTDOWN );
|
|
|
|
LfsWriteLfsRestart( Lfcb, Lfcb->RestartAreaSize, FALSE );
|
|
LfsWriteLfsRestart( Lfcb, Lfcb->RestartAreaSize, TRUE );
|
|
}
|
|
|
|
//
|
|
// Capture the client record after possibly updating above when writing
|
|
// the lfs restart areas
|
|
//
|
|
|
|
ClientRecord = Add2Ptr( Lfcb->ClientArray,
|
|
Lch->ClientArrayByteOffset,
|
|
PLFS_CLIENT_RECORD );
|
|
|
|
//
|
|
// Go ahead and update the Base Lsn in the client area if the value
|
|
// given is not zero.
|
|
//
|
|
|
|
if (Lsn->QuadPart != 0) {
|
|
|
|
LfsSetBaseLsnPriv( Lfcb,
|
|
ClientRecord,
|
|
*Lsn );
|
|
}
|
|
|
|
//
|
|
// Write this restart area as a log record into a log page.
|
|
//
|
|
|
|
WriteEntry.Buffer = Buffer;
|
|
WriteEntry.ByteLength = BufferLength;
|
|
|
|
LfsWriteLogRecordIntoLogPage( Lfcb,
|
|
Lch,
|
|
1,
|
|
&WriteEntry,
|
|
LfsClientRestart,
|
|
NULL,
|
|
LfsZeroLsn,
|
|
LfsZeroLsn,
|
|
0,
|
|
TRUE,
|
|
Lsn );
|
|
|
|
//
|
|
// Update the restart area for the client.
|
|
//
|
|
|
|
ClientRecord->ClientRestartLsn = *Lsn;
|
|
|
|
//
|
|
// Write the restart area to the disk.
|
|
//
|
|
|
|
if (CleanShutdown) {
|
|
|
|
SetFlag( Lfcb->RestartArea->Flags, LFS_CLEAN_SHUTDOWN );
|
|
|
|
LfsWriteLfsRestart( Lfcb, Lfcb->RestartAreaSize, FALSE );
|
|
LfsWriteLfsRestart( Lfcb, Lfcb->RestartAreaSize, TRUE );
|
|
|
|
} else {
|
|
|
|
LfsWriteLfsRestart( Lfcb, Lfcb->RestartAreaSize, TRUE );
|
|
}
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( LfsWriteRestartArea );
|
|
|
|
//
|
|
// Release the log file control block if still held.
|
|
//
|
|
|
|
LfsReleaseLch( Lch );
|
|
|
|
DebugTrace( 0, Dbg, "Lsn (Low) -> %08lx\n", Lsn->LowPart );
|
|
DebugTrace( 0, Dbg, "Log (High) -> %08lx\n", Lsn->HighPart );
|
|
DebugTrace( -1, Dbg, "LfsWriteRestartArea: Exit\n", 0 );
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
LfsSetBaseLsn (
|
|
IN LFS_LOG_HANDLE LogHandle,
|
|
IN LSN BaseLsn
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the client to notify the log service of the
|
|
oldest Lsn he expects to need during restart. The Lfs is allowed to
|
|
reuse any part of the circular log file which logically precedes
|
|
this Lsn. A client may only specify a Lsn which follows the previous
|
|
Lsn specified by this client.
|
|
|
|
Arguments:
|
|
|
|
LogHandle - Pointer to private Lfs structure used to identify this
|
|
client.
|
|
|
|
BaseLsn - This is the oldest Lsn the client may require during a
|
|
restart.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
volatile NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PLCH Lch;
|
|
|
|
PLFCB Lfcb;
|
|
|
|
PLFS_CLIENT_RECORD ClientRecord;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, "LfsSetBaseLsn: Entered\n", 0 );
|
|
DebugTrace( 0, Dbg, "Log Handle -> %08lx\n", LogHandle );
|
|
DebugTrace( 0, Dbg, "Base Lsn (Low) -> %08lx\n", BaseLsn.LowPart );
|
|
DebugTrace( 0, Dbg, "Base Lsn (High) -> %08lx\n", BaseLsn.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.
|
|
//
|
|
|
|
LfsAcquireLchExclusive( 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 );
|
|
|
|
ClientRecord = Add2Ptr( Lfcb->ClientArray,
|
|
Lch->ClientArrayByteOffset,
|
|
PLFS_CLIENT_RECORD );
|
|
|
|
//
|
|
// We simply call the worker routine to advance the base lsn.
|
|
// If we moved forward in the file, we will put our restart area in the
|
|
// queue.
|
|
//
|
|
|
|
LfsSetBaseLsnPriv( Lfcb,
|
|
ClientRecord,
|
|
BaseLsn );
|
|
|
|
LfsWriteLfsRestart( Lfcb, Lfcb->RestartAreaSize, FALSE );
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( LfsSetBaseLsn );
|
|
|
|
//
|
|
// Release the log file control block if held.
|
|
//
|
|
|
|
LfsReleaseLch( Lch );
|
|
|
|
DebugTrace( -1, Dbg, "LfsSetBaseLsn: Exit\n", 0 );
|
|
}
|
|
|
|
} except (LfsExceptionFilter( GetExceptionInformation() )) {
|
|
|
|
Status = GetExceptionCode();
|
|
}
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
|
|
ExRaiseStatus( Status );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine
|
|
//
|
|
|
|
VOID
|
|
LfsSetBaseLsnPriv (
|
|
IN PLFCB Lfcb,
|
|
IN PLFS_CLIENT_RECORD ClientRecord,
|
|
IN LSN BaseLsn
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This worker routine is called internally by Lfs to modify the
|
|
oldest Lsn a client expects to need during restart. The Lfs is allowed to
|
|
reuse any part of the circular log file which logically precedes
|
|
this Lsn. A client may only specify a Lsn which follows the previous
|
|
Lsn specified by this client.
|
|
|
|
Arguments:
|
|
|
|
Lfcb - Log context block for this file.
|
|
|
|
ClientRecord - For the client whose base Lsn is being modified.
|
|
|
|
BaseLsn - This is the oldest Lsn the client may require during a
|
|
restart.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, "LfsSetBaseLsn: Entered\n", 0 );
|
|
DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
|
|
DebugTrace( 0, Dbg, "Base Lsn (Low) -> %08lx\n", BaseLsn.LowPart );
|
|
DebugTrace( 0, Dbg, "Base Lsn (High) -> %08lx\n", BaseLsn.HighPart );
|
|
|
|
//
|
|
// We only proceed if the client is moving forward in the file.
|
|
//
|
|
|
|
if (BaseLsn.QuadPart > Lfcb->OldestLsn.QuadPart) {
|
|
|
|
if (BaseLsn.QuadPart > ClientRecord->OldestLsn.QuadPart) {
|
|
|
|
ClientRecord->OldestLsn = BaseLsn;
|
|
}
|
|
|
|
Lfcb->OldestLsn = BaseLsn;
|
|
|
|
//
|
|
// We walk through all the active clients and find the new
|
|
// oldest Lsn for the log file.
|
|
//
|
|
|
|
LfsFindOldestClientLsn( Lfcb->RestartArea,
|
|
Lfcb->ClientArray,
|
|
&Lfcb->OldestLsn );
|
|
|
|
Lfcb->OldestLsnOffset = LfsLsnToFileOffset( Lfcb, Lfcb->OldestLsn );
|
|
ClearFlag( Lfcb->Flags, LFCB_NO_OLDEST_LSN );
|
|
|
|
LfsFindCurrentAvail( Lfcb );
|
|
}
|
|
|
|
DebugTrace( -1, Dbg, "LfsSetBaseLsnPriv: Exit\n", 0 );
|
|
|
|
return;
|
|
}
|
|
|