#include #include #include #include #include #include #include #include #include #include #include #include "rpdh.h" const DWORD dwFileHeaderLength = 13; const DWORD dwTypeLoc = 2; const DWORD dwFieldLength = 7; const DWORD dwPerfmonTypeLength = 5; LPCSTR szTsvType = "PDH-TSV"; LPCSTR szCsvType = "PDH-CSV"; LPCSTR szBinaryType = "PDH-BIN"; LPCWSTR cszPerfmonLogSig = (LPCWSTR)L"Loges"; #define MemoryAllocate(x) \ ((LPVOID) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, x)) #define MemoryFree(x) \ if( x ){ ((VOID) HeapFree(GetProcessHeap(), 0, x)); x = NULL; } #define MemoryResize(x,y) \ ((LPVOID) HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, x, y)); #define MAKEULONGLONG(low, high) \ ((ULONGLONG) (((DWORD) (low)) | ((ULONGLONG) ((DWORD) (high))) << 32)) #define PDH_BLG_HEADER_SIZE 24 #define PDH_MAX_BUFFER 0x01000000 #define PDH_HEADER_BUFFER 65536 typedef struct _PDHI_BINARY_LOG_RECORD_HEADER { DWORD dwType; DWORD dwLength; } PDHI_BINARY_LOG_RECORD_HEADER, *PPDHI_BINARY_LOG_RECORD_HEADER; PDH_STATUS R_PdhRelog( LPTSTR szSource, HQUERY hQuery, PPDH_RELOG_INFO_W pRelogInfo ) { PDH_STATUS pdhStatus = ERROR_SUCCESS; HLOG hLogOut; ULONG nSampleCount = 0; ULONG nSamplesWritten = 0; ULONG nRecordSkip; pdhStatus = PdhOpenLogW( pRelogInfo->strLog, pRelogInfo->dwFlags, &pRelogInfo->dwFileFormat, hQuery, 0, NULL, &hLogOut ); if( pdhStatus == ERROR_SUCCESS ){ DWORD dwNumEntries = 1; DWORD dwBufferSize = sizeof(PDH_TIME_INFO); PDH_TIME_INFO TimeInfo; ZeroMemory( &TimeInfo, sizeof( PDH_TIME_INFO ) ); pdhStatus = PdhGetDataSourceTimeRange( szSource, &dwNumEntries, &TimeInfo, &dwBufferSize ); if( pRelogInfo->TimeInfo.StartTime > TimeInfo.StartTime && pRelogInfo->TimeInfo.StartTime < TimeInfo.EndTime ){ TimeInfo.StartTime = pRelogInfo->TimeInfo.StartTime; } if( pRelogInfo->TimeInfo.EndTime < TimeInfo.EndTime && pRelogInfo->TimeInfo.EndTime > TimeInfo.StartTime ){ TimeInfo.EndTime = pRelogInfo->TimeInfo.EndTime; } if( TimeInfo.EndTime > TimeInfo.StartTime ){ pdhStatus = PdhSetQueryTimeRange( hQuery, &TimeInfo ); } nRecordSkip = pRelogInfo->TimeInfo.SampleCount >= 1 ? pRelogInfo->TimeInfo.SampleCount : 1; while( ERROR_SUCCESS == pdhStatus ){ if( nSampleCount++ % nRecordSkip ){ pdhStatus = PdhCollectQueryData( hQuery ); continue; } if( ERROR_SUCCESS == pdhStatus ){ pdhStatus = PdhUpdateLog( hLogOut, NULL ); nSamplesWritten++; } } if( PDH_NO_MORE_DATA == pdhStatus || (PDH_ENTRY_NOT_IN_LOG_FILE == pdhStatus && pRelogInfo->dwFileFormat == PDH_LOG_TYPE_BINARY) ){ pdhStatus = PdhCloseLog( hLogOut, 0 ); if( ERROR_SUCCESS == pdhStatus ){ PdhGetDataSourceTimeRange( pRelogInfo->strLog, &dwNumEntries, &pRelogInfo->TimeInfo, &dwBufferSize ); } } } pRelogInfo->TimeInfo.SampleCount = nSamplesWritten; return pdhStatus; } DWORD GetLogFileType ( IN HANDLE hLogFile ) { CHAR cBuffer[MAX_PATH]; CHAR cType[MAX_PATH]; WCHAR wcType[MAX_PATH]; BOOL bStatus; DWORD dwResult; DWORD dwBytesRead; memset (&cBuffer[0], 0, sizeof(cBuffer)); memset (&cType[0], 0, sizeof(cType)); memset (&wcType[0], 0, sizeof(wcType)); // read first log file record SetFilePointer (hLogFile, 0, NULL, FILE_BEGIN); bStatus = ReadFile (hLogFile, (LPVOID)cBuffer, dwFileHeaderLength, &dwBytesRead, NULL); if (bStatus) { // read header record to get type lstrcpynA (cType, (LPSTR)(cBuffer+dwTypeLoc), dwFieldLength+1); if (lstrcmpA(cType, szTsvType) == 0) { dwResult = PDH_LOG_TYPE_TSV; } else if (lstrcmpA(cType, szCsvType) == 0) { dwResult = PDH_LOG_TYPE_CSV; } else if (lstrcmpA(cType, szBinaryType) == 0) { dwResult = PDH_LOG_TYPE_RETIRED_BIN_; } else { // perfmon log file type string is in a different // location than sysmon logs and used wide chars. lstrcpynW (wcType, (LPWSTR)cBuffer, dwPerfmonTypeLength+1); if (lstrcmpW(wcType, cszPerfmonLogSig) == 0) { dwResult = PDH_LOG_TYPE_PERFMON; } else { dwResult = PDH_LOG_TYPE_UNDEFINED; } } } else { // unable to read file dwResult = PDH_LOG_TYPE_UNDEFINED; } return dwResult; } PDH_STATUS R_PdhBlgLogFileHeader(LPCWSTR szFile1, LPCWSTR szFile2) { BOOL bReturn = FALSE; PDH_STATUS Status = ERROR_SUCCESS; LPWSTR szHeaderList1 = NULL; LPWSTR szHeaderList2 = NULL; DWORD dwHeader1 = 0; DWORD dwHeader2 = 0; LPWSTR szName1; LPWSTR szName2; Status = PdhListLogFileHeaderW(szFile1, szHeaderList1, &dwHeader1); if (Status != ERROR_SUCCESS && Status != PDH_MORE_DATA && Status != PDH_INSUFFICIENT_BUFFER){ goto Cleanup; } szHeaderList1 = (LPWSTR)MemoryAllocate( (dwHeader1+1)*sizeof(WCHAR) ); Status = PdhListLogFileHeaderW(szFile1, szHeaderList1, &dwHeader1); if (Status != ERROR_SUCCESS ){ goto Cleanup; } Status = PdhListLogFileHeaderW(szFile2, szHeaderList2, &dwHeader2); if (Status != ERROR_SUCCESS && Status != PDH_MORE_DATA && Status != PDH_INSUFFICIENT_BUFFER){ goto Cleanup; } szHeaderList2 = (LPWSTR)MemoryAllocate( (dwHeader2+1)*sizeof(WCHAR) ); Status = PdhListLogFileHeaderW(szFile2, szHeaderList2, &dwHeader2); if (Status != ERROR_SUCCESS ){ goto Cleanup; } for (szName1 = szHeaderList1, szName2 = szHeaderList2; szName1[0] != L'\0' && szName2[0] != L'\0'; szName1 += (lstrlenW(szName1) + 1), szName2 += (lstrlenW(szName2) + 1)) { if (lstrcmpiW(szName1, szName2) != 0) { varg_printf( g_debug, _T("(%s) != (%s)\n"), szName1, szName2); break; } } if (szName1[0] != L'\0' || szName2[0] != L'\0'){ Status = PDH_HEADER_MISMATCH; } Cleanup: MemoryFree( szHeaderList1 ); MemoryFree( szHeaderList2 ); return Status; } PDH_STATUS R_PdhGetLogFileType( IN LPCWSTR LogFileName, IN LPDWORD LogFileType) { PDH_STATUS pdhStatus = ERROR_SUCCESS; HANDLE hFile; DWORD dwLogFormat; if (LogFileName == NULL) { pdhStatus = PDH_INVALID_ARGUMENT; } else { __try { dwLogFormat = * LogFileType; * LogFileType = dwLogFormat; if (* LogFileName == L'\0') { pdhStatus = PDH_INVALID_ARGUMENT; } } __except (EXCEPTION_EXECUTE_HANDLER) { pdhStatus = PDH_INVALID_ARGUMENT; } } if (pdhStatus == ERROR_SUCCESS) { hFile = CreateFileW(LogFileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == NULL || hFile == INVALID_HANDLE_VALUE) { pdhStatus = PDH_LOG_FILE_OPEN_ERROR; } } if (pdhStatus == ERROR_SUCCESS) { dwLogFormat = GetLogFileType(hFile); CloseHandle(hFile); } if (pdhStatus == ERROR_SUCCESS) { * LogFileType = dwLogFormat; } return pdhStatus; } PDH_STATUS R_PdhRelogCopyFile( HANDLE hOutFile, HANDLE hInFile, BOOL bCopyHeader ) { BOOL bResult = TRUE; PDH_STATUS pdhStatus = ERROR_SUCCESS; DWORD dwSizeLow = 0; DWORD dwSizeHigh = 0; DWORD dwBuffer = PDH_MAX_BUFFER; ULONGLONG lFileSize = 0; ULONGLONG lCurrent = 0; LPBYTE pBuffer = NULL; LPBYTE pCurrent = NULL; PPDHI_BINARY_LOG_RECORD_HEADER pPdhBlgHeader = NULL; dwSizeLow = GetFileSize(hInFile, & dwSizeHigh); lFileSize = MAKEULONGLONG(dwSizeLow, dwSizeHigh); lCurrent = PDH_BLG_HEADER_SIZE + sizeof(PDHI_BINARY_LOG_RECORD_HEADER); if (lCurrent > lFileSize) { pdhStatus = PDH_INVALID_HANDLE; goto Cleanup; } pBuffer = (LPBYTE)MemoryAllocate(dwBuffer); if (pBuffer == NULL) { pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; goto Cleanup; } dwSizeLow = (DWORD) lCurrent; dwSizeHigh = 0; bResult = ReadFile(hInFile, pBuffer, dwSizeLow, & dwSizeHigh, NULL); if ((bResult == FALSE) || (dwSizeLow != dwSizeHigh)) { pdhStatus = PDH_INVALID_DATA; goto Cleanup; } pPdhBlgHeader = (PPDHI_BINARY_LOG_RECORD_HEADER) (pBuffer + PDH_BLG_HEADER_SIZE); if (pPdhBlgHeader->dwType != 0x01024C42) { pdhStatus = PDH_INVALID_ARGUMENT; goto Cleanup; } if (pPdhBlgHeader->dwLength + PDH_BLG_HEADER_SIZE > dwBuffer) { LPBYTE pTemp = pBuffer; pBuffer = (LPBYTE)MemoryResize(pBuffer, pPdhBlgHeader->dwLength); if (pBuffer == NULL) { MemoryFree(pTemp); pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; goto Cleanup; } } if ((lCurrent + dwSizeLow) > lFileSize) { pdhStatus = PDH_INVALID_HANDLE; goto Cleanup; } pCurrent = (LPBYTE) (pBuffer + dwSizeLow); dwSizeLow = pPdhBlgHeader->dwLength - sizeof(PDHI_BINARY_LOG_RECORD_HEADER); dwSizeHigh = 0; bResult = ReadFile(hInFile, pCurrent, dwSizeLow, & dwSizeHigh, NULL); if ((bResult == FALSE) || (dwSizeLow != dwSizeHigh)) { pdhStatus = PDH_INVALID_DATA; goto Cleanup; } lCurrent += dwSizeLow; if (bCopyHeader) { dwSizeLow = dwSizeHigh = (DWORD) lCurrent; bResult = WriteFile(hOutFile, pBuffer, dwSizeLow, & dwSizeHigh, NULL); if ((bResult == FALSE) || (dwSizeLow != dwSizeHigh)) { pdhStatus = PDH_INVALID_DATA; goto Cleanup; } } while (lCurrent + sizeof(PDHI_BINARY_LOG_RECORD_HEADER) < lFileSize) { ZeroMemory(pBuffer, dwBuffer); dwSizeLow = sizeof(PDHI_BINARY_LOG_RECORD_HEADER); dwSizeHigh = 0; bResult = ReadFile(hInFile, pBuffer, dwSizeLow, & dwSizeHigh, NULL); if ((bResult == FALSE) || (dwSizeLow != dwSizeHigh)) { pdhStatus = PDH_INVALID_DATA; goto Cleanup; } pPdhBlgHeader = (PPDHI_BINARY_LOG_RECORD_HEADER) pBuffer; if ((pPdhBlgHeader->dwType & 0x0000FFFF) != 0x00004C42) { pdhStatus = PDH_INVALID_ARGUMENT; goto Cleanup; } if (pPdhBlgHeader->dwLength > dwBuffer) { LPBYTE pTemp = pBuffer; pBuffer = (LPBYTE)MemoryResize(pBuffer, pPdhBlgHeader->dwLength); if (pBuffer == NULL) { MemoryFree(pTemp); pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; goto Cleanup; } } if ((lCurrent + pPdhBlgHeader->dwLength) > lFileSize) { pdhStatus = PDH_INVALID_HANDLE; bResult = FALSE; goto Cleanup; } pCurrent = (LPBYTE) (pBuffer + dwSizeLow); dwSizeLow = pPdhBlgHeader->dwLength - sizeof(PDHI_BINARY_LOG_RECORD_HEADER); dwSizeHigh = 0; bResult = ReadFile(hInFile, pCurrent, dwSizeLow, & dwSizeHigh, NULL); if ((bResult == FALSE) || (dwSizeLow != dwSizeHigh)) { pdhStatus = PDH_INVALID_DATA; goto Cleanup; } if ((pPdhBlgHeader->dwType & 0x00FF0000) == 0x00030000) { dwSizeLow = dwSizeHigh = pPdhBlgHeader->dwLength; bResult = WriteFile( hOutFile, pBuffer, dwSizeLow, & dwSizeHigh, NULL); if ((bResult == FALSE) || (dwSizeLow != dwSizeHigh)) { pdhStatus = PDH_INVALID_DATA; goto Cleanup; } } lCurrent += pPdhBlgHeader->dwLength; } Cleanup: if (pBuffer != NULL){ MemoryFree(pBuffer); } return pdhStatus; } PDH_STATUS R_PdhAppendLog( LPTSTR szSource, LPTSTR szAppend ) { HANDLE hSource = INVALID_HANDLE_VALUE; HANDLE hAppend = INVALID_HANDLE_VALUE; BOOL bResult = TRUE; PDH_STATUS pdhStatus; DWORD dwFormat; PDH_TIME_INFO TimeSource; PDH_TIME_INFO TimeAppend; LPTSTR mszSourceHeader = NULL; LPTSTR mszAppendHeader = NULL; DWORD dwSourceSize = 0; DWORD dwAppendSize = 0; DWORD dwNumEntries = 1; DWORD dwBufferSize = sizeof(PDH_TIME_INFO); // // Compare the log types, both must be Windows 2000 binary // pdhStatus = R_PdhGetLogFileType( szSource, &dwFormat ); if( ERROR_SUCCESS != pdhStatus || PDH_LOG_TYPE_RETIRED_BIN_ != dwFormat ){ if( ERROR_SUCCESS == pdhStatus ){ pdhStatus = PDH_TYPE_MISMATCH; } goto cleanup; } pdhStatus = R_PdhGetLogFileType( szAppend, &dwFormat ); if( ERROR_SUCCESS != pdhStatus || PDH_LOG_TYPE_RETIRED_BIN_ != dwFormat ){ if( ERROR_SUCCESS == pdhStatus ){ pdhStatus = PDH_TYPE_MISMATCH; } goto cleanup; } // // Compare the time ranges, source file must start after append end // ZeroMemory( &TimeSource, sizeof( PDH_TIME_INFO ) ); ZeroMemory( &TimeAppend, sizeof( PDH_TIME_INFO ) ); pdhStatus = PdhGetDataSourceTimeRange( szSource, &dwNumEntries, &TimeSource, &dwBufferSize ); if( ERROR_SUCCESS != pdhStatus ){ goto cleanup; } pdhStatus = PdhGetDataSourceTimeRange( szAppend, &dwNumEntries, &TimeAppend, &dwBufferSize ); if( ERROR_SUCCESS != pdhStatus ){ goto cleanup; } if( TimeSource.EndTime > TimeAppend.StartTime ){ pdhStatus = PDH_TIME_MISMATCH; goto cleanup; } // // Compare the headers, must be exact match // pdhStatus = R_PdhBlgLogFileHeader( szSource, szAppend ); if( ERROR_SUCCESS != pdhStatus ){ goto cleanup; } // // All good, now do the actual append // hSource = CreateFileW( szSource, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); hAppend = CreateFileW( szAppend, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if ( INVALID_HANDLE_VALUE == hSource || INVALID_HANDLE_VALUE == hAppend ){ pdhStatus = PDH_LOG_FILE_OPEN_ERROR; goto cleanup; } SetFilePointer( hSource, NULL, NULL, FILE_END ); pdhStatus = R_PdhRelogCopyFile( hSource, hAppend, FALSE); cleanup: MemoryFree( mszSourceHeader ); MemoryFree( mszAppendHeader ); if ( INVALID_HANDLE_VALUE != hAppend ) { CloseHandle(hAppend); } if ( INVALID_HANDLE_VALUE != hSource ) { CloseHandle(hSource); } return pdhStatus; }