////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2002-2004 Microsoft Corporation // // Module Name: volutil.cpp // // Description: // Utility functions for handling volumes // // Author: Jim Benton (jbenton) 30-Apr-2002 // ////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include "vs_assert.hxx" #include #include #include // IOCTL_VOLUME_IS_OFFLINE #include // MOUNTDEV_NAME #include // NetShareDel #include "vs_inc.hxx" #include #include "schema.h" #include "volutil.h" #define SYMBOLIC_LINK_LENGTH 28 // \DosDevices\X: #define GLOBALROOT_SIZE 14 // \\?\GLOBALROOT const WCHAR SETUP_KEY[] = L"SYSTEM\\Setup"; const WCHAR SETUP_SYSTEMPARTITION[] = L"SystemPartition"; BOOL GetVolumeDrive ( IN WCHAR* pwszVolumePath, IN DWORD cchDriveName, OUT WCHAR* pwszDriveNameBuf ) { CVssAutoPWSZ awszMountPoints; WCHAR* pwszCurrentMountPoint = NULL; BOOL fFound = FALSE; CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"VolumeMountPointExists"); // Get the length of the multi-string array DWORD cchVolumesBufferLen = 0; BOOL bResult = GetVolumePathNamesForVolumeName( pwszVolumePath, NULL, 0, &cchVolumesBufferLen); if (!bResult && (GetLastError() != ERROR_MORE_DATA)) ft.TranslateGenericError(VSSDBG_VSSADMIN, HRESULT_FROM_WIN32(GetLastError()), L"GetVolumePathNamesForVolumeName(%s, 0, 0, %p)", pwszVolumePath, &cchVolumesBufferLen); // Allocate the array awszMountPoints.Allocate(cchVolumesBufferLen); // Get the mount points // Note: this API was introduced in WinXP so it will need to be replaced if backported bResult = GetVolumePathNamesForVolumeName( pwszVolumePath, awszMountPoints, cchVolumesBufferLen, NULL); if (!bResult) ft.Throw(VSSDBG_VSSADMIN, HRESULT_FROM_WIN32(GetLastError()), L"GetVolumePathNamesForVolumeName(%s, %p, %lu, 0)", pwszVolumePath, awszMountPoints, cchVolumesBufferLen); // If the volume has mount points pwszCurrentMountPoint = awszMountPoints; if ( pwszCurrentMountPoint[0] ) { while(!fFound) { // End of iteration? LONG lCurrentMountPointLength = (LONG) ::wcslen(pwszCurrentMountPoint); if (lCurrentMountPointLength == 0) break; // Only a root directory should have a trailing backslash character if (lCurrentMountPointLength == 3 && pwszCurrentMountPoint[1] == L':' && pwszCurrentMountPoint[2] == L'\\') { ft.hr = StringCchCopy(pwszDriveNameBuf, cchDriveName, pwszCurrentMountPoint); if (ft.HrFailed()) ft.Throw(VSSDBG_VSSADMIN, ft.hr, L"StringCChCopy failed %#x", ft.hr); fFound = TRUE; } // Go to the next one. Skip the zero character. pwszCurrentMountPoint += lCurrentMountPointLength + 1; } } return fFound; } BOOL VolumeSupportsQuotas( IN WCHAR* pwszVolume ) { BOOL fSupportsQuotas = FALSE; DWORD dwDontCare = 0; DWORD dwFileSystemFlags = 0; _ASSERTE(pwszVolume != NULL); if (GetVolumeInformation( pwszVolume, NULL, 0, &dwDontCare, &dwDontCare, &dwFileSystemFlags, NULL, 0)) { if (dwFileSystemFlags & FILE_VOLUME_QUOTAS) fSupportsQuotas = TRUE; } return fSupportsQuotas; } // Filter volumes where: // - the supporting device is disconnected // - the supporting device is a floppy // - the volume is not found // All other volumes are assumed valid BOOL VolumeIsValid( IN WCHAR* pwszVolume ) { bool fValid = true; HANDLE hVol = INVALID_HANDLE_VALUE; DWORD cch = 0; DWORD dwRet = 0; CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"VolumeIsValid"); _ASSERTE(pwszVolume != NULL); cch = wcslen(pwszVolume); pwszVolume[cch - 1] = 0; hVol = CreateFile(pwszVolume, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); pwszVolume[cch - 1] = '\\'; dwRet = GetLastError(); if (hVol == INVALID_HANDLE_VALUE && dwRet != ERROR_NOT_READY) { if (dwRet == ERROR_FILE_NOT_FOUND || dwRet == ERROR_DEVICE_NOT_CONNECTED) { fValid = false; } else { ft.Trace(VSSDBG_VSSADMIN, L"Unable to open volume %lS, %#x", pwszVolume, dwRet); } } if (hVol != INVALID_HANDLE_VALUE) CloseHandle(hVol); // Filter floppy drives if (fValid) { fValid = !VolumeIsFloppy(pwszVolume); } return fValid; } DWORD VolumeIsDirty( IN WCHAR* pwszVolume, OUT BOOL* pfDirty ) { HANDLE hVol = INVALID_HANDLE_VALUE; DWORD dwRet = 0; DWORD cBytes = 0; DWORD dwResult = 0; WCHAR wszDeviceName[MAX_PATH+GLOBALROOT_SIZE]; CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"VolumeIsDirty"); _ASSERTE(pwszVolume != NULL); _ASSERTE(pfDirty != NULL); *pfDirty = FALSE; do { dwRet = GetDeviceName(pwszVolume, wszDeviceName); if (dwRet != ERROR_SUCCESS) { ft.hr = HRESULT_FROM_WIN32(dwRet); ft.Trace(VSSDBG_VSSADMIN, L"Unable to get volume device name %lS", pwszVolume); break; } hVol = CreateFile(wszDeviceName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); if (hVol != INVALID_HANDLE_VALUE) { if (DeviceIoControl(hVol, FSCTL_IS_VOLUME_DIRTY, NULL, 0, &dwResult, sizeof(dwResult), &cBytes, NULL)) { *pfDirty = dwResult & VOLUME_IS_DIRTY; } else { dwRet = GetLastError(); ft.Trace(VSSDBG_VSSADMIN, L"DeviceIoControl failed for device %lS, %#x", wszDeviceName, dwRet); break; } CloseHandle(hVol); } else { dwRet = GetLastError(); ft.Trace(VSSDBG_VSSADMIN, L"Unable to open volume %lS, %#x", pwszVolume, dwRet); break; } } while(false); return dwRet; } BOOL VolumeIsMountable( IN WCHAR* pwszVolume ) { CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"VolumeIsMountable"); DWORD cch = 0; HANDLE hVol = INVALID_HANDLE_VALUE; BOOL bIsOffline = FALSE; DWORD bytes = 0; cch = wcslen(pwszVolume); pwszVolume[cch - 1] = 0; hVol = CreateFile(pwszVolume, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); pwszVolume[cch - 1] = '\\'; if (hVol != INVALID_HANDLE_VALUE) { bIsOffline = DeviceIoControl(hVol, IOCTL_VOLUME_IS_OFFLINE, NULL, 0, NULL, 0, &bytes, NULL); CloseHandle(hVol); } else { ft.Trace(VSSDBG_VSSADMIN, L"Unable to open volume %lS, %#x", pwszVolume, GetLastError()); } return !bIsOffline; } BOOL VolumeHasMountPoints( IN WCHAR* pwszVolume ) { CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"VolumeHasMountPoints"); DWORD dwVolumesBufferLen = 0; BOOL bResult = GetVolumePathNamesForVolumeName( pwszVolume, NULL, 0, &dwVolumesBufferLen); if (!bResult && (GetLastError() != ERROR_MORE_DATA)) ft.Throw(VSSDBG_VSSADMIN, HRESULT_FROM_WIN32(GetLastError()), L"GetVolumePathNamesForVolumeName(%s, 0, 0, %p)", pwszVolume, &dwVolumesBufferLen); // More than three characters are needed to store just one mount point (multi-string buffer) // dwVolumesBufferLen == 1 typically for an un-mounted volume. return dwVolumesBufferLen > 3; } BOOL VolumeIsFloppy( WCHAR* pwszVolume ) { HANDLE hDevice = INVALID_HANDLE_VALUE; WCHAR wszDeviceName[MAX_PATH+GLOBALROOT_SIZE]; NTSTATUS Status = ERROR_SUCCESS; IO_STATUS_BLOCK IoStatusBlock; FILE_FS_DEVICE_INFORMATION DeviceInfo; BOOL fIsFloppy = FALSE; DWORD dwRet = GetDeviceName(pwszVolume, wszDeviceName); if (dwRet == ERROR_SUCCESS) { hDevice = CreateFile(wszDeviceName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ |FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); if (hDevice != INVALID_HANDLE_VALUE) { Status = NtQueryVolumeInformationFile( hDevice, &IoStatusBlock, &DeviceInfo, sizeof(DeviceInfo), FileFsDeviceInformation); if (NT_SUCCESS(Status)) { if (DeviceInfo.DeviceType == FILE_DEVICE_DISK && DeviceInfo.Characteristics & FILE_FLOPPY_DISKETTE) { fIsFloppy = TRUE ; } } } } if (hDevice != INVALID_HANDLE_VALUE) CloseHandle(hDevice); return fIsFloppy; } BOOL VolumeIsReady( IN WCHAR* pwszVolume ) { BOOL bIsReady = FALSE; DWORD dwDontCare; CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"VolumeIsReady"); if (GetVolumeInformation( pwszVolume, NULL, 0, &dwDontCare, &dwDontCare, &dwDontCare, NULL, 0)) { bIsReady = TRUE; } else if (GetLastError() != ERROR_NOT_READY) ft.Trace(VSSDBG_VSSADMIN, L"GetVolumeInformation failed for volume %lS, %#x", pwszVolume, GetLastError()); return bIsReady; } BOOL VolumeIsSystem( IN WCHAR* pwszVolume ) { CRegKey cRegKeySetup; DWORD dwRet = 0; WCHAR wszRegDevice[MAX_PATH]; WCHAR wszVolDevice[MAX_PATH+GLOBALROOT_SIZE]; CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"VolumeIsSystem"); dwRet = cRegKeySetup.Open( HKEY_LOCAL_MACHINE, SETUP_KEY, KEY_READ); if (dwRet != ERROR_SUCCESS) { ft.hr = HRESULT_FROM_WIN32(dwRet); ft.Throw(VSSDBG_VSSADMIN, ft.hr, L"Reg key open failed, %#x", dwRet); } DWORD dwLen = MAX_PATH; dwRet = cRegKeySetup.QueryValue(wszRegDevice, SETUP_SYSTEMPARTITION, &dwLen); if (dwRet != ERROR_SUCCESS) { ft.hr = HRESULT_FROM_WIN32(dwRet); ft.Throw(VSSDBG_VSSADMIN, ft.hr, L"Reg key query failed, %#x", dwRet); } dwRet = GetDeviceName(pwszVolume, wszVolDevice); if (dwRet != ERROR_SUCCESS) { ft.hr = HRESULT_FROM_WIN32(dwRet); ft.Throw(VSSDBG_VSSADMIN, ft.hr, L"Unable to get volume device name, %lS, %#x", pwszVolume, dwRet); } if (_wcsicmp(wszVolDevice+GLOBALROOT_SIZE, wszRegDevice) == 0) return TRUE; return FALSE; } BOOL VolumeHoldsPagefile( IN WCHAR* pwszVolume ) { CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"VolumeHoldsPagefile"); // // Retrieve page files. // BYTE* pbBuffer; DWORD dwBufferSize; PSYSTEM_PAGEFILE_INFORMATION pPageFileInfo = NULL; NTSTATUS status; BOOL fFound = FALSE; try { for (dwBufferSize=512; ; dwBufferSize += 512) { // Allocate buffer at 512 bytes increment. Previous allocation is // freed automatically. pbBuffer = (BYTE *) new BYTE[dwBufferSize]; if ( pbBuffer==NULL ) return E_OUTOFMEMORY; status = NtQuerySystemInformation( SystemPageFileInformation, pbBuffer, dwBufferSize, NULL ); if ( status==STATUS_INFO_LENGTH_MISMATCH ) // buffer not big enough. { delete [] pbBuffer; continue; } pPageFileInfo = (PSYSTEM_PAGEFILE_INFORMATION) pbBuffer; if (!NT_SUCCESS(status)) { ft.hr = HRESULT_FROM_NT(status); ft.Throw(VSSDBG_VSSADMIN, ft.hr, L"NtQuerySystemInformation failed, %#x", status); } else break; } // // Walk through each of the page file volumes. Usually the return // looks like "\??\C:\pagefile.sys." If a pagefile is added\extended // \moved, the return will look like // "\Device\HarddiskVolume2\pagefile.sys." // WCHAR *p; WCHAR wszDrive[3] = L"?:"; WCHAR buffer2[MAX_PATH], *pbuffer2 = buffer2; WCHAR wszVolDevice[MAX_PATH+GLOBALROOT_SIZE], *pwszVolDevice = wszVolDevice; DWORD dwRet; dwRet = GetDeviceName(pwszVolume, wszVolDevice); if (dwRet != ERROR_SUCCESS) { ft.hr = HRESULT_FROM_WIN32(dwRet); ft.Throw(VSSDBG_VSSADMIN, ft.hr, L"Unable to get volume device name, %lS, %#x", pwszVolume, dwRet); } for ( ; ; ) { if ( pPageFileInfo==NULL || pPageFileInfo->TotalSize==0 ) // We get 0 on WinPE. break; if ( pPageFileInfo->PageFileName.Length==0) break; p = wcschr(pPageFileInfo->PageFileName.Buffer, L':'); if (p != NULL) { // // Convert drive letter to volume name. // _ASSERTE(p>pPageFileInfo->PageFileName.Buffer); _ASSERTE(towupper(*(p-1))>=L'A'); _ASSERTE(towupper(*(p-1))<=L'Z'); wszDrive[0] = towupper(*(p-1)); dwRet = QueryDosDevice(wszDrive, buffer2, MAX_PATH); if (dwRet == 0) { ft.hr = HRESULT_FROM_NT(dwRet); ft.Throw(VSSDBG_VSSADMIN, ft.hr, L"QueryDosDevice failed, %#x", dwRet); } } else { _ASSERTE(_wcsnicmp(pPageFileInfo->PageFileName.Buffer, L"\\Device", 7)==0 ); p = wcsstr(pPageFileInfo->PageFileName.Buffer,L"\\pagefile.sys"); _ASSERTE( p!=NULL ); if (p == NULL) { ft.hr = E_UNEXPECTED; ft.Throw(VSSDBG_VSSADMIN, ft.hr, L"Unexpected pagefile name format, %lS", pPageFileInfo->PageFileName.Buffer); } *p = L'\0'; ft.hr = StringCchCopy(buffer2, MAX_PATH, pPageFileInfo->PageFileName.Buffer); if (ft.HrFailed()) { ft.Throw(VSSDBG_VSSADMIN, ft.hr, L"StringCchCopy failed, %#x", ft.hr); } } if (_wcsicmp(wszVolDevice+GLOBALROOT_SIZE, buffer2) == 0) { fFound = TRUE; break; } // // Next page file volume. // if (pPageFileInfo->NextEntryOffset == 0) break; pPageFileInfo = (PSYSTEM_PAGEFILE_INFORMATION)((PCHAR)pPageFileInfo + pPageFileInfo->NextEntryOffset); } } catch (...) { delete [] pbBuffer; throw; } delete [] pbBuffer; return fFound; } DWORD GetDeviceName( IN WCHAR* pwszVolume, OUT WCHAR wszDeviceName[MAX_PATH+GLOBALROOT_SIZE] ) /*++ Description: Get the device name of the given device. E.g. \Device\HarddiskVolume### Arguments: pwszVolume - volume GUID name. wszDeviceName - a buffer to receive device name. The buffer size must be MAX_PATH+GLOBALROOT_SIZE (including "\\?\GLOBALROOT"). Return Value: Win32 errors --*/ { DWORD dwRet; BOOL bRet; WCHAR wszMountDevName[MAX_PATH+sizeof(MOUNTDEV_NAME)]; // Based on GetVolumeNameForRoot (in volmount.c), MAX_PATH seems to // be big enough as out buffer for IOCTL_MOUNTDEV_QUERY_DEVICE_NAME. // But we assume the size of device name could be as big as MAX_PATH-1, // so we allocate the buffer size to include MOUNTDEV_NAME size. PMOUNTDEV_NAME pMountDevName; DWORD dwBytesReturned; HANDLE hVol = INVALID_HANDLE_VALUE; CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"GetDeviceName"); DWORD cch = 0; _ASSERTE(pwszVolume != NULL); wszDeviceName[0] = L'\0'; // // query the volume's device object name // cch = wcslen(pwszVolume); pwszVolume[cch - 1] = 0; hVol = CreateFile(pwszVolume, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); pwszVolume[cch - 1] = '\\'; if (hVol != INVALID_HANDLE_VALUE) { bRet = DeviceIoControl( hVol, // handle to device IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, // input data buffer 0, // size of input data buffer wszMountDevName, // output data buffer sizeof(wszMountDevName), // size of output data buffer &dwBytesReturned, NULL // overlapped information ); dwRet = GetLastError(); CloseHandle(hVol); if ( bRet==FALSE ) { ft.Trace(VSSDBG_VSSADMIN, L"GetDeviceName: DeviceIoControl() failed: %X", dwRet); return dwRet; } pMountDevName = (PMOUNTDEV_NAME) wszMountDevName; if (pMountDevName->NameLength == 0) { // TODO: Is this possible? UNKNOWN? _ASSERTE( 0 ); } else { // // copy name // ft.hr = StringCchPrintf(wszDeviceName, MAX_PATH+GLOBALROOT_SIZE, L"\\\\?\\GLOBALROOT" ); if (ft.HrFailed()) ft.Throw(VSSDBG_VSSADMIN, ft.hr, L"StringCchPrintf failed: %#x", ft.hr); CopyMemory(wszDeviceName+GLOBALROOT_SIZE, pMountDevName->Name, pMountDevName->NameLength ); // appending terminating NULL wszDeviceName[pMountDevName->NameLength/2 + GLOBALROOT_SIZE] = L'\0'; } } return ERROR_SUCCESS; } // VolumeMountPointExists is currently written specifically to verify the existence of a mountpoint // as defined by the WMI Win32_MountPoint Directory reference string. This string names the directory // such that the trailing backslash appears only on root directories. This is a basic assumption // made by this function. It is not general purpose. The calling code should be changed to append // the trailing backslash so that this function can be generalized. Mount points are enumerated // by GetVolumePathNamesForVolumeName API with trailing backslashes. BOOL VolumeMountPointExists( IN WCHAR* pwszVolume, IN WCHAR* pwszDirectory ) { CVssAutoPWSZ awszMountPoints; WCHAR* pwszCurrentMountPoint = NULL; BOOL fFound = FALSE; CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"VolumeMountPointExists"); // Get the length of the multi-string array DWORD cchVolumesBufferLen = 0; BOOL bResult = GetVolumePathNamesForVolumeName( pwszVolume, NULL, 0, &cchVolumesBufferLen); if (!bResult && (GetLastError() != ERROR_MORE_DATA)) ft.TranslateGenericError(VSSDBG_VSSADMIN, HRESULT_FROM_WIN32(GetLastError()), L"GetVolumePathNamesForVolumeName(%s, 0, 0, %p)", pwszVolume, &cchVolumesBufferLen); // Allocate the array awszMountPoints.Allocate(cchVolumesBufferLen); // Get the mount points // Note: this API was introduced in WinXP so it will need to be replaced if backported bResult = GetVolumePathNamesForVolumeName( pwszVolume, awszMountPoints, cchVolumesBufferLen, NULL); if (!bResult) ft.Throw(VSSDBG_VSSADMIN, HRESULT_FROM_WIN32(GetLastError()), L"GetVolumePathNamesForVolumeName(%s, %p, %lu, 0)", pwszVolume, awszMountPoints, cchVolumesBufferLen); // If the volume has mount points pwszCurrentMountPoint = awszMountPoints; if ( pwszCurrentMountPoint[0] ) { while(true) { // End of iteration? LONG lCurrentMountPointLength = (LONG) ::wcslen(pwszCurrentMountPoint); if (lCurrentMountPointLength == 0) break; // Only a root directory should have a trailing backslash character if (lCurrentMountPointLength > 2 && pwszCurrentMountPoint[lCurrentMountPointLength-1] == L'\\' && pwszCurrentMountPoint[lCurrentMountPointLength-2] != L':') { pwszCurrentMountPoint[lCurrentMountPointLength-1] = L'\0'; } if (_wcsicmp(pwszDirectory, pwszCurrentMountPoint) == 0) { fFound = TRUE; break; } // Go to the next one. Skip the zero character. pwszCurrentMountPoint += lCurrentMountPointLength + 1; } } return fFound; } void DeleteVolumeDriveLetter( IN WCHAR* pwszVolume, IN WCHAR* pwszDrivePath ) { BOOL fVolumeLocked = FALSE; HANDLE hVolume = INVALID_HANDLE_VALUE; DWORD dwRet = 0; CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"DeleteVolumeDriveLetter"); _ASSERTE(pwszVolume != NULL); _ASSERTE(pwszDrivePath != NULL); // Try to lock the volume DWORD cch = wcslen(pwszVolume); pwszVolume[cch - 1] = 0; hVolume = CreateFile( pwszVolume, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL ); pwszVolume[cch - 1] = '\\'; if (hVolume == INVALID_HANDLE_VALUE) { dwRet = GetLastError(); ft.hr = HRESULT_FROM_WIN32(dwRet); ft.Throw(VSSDBG_VSSADMIN, ft.hr, L"CreateFile(OPEN_EXISTING) failed for %lS, %#x", pwszVolume, dwRet); } dwRet = LockVolume(hVolume); if (dwRet == ERROR_SUCCESS) { fVolumeLocked = TRUE; ft.Trace(VSSDBG_VSSADMIN, L"volume %lS locked", pwszVolume); } else { ft.Trace(VSSDBG_VSSADMIN, L"Unable to lock volume %lS, %#x", pwszVolume, dwRet); } try { if (fVolumeLocked) { // If volume is locked delete the mount point if (!DeleteVolumeMountPoint(pwszDrivePath)) { dwRet = GetLastError(); ft.hr = HRESULT_FROM_WIN32(dwRet); ft.Throw(VSSDBG_VSSADMIN, ft.hr, L"DeleteVolumeMountPoint failed %#x, drivepath<%lS>", dwRet, pwszDrivePath); } } else { // Otherwise, remove the entry from the volume mgr database only // The volume will still be accessible through the drive letter until reboot ft.hr = DeleteDriveLetterFromDB(pwszDrivePath); if (ft.HrFailed()) ft.Throw(VSSDBG_VSSADMIN, ft.hr, L"DeleteDriveLetterFromDB failed %#x, drivepath<%lS>", ft.hr, pwszDrivePath); } } catch (...) { CloseHandle(hVolume); throw; } CloseHandle(hVolume); } HRESULT DeleteDriveLetterFromDB( IN WCHAR* pwszDriveLetter) { CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"DeleteDriveLetterFromDB"); MOUNTMGR_MOUNT_POINT *InputMountPoint=NULL; MOUNTMGR_MOUNT_POINTS *OutputMountPoints=NULL; ULONG ulInputSize = 0; HANDLE hMountMgr = INVALID_HANDLE_VALUE; DWORD dwBytesReturned = 0; DWORD dwRet = 0; BOOL bRet = FALSE; _ASSERTE(pwszDriveLetter != NULL); // // Prepare IOCTL_MOUNTMGR_QUERY_POINTS input // ulInputSize = sizeof(MOUNTMGR_MOUNT_POINT) + SYMBOLIC_LINK_LENGTH; InputMountPoint = (MOUNTMGR_MOUNT_POINT *) new BYTE[ulInputSize]; if ( InputMountPoint==NULL ) { ft.hr = E_OUTOFMEMORY; return ft.hr; } ZeroMemory(InputMountPoint, ulInputSize); InputMountPoint->SymbolicLinkNameOffset = sizeof(MOUNTMGR_MOUNT_POINT); InputMountPoint->SymbolicLinkNameLength = SYMBOLIC_LINK_LENGTH; InputMountPoint->UniqueIdOffset = 0; InputMountPoint->UniqueIdLength = 0; InputMountPoint->DeviceNameOffset = 0; InputMountPoint->DeviceNameLength = 0; // // Fill device name. // WCHAR wszBuffer[SYMBOLIC_LINK_LENGTH/2+1]; LPWSTR pwszBuffer; pwszBuffer = (LPWSTR)((PCHAR)InputMountPoint + InputMountPoint->SymbolicLinkNameOffset); pwszDriveLetter[0] = towupper(pwszDriveLetter[0]); ft.hr = StringCchPrintf(wszBuffer, SYMBOLIC_LINK_LENGTH/2+1, L"\\DosDevices\\%c:", pwszDriveLetter[0] ); if (ft.HrFailed()) { ft.Trace(VSSDBG_VSSADMIN, L"StringCchPrintf failed %#x", ft.hr); goto _bailout; } memcpy(pwszBuffer, wszBuffer, SYMBOLIC_LINK_LENGTH); // // Allocate space for output // OutputMountPoints = (MOUNTMGR_MOUNT_POINTS *) new WCHAR[4096]; if ( OutputMountPoints==NULL ) { ft.hr = E_OUTOFMEMORY; goto _bailout; } // // Open mount manager // hMountMgr = CreateFile( MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if ( hMountMgr==INVALID_HANDLE_VALUE ) { dwRet = GetLastError(); ft.hr = HRESULT_FROM_WIN32(dwRet); ft.Trace(VSSDBG_VSSADMIN, L"DeleteDriveLetterFromDB CreateFile failed %#x", dwRet); goto _bailout; } // // Issue IOCTL_MOUNTMGR_DELETE_POINTS_DBONLY. // bRet = DeviceIoControl( hMountMgr, IOCTL_MOUNTMGR_DELETE_POINTS_DBONLY, InputMountPoint, ulInputSize, OutputMountPoints, 4096 * sizeof(WCHAR), &dwBytesReturned, NULL ); dwRet = GetLastError(); // Save error code. CloseHandle(hMountMgr); hMountMgr = NULL; if ( bRet==FALSE ) { ft.hr = HRESULT_FROM_WIN32(dwRet); ft.Trace(VSSDBG_VSSADMIN, L"DeleteDriveLetterFromDB DeviceIoControl failed %#x", dwRet); goto _bailout; } delete [] InputMountPoint; delete [] OutputMountPoints; return S_OK; _bailout: delete [] InputMountPoint; delete [] OutputMountPoints; return ft.hr; } DWORD LockVolume( IN HANDLE hVolume ) { DWORD dwBytes = 0; BOOL fRet = FALSE; int nCount = 0; while ( fRet==FALSE ) { fRet = DeviceIoControl( hVolume, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &dwBytes, NULL ); if (fRet == FALSE) { DWORD dwRet = GetLastError(); if (dwRet != ERROR_ACCESS_DENIED || nCount>=60) { return dwRet; } nCount++; Sleep( 500 ); // Wait for half second up to 60 times (30s). } } return ERROR_SUCCESS; } // Returns TRUE if the drive letter is available BOOL IsDriveLetterAvailable ( IN WCHAR* pwszDriveLetter ) { int iLen = 0; BOOL fFound = FALSE; DWORD cchBufLen = 0; CVssAutoPWSZ awszDriveStrings; CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"IsDriveLetterAvailable"); _ASSERTE(pwszDriveLetter != NULL); // How much space needed for drive strings? cchBufLen = GetLogicalDriveStrings(0, NULL); if (cchBufLen == 0) { ft.hr = HRESULT_FROM_WIN32(GetLastError()); ft.Trace(VSSDBG_VSSADMIN, L"GetLogicalDriveStrings failed %#x", GetLastError()); } else { // Allocate space for the drive strings awszDriveStrings.Allocate(cchBufLen); // Get the drive strings if (GetLogicalDriveStrings(cchBufLen, awszDriveStrings) == 0) { ft.hr = HRESULT_FROM_WIN32(GetLastError()); ft.Trace(VSSDBG_VSSADMIN, L"GetLogicalDriveStrings failed %#x", GetLastError()); } else { WCHAR* pwcTempDriveString = awszDriveStrings; WCHAR wcDriveLetter = towupper(pwszDriveLetter[0]); // Look for the drive letter in the list of system drive letters while (!fFound) { iLen = lstrlen(pwcTempDriveString); if (iLen == 0) break; pwcTempDriveString[0] = towupper(pwcTempDriveString[0]); if (pwcTempDriveString[0] == wcDriveLetter) { fFound = TRUE; break; } pwcTempDriveString = &pwcTempDriveString [iLen + 1]; } } } return !fFound; } BOOL IsDriveLetterSticky( IN WCHAR* pwszDriveLetter ) { BOOL fFound = FALSE; WCHAR wszTempVolumeName [MAX_PATH+1]; CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"IsDriveLetterSticky"); WCHAR wszDrivePath[g_cchDriveName]; _ASSERTE(pwszDriveLetter != NULL); wszDrivePath[0] = towupper(pwszDriveLetter[0]); wszDrivePath[1] = L':'; wszDrivePath[2] = L'\\'; wszDrivePath[3] = L'\0'; if (GetVolumeNameForVolumeMountPoint( wszDrivePath, wszTempVolumeName, MAX_PATH)) { WCHAR wszCurrentDrivePath[g_cchDriveName]; if (GetVolumeDrive(wszTempVolumeName, g_cchDriveName, wszCurrentDrivePath)) { wszCurrentDrivePath[0] = towupper(wszCurrentDrivePath[0]); if (wszDrivePath[0] == wszCurrentDrivePath[0]) fFound = TRUE; } } return fFound; } BOOL IsBootDrive( IN WCHAR* pwszDriveLetter ) { WCHAR wszSystemDirectory[MAX_PATH+1]; CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"IsBootDrive"); _ASSERTE(pwszDriveLetter != NULL); WCHAR wcDrive = towupper(pwszDriveLetter[0]); if (!GetSystemDirectory(wszSystemDirectory, MAX_PATH+1)) { DWORD dwErr = GetLastError(); ft.hr = HRESULT_FROM_WIN32(dwErr); ft.Throw(VSSDBG_VSSADMIN, ft.hr, L"GetSystemDirectory failed %#x", dwErr); } wszSystemDirectory[0] = towupper(wszSystemDirectory[0]); if (wcDrive == wszSystemDirectory[0]) return TRUE; return FALSE; } BOOL DeleteNetworkShare( IN WCHAR* pwszDriveRoot ) { NET_API_STATUS status; WCHAR wszShareName[3]; wszShareName[0] = pwszDriveRoot[0]; wszShareName[1] = L'$'; wszShareName[2] = L'\0'; status = NetShareDel(NULL, wszShareName, 0); if ( status!=NERR_Success ) return FALSE; else return TRUE; }