/*++ Copyright (c) 2000-2001 Microsoft Corporation Module Name: volstate.cpp Abstract: Contains implementation of the volume state class. This class maintains state about one volume. Author: Stefan R. Steiner [ssteiner] 03-14-2000 Revision History: --*/ #include "stdafx.h" CFsdVolumeStateManager::CFsdVolumeStateManager( IN CDumpParameters *pcDumpParameters ) : m_cVolumeStateList( BSHASHMAP_MEDIUM ), m_pcParams( pcDumpParameters ), m_pcExclManager( NULL ) { if ( m_pcParams->m_bUseExcludeProcessor ) { m_pcExclManager = new CFsdExclusionManager( m_pcParams ); if ( m_pcExclManager == NULL ) { m_pcParams->ErrPrint( L"CFsdVolumeStateManager::CFsdVolumeStateManager - Can't init CFsdExclusionManager, out of memory" ); throw E_OUTOFMEMORY; } } } CFsdVolumeStateManager::~CFsdVolumeStateManager() { // // Need to delete all volume state objects // SFsdVolumeId sFsdId; CFsdVolumeState *pcVolState; m_cVolumeStateList.StartEnum(); while ( m_cVolumeStateList.GetNextEnum( &sFsdId, &pcVolState ) ) { delete pcVolState; } m_cVolumeStateList.EndEnum(); delete m_pcExclManager; } VOID CFsdVolumeStateManager::PrintHardLinkInfo() { // // Let's iterate through all of the volumes managed by this // manager. // SFsdVolumeId sFsdId; CFsdVolumeState *pcVolState; m_pcParams->DumpPrint( L"" ); m_pcParams->DumpPrint( L"----------------------------------------------------------------------------" ); m_pcParams->DumpPrint( L"HardLink Information" ); m_cVolumeStateList.StartEnum(); while ( m_cVolumeStateList.GetNextEnum( &sFsdId, &pcVolState ) ) { m_pcParams->DumpPrint( L"----------------------------------------------------------------------------" ); m_pcParams->DumpPrint( L"For volume: '%s'", pcVolState->GetVolumePath() ); pcVolState->PrintHardLinkInfo(); } m_cVolumeStateList.EndEnum(); } /*++ Routine Description: Arguments: Return Value: ERROR_ALREADY_EXISTS - The volume already exists. The returned volume state object pointer is valid. ERROR_CAN_NOT_COMPLETE - Unexpected error --*/ DWORD CFsdVolumeStateManager::GetVolumeState( IN const CBsString& cwsVolumePath, OUT CFsdVolumeState **ppcVolState ) { *ppcVolState = NULL; try { WCHAR wszVolumePath[ FSD_MAX_PATH ]; // // Temporary workaround for bug in GetVolumeInformationW() // BOOL bFixed = FALSE; if ( cwsVolumePath.Left( 2 ) == L"\\\\" && cwsVolumePath.Left( 4 ) != L"\\\\?\\" ) { // // reduce to the minimal \\machine\sharename\ form // ::wcscpy( wszVolumePath, cwsVolumePath ); LPWSTR pswz; pswz = ::wcschr( wszVolumePath + 2, L'\\' ); if ( pswz != NULL ) { pswz = ::wcschr( pswz + 1, L'\\' ); if ( pswz != NULL ) { pswz[1] = '\0'; bFixed = TRUE; } } } if ( bFixed == FALSE ) { // // Get the volume path that contains this volume // if ( !::GetVolumePathNameW( cwsVolumePath, wszVolumePath, FSD_MAX_PATH ) ) { m_pcParams->ErrPrint( L"CFsdVolumeStateManager - GetVolumePathName( '%s', ... ) returned dwRet: %d", cwsVolumePath.c_str(), ::GetLastError() ); return ::GetLastError(); } } // // Initialize a new volume state object // CFsdVolumeState *pcFsdVolumeState; pcFsdVolumeState = new CFsdVolumeState( m_pcParams, wszVolumePath ); if ( pcFsdVolumeState == NULL ) { m_pcParams->ErrPrint( L"CFsdVolumeStateManager, out of memory, can't get volume information" ); return ::GetLastError(); } // // Now get the information about the volume. // BUGBUG: Note that GetVolumeInformationW returns // ERROR_DIR_NOT_ROOT when encountering a junction on a // remote share. // if ( !::GetVolumeInformationW( wszVolumePath, NULL, 0, &pcFsdVolumeState->m_dwVolSerialNumber, &pcFsdVolumeState->m_dwMaxComponentLength, &pcFsdVolumeState->m_dwFileSystemFlags, pcFsdVolumeState->m_cwsFileSystemName.GetBufferSetLength( 64 ), 64 ) ) { pcFsdVolumeState->m_cwsFileSystemName.ReleaseBuffer(); m_pcParams->ErrPrint( L"CFsdVolumeStateManager - GetVolumeInformation( '%s', ... ) returned dwRet: %d " L"(if 144 probably hit bug in GetVolumeInformation when accessing remote mountpoints)", wszVolumePath, ::GetLastError() ); delete pcFsdVolumeState; return ::GetLastError(); } pcFsdVolumeState->m_cwsFileSystemName.ReleaseBuffer(); #if 0 SFsdVolumeId sVolIdTest; CBsString cwsRealVolumePath; GetVolumeIdAndPath( m_pcParams, cwsVolumePath, &sVolIdTest, cwsRealVolumePath ); assert( sVolIdTest.m_dwVolSerialNumber == pcFsdVolumeState->m_dwVolSerialNumber ); printf("VolumeSerialNumber: 0x%08x, 0x%08x\n", pcFsdVolumeState->m_dwVolSerialNumber, sVolIdTest.m_dwVolSerialNumber ); #endif // // Now see if this volume already exists in the list of volume states // LONG lRet; SFsdVolumeId sVolId; sVolId.m_dwVolSerialNumber = pcFsdVolumeState->m_dwVolSerialNumber; if ( m_cVolumeStateList.Find( sVolId, ppcVolState ) == TRUE ) { // // Already exists in the list, return it. Also delete the vol state // object that's not needed. // delete pcFsdVolumeState; return ERROR_ALREADY_EXISTS; } // // Not found, insert it into the list // lRet = m_cVolumeStateList.Insert( sVolId, pcFsdVolumeState ); if ( lRet != BSHASHMAP_NO_ERROR ) { assert( lRet != BSHASHMAP_ALREADY_EXISTS ); delete pcFsdVolumeState; return ERROR_CAN_NOT_COMPLETE; } // // Now get the exclusion processor for this volume if necessary // if ( m_pcExclManager != NULL ) { m_pcExclManager->GetFileSystemExcludeProcessor( cwsVolumePath, &sVolId, &pcFsdVolumeState->m_pcFSExclProcessor ); } CFsdVolumeState *pcFindFsdVolumeState; *ppcVolState = pcFsdVolumeState; return ERROR_SUCCESS; } catch ( HRESULT hr ) { if ( hr == E_OUTOFMEMORY ) m_pcParams->ErrPrint( L"CFsdVolumeStateManager::GetVolumeState - Out of memory ( '%s' )", cwsVolumePath.c_str() ); else m_pcParams->ErrPrint( L"CFsdVolumeStateManager::GetVolumeState - Unexpected hr exception: 0x%08x ( '%s )", hr, cwsVolumePath.c_str() ); return ERROR_CAN_NOT_COMPLETE; } catch ( ... ) { m_pcParams->ErrPrint( L"CFsdVolumeStateManager::GetVolumeState - '%s' caught an unexpected exception", cwsVolumePath.c_str() ); return ERROR_CAN_NOT_COMPLETE; } } /*++ Routine Description: Gets the ID of the volume containing any file. Arguments: Return Value: ERROR_CAN_NOT_COMPLETE - General error --*/ DWORD CFsdVolumeStateManager::GetVolumeIdAndPath( IN CDumpParameters *pcParams, IN const CBsString& cwsPathOnVolume, OUT SFsdVolumeId *psVolId, OUT CBsString& cwsVolPath ) { try { psVolId->m_dwVolSerialNumber = 0; WCHAR wszVolumePath[ FSD_MAX_PATH ]; // // First get the mountpoint of the volume // if ( !GetVolumePathNameW( cwsPathOnVolume, wszVolumePath, FSD_MAX_PATH ) ) { pcParams->ErrPrint( L"CFsdVolumeStateManager::GetVolumeIdAndPath - GetVolumePathName( '%s', ... ) returned dwRet: %d", cwsPathOnVolume.c_str(), ::GetLastError() ); return ::GetLastError(); } // // Now open the volume in order to query filesystem info // HANDLE hFile; hFile = ::CreateFileW( wszVolumePath, FILE_GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL ); if ( hFile == INVALID_HANDLE_VALUE ) { //pcParams->ErrPrint( L"CFsdVolumeStateManager::GetVolumeIdAndPath - CreateFile( '%s', ... ) returned dwRet: %d", // wszVolumePath, ::GetLastError() ); return ::GetLastError(); } IO_STATUS_BLOCK iosb ; BYTE buffer[1024] ; FILE_FS_VOLUME_INFORMATION *fsinfo = (FILE_FS_VOLUME_INFORMATION *)buffer; fsinfo->VolumeSerialNumber = 0; NTSTATUS ntStat; ntStat = ::NtQueryVolumeInformationFile( hFile, &iosb, fsinfo, sizeof(buffer), FileFsVolumeInformation ); ::CloseHandle( hFile ); if ( ntStat != STATUS_SUCCESS ) { pcParams->ErrPrint( L"CFsdVolumeStateManager::GetVolumeIdAndPath - NtQueryVolumeInformationFile( '%s', ... ) returned dwRet: %0x08x", wszVolumePath, ntStat ); return ERROR_CAN_NOT_COMPLETE; } psVolId->m_dwVolSerialNumber = fsinfo->VolumeSerialNumber; cwsVolPath = wszVolumePath; } catch ( ... ) { pcParams->ErrPrint( L"CFsdVolumeStateManager::GetVolumeIdAndPath - '%s' caught an unexpected exception", cwsPathOnVolume.c_str() ); return ERROR_CAN_NOT_COMPLETE; } return ERROR_SUCCESS; }