/*++ 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