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
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;
|
|
}
|
|
|