Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1112 lines
33 KiB

//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2002-2004 Microsoft Corporation
//
// Module Name: volutil.cpp
//
// Description:
// Utility functions for handling volumes
//
// Author: Jim Benton (jbenton) 30-Apr-2002
//
//////////////////////////////////////////////////////////////////////////////
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <map>
#include "vs_assert.hxx"
#include <atlbase.h>
#include <winioctl.h>
#include <ntddvol.h> // IOCTL_VOLUME_IS_OFFLINE
#include <mountmgr.h> // MOUNTDEV_NAME
#include <lm.h> // NetShareDel
#include "vs_inc.hxx"
#include <strsafe.h>
#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;
}