Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

3619 lines
94 KiB

/*++
Copyright (c) 1998-1999 Microsoft Corporation
Module Name:
srlog.c
Abstract:
this file implements the sr logging functionality
Author:
Kanwaljit Marok (kmarok) 01-May-2000
Revision History:
--*/
#include "precomp.h"
#include "srdefs.h"
//
// Some SR_LOG related macros
//
#define MAX_RENAME_TRIES 1000
#define SR_LOG_FLAGS_ENABLE 0x00000001
#define SR_LOG_FLAGS_DIRTY 0x00000010
#define SR_MAX_LOG_FILE_SIZE (1024*1024)
//
// system volume information\_restore{machineguid}
//
#define SR_DATASTORE_PREFIX_LENGTH 79 * sizeof(WCHAR)
//
// Length of \_restore.{machineguid}\RPXX\S0000000.ACL
//
#define SR_ACL_FILENAME_LENGTH (SR_DATASTORE_PREFIX_LENGTH + \
32* sizeof(WCHAR))
#define SR_INLINE_ACL_SIZE(AclInfoSize) (sizeof(RECORD_HEADER)+ AclInfoSize)
#define SR_FILE_ACL_SIZE(pVolumeName) (sizeof(RECORD_HEADER) + \
pVolumeName->Length + \
SR_ACL_FILENAME_LENGTH)
#define UPDATE_LOG_OFFSET( pLogContext, BytesWritten ) \
((pLogContext)->FileOffset += BytesWritten)
#define RESET_LOG_BUFFER( pLogContext ) \
((pLogContext)->BufferOffset = 0, \
(pLogContext)->LastBufferOffset = 0)
#define RESET_LOG_CONTEXT( pLogContext ) \
((pLogContext)->FileOffset = 0, \
RESET_LOG_BUFFER( pLogContext ), \
(pLogContext)->LoggingFlags = 0, \
(pLogContext)->AllocationSize = 0)
#define SET_ENABLE_FLAG( pLogContext ) \
SetFlag( pLogContext->LoggingFlags, SR_LOG_FLAGS_ENABLE )
#define CLEAR_ENABLE_FLAG( pLogContext ) \
ClearFlag( pLogContext->LoggingFlags, SR_LOG_FLAGS_ENABLE )
#define SET_DIRTY_FLAG( pLogContext ) \
SetFlag( pLogContext->LoggingFlags, SR_LOG_FLAGS_DIRTY)
#define CLEAR_DIRTY_FLAG( pLogContext ) \
ClearFlag( pLogContext->LoggingFlags, SR_LOG_FLAGS_DIRTY )
//
// Context passed to SrCreateFile
//
typedef struct _SR_OPEN_CONTEXT {
//
// Path to file
//
PUNICODE_STRING pPath;
//
// Handle will be returned here
//
HANDLE Handle;
//
// Open options
//
ACCESS_MASK DesiredAccess;
ULONG FileAttributes;
ULONG ShareAccess;
ULONG CreateDisposition;
ULONG CreateOptions;
PSR_DEVICE_EXTENSION pExtension;
} SR_OPEN_CONTEXT, *PSR_OPEN_CONTEXT;
//
// Note : These api can be called only when the FileSystem
// is online and it is safe to read/write data.
//
VOID
SrPackString(
IN PBYTE pBuffer,
IN DWORD BufferSize,
IN DWORD RecordType,
IN PUNICODE_STRING pString
);
NTSTATUS
SrPackLogHeader(
IN PSR_LOG_HEADER *ppLogHeader,
IN PUNICODE_STRING pVolumePath
);
NTSTATUS
SrPackAclInformation(
IN PBYTE pBuffer,
IN PSECURITY_DESCRIPTOR pSecInfo,
IN ULONG SecInfoSize,
IN PSR_DEVICE_EXTENSION pExtension,
IN BOOLEAN bInline
);
VOID
SrLoggerFlushDpc(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
);
VOID
SrLoggerFlushWorkItem (
IN PDEVICE_OBJECT DeviceObject,
IN PVOID Context
);
VOID
SrLoggerAddLogContext(
IN PSR_LOGGER_CONTEXT pLoggerInfo,
IN PSR_LOG_CONTEXT pLogContext
);
NTSTATUS
SrLoggerRemoveLogContext(
IN PSR_LOGGER_CONTEXT pLoggerInfo,
IN PSR_LOG_CONTEXT pLogContext
);
NTSTATUS
SrLogOpen(
IN PSR_LOG_CONTEXT pLogContext
);
NTSTATUS
SrLogClose(
IN PSR_LOG_CONTEXT pLogContext
);
NTSTATUS
SrLogCheckAndRename(
IN PSR_DEVICE_EXTENSION pExtension,
IN PUNICODE_STRING pLogPath
);
NTSTATUS
SrpLogWriteSynchronous(
IN PSR_DEVICE_EXTENSION pExtension,
IN PSR_LOG_CONTEXT pLogContext,
IN PSR_LOG_ENTRY pLogEntry
);
#ifndef SYNC_LOG_WRITE
NTSTATUS
SrpLogWriteAsynchronous(
IN PSR_DEVICE_EXTENSION pExtension,
IN PSR_LOG_CONTEXT pLogContext,
IN PSR_LOG_ENTRY pLogEntry
);
#endif
NTSTATUS
SrLogFlush (
IN PSR_LOG_CONTEXT pLogContext
);
NTSTATUS
SrLogSwitch(
IN PSR_LOG_CONTEXT pLogContext
);
NTSTATUS
SrGetRestorePointPath(
IN PUNICODE_STRING pVolumeName,
IN USHORT RestPtPathLength,
OUT PUNICODE_STRING pRestPtPath
);
NTSTATUS
SrGetAclFileName(
IN PUNICODE_STRING pVolumeName,
IN USHORT AclFileNameLength,
OUT PUNICODE_STRING pAclFileName
);
NTSTATUS
SrCreateFile(
IN PSR_OPEN_CONTEXT pOpenContext
);
//
// linker commands
//
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, SrPackString )
#pragma alloc_text( PAGE, SrPackLogEntry )
#pragma alloc_text( PAGE, SrPackLogHeader )
#pragma alloc_text( PAGE, SrPackDebugInfo )
#pragma alloc_text( PAGE, SrPackAclInformation )
#pragma alloc_text( PAGE, SrLoggerStart )
#pragma alloc_text( PAGE, SrLoggerStop )
#pragma alloc_text( PAGE, SrLoggerFlushWorkItem )
#pragma alloc_text( PAGE, SrLoggerAddLogContext )
#pragma alloc_text( PAGE, SrLoggerRemoveLogContext )
#pragma alloc_text( PAGE, SrLoggerSwitchLogs )
#pragma alloc_text( PAGE, SrCreateFile )
#pragma alloc_text( PAGE, SrLogOpen )
#pragma alloc_text( PAGE, SrLogClose )
#pragma alloc_text( PAGE, SrLogCheckAndRename )
#pragma alloc_text( PAGE, SrLogStart )
#pragma alloc_text( PAGE, SrLogStop )
#pragma alloc_text( PAGE, SrLogFlush )
#ifdef SYNC_LOG_WRITE
#pragma alloc_text( PAGE, SrpLogWriteSynchronous )
#else
#pragma alloc_text( PAGE, SrpLogWriteAsynchronous )
#endif
#pragma alloc_text( PAGE, SrLogFlush )
#pragma alloc_text( PAGE, SrLogWrite )
#pragma alloc_text( PAGE, SrLogSwitch )
#pragma alloc_text( PAGE, SrGetRestorePointPath )
#pragma alloc_text( PAGE, SrGetLogFileName )
#pragma alloc_text( PAGE, SrGetAclFileName )
#pragma alloc_text( PAGE, SrGetAclInformation )
#endif // ALLOC_PRAGMA
/////////////////////////////////////////////////////////////////////
//
// Packing/Marshaling Routines : Marshals information into records
//
/////////////////////////////////////////////////////////////////////
//++
// Function:
// SrPackString
//
// Description:
// This function packs a string into a record.
//
// Arguments:
// Pointer to memory to create the entry
// Size of memory
// Entry type
// Pointer to unicode string
//
// Return Value:
// None
//--
static
VOID
SrPackString(
IN PBYTE pBuffer,
IN DWORD BufferSize,
IN DWORD RecordType,
IN PUNICODE_STRING pString
)
{
PRECORD_HEADER pHeader = (PRECORD_HEADER)pBuffer;
PAGED_CODE();
UNREFERENCED_PARAMETER( BufferSize );
ASSERT( pBuffer );
ASSERT( pString );
pHeader->RecordSize = STRING_RECORD_SIZE( pString );
ASSERT( pHeader->RecordSize <= BufferSize );
pHeader->RecordType = RecordType;
//
// Copy string contents
//
RtlCopyMemory( pBuffer + sizeof(RECORD_HEADER),
pString->Buffer,
pString->Length );
//
// Add null terminator
//
*(PWCHAR)( pBuffer + sizeof(RECORD_HEADER) + pString->Length ) = UNICODE_NULL;
} // SrPackString
//++
// Function:
// SrPackLogEntry
//
// Description:
// This function allocates and fills a SR_LOG_ENTRY structure. The
// caller is responsible for freeing the memory returned in ppLogEntry.
//
// Arguments:
// ppLogEntry - Pointer to a SR_LOG_ENTRY pointer. This gets set to the
// the log entry structure that is allocated and initialized by this
// routine.
// EntryType - The type of log entry this is.
// Attributes - The attributes for this file.
// SequenceNum - The sequence number for this log entry.
// pAclInfo - The ACL information for the file being modified, if needed.
// AclInfoSize - The size in bytes of pAclInfo, if needed.
// pDebugBlob - The debug blob to log, if needed.
// pPath1 - The first full path for the file or dir that this log entry
// pertains to, if needed.
// Path1StreamLength - The length of the stream component of the name
// in pPath1, if needed.
// pTempPath - The path to the temporary file in the restore location,
// if needed.
// pPath2 - The second full path for the file or dir that this log entry
// pertains to, if needed.
// Path2StreamLength - The length of the stream component of the name
// in pPath2, if needed.
// pExtension - The SR device extension for this volume.
// pShortName - The short name for the file or dir that this log entry
// pertains to, if needed.
//
// Return Value:
// This function returns STATUS_INSUFFICIENT_RESOURCES if it cannot
// allocate a log entry record large enough to store this entry.
//
// If there is a problem getting the ACL info, that error status is
// returned.
//
// If one of the parameters is ill-formed, STATUS_INVALID_PARAMETER
// is returned.
//
// Otherwise, STATUS_SUCCESS is returned.
//--
NTSTATUS
SrPackLogEntry(
OUT PSR_LOG_ENTRY *ppLogEntry,
IN ULONG EntryType,
IN ULONG Attributes,
IN INT64 SequenceNum,
IN PSECURITY_DESCRIPTOR pAclInfo OPTIONAL,
IN ULONG AclInfoSize OPTIONAL,
IN PVOID pDebugBlob OPTIONAL,
IN PUNICODE_STRING pPath1,
IN USHORT Path1StreamLength,
IN PUNICODE_STRING pTempPath OPTIONAL,
IN PUNICODE_STRING pPath2 OPTIONAL,
IN USHORT Path2StreamLength OPTIONAL,
IN PSR_DEVICE_EXTENSION pExtension,
IN PUNICODE_STRING pShortName OPTIONAL
)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
DWORD Size = 0;
DWORD RequiredSize = 0;
DWORD RecordSize = 0;
PBYTE pLoc = NULL;
DWORD EntryFlags = 0;
BOOLEAN bAclInline = TRUE;
PUCHAR pBuffer = NULL;
PUNICODE_STRING pVolumeName;
PSR_LOG_DEBUG_INFO pDebugInfo = (PSR_LOG_DEBUG_INFO) pDebugBlob;
//
// Unicode strings used for string manipulation.
//
UNICODE_STRING Path1Fix;
UNICODE_STRING TempPathFix;
UNICODE_STRING Path2Fix;
PAGED_CODE();
ASSERT( pPath1 != NULL );
ASSERT( pExtension != NULL );
ASSERT( ppLogEntry != NULL );
pVolumeName = pExtension->pNtVolumeName;
ASSERT( pVolumeName != NULL );
// ====================================================================
//
// Prepare the necessary fields for the log entry.
//
// ====================================================================
//
// Remove the volume prefix from pPath1 and add the stream name to the
// visible portion of the name, if there is one.
//
ASSERT( RtlPrefixUnicodeString( pVolumeName, pPath1, FALSE ) );
ASSERT( IS_VALID_SR_STREAM_STRING( pPath1, Path1StreamLength ) );
Path1Fix.Length = Path1Fix.MaximumLength = (pPath1->Length + Path1StreamLength) - pVolumeName->Length;
Path1Fix.Buffer = (PWSTR)((PBYTE)pPath1->Buffer + pVolumeName->Length);
//
// Find the file name component of the pTempPath if that was passed in.
//
if (pTempPath != NULL)
{
PWSTR pFileName = NULL;
ULONG FileNameLength;
Status = SrFindCharReverse( pTempPath->Buffer,
pTempPath->Length,
L'\\',
&pFileName,
&FileNameLength );
if (!NT_SUCCESS( Status ))
{
Status = STATUS_INVALID_PARAMETER;
goto SrPackLogEntry_Exit;
}
ASSERT( pFileName != NULL );
//
// Move past the leading '\\'
//
pFileName++;
FileNameLength -= sizeof( WCHAR );
TempPathFix.Length = TempPathFix.MaximumLength = (USHORT) FileNameLength;
TempPathFix.Buffer = pFileName;
}
//
// Remove the volume prefix from pPath2 if that was provided. Also, add
// the stream component to the visible portion of the name, if there
// is a stream component.
//
if (pPath2 != NULL)
{
ASSERT( IS_VALID_SR_STREAM_STRING( pPath2, Path2StreamLength ) );
if (RtlPrefixUnicodeString( pVolumeName,
pPath2,
FALSE ))
{
Path2Fix.Length = Path2Fix.MaximumLength = (pPath2->Length + Path2StreamLength) - pVolumeName->Length;
Path2Fix.Buffer = (PWSTR)((PBYTE)pPath2->Buffer + pVolumeName->Length);
}
else
{
Path2Fix.Length = Path2Fix.MaximumLength = (pPath2->Length + Path2StreamLength);
Path2Fix.Buffer = pPath2->Buffer;
}
}
// ====================================================================
//
// Calculate the total size needed for the log entry based on the
// components that we must log.
//
// ====================================================================
// First, account for the SR_LOG_ENTRY header.
RequiredSize = FIELD_OFFSET(SR_LOG_ENTRY, SubRecords);
// Count pPath1
RequiredSize += ( STRING_RECORD_SIZE(&Path1Fix) );
// Count pTempPath, if we've got one
if (pTempPath)
{
RequiredSize += ( STRING_RECORD_SIZE(&TempPathFix) );
}
// Count pPath2, if we've got one
if (pPath2)
{
RequiredSize += ( STRING_RECORD_SIZE(&Path2Fix) );
}
// Count pAclInfo, if we've got one. At this point, we assume that the
// Acl will be stored inline.
if( pAclInfo )
{
RequiredSize += SR_INLINE_ACL_SIZE( AclInfoSize );
}
// Count pDebugInfo, if we've got any
if (pDebugInfo)
{
RequiredSize += pDebugInfo->Header.RecordSize;
}
// Count pShortName, if we've got one
if (pShortName != NULL && pShortName->Length > 0)
{
RequiredSize += ( STRING_RECORD_SIZE(pShortName) );
}
//
// increment the size to accomodate the entry size at the end
//
RequiredSize += sizeof(DWORD);
// ====================================================================
//
// Check if we meet the buffer size requirements and initialize the
// record if we do.
//
// ====================================================================
//
// First, determine if we should keep the AclInfo inline or not.
//
if (SR_INLINE_ACL_SIZE( AclInfoSize ) > SR_MAX_INLINE_ACL_SIZE)
{
SrTrace( LOG, ("SR!Changing Acl to Non-resident form\n"));
bAclInline = FALSE;
RequiredSize -= SR_INLINE_ACL_SIZE( AclInfoSize );
RequiredSize += SR_FILE_ACL_SIZE( pVolumeName );
}
//
// Now allocate the buffer that will hold the log entry.
//
pBuffer = SrAllocateLogEntry( RequiredSize );
if (pBuffer == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto SrPackLogEntry_Exit;
}
// ====================================================================
//
// We've got a big enough LogEntry, so now properly fill the LogEntry.
//
// ====================================================================
//
// Initialize the static part of SR_LOG_ENTRY
//
RtlZeroMemory( pBuffer, RequiredSize );
//
// StreamOverwrite should be StreamChange
//
if (EntryType == SrEventStreamOverwrite)
{
EntryType = SrEventStreamChange;
}
((PSR_LOG_ENTRY) pBuffer)->MagicNum = SR_LOG_MAGIC_NUMBER;
((PSR_LOG_ENTRY) pBuffer)->EntryType = EntryType;
((PSR_LOG_ENTRY) pBuffer)->EntryFlags = EntryFlags;
((PSR_LOG_ENTRY) pBuffer)->Attributes = Attributes;
((PSR_LOG_ENTRY) pBuffer)->SequenceNum = SequenceNum;
Size = FIELD_OFFSET( SR_LOG_ENTRY, SubRecords );
//
// add first filename string
//
pLoc = pBuffer + Size;
RecordSize = STRING_RECORD_SIZE( &Path1Fix );
SrPackString( pLoc,
RecordSize,
RecordTypeFirstPath,
&Path1Fix );
Size += RecordSize;
//
// add temp filename if passed
//
if( pTempPath )
{
pLoc = pBuffer + Size;
RecordSize = STRING_RECORD_SIZE( &TempPathFix );
SrPackString( pLoc,
RecordSize,
RecordTypeTempPath,
&TempPathFix );
((PSR_LOG_ENTRY) pBuffer)->EntryFlags |= ENTRYFLAGS_TEMPPATH;
Size += RecordSize;
}
//
// add second filename string if passed in
//
if( pPath2 )
{
pLoc = pBuffer + Size;
RecordSize = STRING_RECORD_SIZE( &Path2Fix );
SrPackString( pLoc,
RecordSize,
RecordTypeSecondPath,
&Path2Fix );
((PSR_LOG_ENTRY) pBuffer)->EntryFlags |= ENTRYFLAGS_SECONDPATH;
Size += RecordSize;
}
//
// Pack and add the Acl information appropriately
//
if( pAclInfo )
{
pLoc = pBuffer + Size;
Status = SrPackAclInformation( pLoc,
pAclInfo,
AclInfoSize,
pExtension,
bAclInline );
if (!NT_SUCCESS( Status ))
goto SrPackLogEntry_Exit;
((PSR_LOG_ENTRY) pBuffer)->EntryFlags |= ENTRYFLAGS_ACLINFO;
if (bAclInline)
{
Size += SR_INLINE_ACL_SIZE( AclInfoSize );
}
else
{
Size += SR_FILE_ACL_SIZE( pVolumeName );
}
}
//
// Pack debug info if passed in
//
if (pDebugBlob)
{
pLoc = pBuffer + Size;
RtlCopyMemory( pLoc,
pDebugInfo,
pDebugInfo->Header.RecordSize );
((PSR_LOG_ENTRY) pBuffer)->EntryFlags |= ENTRYFLAGS_DEBUGINFO;
Size += pDebugInfo->Header.RecordSize;
}
//
// pack and add the short name, if supplied
//
if (pShortName != NULL && pShortName->Length > 0)
{
pLoc = pBuffer + Size;
RecordSize = STRING_RECORD_SIZE( pShortName );
SrPackString( pLoc,
RecordSize,
RecordTypeShortName,
pShortName );
((PSR_LOG_ENTRY) pBuffer)->EntryFlags |= ENTRYFLAGS_SHORTNAME;
Size += RecordSize;
}
//
// increment the size to accomodate the entry size at the end
//
Size += sizeof(DWORD);
//
// fill in the header fields : record size, record type and
// update the size at the end
//
((PSR_LOG_ENTRY) pBuffer)->Header.RecordSize = Size;
((PSR_LOG_ENTRY) pBuffer)->Header.RecordType = RecordTypeLogEntry;
UPDATE_END_SIZE( pBuffer, Size );
*ppLogEntry = (PSR_LOG_ENTRY) pBuffer;
Status = STATUS_SUCCESS;
SrPackLogEntry_Exit:
RETURN(Status);
} // SrPackLogEntry
//++
// Function:
// SrPackLogHeader
//
// Description:
// This function creates a proper SR_LOG_HEADER entry. It allocates
// the LogEntry structure so that it is big enough to store this header.
//
// Note: The caller is responsible for freeing the SR_LOG_ENTRY allocated.
//
// Arguments:
// ppLogHeader - Pointer to the PSR_LOG_HEADER that get set to the
// allocated log header address.
// pVolumePath - The volume path for this volume.
//
// Return Value:
// Returns STATUS_INSUFFICIENT_RESOURCES if the SR_LOG_ENTRY cannot
// be allocated. Otherwise, it returns STATUS_SUCCESS.
//--
NTSTATUS
SrPackLogHeader(
IN PSR_LOG_HEADER *ppLogHeader,
IN PUNICODE_STRING pVolumePath
)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
DWORD RequiredSize = 0;
DWORD SubRecordSize = 0;
DWORD Size = 0;
PBYTE pLoc = NULL;
PBYTE pBuffer = NULL;
PAGED_CODE();
ASSERT( ppLogHeader != NULL );
ASSERT( pVolumePath != NULL );
// ====================================================================
//
// First, figure out how much of the buffer we need to use.
//
// ====================================================================
RequiredSize = FIELD_OFFSET(SR_LOG_HEADER, SubRecords);
// Count the volume path.
RequiredSize += ( STRING_RECORD_SIZE(pVolumePath) );
// Increment the size to accomodate the LogHeader size at the end
RequiredSize += sizeof(DWORD);
// ====================================================================
//
// Second, make sure that the buffer passed in is large enough for
// the LogHeader.
//
// ====================================================================
Size = FIELD_OFFSET(SR_LOG_HEADER, SubRecords);
pBuffer = SrAllocateLogEntry( RequiredSize );
if (pBuffer == NULL)
{
//
// Not enough memory to pack the entry
//
Status = STATUS_INSUFFICIENT_RESOURCES;
goto SrPackLogHeader_Exit;
}
//
// Initialize the static part of SR_LOG_HEADER
//
RtlZeroMemory( pBuffer, RequiredSize );
((PSR_LOG_HEADER) pBuffer)->MagicNum = SR_LOG_MAGIC_NUMBER ;
((PSR_LOG_HEADER) pBuffer)->LogVersion = SR_LOG_VERSION ;
// ====================================================================
//
// Finally, the buffer is large enough for the LogHeader, so fill
// the buffer with the header.
//
// ====================================================================
Size = FIELD_OFFSET(SR_LOG_HEADER, SubRecords);
//
// Add the volume prefix
//
pLoc = (PBYTE)(&((PSR_LOG_HEADER)pBuffer)->SubRecords);
SubRecordSize = STRING_RECORD_SIZE( pVolumePath );
SrPackString( pLoc,
SubRecordSize,
RecordTypeVolumePath,
pVolumePath );
Size += SubRecordSize;
//
// Increment the size to accomodate the LogHeader size at the end
//
Size += sizeof(DWORD);
//
// Fill in the header fields : record size, record type and
// update the size at the end
//
ASSERT( RequiredSize == Size );
((PSR_LOG_HEADER) pBuffer)->Header.RecordSize = Size;
((PSR_LOG_HEADER) pBuffer)->Header.RecordType = RecordTypeLogHeader;
UPDATE_END_SIZE( pBuffer, Size );
*ppLogHeader = (PSR_LOG_HEADER) pBuffer;
Status = STATUS_SUCCESS;
SrPackLogHeader_Exit:
RETURN( Status );
} // SrPackLogHeader
//++
// Function:
// SrPackDebugInfo
//
// Description:
// This function creates a properly formatted debug info from
// the supplied data. if NULL is passed instead of the buffer
// then the API returns the size required to pack the entry.
//
// Arguments:
// Pointer to log entry buffer
//
// Return Value:
// This function returns STATUS_XXX
//--
NTSTATUS
SrPackDebugInfo(
IN PBYTE pBuffer,
IN DWORD BufferSize
)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
DWORD Size = 0;
PCHAR pStr;
PEPROCESS peProcess;
PAGED_CODE();
ASSERT( pBuffer != NULL );
Size = sizeof(SR_LOG_DEBUG_INFO);
if (BufferSize < Size)
{
//
// Not enough memory to pack the entry
//
Status = STATUS_INSUFFICIENT_RESOURCES;
goto SrPackDebugInfo_Exit;
}
//
// fill in the header fields : record size, record type
//
((PSR_LOG_DEBUG_INFO)pBuffer)->Header.RecordSize = Size;
((PSR_LOG_DEBUG_INFO)pBuffer)->Header.RecordType =
RecordTypeDebugInfo;
((PSR_LOG_DEBUG_INFO)pBuffer)->ThreadId = PsGetCurrentThreadId() ;
((PSR_LOG_DEBUG_INFO)pBuffer)->ProcessId = PsGetCurrentProcessId();
pStr = ((PSR_LOG_DEBUG_INFO)pBuffer)->ProcessName;
*pStr = 0;
peProcess = PsGetCurrentProcess();
RtlCopyMemory( pStr,
((PBYTE)peProcess) + global->ProcNameOffset,
PROCESS_NAME_MAX );
pStr[ PROCESS_NAME_MAX ] = 0;
Status = STATUS_SUCCESS;
SrPackDebugInfo_Exit:
RETURN(Status);
} // SrPackDebugInfo
//++
// Function:
// SrPackAclInformation
//
// Description:
// This function creates a properly formatted Acl record from
// the supplied data.
//
// Arguments:
// Pointer to log entry buffer
//
// Return Value:
// This function returns STATUS_XXX
//--
NTSTATUS
SrPackAclInformation(
IN PBYTE pBuffer,
IN PSECURITY_DESCRIPTOR pSecInfo,
IN ULONG SecInfoSize,
IN PSR_DEVICE_EXTENSION pExtension,
IN BOOLEAN bInline
)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PRECORD_HEADER pHeader = (PRECORD_HEADER)pBuffer;
PUNICODE_STRING pAclFileName = NULL;
HANDLE AclFileHandle = NULL;
PUNICODE_STRING pVolumeName;
PAGED_CODE();
ASSERT( pExtension != NULL );
pVolumeName = pExtension->pNtVolumeName;
ASSERT( pVolumeName != NULL );
try
{
ASSERT( pBuffer != NULL );
ASSERT( pSecInfo != NULL );
ASSERT( SecInfoSize != 0 );
//
// CODEWORK: Convert ACL to Self contained form ??
//
if (bInline)
{
//
// Just format and put the contents into the buffer
//
pHeader->RecordSize = sizeof( RECORD_HEADER ) +
SecInfoSize;
pHeader->RecordType = RecordTypeAclInline;
RtlCopyMemory( pBuffer + sizeof(RECORD_HEADER),
pSecInfo,
SecInfoSize );
Status = STATUS_SUCCESS;
}
else
{
SR_OPEN_CONTEXT OpenContext;
IO_STATUS_BLOCK IoStatusBlock;
//
// Write the contents out to a temp file and create a
// AclFile record.
//
Status = SrAllocateFileNameBuffer( SR_MAX_FILENAME_LENGTH,
&pAclFileName );
if (!NT_SUCCESS( Status ))
leave;
Status = SrGetAclFileName( pVolumeName,
SR_FILENAME_BUFFER_LENGTH,
pAclFileName );
if (!NT_SUCCESS( Status ))
leave;
//
// Open Acl file and write the security info in that file
//
OpenContext.pPath = pAclFileName;
OpenContext.Handle = NULL;
OpenContext.DesiredAccess = FILE_GENERIC_WRITE | SYNCHRONIZE;
OpenContext.FileAttributes = FILE_ATTRIBUTE_NORMAL;
OpenContext.ShareAccess = 0;
OpenContext.CreateDisposition = FILE_OVERWRITE_IF; // OPEN always
OpenContext.CreateOptions = /*FILE_NO_INTERMEDIATE_BUFFERING |*/
FILE_WRITE_THROUGH |
FILE_SYNCHRONOUS_IO_NONALERT;
OpenContext.pExtension = pExtension;
Status = SrPostSyncOperation(SrCreateFile,
&OpenContext);
if (NT_SUCCESS(Status))
{
LARGE_INTEGER Offset;
ASSERT(OpenContext.Handle != NULL);
AclFileHandle = OpenContext.Handle;
Offset.QuadPart = 0;
Status = ZwWriteFile( AclFileHandle,
NULL, // Event
NULL, // ApcRoutine
NULL, // ApcContext
&IoStatusBlock,
pSecInfo,
SecInfoSize,
&Offset, // ByteOffset
NULL ); // Key
if (NT_SUCCESS(Status))
{
//
// Create AclFile type entry
//
SrPackString( pBuffer,
STRING_RECORD_SIZE( pAclFileName ),
RecordTypeAclFile,
pAclFileName );
}
} else {
ASSERT(OpenContext.Handle == NULL);
}
}
}
finally
{
if (pAclFileName != NULL)
{
SrFreeFileNameBuffer( pAclFileName );
pAclFileName = NULL;
}
if (AclFileHandle != NULL)
{
ZwClose(AclFileHandle);
AclFileHandle = NULL;
}
}
RETURN(Status);
} // SrPackAclInformation
/////////////////////////////////////////////////////////////////////
//
// Logger Routines : Manipulate Logger object
//
/////////////////////////////////////////////////////////////////////
//++
// Function:
// SrLoggerStart
//
// Description:
// This function initializes the logger and enables the flushing
// routines.
//
// Arguments:
// PDEVICE_OBJECT pDeviceObject
// PSR_LOGGER_CONTEXT * pLogger
//
// Return Value:
// STATUS_XXX
//--
NTSTATUS
SrLoggerStart(
IN PDEVICE_OBJECT pDeviceObject,
OUT PSR_LOGGER_CONTEXT * ppLogger
)
{
NTSTATUS Status;
PSR_LOGGER_CONTEXT pInitInfo = NULL;
PIO_WORKITEM pWorkItem = NULL;
UNREFERENCED_PARAMETER( pDeviceObject );
ASSERT(IS_VALID_DEVICE_OBJECT(pDeviceObject));
ASSERT(ppLogger != NULL);
PAGED_CODE();
try
{
Status = STATUS_SUCCESS;
*ppLogger = NULL;
//
// Allocate Logging Init info from NonPagedPool
//
pInitInfo = SR_ALLOCATE_STRUCT( NonPagedPool,
SR_LOGGER_CONTEXT,
SR_LOGGER_CONTEXT_TAG );
if (pInitInfo == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
leave;
}
RtlZeroMemory( pInitInfo, sizeof( SR_LOGGER_CONTEXT ) );
pInitInfo->Signature = SR_LOGGER_CONTEXT_TAG;
pInitInfo->ActiveContexts = 0;
#ifdef USE_LOOKASIDE
//
// Initialize Lookaside list used in logging module
//
ExInitializeNPagedLookasideList( &pInitInfo->LogBufferLookaside,
NULL,
NULL,
0,
_globals.LogBufferSize,
SR_LOG_BUFFER_TAG,
0 );
#endif
#ifndef SYNC_LOG_WRITE
//
// Allocate work item for the DPC
//
pWorkItem = IoAllocateWorkItem(global->pControlDevice);
if (pWorkItem == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
leave;
}
//
// Initialize Dpc and timer object
//
KeInitializeTimer( &pInitInfo->Timer );
KeInitializeDpc ( &pInitInfo->Dpc,
SrLoggerFlushDpc,
pWorkItem );
//
// Start the timer for log flushing
//
KeSetTimer( &pInitInfo->Timer,
global->LogFlushDueTime,
&pInitInfo->Dpc );
#endif
*ppLogger = pInitInfo;
} finally {
Status = FinallyUnwind(SrLoggerStart, Status);
if (!NT_SUCCESS( Status ))
{
if (pInitInfo != NULL) {
SrLoggerStop( pInitInfo );
}
*ppLogger = pInitInfo = NULL;
if (pWorkItem != NULL) {
IoFreeWorkItem(pWorkItem);
}
pWorkItem = NULL;
}
}
RETURN(Status);
} // SrLoggerStart
//++
// Function:
// SrLoggerStop
//
// Description:
// This function stops the logger and frees the related resources
//
// Arguments:
// LoggerInfo pointer
//
// Return Value:
// STATUS_XXX
//--
//++
NTSTATUS
SrLoggerStop(
IN PSR_LOGGER_CONTEXT pLogger
)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PAGED_CODE();
ASSERT(IS_VALID_LOGGER_CONTEXT(pLogger));
try {
//
// Stop the timer routines
//
KeCancelTimer( &pLogger->Timer );
//
// Active log contexts must be zero other wise we are leaking
//
ASSERT( pLogger->ActiveContexts == 0 );
#ifdef USE_LOOKASIDE
//
// Free the lookaside lists
//
if (IS_LOOKASIDE_INITIALIZED(&pLogger->LogEntryLookaside))
{
ExDeletePagedLookasideList(
&pLogger->LogEntryLookaside);
}
if (IS_LOOKASIDE_INITIALIZED(&pLogger->LogBufferLookaside))
{
ExDeleteNPagedLookasideList(
&pLogger->LogBufferLookaside);
}
#endif
SR_FREE_POOL_WITH_SIG( pLogger, SR_LOGGER_CONTEXT_TAG );
Status = STATUS_SUCCESS;
} finally {
Status = FinallyUnwind(SrLoggerStop, Status);
}
RETURN(Status);
} // SrLoggerStop
#ifndef SYNC_LOG_WRITE
//++
// Function:
// SrLoggerFlushDpc
//
// Description:
// This function is a DPC called to flush the log buffers. This will
// queue a workitem to flush the buffers. This should not be paged.
//
// Arguments:
// IN PKDPC Dpc
// IN PVOID DeferredContext
// IN PVOID SystemArgument1
// IN PVOID SystemArgument2
//
// Return Value:
// None
//--
VOID
SrLoggerFlushDpc(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
{
PIO_WORKITEM pSrWorkItem;
UNREFERENCED_PARAMETER( Dpc );
UNREFERENCED_PARAMETER( SystemArgument1 );
UNREFERENCED_PARAMETER( SystemArgument2 );
SrTrace( LOG, ("SR!SrLoggerFlushDpc Called...\n"));
pSrWorkItem = (PIO_WORKITEM) DeferredContext;
ASSERT(pSrWorkItem != NULL);
IoQueueWorkItem( pSrWorkItem,
SrLoggerFlushWorkItem,
DelayedWorkQueue,
pSrWorkItem );
} // SrLoggerFlushDpc
//++
// Function:
// SrLoggerFlushWorkItem
//
// Description:
// This Workitem queued by DPC will actually flush the log buffers.
//
// Arguments:
// IN PDEVICE_OBJECT DeviceObject
// IN PVOID Context
//
// Return Value:
// None
//--
VOID
SrLoggerFlushWorkItem (
IN PDEVICE_OBJECT DeviceObject,
IN PVOID Context
)
{
NTSTATUS Status;
PLIST_ENTRY pListEntry;
PSR_DEVICE_EXTENSION pExtension;
PAGED_CODE();
UNREFERENCED_PARAMETER(DeviceObject);
UNREFERENCED_PARAMETER(Context);
SrTrace(LOG, ("sr!SrLoggerFlushWorkItem: enter\n"));
try {
//
// Grab the DeviceExtensionListLock as we are about to
// iterate through this list. Since we are only going to be
// reading this list, we can get this lock shared.
//
SrAcquireDeviceExtensionListLockShared();
Status = STATUS_SUCCESS;
#if DBG
//
// sleep for twice the DPC interval to prove that 2 DPC's
// are not coming in at the same time
//
if (global->DebugControl & SR_DEBUG_DELAY_DPC)
{
LARGE_INTEGER Interval;
Interval.QuadPart = -1 * (10 * NANO_FULL_SECOND);
KeDelayExecutionThread(KernelMode, TRUE, &Interval);
}
#endif
ASSERT( global->pLogger != NULL );
//
// loop over all volumes flushing data to the disk.
//
for (pListEntry = global->DeviceExtensionListHead.Flink;
pListEntry != &global->DeviceExtensionListHead;
pListEntry = pListEntry->Flink)
{
pExtension = CONTAINING_RECORD( pListEntry,
SR_DEVICE_EXTENSION,
ListEntry );
ASSERT(IS_VALID_SR_DEVICE_EXTENSION(pExtension));
//
// Do a quick unsafe check to see if we have started logging this
// volume yet. If it appears we haven't, just continue onto the
// next entry in the list. If logging is just about to begin,
// we catch it on the next firing of this timer.
//
if (pExtension->pLogContext == NULL) {
continue;
}
//
// It looks like we have started to log, so get the necessary
// locks and do our work to flush the volume.
//
try {
SrAcquireActivityLockExclusive( pExtension );
if (pExtension->pLogContext != NULL)
{
//
// yes, flush for this volume
//
Status = SrLogFlush( pExtension->pLogContext );
if (!NT_SUCCESS( Status ))
{
//
// Disable volume
//
Status = SrNotifyVolumeError( pExtension,
pExtension->pLogContext->pLogFilePath,
Status,
SrEventVolumeError );
//
// Stop Logging
//
SrLogStop( pExtension, TRUE, TRUE );
}
}
} finally {
SrReleaseActivityLock( pExtension );
}
}
} finally {
Status = FinallyUnwind(SrLoggerFlushWorkItem, Status);
//
// fire the timer again, do this with the global lock
// held so that it can be cancelled in SrLoggerStop.
//
//
// The DPC is going to reuse the work item
//
KeSetTimer( &global->pLogger->Timer,
global->LogFlushDueTime,
&global->pLogger->Dpc );
SrReleaseDeviceExtensionListLock();
}
SrTrace(LOG, ("sr!SrLoggerFlushWorkItem: exit\n"));
} // SrLoggerFlushWorkItem
#endif
//++
// Function:
// SrLoggerAddLogContext
//
// Description:
// This function adds a given Log Context to the logger
//
// Arguments:
// pointer to LoggerInfo
// pointer to LogContext
//
// Return Value:
// STATUS_XXX
//--
VOID
SrLoggerAddLogContext(
IN PSR_LOGGER_CONTEXT pLogger,
IN PSR_LOG_CONTEXT pLogContext
)
{
PAGED_CODE();
UNREFERENCED_PARAMETER( pLogContext );
ASSERT(IS_VALID_LOGGER_CONTEXT(pLogger));
ASSERT(IS_VALID_LOG_CONTEXT(pLogContext));
InterlockedIncrement(&pLogger->ActiveContexts);
} // SrLoggerAddLogContext
//++
// Function:
// SrLoggerRemoveLogContext
//
// Description:
// This Workitem queued by DPC will actually flush the log buffers.
//
// Arguments:
// pointer to LoggerInfo
// pointer to LogContext
//
// Return Value:
// STATUS_XXX
//--
NTSTATUS
SrLoggerRemoveLogContext(
IN PSR_LOGGER_CONTEXT pLogger,
IN PSR_LOG_CONTEXT pLogContext
)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PAGED_CODE();
UNREFERENCED_PARAMETER( pLogContext );
ASSERT(IS_VALID_LOGGER_CONTEXT(pLogger));
ASSERT(IS_VALID_LOG_CONTEXT(pLogContext));
InterlockedDecrement(&pLogger->ActiveContexts);
Status = STATUS_SUCCESS;
RETURN(Status);
} // SrLoggerRemoveLogContext
//++
// Function:
// SrLoggerSwitchLogs
//
// Description:
// This function loops thru the log contexts and switches all the
// log contexts
//
// Arguments:
// pointer to LoggerInfo
//
// Return Value:
// STATUS_XXX
//--
NTSTATUS
SrLoggerSwitchLogs(
IN PSR_LOGGER_CONTEXT pLogger
)
{
NTSTATUS Status;
PLIST_ENTRY pListEntry;
PSR_DEVICE_EXTENSION pExtension;
PAGED_CODE();
UNREFERENCED_PARAMETER( pLogger );
ASSERT(IS_VALID_LOGGER_CONTEXT(pLogger));
Status = STATUS_SUCCESS;
try {
SrAcquireDeviceExtensionListLockShared();
//
// loop over all volumes switching their logs
//
for (pListEntry = _globals.DeviceExtensionListHead.Flink;
pListEntry != &_globals.DeviceExtensionListHead;
pListEntry = pListEntry->Flink)
{
pExtension = CONTAINING_RECORD( pListEntry,
SR_DEVICE_EXTENSION,
ListEntry );
ASSERT(IS_VALID_SR_DEVICE_EXTENSION(pExtension));
//
// We only have to do work if this is a volume device object,
// not if this is a device object that is attached to a file
// system's control device object.
//
if (FlagOn( pExtension->FsType, SrFsControlDeviceObject ))
{
continue;
}
try {
SrAcquireActivityLockExclusive( pExtension );
//
// have we started logging on this volume?
//
if (pExtension->pLogContext != NULL)
{
//
// yes, switch for this volume
//
Status = SrLogSwitch(pExtension->pLogContext);
}
else
{
Status = SrLogNormalize( pExtension );
}
if (!NT_SUCCESS( Status ))
{
//
// Disable volume
//
Status = SrNotifyVolumeError( pExtension,
NULL,
Status,
SrEventVolumeError );
if (pExtension->pLogContext != NULL)
{
//
// Stop Logging
//
SrLogStop( pExtension, TRUE, TRUE );
}
}
} finally {
Status = FinallyUnwind(SrLoggerSwitchLogs, Status);
SrReleaseActivityLock( pExtension );
}
}
} finally {
Status = FinallyUnwind(SrLoggerSwitchLogs, Status);
SrReleaseDeviceExtensionListLock();
}
RETURN(Status);
} // SrLoggerSwitchLogs
/////////////////////////////////////////////////////////////////////
//
// Log Routines : Manipulate individual Logs
//
/////////////////////////////////////////////////////////////////////
//++
// Function:
// SrCreateFile
//
// Description:
// This function just does a create on file, no sync needed
// written so that it can be called in a separate thread to avoid
// stack overflows via SrIoCreateFile
//
// Arguments:
//
// pOpenContext - pointer to the open context
//
// Return Value:
// This function returns STATUS_XXX
//--
NTSTATUS
SrCreateFile(
IN PSR_OPEN_CONTEXT pOpenContext
)
{
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
NTSTATUS Status;
PAGED_CODE();
ASSERT(pOpenContext != NULL);
ASSERT(pOpenContext->pPath != NULL);
SrTrace(LOG, ("Opening file %wZ", pOpenContext->pPath));
InitializeObjectAttributes( &ObjectAttributes,
pOpenContext->pPath,
OBJ_KERNEL_HANDLE,
NULL,
NULL );
ASSERT( pOpenContext->pExtension != NULL );
Status = SrIoCreateFile(
&pOpenContext->Handle,
pOpenContext->DesiredAccess,
&ObjectAttributes,
&IoStatusBlock,
NULL, // AllocationSize
pOpenContext->FileAttributes,
pOpenContext->ShareAccess,
pOpenContext->CreateDisposition,
pOpenContext->CreateOptions,
NULL, // EaBuffer
0, // EaLength
0,
pOpenContext->pExtension->pTargetDevice );
return Status;
}
//++
// Function:
// SrLogOpen
//
// Description:
// This function creates a log file, no sync needed as it is always
// called internally
//
// Arguments:
// Pointer to open context
//
// Return Value:
// This function returns STATUS_XXX
//--
NTSTATUS
SrLogOpen(
IN PSR_LOG_CONTEXT pLogContext
)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PSR_LOG_HEADER pLogHeader = NULL;
SR_OPEN_CONTEXT OpenContext;
PAGED_CODE();
ASSERT(IS_VALID_LOG_CONTEXT(pLogContext));
ASSERT(pLogContext->pLogFilePath != NULL );
ASSERT(pLogContext->LogHandle == NULL );
ASSERT(pLogContext->pLogFileObject == NULL);
ASSERT( IS_ACTIVITY_LOCK_ACQUIRED_EXCLUSIVE( pLogContext->pExtension ) ||
IS_LOG_LOCK_ACQUIRED_EXCLUSIVE( pLogContext->pExtension ) );
try
{
SrTrace(FUNC_ENTRY, ("SR!SrLogOpen\n"));
//
// Initialize the open context with the file create parameters
//
OpenContext.pPath = pLogContext->pLogFilePath;;
OpenContext.Handle = NULL;
OpenContext.DesiredAccess = FILE_GENERIC_WRITE |
SYNCHRONIZE |
FILE_APPEND_DATA;
OpenContext.FileAttributes = FILE_ATTRIBUTE_NORMAL;
OpenContext.ShareAccess = FILE_SHARE_READ;
OpenContext.CreateDisposition = FILE_OVERWRITE_IF; // OPEN always
OpenContext.CreateOptions = FILE_SYNCHRONOUS_IO_NONALERT;
OpenContext.pExtension = pLogContext->pExtension;
SrTrace(LOG, ("Opening Log in another thread %wZ", pLogContext->pLogFilePath));
Status = SrPostSyncOperation(SrCreateFile,
&OpenContext);
if (NT_SUCCESS(Status))
{
SrTrace(LOG, (" - Succeeded\n"));
ASSERT(OpenContext.Handle != NULL);
pLogContext->LogHandle = OpenContext.Handle;
//
// Also get the file object associated with this handle.
//
Status = ObReferenceObjectByHandle( pLogContext->LogHandle,
0,
*IoFileObjectType,
KernelMode,
(PVOID *) &(pLogContext->pLogFileObject),
NULL );
if (!NT_SUCCESS( Status ))
{
leave;
}
//
// Initialize log context poperly for this log
//
#ifndef SYNC_LOG_WRITE
RtlZeroMemory( pLogContext->pLogBuffer, _globals.LogBufferSize );
#endif
RESET_LOG_CONTEXT( pLogContext );
//
// Enable log context
//
SET_ENABLE_FLAG(pLogContext);
//
// CODEWORK:kmarok: need to decide if we want to preallocate
// the log file and do it here
//
//
// Write the log header as the first entry into
// the newly created log
//
Status = SrPackLogHeader( &pLogHeader,
pLogContext->pLogFilePath);
if (!NT_SUCCESS( Status ))
leave;
Status = SrLogWrite( NULL,
pLogContext,
(PSR_LOG_ENTRY)pLogHeader );
if (!NT_SUCCESS( Status ))
leave;
//
// Clear dirty flag because we haven't actually written any
// data
//
CLEAR_DIRTY_FLAG(pLogContext);
}
else
{
SrTrace(LOG, (" - Failed (0x%X) \n", Status));
}
} finally {
Status = FinallyUnwind(SrLogOpen, Status);
if (!NT_SUCCESS( Status ))
{
//
// Since the open failed, disable the log
//
CLEAR_ENABLE_FLAG(pLogContext);
if (pLogContext->pLogFileObject != NULL)
{
ObDereferenceObject( pLogContext->pLogFileObject );
pLogContext->pLogFileObject = NULL;
}
if (pLogContext->LogHandle)
{
ZwClose( pLogContext->LogHandle );
pLogContext->LogHandle = NULL;
}
}
if (pLogHeader != NULL)
{
SrFreeLogEntry( pLogHeader );
NULLPTR( pLogHeader );
}
}
RETURN(Status);
} // SrLogOpen
//++
// Function:
// SrLogClose
//
// Description:
// This function closes the current log
//
// Arguments:
// Pointer to Log Context
//
// Return Value:
// This function returns STATUS_XXX
//--
NTSTATUS
SrLogClose(
IN PSR_LOG_CONTEXT pLogContext
)
{
NTSTATUS Status = STATUS_SUCCESS;
NTSTATUS TempStatus = STATUS_SUCCESS;
PAGED_CODE();
ASSERT(IS_VALID_LOG_CONTEXT(pLogContext));
ASSERT( IS_ACTIVITY_LOCK_ACQUIRED_EXCLUSIVE( pLogContext->pExtension ) ||
IS_LOG_LOCK_ACQUIRED_EXCLUSIVE( pLogContext->pExtension ) );
//
// Flush & Close the log file
//
if ( pLogContext->LogHandle )
{
#ifndef SYNC_LOG_WRITE
//
// We only need to force flush here if we are not doing synchronous
// log writes.
//
Status = SrLogFlush( pLogContext );
#endif
//
// The close operation is only going to fail if the LogHandle
// is invalid. We need to close the handle even if we hit an
// error trying to flush the log, but it is the value returned from
// flushing the log that the caller really cares about, not the
// return value from closing the handle, so just store it in a
// temp variable and validate in checked builds.
//
ObDereferenceObject( pLogContext->pLogFileObject );
TempStatus = ZwClose( pLogContext->LogHandle );
CHECK_STATUS( TempStatus );
}
//
// modify the log context to indicate the log is closed
// donot clear the LogBuffer member ( reused )
//
pLogContext->LogHandle = NULL;
pLogContext->pLogFileObject = NULL;
RESET_LOG_CONTEXT(pLogContext);
RETURN( Status );
} // SrLogClose
//++
// Function:
// SrLogCheckAndRename
//
// Description:
// This function checks and backsup a log file
//
// Arguments:
// pExtension - The SR_DEVICE_EXTENSION for this volume.
// pLogPath - The full path and file name to the change log.
//
// Return Value:
// This function returns STATUS_XXX
//--
NTSTATUS
SrLogCheckAndRename(
IN PSR_DEVICE_EXTENSION pExtension,
IN PUNICODE_STRING pLogPath
)
{
INT i = 1;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
HANDLE LogHandle = NULL;
PUNICODE_STRING pRenamedFile = NULL;
PFILE_RENAME_INFORMATION pRenameInformation = NULL;
ULONG CharCount;
ULONG RenameInformationLength;
IO_STATUS_BLOCK IoStatusBlock;
SR_OPEN_CONTEXT OpenContext;
PAGED_CODE();
ASSERT( pLogPath != NULL );
SrTrace(FUNC_ENTRY, ("SR!SrLogCheckAndRename\n"));
ASSERT( IS_ACTIVITY_LOCK_ACQUIRED_EXCLUSIVE( pExtension ) ||
IS_LOG_LOCK_ACQUIRED_EXCLUSIVE( pExtension) );
try
{
//
// Do the create
//
OpenContext.pPath = pLogPath;
OpenContext.Handle = NULL;
OpenContext.DesiredAccess = DELETE;
OpenContext.FileAttributes = FILE_ATTRIBUTE_NORMAL;
OpenContext.ShareAccess = 0;
OpenContext.CreateDisposition = FILE_OPEN;
OpenContext.CreateOptions = FILE_NO_INTERMEDIATE_BUFFERING
| FILE_SYNCHRONOUS_IO_NONALERT;
OpenContext.pExtension = pExtension;
Status = SrPostSyncOperation(SrCreateFile,
&OpenContext);
if (Status == STATUS_OBJECT_NAME_NOT_FOUND ||
!NT_SUCCESS( Status ))
{
ASSERT(OpenContext.Handle == NULL);
//
// we need to check for file not found status
//
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
{
Status = STATUS_SUCCESS;
}
leave;
}
//
// File exists so try to rename the file
//
LogHandle = OpenContext.Handle;
ASSERT(LogHandle != NULL);
RenameInformationLength = sizeof(FILE_RENAME_INFORMATION)
+ pLogPath->Length
+ sizeof(WCHAR) // the "."
+ MAX_ULONG_LENGTH // the "%d"
+ sizeof(WCHAR) ; // a NULL
pRenameInformation = SR_ALLOCATE_POOL( PagedPool,
RenameInformationLength,
SR_RENAME_BUFFER_TAG );
if (pRenameInformation == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
leave;
}
Status = SrAllocateFileNameBuffer( pLogPath->Length
+ sizeof(WCHAR) // the "."
+ MAX_ULONG_LENGTH, // the "%d"
&pRenamedFile );
if (!NT_SUCCESS( Status ))
leave;
while( 1 )
{
RtlZeroMemory(&IoStatusBlock, sizeof(IoStatusBlock));
//
// Construct possible backup filename
//
RtlCopyMemory( pRenamedFile->Buffer,
pLogPath->Buffer,
pLogPath->Length );
pRenamedFile->Length = pLogPath->Length;
//
// and append backup suffix
//
CharCount = swprintf( &pRenamedFile->Buffer[
pRenamedFile->Length/sizeof(WCHAR)],
L".%d",
i++ );
ASSERT(CharCount <= MAX_ULONG_LENGTH);
pRenamedFile->Length += (USHORT)CharCount * sizeof(WCHAR);
ASSERT(pRenamedFile->Length <= pRenamedFile->MaximumLength);
//
// now initialize the rename info struct
//
RtlCopyMemory( (PUCHAR)&pRenameInformation->FileName[0],
pRenamedFile->Buffer,
pRenamedFile->Length + sizeof(WCHAR) );
pRenameInformation->ReplaceIfExists = FALSE;
pRenameInformation->RootDirectory = NULL;
pRenameInformation->FileNameLength = pRenamedFile->Length;
SrTrace( LOG, ("SR!SrLogCheckAndRename: renaming to %ws\n",
&pRenameInformation->FileName[0] ));
Status = ZwSetInformationFile( LogHandle,
&IoStatusBlock,
pRenameInformation,
RenameInformationLength,
FileRenameInformation );
if ( NT_SUCCESS_NO_DBGBREAK( Status ) ||
(Status != STATUS_OBJECT_NAME_COLLISION) ||
(i > MAX_RENAME_TRIES) )
{
break;
}
}
//
// To get DBG messages for unexpected errors in DEBUG builds...
//
CHECK_STATUS( Status );
}
finally
{
if ( LogHandle != NULL )
{
ZwClose(LogHandle);
LogHandle = NULL;
}
if ( pRenamedFile != NULL )
{
SrFreeFileNameBuffer( pRenamedFile );
pRenamedFile = NULL;
}
if ( pRenameInformation != NULL )
{
SR_FREE_POOL(pRenameInformation, SR_RENAME_BUFFER_TAG);
pRenameInformation = NULL;
}
}
RETURN(Status);
} // SrLogCheckAndRename
//
// Public API implementation
//
//++
// Function:
// SrLogStart
//
// Description:
// This function prepares the driver for Logging
// requests.
//
// Arguments:
// Pointer to Log Path
// Pointer to device extension
// Pointer to handle
//
// Return Value:
// This function returns STATUS_XXX
//--
NTSTATUS
SrLogStart(
IN PUNICODE_STRING pLogPath,
IN PSR_DEVICE_EXTENSION pExtension,
OUT PSR_LOG_CONTEXT * ppLogContext
)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PSR_LOG_CONTEXT pLogContext = NULL;
PAGED_CODE();
ASSERT(pLogPath != NULL);
ASSERT(ppLogContext != NULL);
ASSERT( IS_ACTIVITY_LOCK_ACQUIRED_EXCLUSIVE( pExtension ) ||
IS_LOG_LOCK_ACQUIRED_EXCLUSIVE( pExtension ));
try
{
*ppLogContext = NULL;
//
// should we short circuit out of here for testing mode?
//
if (!SR_LOGGING_ENABLED( pExtension ) ||
_globals.DontBackup)
{
leave;
}
*ppLogContext = SR_ALLOCATE_STRUCT( PagedPool,
SR_LOG_CONTEXT,
SR_LOG_CONTEXT_TAG );
if ( *ppLogContext == NULL )
{
Status = STATUS_INSUFFICIENT_RESOURCES;
leave;
}
pLogContext = *ppLogContext;
RtlZeroMemory( pLogContext, sizeof(SR_LOG_CONTEXT) );
pLogContext->Signature = SR_LOG_CONTEXT_TAG;
pLogContext->pExtension = pExtension;
//
// grab a buffer to store the file name
//
Status = SrAllocateFileNameBuffer( pLogPath->Length,
&(pLogContext->pLogFilePath) );
if (!NT_SUCCESS( Status ))
leave;
//
// Store our backpointer to the device extension
//
pLogContext->pExtension = pExtension;
//
// Save the filename in the context
//
RtlCopyMemory( pLogContext->pLogFilePath->Buffer,
pLogPath->Buffer,
pLogPath->Length );
pLogContext->pLogFilePath->Buffer
[pLogPath->Length/sizeof(WCHAR)] = UNICODE_NULL;
pLogContext->pLogFilePath->Length = pLogPath->Length;
#ifndef SYNC_LOG_WRITE
//
// We only need a buffer to cache the log entries if we are doing
// asynchronous log writes.
//
pLogContext->pLogBuffer = SrAllocateLogBuffer( _globals.LogBufferSize );
if ( pLogContext->pLogBuffer == NULL )
{
Status = STATUS_INSUFFICIENT_RESOURCES;
leave;
}
#endif
//
// rename the old log file if one exists
//
Status = SrLogCheckAndRename( pExtension, pLogContext->pLogFilePath);
if (!NT_SUCCESS(Status))
leave;
//
// try and open the log file
//
Status = SrLogOpen( pLogContext );
if (NT_SUCCESS(Status))
{
//
// Add the context to the logger
//
//
SrLoggerAddLogContext( global->pLogger,
pLogContext );
}
//
// Important: We should not fail after calling SrLoggerAddContext above
// because the finally clause assumes that if there's a failure,
// SrLoggerAddContext was not called yet. If this changes, use a
// BOOLEAN to appropriately indicate if SrLoggerAddContext is already
// called and use that to condition the call in the finally clause.
//
}
finally
{
Status = FinallyUnwind(SrLogStart, Status);
//
// if we are unsuccessful for some reason then clean up
// the logging structs (if necessary) .
//
if ((!NT_SUCCESS( Status )) && (pLogContext != NULL))
{
//
// We assume as per note above that the context count is not
// incremented. Increment it now because SrLogStop will decrement it
//
SrLoggerAddLogContext(global->pLogger,
pLogContext);
//
// Stop the log
//
SrLogStop( pExtension, TRUE, TRUE );
*ppLogContext = pLogContext = NULL;
}
}
RETURN(Status);
} // SrLogStart
//++
// Function:
// SrLogStop
//
// Description:
// This function closes / frees any resources used
// SR logging
//
// Arguments:
// pExtension - The SR device extension on this volume which contains
// our logging information.
// PurgeContexts - TRUE if we should purge all our contexts at this time
// CheckLog - TRUE if we should try to check and rename the log at
// this time. Note that checking the log could cause the volume
// to be remounted at this time. Therefore, if we don't want
// that to happen (i.e., during shutdown), CheckLog should be
// FALSE.
//
// Return Value:
// This function returns STATUS_XXX
//--
NTSTATUS
SrLogStop(
IN PSR_DEVICE_EXTENSION pExtension,
IN BOOLEAN PurgeContexts,
IN BOOLEAN CheckLog
)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PSR_LOG_CONTEXT pLogContext = pExtension->pLogContext;
PAGED_CODE();
//
// context must have been intialized by calling SrLogStart
//
ASSERT(IS_VALID_LOG_CONTEXT(pLogContext));
ASSERT( IS_ACTIVITY_LOCK_ACQUIRED_EXCLUSIVE( pLogContext->pExtension ) ||
IS_LOG_LOCK_ACQUIRED_EXCLUSIVE( pLogContext->pExtension ) );
try
{
//
// If we are diabling the volume then free all of the contexts
//
if (PurgeContexts)
{
SrDeleteAllContexts( pExtension );
}
//
// Close the log handle
//
if ( pLogContext->LogHandle )
{
SrTrace( LOG, ("Stopped logging : %wZ\n",
pLogContext->pLogFilePath));
Status = SrLogClose( pLogContext );
//
// Rename this log to the backup log for uniformity.
//
if (NT_SUCCESS(Status) && CheckLog)
{
Status = SrLogCheckAndRename( pExtension,
pLogContext->pLogFilePath );
}
}
//
// Remove the context from logger
//
SrLoggerRemoveLogContext( global->pLogger,
pLogContext );
//
// Free buffers
//
#ifdef SYNC_LOG_WRITE
//
// If we are doing synchronous log writes, we shouldn't have a
// buffer to free here.
//
ASSERT( pLogContext->pLogBuffer == NULL );
#else
//
// If we are doing asynchronous log writes, we need to free the buffer
// used to collect log entries.
//
if ( pLogContext->pLogBuffer )
{
SrFreeLogBuffer( pLogContext->pLogBuffer );
pLogContext->pLogBuffer = NULL;
}
#endif
if ( pLogContext->pLogFilePath )
{
SrFreeFileNameBuffer( pLogContext->pLogFilePath );
pLogContext->pLogFilePath = NULL;
}
SR_FREE_POOL_WITH_SIG(pLogContext, SR_LOG_CONTEXT_TAG);
//
// Set logging state in extension
//
pExtension->pLogContext = NULL;
pExtension->DriveChecked = FALSE;
Status = STATUS_SUCCESS;
}
finally
{
Status = FinallyUnwind(SrLogStop, Status);
}
RETURN(Status);
} // SrLogStop
//++
// Function:
// SrLogNormalize
//
// Description:
// This function ensures that any closed change.log files have been
// renamed to change[#].log format for restore. Since we can't always
// ensure that the change.log got renamed when the log was stopped (we
// can't do this on shutdown), we need to do this normalization for
// Restore.
//
// Arguments:
// pExtension - The SR Device Extension for this volume.
//
// Return Value:
// This function returns STATUS_SUCCESS if we were able to do the
// log normalization or the appropriate error code otherwise.
//--
NTSTATUS
SrLogNormalize (
IN PSR_DEVICE_EXTENSION pExtension
)
{
NTSTATUS status;
PUNICODE_STRING pLogPath = NULL;
ASSERT( IS_ACTIVITY_LOCK_ACQUIRED_EXCLUSIVE( pExtension ) ||
IS_LOG_LOCK_ACQUIRED_EXCLUSIVE( pExtension ) );
//
// Check to see if we have a restore location for this volume yet.
// If not, we don't have to do any more work.
//
status = SrCheckForRestoreLocation( pExtension );
if (!NT_SUCCESS_NO_DBGBREAK( status ))
{
status = STATUS_SUCCESS;
goto SrLogNormalize_Exit;
}
//
// We do have a restore location, so build up the log path.
//
status = SrAllocateFileNameBuffer(SR_MAX_FILENAME_LENGTH, &pLogPath);
if (!NT_SUCCESS(status))
{
goto SrLogNormalize_Exit;
}
status = SrGetLogFileName( pExtension->pNtVolumeName,
SR_FILENAME_BUFFER_LENGTH,
pLogPath );
if (!NT_SUCCESS(status))
{
goto SrLogNormalize_Exit;
}
status = SrLogCheckAndRename( pExtension, pLogPath );
SrLogNormalize_Exit:
if (pLogPath != NULL)
{
SrFreeFileNameBuffer( pLogPath );
}
RETURN( status );
}
//++
// Function:
// SrLogWrite
//
// Description:
// This function writes the entry into the log cache and
// flushes the cache in case the entry cannot fit in.
//
// Arguments:
// Pointer to Device object
//
// Return Value:
// This function returns STATUS_XXX
//--
NTSTATUS
SrLogWrite(
IN PSR_DEVICE_EXTENSION pOptionalExtension OPTIONAL,
IN PSR_LOG_CONTEXT pOptionalLogContext OPTIONAL,
IN PSR_LOG_ENTRY pLogEntry
)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PSR_LOG_CONTEXT pLogContext = NULL;
PSR_DEVICE_EXTENSION pExtension = NULL;
PAGED_CODE();
ASSERT(pOptionalExtension == NULL || IS_VALID_SR_DEVICE_EXTENSION(pOptionalExtension));
ASSERT(pOptionalExtension != NULL || pOptionalLogContext != NULL);
ASSERT(pLogEntry != NULL);
if (pOptionalExtension != NULL)
{
//
// We need to ensure that the volume hasn't been disabled before
// we do anything.
//
if (pOptionalExtension->Disabled) {
Status = STATUS_SUCCESS;
goto SrLogWrite_Exit;
}
//
// we need to make sure our disk structures are good and logging
// has been started. it's possible we have been called simply to
// log and logging was stopped due to volume lock. we have to
// check with the global lock held.
//
Status = SrCheckVolume(pOptionalExtension, FALSE);
if (!NT_SUCCESS( Status ))
goto SrLogWrite_Exit;
pLogContext = pOptionalExtension->pLogContext;
}
else
{
//
// use the free form context passed in (only SrLogOpen does this)
//
pLogContext = pOptionalLogContext;
}
ASSERT(IS_VALID_LOG_CONTEXT(pLogContext));
if (pLogContext == NULL)
{
//
// this is unexpected, but need to protect against
//
Status = STATUS_INVALID_PARAMETER;
CHECK_STATUS(Status);
goto SrLogWrite_Exit;
}
pExtension = pLogContext->pExtension;
ASSERT( pExtension != NULL );
ASSERT( IS_ACTIVITY_LOCK_ACQUIRED_SHARED( pExtension ) ||
IS_ACTIVITY_LOCK_ACQUIRED_EXCLUSIVE( pExtension ) );
try {
SrAcquireLogLockExclusive( pExtension );
//
// Check to make sure the volume is still enabled.
//
if (!SR_LOGGING_ENABLED( pExtension ))
{
Status = STATUS_SUCCESS;
leave;
}
//
// bail if logging is disabled for this context
//
if (!FlagOn(pLogContext->LoggingFlags, SR_LOG_FLAGS_ENABLE))
{
leave;
}
//
// check the log file, if it is greater than 1Mb switch the log
//
if ( (pLogContext->FileOffset +
pLogContext->BufferOffset +
pLogEntry->Header.RecordSize) > SR_MAX_LOG_FILE_SIZE )
{
Status = SrLogSwitch( pLogContext );
if (!NT_SUCCESS( Status ))
{
leave;
}
}
#ifdef SYNC_LOG_WRITE
Status = SrpLogWriteSynchronous( pExtension,
pLogContext,
pLogEntry);
#else
Status = SrpLogWriteAsynchronous( pExtension,
pLogContext,
pLogEntry);
#endif
} finally {
Status = FinallyUnwind(SrLogWrite, Status);
SrReleaseLogLock(pExtension);
}
SrLogWrite_Exit:
RETURN(Status);
} // SrLogWrite
//++
// Function:
// SrLogWriteSynchronous
//
// Description:
//
// This function writes each log entry to the current change log
// and ensures the updated change log is flushed to disk before
// it returns.
//
// Arguments:
//
// pExtension - The device extension for the volume whose change
// log is going to be updated.
// pLogContext - The log context that contains the information
// about which change log should updated.
// pLogEntry - The log entry to be written.
//
// Return Value:
// This function returns STATUS_XXX
//--
NTSTATUS
SrpLogWriteSynchronous(
IN PSR_DEVICE_EXTENSION pExtension,
IN PSR_LOG_CONTEXT pLogContext,
IN PSR_LOG_ENTRY pLogEntry
)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PAGED_CODE();
UNREFERENCED_PARAMETER( pExtension );
ASSERT( IS_VALID_SR_DEVICE_EXTENSION( pExtension ) );
ASSERT( IS_VALID_LOG_CONTEXT( pLogContext ) );
ASSERT( pLogEntry != NULL );
ASSERT( IS_LOG_LOCK_ACQUIRED_EXCLUSIVE( pExtension ) );
//
// In this mode, we are not buffering the log entries, so just point
// the pLogContext->pLogBuffer to the current pLogEntry so save the
// copy into the buffer.
//
ASSERT( pLogContext->pLogBuffer == NULL );
pLogContext->pLogBuffer = (PBYTE) pLogEntry;
pLogContext->BufferOffset = pLogEntry->Header.RecordSize;
SET_DIRTY_FLAG(pLogContext);
Status = SrLogFlush( pLogContext );
//
// Clear out the pLogBuffer reference to the pLogEntry whether or
// not the flush succeeded.
//
pLogContext->pLogBuffer = NULL;
RETURN(Status);
}
#ifndef SYNC_LOG_WRITE
//++
// Function:
// SrLogWriteAsynchronous
//
// Description:
//
// This function buffers log writes then flushes the writes when the
// buffer is full.
//
// Arguments:
//
// pExtension - The device extension for the volume whose change
// log is going to be updated.
// pLogContext - The log context that contains the information
// about which change log should updated.
// pLogEntry - The log entry to be written.
//
// Return Value:
// This function returns STATUS_XXX
//--
NTSTATUS
SrpLogWriteAsynchronous(
IN PSR_DEVICE_EXTENSION pExtension,
IN PSR_LOG_CONTEXT pLogContext,
IN PSR_LOG_ENTRY pLogEntry
)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PAGED_CODE();
ASSERT( IS_VALID_SR_DEVICE_EXTENSION( pExtension ) );
ASSERT( IS_VALID_LOG_CONTEXT( pLogContext ) );
ASSERT( pLogEntry != NULL );
ASSERT( IS_LOG_LOCK_ACQUIRED_EXCLUSIVE( pExtension ) );
//
// Check if the entry can fit into the current buffer, if not
// then adjust the last entry of the buffer and write it down
// to the disk
//
if ( (pLogContext->BufferOffset + pLogEntry->Header.RecordSize) >
_globals.LogBufferSize )
{
//
// check to make sure we have 50mb free, we are about to
// grow the file
//
Status = SrCheckFreeDiskSpace( pLogContext->LogHandle,
pExtension->pNtVolumeName );
if (!NT_SUCCESS( Status ))
{
goto SrpLogWriteAsynchronous_Exit;
}
//
// set the dirty flag because we have updated the buffer
//
SET_DIRTY_FLAG(pLogContext);
Status = SrLogFlush( pLogContext );
if (!NT_SUCCESS( Status ))
{
goto SrpLogWriteAsynchronous_Exit;
}
//
// Check to make sure that the pLogEntry itself isn't bigger
// than the _globals.LogBufferSize.
//
if (pLogEntry->Header.RecordSize > _globals.LogBufferSize)
{
PBYTE pLogBuffer;
//
// The log was just successfully flushed, therefore there should
// be no data in the buffer yet.
//
ASSERT( pLogContext->BufferOffset == 0 );
//
// For synchronous writes, we don't expect there to be a log buffer
// in the context so we will save this off in a local and NULL
// this parameter in the pLogContext while we make this call.
//
pLogBuffer = pLogContext->pLogBuffer;
pLogContext->pLogBuffer = NULL;
Status = SrpLogWriteSynchronous( pExtension,
pLogContext,
pLogEntry );
//
// We ALWAYS need to restore this pointer in the pLogContext.
//
pLogContext->pLogBuffer = pLogBuffer;
CHECK_STATUS( Status );
//
// Whether we succeeded or failed, we want to skip the logic below
// and exit now.
//
goto SrpLogWriteAsynchronous_Exit;
}
}
//
// We now have enough room in the buffer for this pLogEntry, so append the
// entry to the buffer.
//
RtlCopyMemory( pLogContext->pLogBuffer + pLogContext->BufferOffset,
pLogEntry,
pLogEntry->Header.RecordSize );
//
// Update buffer pointers to reflect the entry has been added
//
pLogContext->LastBufferOffset = pLogContext->BufferOffset;
pLogContext->BufferOffset += pLogEntry->Header.RecordSize;
SET_DIRTY_FLAG(pLogContext);
//
// We were able to copy into the buffer successfully, so return
// STATUS_SUCCESS.
//
Status = STATUS_SUCCESS;
SrpLogWriteAsynchronous_Exit:
RETURN(Status);
}
#endif
//++
// Function:
// SrLogFlush
//
// Description:
// This function flushes the buffer contents in memory
// to the log, it doesn't increment file offset in the
// log context.
//
// Arguments:
// Pointer to new Log Context
//
// Return Value:
// This function returns STATUS_XXX
//--
NTSTATUS
SrLogFlush(
IN PSR_LOG_CONTEXT pLogContext
)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
IO_STATUS_BLOCK IoStatusBlock;
BOOLEAN ExtendLogFile = FALSE;
PAGED_CODE();
ASSERT(IS_VALID_LOG_CONTEXT(pLogContext));
ASSERT( IS_ACTIVITY_LOCK_ACQUIRED_EXCLUSIVE( pLogContext->pExtension ) ||
IS_LOG_LOCK_ACQUIRED_EXCLUSIVE(pLogContext->pExtension));
SrTrace(FUNC_ENTRY, ("SR!SrLogFlush\n") );
//
// should we short circuit out of here for testing mode?
//
if (global->DontBackup)
{
Status = STATUS_SUCCESS;
goto SrLogFlush_Exit;
}
//
// bail if the context is disabled
//
if (!FlagOn( pLogContext->LoggingFlags, SR_LOG_FLAGS_ENABLE ))
{
goto SrLogFlush_Exit;
}
ASSERT( pLogContext->LogHandle != NULL );
if (FlagOn( pLogContext->LoggingFlags, SR_LOG_FLAGS_DIRTY ))
{
SrTrace( LOG, ("Flushing Log :%wZ", pLogContext->pLogFilePath));
//
// Do we need to extend our log file? We can possibly loop here if
// the amount of data we need to write is greater than our
// allocation unit. We want to make sure that if we do have to extend
// the file that we have at least extended it enough for our
// current write.
//
while ((pLogContext->BufferOffset + pLogContext->FileOffset) >
pLogContext->AllocationSize)
{
//
// This file will need to be extended for this write.
//
ExtendLogFile = TRUE;
pLogContext->AllocationSize += _globals.LogAllocationUnit;
}
if (ExtendLogFile)
{
FILE_ALLOCATION_INFORMATION fileAllocInfo;
fileAllocInfo.AllocationSize.QuadPart = pLogContext->AllocationSize;
Status = SrSetInformationFile( pLogContext->pExtension->pTargetDevice,
pLogContext->pLogFileObject,
&fileAllocInfo,
sizeof( fileAllocInfo ),
FileAllocationInformation );
if ((Status == STATUS_NO_SUCH_DEVICE) ||
(Status == STATUS_INVALID_HANDLE) ||
!NT_SUCCESS( Status ))
{
SrTrace( LOG, ("SrLogFlush: Log extension failed: 0x%x\n", Status) );
goto SrLogFlush_Exit;
}
}
//
// Write the buffer to the disk. We have opened this file in append
// only mode, so the file system will maintain the current file
// position for us. This handle was open for SYNCHRONOUS access,
// therefore, this IO will not return until it has completed.
//
ASSERT( pLogContext->pLogBuffer != NULL );
Status = ZwWriteFile( pLogContext->LogHandle,
NULL, // Event
NULL, // ApcRoutine
NULL, // ApcContext
&IoStatusBlock,
pLogContext->pLogBuffer,
pLogContext->BufferOffset,
NULL, // ByteOffset
NULL ); // Key
//
// Handle STATUS_NO_SUCH_DEVICE because we expect this when
// HotPlugable devices are removed by surprise.
//
// Handle STATUS_INVALID_HANDLE because we expect this when
// a force-dismount came through on a volume.
//
if ((Status == STATUS_NO_SUCH_DEVICE) ||
(Status == STATUS_INVALID_HANDLE) ||
!NT_SUCCESS( Status ))
{
SrTrace( LOG,("SrLogFlush: Write failed: 0x%x\n", Status) );
goto SrLogFlush_Exit;
}
Status = SrFlushBuffers( pLogContext->pExtension->pTargetDevice,
pLogContext->pLogFileObject );
if (!NT_SUCCESS( Status ))
{
SrTrace( LOG,("SrLogFlush: Flush failed: 0x%x\n", Status) );
goto SrLogFlush_Exit;
}
SrTrace( LOG,("SrLogFlush: Flush succeeded!\n"));
//
// We've dumped the buffer to disk, so update our file offset with the
// number of bytes we have written, reset our buffer pointer, and
// clear the dirty flag on this log context since it is no longer
// dirty.
//
ASSERT( pLogContext->BufferOffset == IoStatusBlock.Information );
UPDATE_LOG_OFFSET( pLogContext, pLogContext->BufferOffset );
RESET_LOG_BUFFER( pLogContext );
CLEAR_DIRTY_FLAG( pLogContext );
}
//
// If we got here, the flush was successful, so return that status.
//
Status = STATUS_SUCCESS;
SrLogFlush_Exit:
#if DBG
if (Status == STATUS_NO_SUCH_DEVICE ||
Status == STATUS_INVALID_HANDLE)
{
return Status;
}
#endif
RETURN(Status);
} // SrLogFlush
//++
// Function:
// SrLogSwitch
//
// Description:
// This function Switches the current log to the
// new log.
//
// Arguments:
// Pointer to new log context.
//
// Return Value:
// This function returns STATUS_XXX
//--
NTSTATUS
SrLogSwitch(
IN PSR_LOG_CONTEXT pLogContext
)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PAGED_CODE();
ASSERT(IS_VALID_LOG_CONTEXT(pLogContext));
ASSERT( IS_ACTIVITY_LOCK_ACQUIRED_EXCLUSIVE( pLogContext->pExtension ) ||
IS_LOG_LOCK_ACQUIRED_EXCLUSIVE( pLogContext->pExtension ) );
//
// bail if the context is disabled
//
if (!FlagOn( pLogContext->LoggingFlags, SR_LOG_FLAGS_ENABLE ))
{
Status = STATUS_UNSUCCESSFUL;
goto SrLogSwitch_Exit;
}
if (pLogContext->LogHandle)
{
//
// flush and close current log ( close - flushes also )
//
Status = SrLogClose(pLogContext);
if ( NT_SUCCESS(Status) )
{
//
// rename the old log file if one exists
//
Status = SrLogCheckAndRename( pLogContext->pExtension,
pLogContext->pLogFilePath );
if (NT_SUCCESS(Status))
{
//
// try and open the log file
//
Status = SrLogOpen( pLogContext );
}
}
}
SrLogSwitch_Exit:
RETURN(Status);
} // SrLogSwitch
/////////////////////////////////////////////////////////////////////
//
// Misc Routines : Misc routines required by the logging module
//
/////////////////////////////////////////////////////////////////////
//++
// Function:
// SrGetRestorePointPath
//
// Description:
// This function creates a path for the restore point dir.
//
// Arguments:
// Pointer to log entry buffer
// Length of the Log filename buffer
// Pointer to the Log filename buffer
//
// Return Value:
// This function returns STATUS_XXX
//--
NTSTATUS
SrGetRestorePointPath(
IN PUNICODE_STRING pVolumeName,
IN USHORT RestPtPathLength,
OUT PUNICODE_STRING pRestPtPath
)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
ULONG CharCount;
PAGED_CODE();
ASSERT(pVolumeName != NULL);
ASSERT(pRestPtPath != NULL);
try
{
//
// Copy the volume name in the log file
//
pRestPtPath->Buffer = (PWSTR)(pRestPtPath+1);
//
// TODO:(CODEWORK: grab a lock around global?)
//
//
// construct our restore point location string
//
CharCount = swprintf( pRestPtPath->Buffer,
VOLUME_FORMAT RESTORE_LOCATION,
pVolumeName,
global->MachineGuid );
pRestPtPath->Length = (USHORT)CharCount * sizeof(WCHAR);
pRestPtPath->MaximumLength = RestPtPathLength;
if ( pRestPtPath->Length > RestPtPathLength )
{
Status = STATUS_INSUFFICIENT_RESOURCES;
leave;
}
//
// Append the restore point directory
//
CharCount = swprintf(
&pRestPtPath->Buffer[pRestPtPath->Length/sizeof(WCHAR)],
L"\\" RESTORE_POINT_PREFIX L"%d\\",
global->FileConfig.CurrentRestoreNumber );
pRestPtPath->Length += (USHORT)CharCount * sizeof(WCHAR);
pRestPtPath->MaximumLength = RestPtPathLength;
if ( pRestPtPath->Length > RestPtPathLength )
{
Status = STATUS_INSUFFICIENT_RESOURCES;
leave;
}
//
// NULL terminate it
//
pRestPtPath->Buffer[pRestPtPath->Length/sizeof(WCHAR)] = UNICODE_NULL;
Status = STATUS_SUCCESS;
}
finally
{
}
RETURN(Status);
} // SrGetRestorePointPath
//++
// Function:
// SrGetLogFileName
//
// Description:
// This function creates a path for change log in restore point dir.
//
// Arguments:
// Pointer to log entry buffer
// Length of the Log filename buffer
// Pointer to the Log filename buffer
//
// Return Value:
// This function returns STATUS_XXX
//--
NTSTATUS
SrGetLogFileName(
IN PUNICODE_STRING pVolumeName,
IN USHORT LogFileNameLength,
OUT PUNICODE_STRING pLogFileName
)
{
NTSTATUS Status;
PAGED_CODE();
ASSERT(pVolumeName != NULL);
ASSERT(pLogFileName != NULL);
//
// Get restore point path
//
Status = SrGetRestorePointPath( pVolumeName,
LogFileNameLength,
pLogFileName );
if (NT_SUCCESS(Status))
{
//
// Append changelog file name
//
Status = RtlAppendUnicodeToString( pLogFileName, s_cszCurrentChangeLog );
}
RETURN(Status);
} // SrGetLogFileName
//++
// Function:
// SrGetAclFileName
//
// Description:
// This function creates a path for Acl file in restore point dir.
//
// Arguments:
// Pointer to log entry buffer
// Length of the Log filename buffer
// Pointer to the Log filename buffer
//
// Return Value:
// This function returns STATUS_XXX
//--
NTSTATUS
SrGetAclFileName(
IN PUNICODE_STRING pVolumeName,
IN USHORT AclFileNameLength,
OUT PUNICODE_STRING pAclFileName
)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
ULONG CharCount;
ULONG NextFileNumber;
PAGED_CODE();
ASSERT(pVolumeName != NULL);
ASSERT(pAclFileName != NULL);
//
// Get restore point path
//
Status = SrGetRestorePointPath( pVolumeName,
AclFileNameLength,
pAclFileName );
if (NT_SUCCESS(Status))
{
//
// Generate a new file number and append it to the path above
//
Status = SrGetNextFileNumber(&NextFileNumber);
if (!NT_SUCCESS( Status ))
goto End;
//
// use the "S" prefix (e.g. "S0000001.Acl" )
//
CharCount = swprintf( &pAclFileName->Buffer[pAclFileName->Length/sizeof(WCHAR)],
ACL_FILE_PREFIX L"%07d" ACL_FILE_SUFFIX,
NextFileNumber );
pAclFileName->Length += (USHORT)CharCount * sizeof(WCHAR);
if ( pAclFileName->Length > AclFileNameLength )
{
goto End;
}
//
// NULL terminate it
//
pAclFileName->Buffer[pAclFileName->Length/sizeof(WCHAR)]=UNICODE_NULL;
}
End:
RETURN(Status);
} // SrGetAclFileName
//++
// Function:
// SrGetAclInformation
//
// Description:
// This function gets the acl information from the given file
// and packs it into a sub-record
//
// Arguments:
// Pointer to filename
// Pointer to variable to return the address of security info
// Pointer to variable to return the size of security info
//
// Return Value:
// This function returns STATUS_XXX
//--
NTSTATUS
SrGetAclInformation (
IN PFILE_OBJECT pFileObject,
IN PSR_DEVICE_EXTENSION pExtension,
OUT PSECURITY_DESCRIPTOR * ppSecurityDescriptor,
OUT PULONG pSizeNeeded
)
{
NTSTATUS Status = STATUS_SUCCESS;
PSECURITY_DESCRIPTOR pSecurityDescriptor = NULL;
ULONG SizeNeeded = 256;
struct
{
FILE_FS_ATTRIBUTE_INFORMATION Info;
WCHAR Buffer[ 50 ];
} FileFsAttrInfoBuffer;
PAGED_CODE();
ASSERT(IS_VALID_FILE_OBJECT(pFileObject));
ASSERT(pSizeNeeded != NULL);
ASSERT(ppSecurityDescriptor != NULL);
try
{
*ppSecurityDescriptor = NULL;
*pSizeNeeded = 0;
//
// First check if we already know if the fs supports acls
// Do this check now so we can leave real fast if the volume
// doesn't support ACLs
//
if (pExtension->CachedFsAttributes &&
(!FlagOn(pExtension->FsAttributes, FILE_PERSISTENT_ACLS))) {
leave;
}
//
// Check if the file system supports acl stuff if necessary
//
if (!pExtension->CachedFsAttributes) {
//
// We need to check now
//
Status = SrQueryVolumeInformationFile( pExtension->pTargetDevice,
pFileObject,
&FileFsAttrInfoBuffer.Info,
sizeof(FileFsAttrInfoBuffer),
FileFsAttributeInformation,
NULL );
if (!NT_SUCCESS( Status ))
leave;
//
// Stow away the attributes for later use
//
pExtension->CachedFsAttributes = TRUE;
pExtension->FsAttributes = FileFsAttrInfoBuffer.Info.FileSystemAttributes;
if (!FlagOn(pExtension->FsAttributes, FILE_PERSISTENT_ACLS))
leave;
}
//
// Read in the security information from the source file
// (looping until we get a big enough buffer).
//
while (TRUE )
{
//
// Alloc a buffer to hold the security info.
//
pSecurityDescriptor = SR_ALLOCATE_ARRAY( PagedPool,
UCHAR,
SizeNeeded,
SR_SECURITY_DATA_TAG );
if (NULL == pSecurityDescriptor)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
leave;
}
//
// Query the security info
//
Status = SrQuerySecurityObject( pExtension->pTargetDevice,
pFileObject,
DACL_SECURITY_INFORMATION
|SACL_SECURITY_INFORMATION
|OWNER_SECURITY_INFORMATION
|GROUP_SECURITY_INFORMATION,
pSecurityDescriptor,
SizeNeeded,
&SizeNeeded );
//
// Not enough buffer?
//
if (STATUS_BUFFER_TOO_SMALL == Status ||
STATUS_BUFFER_OVERFLOW == Status)
{
//
// Get a bigger buffer and try again.
//
SR_FREE_POOL( pSecurityDescriptor,
SR_SECURITY_DATA_TAG );
pSecurityDescriptor = NULL;
SizeNeeded *= 2;
continue;
}
break;
}
if (!NT_SUCCESS( Status ))
leave;
//
// Security descriptor should be self relative.
//
ASSERT(((PISECURITY_DESCRIPTOR_RELATIVE)pSecurityDescriptor)->Control & SE_SELF_RELATIVE);
//
// return the security information
//
*ppSecurityDescriptor = pSecurityDescriptor;
pSecurityDescriptor = NULL;
*pSizeNeeded = SizeNeeded;
SrTrace( LOG, ("sr!SrGetAclInformation: returning [0x%p,%d]\n",
*ppSecurityDescriptor,
SizeNeeded ));
} finally {
Status = FinallyUnwind(SrGetAclInformation, Status);
if (pSecurityDescriptor != NULL)
{
SR_FREE_POOL( pSecurityDescriptor, SR_SECURITY_DATA_TAG );
pSecurityDescriptor = NULL;
}
}
RETURN(Status);
} // SrGetAclInformation