|
|
/*
Copyright (c) 2000-2001 Microsoft Corporation
Module Name:
extattr.cpp
Abstract:
Get's additional file attributes beyond what you get with FindFirstFile/FindNextFile.
Author:
Stefan R. Steiner [ssteiner] 02-27-2000
Revision History:
--*/
#include "stdafx.h"
#include <ntioapi.h>
#include <aclapi.h>
#include <sddl.h>
#include "direntrs.h"
#include "extattr.h"
#include "hardlink.h"
#define READ_BUF_SIZE ( 1024 * 1024 )
#define FSD_SHARE_MODE ( FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE )
#define FSD_MS_HSM_REPARSE_TAG 0xC0000004
static VOID eaGetSecurityInfo( IN CDumpParameters *pcParams, IN const CBsString &cwsFileName, OUT SFileExtendedInfo *psExtendedInfo );
static VOID eaGetFileInformationByHandle( IN CDumpParameters *pcParams, IN const CBsString &cwsFileName, IN OUT SDirectoryEntry *psDirEntry, OUT SFileExtendedInfo *psExtendedInfo );
static VOID eaGetAlternateStreamInfo( IN CDumpParameters *pcParams, IN const CBsString &cwsFileName, OUT SFileExtendedInfo *psExtendedInfo );
static VOID eaGetReparsePointInfo( IN CDumpParameters *pcParams, IN const CBsString &cwsFileName, IN OUT ULONGLONG *pullBytesChecksummed, IN OUT SDirectoryEntry *psDirEntry, OUT SFileExtendedInfo *psExtendedInfo );
static BOOL eaChecksumRawEncryptedData( IN CDumpParameters *pcParams, IN const CBsString& cwsFileName, IN OUT SFileExtendedInfo *psExtendedInfo );
static BOOL eaChecksumStream( IN const CBsString& cwsStreamPath, IN OUT ULONGLONG *pullBytesChecksummed, IN OUT DWORD *pdwRunningCheckSum );
static DWORD eaChecksumBlock( IN DWORD dwRunningChecksum, IN LPBYTE pBuffer, IN DWORD dwBufSize );
static VOID eaConvertUserSidToString ( IN CDumpParameters *pcParams, IN PSID pSid, OUT CBsString *pcwsSid );
static VOID eaConvertGroupSidToString ( IN CDumpParameters *pcParams, IN PSID pSid, OUT CBsString *pcwsSid );
static VOID eaConvertSidToString ( IN CDumpParameters *pcParams, IN PSID pSid, OUT CBsString *pcwsSid );
static DWORD eaChecksumHSMReparsePoint( IN CDumpParameters *pcParams, IN PREPARSE_DATA_BUFFER pReparseData, IN DWORD dwTotalSize // Size of reparse point data
);
static VOID eaGetObjectIdInfo( IN CDumpParameters *pcParams, IN const CBsString &cwsFileName, IN OUT ULONGLONG *pullBytesChecksummed, IN OUT SDirectoryEntry *psDirEntry, IN OUT SFileExtendedInfo *psExtendedInfo );
/*++
Routine Description:
Performs all of the checksums, and retrieves the security info for one file.
Arguments:
Return Value:
--*/ VOID GetExtendedFileInfo( IN CDumpParameters *pcParams, IN CFsdVolumeState *pcFsdVolState, IN const CBsString& cwsDirPath, IN BOOL bSingleEntryOutput, IN OUT SDirectoryEntry *psDirEntry, OUT SFileExtendedInfo *psExtendedInfo ) { CBsString cwsFullPath( cwsDirPath );
//
// If we are dumping an individual file's data, cwsDirPath has the complete
// path to the file, otherwise glue the filename from the find data structure
// to the path.
//
if ( !bSingleEntryOutput ) { cwsFullPath += psDirEntry->GetFileName(); }
//
// Get the information that retrieved from GetFileInformationByHandle
//
::eaGetFileInformationByHandle( pcParams, cwsFullPath, psDirEntry, psExtendedInfo );
if ( psExtendedInfo->lNumberOfLinks > 1 && pcParams->m_eFsDumpType != eFsDumpFile ) { if ( pcFsdVolState->IsHardLinkInList( psExtendedInfo->ullFileIndex, cwsDirPath, psDirEntry->GetFileName(), &psDirEntry->m_sFindData, psExtendedInfo ) ) { //
// Found the link in the list, return with the previous link's information, except
// zero out the number of bytes checksummed so that total counts remain accurate.
//
psExtendedInfo->ullTotalBytesChecksummed = 0; psExtendedInfo->ullTotalBytesNamedDataStream = 0; return; } }
//
// Get the security information.
//
::eaGetSecurityInfo( pcParams, cwsFullPath, psExtendedInfo );
eaGetObjectIdInfo( pcParams, cwsFullPath, &psExtendedInfo->ullTotalBytesChecksummed, psDirEntry, psExtendedInfo );
//
// Get the reparse point information if necessary
//
if ( psDirEntry->m_sFindData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ) ::eaGetReparsePointInfo( pcParams, cwsFullPath, &psExtendedInfo->ullTotalBytesChecksummed, psDirEntry, psExtendedInfo );
//
// Get the raw encryption data checksum if necessary
//
if ( !pcParams->m_bNoChecksums && psDirEntry->m_sFindData.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED && !( psDirEntry->m_sFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ) ::eaChecksumRawEncryptedData( pcParams, cwsFullPath, psExtendedInfo );
//
// Checksum the unnamed datastream if this is not a directory
//
if ( !pcParams->m_bNoChecksums && !( psDirEntry->m_sFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ) { DWORD dwChecksum = 0; ULONGLONG ullFileSize = ( ( ULONGLONG )( psDirEntry->m_sFindData.nFileSizeHigh ) << 32 ) + psDirEntry->m_sFindData.nFileSizeLow;
if ( ullFileSize == 0 ) { //
// In this case the default value for checksum of -------- is correct.
//
} else if ( psDirEntry->m_sFindData.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE && pcParams->m_bDontChecksumHighLatencyData ) { psExtendedInfo->cwsUnnamedStreamChecksum = L"HighLtcy"; } else if ( ::eaChecksumStream( cwsFullPath, &psExtendedInfo->ullTotalBytesChecksummed, &dwChecksum ) ) { psExtendedInfo->cwsUnnamedStreamChecksum.Format( pcParams->m_pwszULongHexFmt, dwChecksum ); } else { psExtendedInfo->cwsUnnamedStreamChecksum.Format( L"<%6d>", ::GetLastError() ); } }
//
// Get info on and checksum the named data streams
//
::eaGetAlternateStreamInfo( pcParams, cwsFullPath, psExtendedInfo );
//
// If this file is multiply linked, add it to the hard-link file list
//
if ( psExtendedInfo->lNumberOfLinks > 1 && pcParams->m_eFsDumpType != eFsDumpFile ) { pcFsdVolState->AddHardLinkToList( psExtendedInfo->ullFileIndex, cwsDirPath, psDirEntry->GetFileName(), &psDirEntry->m_sFindData, psExtendedInfo ); } }
/*++
Routine Description:
Gets the security information for a file
Arguments:
Return Value:
--*/ static VOID eaGetSecurityInfo( IN CDumpParameters *pcParams, IN const CBsString &cwsFileName, OUT SFileExtendedInfo *psExtendedInfo ) { //
// Now get the security information
//
PACL psDacl = NULL, psSacl = NULL; PSID pOwnerSid = NULL, pGroupSid = NULL; DWORD dwRet; DWORD dwSaclErrorRetCode = ERROR_SUCCESS;
PSECURITY_DESCRIPTOR pDesc = NULL;
try { dwRet = ::GetNamedSecurityInfoW( ( LPWSTR )cwsFileName.c_str(), // strange API, should ask for const
SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION, &pOwnerSid, &pGroupSid, &psDacl, &psSacl, &pDesc );
//
// If it didn't work, try again without the Sacl information
//
if ( dwRet != ERROR_SUCCESS ) { dwSaclErrorRetCode = dwRet; psSacl = NULL; dwRet = ::GetNamedSecurityInfoW( ( LPWSTR )cwsFileName.c_str(), // strange API, should ask for const
SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION, &pOwnerSid, &pGroupSid, &psDacl, NULL, &pDesc ); }
#if 0
//
// Test code to find security API problem
//
pDesc = ::LocalAlloc( LMEM_FIXED, 4096 ); DWORD dwLengthNeeded; dwRet = ERROR_SUCCESS;
if ( !::GetFileSecurityW( cwsFileName, // DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
DACL_SECURITY_INFORMATION, // | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
pDesc, 4096, &dwLengthNeeded ) ) dwRet = ::GetLastError(); if ( dwRet == ERROR_SUCCESS ) wprintf( L"Got security descripter for '%s'\n", cwsFileName.c_str() ); else wprintf( L"Error getting descripter for '%s', dwRet: %d\n", cwsFileName.c_str(), dwRet ); #endif
if ( dwRet == ERROR_SUCCESS ) { if ( pDesc && pcParams->m_bEnableSDCtrlWordDump ) { SECURITY_DESCRIPTOR_CONTROL sdc; DWORD dwDescRevision; if ( ::GetSecurityDescriptorControl( pDesc, &sdc, &dwDescRevision ) ) psExtendedInfo->wSecurityDescriptorControl = ( WORD )( sdc & ~SE_SELF_RELATIVE ); else psExtendedInfo->wSecurityDescriptorControl = -1; } else psExtendedInfo->wSecurityDescriptorControl = -1;
if ( psDacl ) { psExtendedInfo->lNumDACEs = 0; psExtendedInfo->wDACLSize = 0; //
// Checksum the DACL data if necessary.
// n.b. We only take into account ACEs that are inherited.
//
if ( psDacl->AclSize > 0 ) { DWORD dwChecksum = 0; //
// The first ACE is right after the ACL header
//
PACE_HEADER pAceHeader = ( PACE_HEADER )( psDacl + 1 ); for ( USHORT aceNum = 0; aceNum < psDacl->AceCount; ++aceNum ) { //
// Skip if an inherited ACE
//
if ( !( pAceHeader->AceFlags & INHERITED_ACE ) ) { dwChecksum += ::eaChecksumBlock( dwChecksum, ( LPBYTE )pAceHeader, pAceHeader->AceSize ); ++psExtendedInfo->lNumDACEs; psExtendedInfo->wDACLSize += pAceHeader->AceSize; if ( pcParams->m_bPrintDebugInfo ) wprintf( L"\t%d: f: %04x, t: %04x, s: %u\n", aceNum, pAceHeader->AceFlags, pAceHeader->AceType, pAceHeader->AceSize ); } pAceHeader = ( PACE_HEADER )( ( ( LPBYTE )pAceHeader ) + pAceHeader->AceSize ); } if ( psExtendedInfo->wDACLSize > 0 ) { psExtendedInfo->cwsDACLChecksum.Format( pcParams->m_pwszULongHexFmt, dwChecksum ); psExtendedInfo->ullTotalBytesChecksummed += psExtendedInfo->wDACLSize; } } } else psExtendedInfo->lNumDACEs = 0; // probably FAT or CDROM fs
if ( psSacl ) { psExtendedInfo->lNumSACEs = 0; psExtendedInfo->wSACLSize = 0;
//
// Checksum the SACL data if necessary
// n.b. We only take into account ACEs that are inherited.
//
if ( psSacl->AclSize > 0 ) { DWORD dwChecksum = 0; //
// The first ACE is right after the ACL header
//
PACE_HEADER pAceHeader = ( PACE_HEADER )( psSacl + 1 ); for ( USHORT aceNum = 0; aceNum < psSacl->AceCount; ++aceNum ) { //
// Skip if an inherited ACE
//
if ( !( pAceHeader->AceFlags & INHERITED_ACE ) ) { dwChecksum += ::eaChecksumBlock( dwChecksum, ( LPBYTE )pAceHeader, pAceHeader->AceSize ); ++psExtendedInfo->lNumSACEs; psExtendedInfo->wSACLSize += pAceHeader->AceSize; if ( pcParams->m_bPrintDebugInfo ) wprintf( L"\ts%d: f: %04x, t: %04x, s: %u\n", aceNum, ( DWORD)( pAceHeader->AceFlags ), pAceHeader->AceType, (DWORD)( pAceHeader->AceSize ) ); } pAceHeader = ( PACE_HEADER )( ( ( LPBYTE )pAceHeader ) + pAceHeader->AceSize ); } if ( psExtendedInfo->wSACLSize > 0 ) { psExtendedInfo->cwsSACLChecksum.Format( pcParams->m_pwszULongHexFmt, dwChecksum ); psExtendedInfo->ullTotalBytesChecksummed += psExtendedInfo->wSACLSize; } } } else if ( dwSaclErrorRetCode != ERROR_SUCCESS ) { psExtendedInfo->lNumSACEs = -1; psExtendedInfo->wSACLSize = -1; psExtendedInfo->cwsSACLChecksum.Format( L"<%6d>", dwSaclErrorRetCode ); } else psExtendedInfo->lNumSACEs = 0; // none
eaConvertUserSidToString( pcParams, pOwnerSid, &psExtendedInfo->cwsOwnerSid ); eaConvertGroupSidToString( pcParams, pGroupSid, &psExtendedInfo->cwsGroupSid );
::LocalFree( pDesc ); } else { //
// Error getting security information
//
psExtendedInfo->lNumDACEs = -1; psExtendedInfo->lNumSACEs = -1; psExtendedInfo->wDACLSize = -1; psExtendedInfo->wSACLSize = -1; psExtendedInfo->cwsDACLChecksum.Format( L"<%6d>", dwRet ); psExtendedInfo->cwsSACLChecksum.Format( L"<%6d>", dwRet ); psExtendedInfo->cwsOwnerSid.Format( L"<%6d>", dwRet ); psExtendedInfo->cwsGroupSid.Format( L"<%6d>", dwRet ); } } catch( ... ) { psExtendedInfo->lNumDACEs = -1; psExtendedInfo->lNumSACEs = -1; psExtendedInfo->wDACLSize = -1; psExtendedInfo->wSACLSize = -1; psExtendedInfo->cwsOwnerSid.Format( L"<%6d>", ::GetLastError() ); psExtendedInfo->cwsGroupSid.Format( L"<%6d>", ::GetLastError() ); } }
static VOID eaGetFileInformationByHandle( IN CDumpParameters *pcParams, IN const CBsString &cwsFileName, IN OUT SDirectoryEntry *psDirEntry, OUT SFileExtendedInfo *psExtendedInfo ) { HANDLE hFile;
//
// Note that while we do have to open the file, not even read access is needed
//
hFile = ::CreateFileW( cwsFileName, 0, FSD_SHARE_MODE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL ); if ( hFile == INVALID_HANDLE_VALUE ) { psExtendedInfo->lNumberOfLinks = -1; return; }
//
// Now get the additional attributes
//
BY_HANDLE_FILE_INFORMATION sFileInfo; if ( ::GetFileInformationByHandle( hFile, &sFileInfo ) ) { psExtendedInfo->lNumberOfLinks = ( LONG )sFileInfo.nNumberOfLinks; psExtendedInfo->ullFileIndex = ( ( ULONGLONG )sFileInfo.nFileIndexHigh << 32 ) + sFileInfo.nFileIndexLow; if ( psExtendedInfo->lNumberOfLinks > 1 || psDirEntry->m_sFindData.ftLastWriteTime.dwLowDateTime == 0 ) { //
// Expect that the FindFirst/NextFile dir entry is stale or non-existant. Use information
// from this call.
//
psDirEntry->m_sFindData.dwFileAttributes = sFileInfo.dwFileAttributes; psDirEntry->m_sFindData.ftCreationTime = sFileInfo.ftCreationTime; psDirEntry->m_sFindData.ftLastAccessTime = sFileInfo.ftLastAccessTime; psDirEntry->m_sFindData.ftLastWriteTime = sFileInfo.ftLastWriteTime; psDirEntry->m_sFindData.nFileSizeHigh = sFileInfo.nFileSizeHigh; psDirEntry->m_sFindData.nFileSizeLow = sFileInfo.nFileSizeLow; } } else psExtendedInfo->lNumberOfLinks = -1;
::CloseHandle( hFile ); }
static VOID eaGetAlternateStreamInfo( IN CDumpParameters *pcParams, IN const CBsString &cwsFileName, OUT SFileExtendedInfo *psExtendedInfo ) { NTSTATUS Status; HANDLE hFile;
//
// Note that while we do have to open the file, not even read access is needed
//
hFile = CreateFileW( cwsFileName, FILE_GENERIC_READ, // | ACCESS_SYSTEM_SECURITY,
FSD_SHARE_MODE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL ); if ( hFile == INVALID_HANDLE_VALUE ) { psExtendedInfo->lNumNamedDataStreams = -1; psExtendedInfo->lNumPropertyStreams = -1; psExtendedInfo->cwsNamedDataStreamChecksum.Format( L"<%6d>", ::GetLastError() ); return; }
//
// Loop until we read the file information
//
LPBYTE pBuffer = NULL; ULONG ulBuffSize = 1024; IO_STATUS_BLOCK iosb; static const WCHAR * const pwszDefaultStreamName = L"::$DATA"; static const ULONG ulDefaultStreamNameLength = 7;
while ( TRUE ) { pBuffer = new BYTE[ ulBuffSize ]; if ( pBuffer == NULL ) throw E_OUTOFMEMORY;
Status = ::NtQueryInformationFile( hFile, &iosb, pBuffer, ulBuffSize, FileStreamInformation ); //
// If we succeeded in getting data, when have the data so party on and get out of
// the loop
//
if ( NT_SUCCESS( Status ) && iosb.Information != 0 ) { break; }
//
// If the error isn't overflow, get out
//
if ( Status != STATUS_BUFFER_OVERFLOW && Status != STATUS_BUFFER_TOO_SMALL ) { //
// NOTE: If status is successful, we didn't get any data but it's not
// an error. Happens a lot with directories since they don't have a default
// unnamed stream.
//
if ( !NT_SUCCESS( Status ) ) { //
// Another kind of error
// BUGBUG: if not NTFS, C000000D occurs. Should not try this on
// a non-NTFS volume
//psExtendedInfo->lNumNamedDataStreams = -1;
//psExtendedInfo->dwNamedDataStreamChecksum = ::GetLastError();
//psExtendedInfo->bNamedDataStreamHadError = TRUE;
} delete [] pBuffer; ::CloseHandle( hFile ); return; }
//
// Increase the size of the buffer
//
ulBuffSize <<= 1; // double it each try
delete [] pBuffer; pBuffer = NULL; }
//
// If we are here, we have a valid FileStreamInformation buffer
//
::CloseHandle( hFile );
PFILE_STREAM_INFORMATION pFSI; pFSI = ( PFILE_STREAM_INFORMATION ) pBuffer;
BOOL bHadError = FALSE; DWORD dwChecksum = 0;
//
// Now loop through the named streams.
//
while ( TRUE ) { if ( pFSI->StreamNameLength != sizeof( WCHAR ) * ulDefaultStreamNameLength || wcsncmp( pFSI->StreamName, pwszDefaultStreamName, ulDefaultStreamNameLength ) != 0 ) { LPWSTR pwszDataStr;
pwszDataStr = ::wcsstr( pFSI->StreamName, L":$DATA" ); if ( pwszDataStr != NULL ) { pwszDataStr[0] = L'\0'; // Strip off the :$DATA off of name
++psExtendedInfo->lNumNamedDataStreams; // wprintf( L" %8I64u '%-*.*s' : %d\n", pFSI->StreamSize, pFSI->StreamNameLength / 2,
// pFSI->StreamNameLength / 2, pFSI->StreamName, pFSI->StreamNameLength );
psExtendedInfo->ullTotalBytesNamedDataStream += ( ULONGLONG )pFSI->StreamSize.QuadPart;
if ( !pcParams->m_bNoChecksums && !bHadError ) { //
// Put into the checksum the name of the stream
//
dwChecksum = ::eaChecksumBlock( dwChecksum, ( LPBYTE )pFSI->StreamName, ::wcslen( pFSI->StreamName ) * sizeof WCHAR ); //
// Now checksum the data in the stream
//
if ( ::eaChecksumStream( cwsFileName + pFSI->StreamName, &psExtendedInfo->ullTotalBytesChecksummed, &dwChecksum ) ) { psExtendedInfo->cwsNamedDataStreamChecksum.Format( pcParams->m_pwszULongHexFmt, dwChecksum ); } else { psExtendedInfo->cwsNamedDataStreamChecksum.Format( L"<%6d>", ::GetLastError() ); bHadError = TRUE; } } } else { //
// Not an named data stream, probably a property stream
// BUGBUG: need to verify that this is a property stream
//
++psExtendedInfo->lNumPropertyStreams; } }
if ( pFSI->NextEntryOffset == 0 ) break; pFSI = ( PFILE_STREAM_INFORMATION )( pFSI->NextEntryOffset + ( PBYTE ) pFSI ); }
if ( !bHadError && !pcParams->m_bNoChecksums && psExtendedInfo->lNumNamedDataStreams > 0 ) { psExtendedInfo->cwsNamedDataStreamChecksum.Format( pcParams->m_pwszULongHexFmt, dwChecksum ); }
if ( pBuffer != NULL ) delete [] pBuffer;
}
static VOID eaGetReparsePointInfo( IN CDumpParameters *pcParams, IN const CBsString &cwsFileName, IN OUT ULONGLONG *pullBytesChecksummed, IN OUT SDirectoryEntry *psDirEntry, IN OUT SFileExtendedInfo *psExtendedInfo ) { HANDLE hFile = INVALID_HANDLE_VALUE; BOOL bRet = TRUE; LPBYTE pReadBuffer = NULL; DWORD dwChecksum = 0;
try { //
// Now get the reparse point data buffer
//
pReadBuffer = ( LPBYTE )::VirtualAlloc( NULL, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, MEM_COMMIT, PAGE_READWRITE ); if ( pReadBuffer == NULL ) { bRet = FALSE; goto EXIT; }
//
// Open the file in order to read reparse point data
//
hFile = ::CreateFileW( cwsFileName, GENERIC_READ, FSD_SHARE_MODE, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL ); if ( hFile == INVALID_HANDLE_VALUE ) { bRet = FALSE; goto EXIT; }
//
// Now get the reparse point data
//
DWORD dwBytesReturned; if ( !::DeviceIoControl( hFile, FSCTL_GET_REPARSE_POINT, NULL, // lpInBuffer; must be NULL
0, // nInBufferSize; must be zero
( LPVOID )pReadBuffer, // pointer to output buffer
MAXIMUM_REPARSE_DATA_BUFFER_SIZE, // size of output buffer
&dwBytesReturned, // receives number of bytes returned
NULL // pointer to OVERLAPPED structure
) ) { bRet = FALSE; goto EXIT; }
PREPARSE_DATA_BUFFER pReparseData; pReparseData = ( PREPARSE_DATA_BUFFER )pReadBuffer ; psExtendedInfo->ulReparsePointTag = pReparseData->ReparseTag; psExtendedInfo->wReparsePointDataSize = ( WORD )dwBytesReturned;
if ( !pcParams->m_bNoSpecialReparsePointProcessing && psExtendedInfo->ulReparsePointTag == FSD_MS_HSM_REPARSE_TAG ) { //
// To make sure that dumps don't get many miscompares we
// need to tweak the attributes. Raid #153050
//
if ( pcParams->m_bDontChecksumHighLatencyData ) { //
// Need to always make this file look like it is offline.
// In this case, we need to always enable the FILE_ATTRIBUTE_OFFLINE
// flag.
//
psDirEntry->m_sFindData.dwFileAttributes |= FILE_ATTRIBUTE_OFFLINE; } else { //
// Need to always make this file look like it is cached.
// In this case, we need to always disable the FILE_ATTRIBUTE_OFFLINE
// flag.
//
psDirEntry->m_sFindData.dwFileAttributes &= ~FILE_ATTRIBUTE_OFFLINE; }
//
// Call a special HSM checksum function which filters out certain
// dynamic fields before checksumming the data.
//
dwChecksum = eaChecksumHSMReparsePoint( pcParams, pReparseData, dwBytesReturned ); } else { //
// Now checksum all of the reparse point data
//
dwChecksum = ::eaChecksumBlock( 0, pReadBuffer, dwBytesReturned ); }
psExtendedInfo->cwsReparsePointDataChecksum.Format( pcParams->m_pwszULongHexFmt, dwChecksum );
*pullBytesChecksummed += dwBytesReturned; } catch( ... ) { bRet = FALSE; } EXIT:
if ( pReadBuffer != NULL ) ::VirtualFree( pReadBuffer, 0, MEM_RELEASE );
if ( hFile != INVALID_HANDLE_VALUE ) ::CloseHandle( hFile );
if ( bRet == FALSE ) psExtendedInfo->cwsReparsePointDataChecksum.Format( L"<%6d>", ::GetLastError() ); }
static BOOL eaChecksumStream( IN const CBsString& cwsStreamPath, IN OUT ULONGLONG *pullBytesChecksummed, IN OUT DWORD *pdwRunningCheckSum ) { LPBYTE pReadBuffer; BOOL bRet = TRUE;
pReadBuffer = ( LPBYTE )::VirtualAlloc( NULL, READ_BUF_SIZE, MEM_COMMIT, PAGE_READWRITE ); if ( pReadBuffer == NULL ) return FALSE;
//
// Open file with NO_BUFFERING.
//
HANDLE hFile = INVALID_HANDLE_VALUE; try { hFile = ::CreateFileW( cwsStreamPath, GENERIC_READ, FSD_SHARE_MODE, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_NO_RECALL | FILE_FLAG_SEQUENTIAL_SCAN, NULL ); if ( hFile == INVALID_HANDLE_VALUE ) { bRet = FALSE; goto EXIT; }
DWORD dwBytesRead; while ( ::ReadFile( hFile, pReadBuffer, READ_BUF_SIZE, &dwBytesRead, NULL ) ) { if ( dwBytesRead == 0 ) break; *pdwRunningCheckSum = ::eaChecksumBlock( *pdwRunningCheckSum, pReadBuffer, dwBytesRead ); *pullBytesChecksummed += dwBytesRead; } if ( ::GetLastError() != ERROR_SUCCESS ) bRet = FALSE; } catch( ... ) { bRet = FALSE; }
EXIT: if ( hFile != INVALID_HANDLE_VALUE ) ::CloseHandle( hFile );
::VirtualFree( pReadBuffer, 0, MEM_RELEASE );
return bRet; }
//
// This class maintains the encryption context
//
class CFsdEncryptionContext { public: CFsdEncryptionContext() : m_hDoneEvent( NULL ), m_dwChecksum( 0 ), m_ullBytesRead( 0 ) { m_hDoneEvent = ::CreateEventW( NULL, TRUE, FALSE, NULL ); if ( m_hDoneEvent == NULL ) throw ::GetLastError(); }
~CFsdEncryptionContext() { if ( m_hDoneEvent != NULL ) ::CloseHandle( m_hDoneEvent ); }
DWORD WaitForDoneEvent() { DWORD dwRet;
dwRet = ::WaitForSingleObject( m_hDoneEvent, INFINITE ); if ( dwRet == WAIT_OBJECT_0 ) return ERROR_SUCCESS; else if ( dwRet == WAIT_TIMEOUT ) return ERROR_TIMEOUT; return ::GetLastError(); }
VOID FireDoneEvent() { ::SetEvent( m_hDoneEvent ); }
DWORD GetChecksum() { return m_dwChecksum; }
ULONGLONG GetBytesRead() { return m_ullBytesRead; }
static DWORD WINAPI ExportCallback( IN PBYTE pbData, IN PVOID pvCallbackContext, IN ULONG ulLength ) { CFsdEncryptionContext *pcThis = static_cast< CFsdEncryptionContext * >( pvCallbackContext ); pcThis->m_dwChecksum = ::eaChecksumBlock( pcThis->m_dwChecksum, pbData, ulLength ); pcThis->m_ullBytesRead += ulLength; return ERROR_SUCCESS; }
private: HANDLE m_hDoneEvent; DWORD m_dwChecksum; ULONGLONG m_ullBytesRead; };
static BOOL eaChecksumRawEncryptedData( IN CDumpParameters *pcParams, IN const CBsString& cwsFileName, IN OUT SFileExtendedInfo *psExtendedInfo ) { PVOID pvContext = NULL; DWORD dwRet = ERROR_SUCCESS; CFsdEncryptionContext cEncryptionContext;
try { //
// Open this puppy up
//
dwRet = ::OpenEncryptedFileRawW( cwsFileName, 0, &pvContext ); if ( dwRet == ERROR_SUCCESS ) { //wprintf( L"**** Opened encrypted file '%s'\n", cwsFileName.c_str() );
dwRet = ::ReadEncryptedFileRaw( CFsdEncryptionContext::ExportCallback, &cEncryptionContext, pvContext ); if ( dwRet == ERROR_SUCCESS ) { //wprintf( L"**** Called read on encrypted file, bytes read: %u, checksum: %u\n",
// cEncryptionContext.GetBytesRead(), cEncryptionContext.GetChecksum() );
psExtendedInfo->cwsEncryptedRawDataChecksum.Format( pcParams->m_pwszULongHexFmt, cEncryptionContext.GetChecksum() ); psExtendedInfo->ullTotalBytesChecksummed += cEncryptionContext.GetBytesRead(); } } } catch( ... ) { dwRet = ERROR_EXCEPTION_IN_SERVICE; // ???
}
if ( pvContext != NULL ) ::CloseEncryptedFileRaw( pvContext );
if ( dwRet != ERROR_SUCCESS ) psExtendedInfo->cwsEncryptedRawDataChecksum.Format( L"<%6d>", dwRet );
return dwRet == ERROR_SUCCESS; }
/*++
Routine Description:
Checksums a block of data. The block needs to be DWORD aligned for performance and correctness since this function assumes it can zero out up to 3 bytes beyond the end of the buffer. Also, only the last buffer in a series of buffers can have unaligned data at the end of the buffer.
Arguments:
dwRunningChecksum - The previous checksum from a previous call. Should be zero if this is the first block to checksum in a series of blocks.
pBuffer - Pointer to the buffer to checksum.
dwBufSize - This should always be a multiple of a DWORD, except for the last block in a series.
Return Value:
The checksum.
--*/ static DWORD eaChecksumBlock( IN DWORD dwRunningChecksum, IN LPBYTE pBuffer, IN DWORD dwBufSize ) { //
// Need to zero out any additional bytes not aligned.
//
DWORD dwBytesToZero; DWORD dwBufSizeInDWords;
dwBytesToZero = dwBufSize % sizeof( DWORD ); dwBufSizeInDWords = ( dwBufSize + ( sizeof( DWORD ) - 1 ) ) / sizeof( DWORD ); // int div
while ( dwBytesToZero-- ) pBuffer[ dwBufSize + dwBytesToZero ] = 0;
LPDWORD pdwBuf = ( LPDWORD )pBuffer;
// BUGBUG: Need better checksum
for ( DWORD dwIdx = 0; dwIdx < dwBufSizeInDWords; ++dwIdx ) dwRunningChecksum += ( dwRunningChecksum << 1 ) | pdwBuf[ dwIdx ];
return dwRunningChecksum; }
/*++
Routine Description:
Converts a user SID to a string. Has a simple one element cache to speed up conversions. This is especially useful when the user wants the symbolic DOMAIN\ACCOUNT strings.
Arguments:
Return Value:
NONE
--*/ static VOID eaConvertUserSidToString ( IN CDumpParameters *pcParams, IN PSID pSid, OUT CBsString *pcwsSid ) { static CBsString cwsCachedSidString; static PSID pCachedSid = NULL;
//
// Is the cached SID the same as the passed in one. If so,
// return the cached sid string.
//
if ( pCachedSid != NULL && ::EqualSid( pSid, pCachedSid ) ) { *pcwsSid = cwsCachedSidString; return; }
//
// Convert the SID into a string
//
::eaConvertSidToString( pcParams, pSid, pcwsSid );
//
// Now cache the sid
//
cwsCachedSidString = *pcwsSid; if ( pCachedSid != NULL ) free( pCachedSid ); size_t cSidLength = ( size_t )::GetLengthSid( pSid ); pCachedSid = ( PSID )malloc( cSidLength ); if ( pCachedSid == NULL ) // prefix #171666
{ pcParams->ErrPrint( L"eaConvertUserSidToString - Can't allocate memory, out of memory" ); throw E_OUTOFMEMORY; } ::CopySid( ( DWORD )cSidLength, pCachedSid, pSid ); }
/*++
Routine Description:
Converts a group SID to a string. Has a simple one element cache to speed up conversions. This is especially useful when the user wants the symbolic DOMAIN\ACCOUNT strings.
Arguments:
Return Value:
NONE
--*/ static VOID eaConvertGroupSidToString ( IN CDumpParameters *pcParams, IN PSID pSid, OUT CBsString *pcwsSid ) { static CBsString cwsCachedSidString; static PSID pCachedSid = NULL;
//
// Is the cached SID the same as the passed in one. If so,
// return the cached sid string.
//
if ( pCachedSid != NULL && ::EqualSid( pSid, pCachedSid ) ) { *pcwsSid = cwsCachedSidString; return; }
//
// Convert the SID into a string
//
::eaConvertSidToString( pcParams, pSid, pcwsSid );
//
// Now cache the sid
//
cwsCachedSidString = *pcwsSid; if ( pCachedSid != NULL ) free( pCachedSid ); size_t cSidLength = ( size_t )::GetLengthSid( pSid ); pCachedSid = ( PSID )malloc( cSidLength ); if ( pCachedSid == NULL ) // prefix #171665
{ pcParams->ErrPrint( L"eaConvertGroupSidToString - Can't allocate memory, out of memory" ); throw E_OUTOFMEMORY; } ::CopySid( ( DWORD )cSidLength, pCachedSid, pSid ); }
/*++
Routine Description:
Converts a SID to a string.
Arguments:
Return Value:
NONE
--*/ static VOID eaConvertSidToString ( IN CDumpParameters *pcParams, IN PSID pSid, OUT CBsString *pcwsSid ) { if ( pcParams->m_bShowSymbolicSIDNames ) { CBsString cwsAccountName; CBsString cwsDomainName; SID_NAME_USE eSidNameUse; DWORD dwAccountNameSize = 1024; DWORD dwDomainNameSize = 1024;
if ( ::LookupAccountSidW( NULL, pSid, cwsAccountName.GetBufferSetLength( dwAccountNameSize ), &dwAccountNameSize, cwsDomainName.GetBufferSetLength( dwDomainNameSize ), &dwDomainNameSize, &eSidNameUse ) ) { cwsAccountName.ReleaseBuffer(); cwsDomainName.ReleaseBuffer(); *pcwsSid = L"'"; *pcwsSid += cwsDomainName; *pcwsSid += L"\\"; *pcwsSid += cwsAccountName; *pcwsSid += L"'"; return; } } LPWSTR pwszSid;
if ( ::ConvertSidToStringSid( pSid, &pwszSid ) ) { *pcwsSid = pwszSid; ::LocalFree( pwszSid ); } else { pcwsSid->Format( L"<%6d>", ::GetLastError() ); } }
///////////////////////////////////////////////////////////////////////////
//
// FROM: base\fs\hsm\inc\rpdata.h
//
//////////////////////////////////////////////////////////////////////////
#define RP_RESV_SIZE 52
//
// Placeholder data - all versions unioned together
//
typedef struct _RP_PRIVATE_DATA { CHAR reserved[RP_RESV_SIZE]; // Must be 0
ULONG bitFlags; // bitflags indicating status of the segment
LARGE_INTEGER migrationTime; // When migration occurred
GUID hsmId; GUID bagId; LARGE_INTEGER fileStart; LARGE_INTEGER fileSize; LARGE_INTEGER dataStart; LARGE_INTEGER dataSize; LARGE_INTEGER fileVersionId; LARGE_INTEGER verificationData; ULONG verificationType; ULONG recallCount; LARGE_INTEGER recallTime; LARGE_INTEGER dataStreamStart; LARGE_INTEGER dataStreamSize; ULONG dataStream; ULONG dataStreamCRCType; LARGE_INTEGER dataStreamCRC; } RP_PRIVATE_DATA, *PRP_PRIVATE_DATA;
typedef struct _RP_DATA { GUID vendorId; // Unique HSM vendor ID -- This is first to match REPARSE_GUID_DATA_BUFFER
ULONG qualifier; // Used to checksum the data
ULONG version; // Version of the structure
ULONG globalBitFlags; // bitflags indicating status of the file
ULONG numPrivateData; // number of private data entries
GUID fileIdentifier; // Unique file ID
RP_PRIVATE_DATA data; // Vendor specific data
} RP_DATA, *PRP_DATA;
//
// This function specifically zero's out certain HSM reparse point
// fields before computing the checksum. The fields which are
// zero'ed out are dynamic values and can cause miscompares.
//
static DWORD eaChecksumHSMReparsePoint( IN CDumpParameters *pcParams, IN PREPARSE_DATA_BUFFER pReparseData, IN DWORD dwTotalSize // Size of reparse point data
) { if ( dwTotalSize >= 8 && pReparseData->ReparseDataLength >= sizeof RP_DATA ) { //
// If structure is not at least as large as the HSM RP_DATA structure,
// then it doesn't appear to be a valid HSM RP_DATA structure.
//
PRP_DATA pRpData = ( PRP_DATA ) pReparseData->GenericReparseBuffer.DataBuffer;
//
// Zero out the proper fields
//
pRpData->qualifier = 0; pRpData->globalBitFlags = 0; pRpData->data.bitFlags = 0; pRpData->data.recallCount = 0; pRpData->data.recallTime.LowPart = 0; pRpData->data.recallTime.HighPart = 0; } else { pcParams->ErrPrint( L"Warning, HSM reparse point not valid, size: %u\n", dwTotalSize ); }
return ::eaChecksumBlock( 0, ( LPBYTE )pReparseData, dwTotalSize ); }
static VOID eaGetObjectIdInfo( IN CDumpParameters *pcParams, IN const CBsString &cwsFileName, IN OUT ULONGLONG *pullBytesChecksummed, IN OUT SDirectoryEntry *psDirEntry, IN OUT SFileExtendedInfo *psExtendedInfo ) { HANDLE hFile = INVALID_HANDLE_VALUE; BOOL bRet = TRUE; DWORD dwChecksum = 0;
try { //
// Open the file in order to read the object id
//
hFile = ::CreateFileW( cwsFileName, GENERIC_READ, FSD_SHARE_MODE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL ); if ( hFile == INVALID_HANDLE_VALUE ) { bRet = FALSE; goto EXIT; }
FILE_OBJECTID_BUFFER sObjIdBuffer; DWORD dwBytesReturned;
//
// Now get the object id info
//
if ( !::DeviceIoControl( hFile, FSCTL_GET_OBJECT_ID, NULL, // lpInBuffer; must be NULL
0, // nInBufferSize; must be zero
( LPVOID )&sObjIdBuffer, // pointer to output buffer
sizeof FILE_OBJECTID_BUFFER,// size of output buffer
&dwBytesReturned, // receives number of bytes returned
NULL // pointer to OVERLAPPED structure
) ) { bRet = FALSE; goto EXIT; }
//
// Load up the object id
//
LPWSTR pwszObjIdGuid;
// Check for RPC_S_OK added for Prefix bug #192596
if ( ::UuidToStringW( (GUID *)sObjIdBuffer.ObjectId, ( unsigned short ** )&pwszObjIdGuid ) == RPC_S_OK ) { psExtendedInfo->cwsObjectId = pwszObjIdGuid; ::RpcStringFreeW( ( unsigned short ** )&pwszObjIdGuid ); }
//
// Now checksum all of the extended object id data if necessary
//
if ( pcParams->m_bEnableObjectIdExtendedDataChecksums ) { dwChecksum = ::eaChecksumBlock( 0, sObjIdBuffer.ExtendedInfo, sizeof( sObjIdBuffer.ExtendedInfo ) ); psExtendedInfo->cwsObjectIdExtendedDataChecksum.Format( pcParams->m_pwszULongHexFmt, dwChecksum );
*pullBytesChecksummed += sizeof( sObjIdBuffer.ExtendedInfo ); } } catch( ... ) { bRet = FALSE; } EXIT:
if ( hFile != INVALID_HANDLE_VALUE ) ::CloseHandle( hFile );
// if ( bRet == FALSE )
// psExtendedInfo->cwsReparsePointDataChecksum.Format( L"<%6d>", ::GetLastError() );
}
|