654 lines
15 KiB
C
654 lines
15 KiB
C
/*++
|
||
|
||
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)
|
||
|
||
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.
|
||
//
|
||
|
||
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 );
|
||
|
||
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.
|
||
//
|
||
|
||
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 );
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
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 );
|
||
|
||
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;
|
||
}
|
||
|