Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

889 lines
28 KiB

///////////////////////////////////////////////////////////////////////////////
/* File: fsobject.cpp
Description: Contains member function definitions for class FSObject and
it's derived subclasses.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/22/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
#include "pch.h" // PCH
#pragma hdrstop
#include "dskquota.h"
#include "fsobject.h"
#include "pathstr.h"
//
// Verify that build is UNICODE.
//
#if !defined(UNICODE)
# error This module must be compiled UNICODE.
#endif
///////////////////////////////////////////////////////////////////////////////
/* Function: FSObject::~FSObject
Description: Destructor. Frees object's name buffer.
Arguments: None.
Returns: Nothing.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/22/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
FSObject::~FSObject(
VOID
)
{
DBGTRACE((DM_CONTROL, DL_MID, TEXT("FSObject::~FSObject")));
}
///////////////////////////////////////////////////////////////////////////////
/* Function: FSObject::AddRef
Description: Increments object reference count.
Note this is not a member of IUnknown; but it works the same.
Arguments: None.
Returns: New reference count value.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/22/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
ULONG
FSObject::AddRef(
VOID
)
{
DBGTRACE((DM_CONTROL, DL_LOW, TEXT("FSObject::AddRef")));
DBGPRINT((DM_CONTROL, DL_LOW, TEXT("\t0x%08X %d -> %d"),
this, m_cRef, m_cRef + 1));
ULONG ulReturn = m_cRef + 1;
InterlockedIncrement(&m_cRef);
return ulReturn;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: FSObject::Release
Description: Decrements object reference count. If count drops to 0,
object is deleted. Note this is not a member of IUnknown; but it
works the same.
Arguments: None.
Returns: New reference count value.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/22/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
ULONG
FSObject::Release(
VOID
)
{
DBGTRACE((DM_CONTROL, DL_LOW, TEXT("FSObject::Release")));
DBGPRINT((DM_CONTROL, DL_LOW, TEXT("\t0x%08X %d -> %d"),
this, m_cRef, m_cRef - 1));
ULONG ulReturn = m_cRef - 1;
if (InterlockedDecrement(&m_cRef) == 0)
{
delete this;
ulReturn = 0;
}
return ulReturn;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: FSObject::ObjectSupportsQuotas
Description: Determine a file system object's type (and locality) from
it's name string.
Arguments:
pszFSObjName - Volume root name. (i.e. "C:\", "\\scratch\scratch").
Returns:
S_OK - Success. Supports quotas.
ERROR_NOT_SUPPORTED (hr) - File system doesn't support quotas.
Other win32 error - Couldn't get volume information.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/24/96 Initial creation. BrianAu
08/16/96 Added pSupportsQuotas. BrianAu
12/05/96 Disabled check for $DeadMeat volume label. BrianAu
Leave the code in place for a while. I'll remove
it later when we're sure it's not needed.
07/03/97 Changed name from ObjectTypeFromName. BrianAu
Changed logic to indicate reason for not supporting
quotas.
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
FSObject::ObjectSupportsQuotas(
LPCTSTR pszFSObjName
)
{
DBGTRACE((DM_CONTROL, DL_MID, TEXT("FSObject::ObjectSupportsQuotas")));
DBGPRINT((DM_CONTROL, DL_MID, TEXT("\tobject = \"%s\""), pszFSObjName ? pszFSObjName : TEXT("<null>")));
HRESULT hr = E_FAIL;
DWORD dwFileSysFlags = 0;
TCHAR szFileSysName[MAX_PATH];
DBGASSERT((NULL != pszFSObjName));
if (GetVolumeInformation(
pszFSObjName,
NULL, 0,
NULL, 0,
&dwFileSysFlags,
szFileSysName,
ARRAYSIZE(szFileSysName)))
{
//
// Does the file system support quotas?
//
if (0 != (dwFileSysFlags & FILE_VOLUME_QUOTAS))
{
//
// Yes, it does.
//
hr = S_OK;
DBGPRINT((DM_CONTROL, DL_LOW, TEXT("Vol \"%s\" supports quotas"), pszFSObjName));
}
else
{
//
// Doesn't support quotas.
//
hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
DBGPRINT((DM_CONTROL, DL_HIGH, TEXT("File system \"%s\" on \"%s\" doesn't support quotas."),
szFileSysName, pszFSObjName));
}
}
else
{
DWORD dwErr = GetLastError();
hr = HRESULT_FROM_WIN32(dwErr);
DBGERROR((TEXT("Error %d calling GetVolumeInformation for \"%s\""), dwErr, pszFSObjName));
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: FSObject::Create
Description: 2 overloaded functions.
Static functions for creating a File System object of the
proper type. Clients call Create with an object name string or
a reference to an existing FSObject instance.
Arguments:
pszFSObjName - Address of volume root string.
ppNewObject - Address of FSObject pointer to accept the address of the
new file system object.
ObjToClone - Reference to file system object to be cloned.
Returns:
NOERROR - Success.
E_OUTOFMEMORY - Insufficient memory.
ERROR_ACCESS_DENIED (hr) - Insufficient access to open device.
ERROR_FILE_NOT_FOUND (hr) - Disk device not found.
ERROR_INVALID_NAME (hr) - Object name is invalid.
ERROR_NOT_SUPPORTED (hr) - Volume doesn't support quotas.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/23/96 Initial creation. BrianAu
09/05/96 Added exception handling. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
FSObject::Create(
LPCTSTR pszFSObjName,
DWORD dwAccess,
FSObject **ppNewObject
)
{
DBGTRACE((DM_CONTROL, DL_MID, TEXT("FSObject::Create")));
DBGPRINT((DM_CONTROL, DL_MID, TEXT("\tVol = \"%s\""), pszFSObjName ? pszFSObjName : TEXT("<null>")));
HRESULT hr = NOERROR;
DBGASSERT((NULL != pszFSObjName));
DBGASSERT((NULL != ppNewObject));
*ppNewObject = NULL;
FSObject *pNewObject = NULL;
try
{
hr = FSObject::ObjectSupportsQuotas(pszFSObjName);
if (SUCCEEDED(hr))
{
hr = FSObject_CreateLocalVolume(pszFSObjName, &pNewObject);
if (SUCCEEDED(hr))
{
//
// Do any subclass-specific initialization.
// i.e.: Volume opens the volume device.
//
hr = pNewObject->Initialize(dwAccess);
if (SUCCEEDED(hr))
{
//
// Return ptr to caller.
//
DBGPRINT((DM_CONTROL, DL_MID, TEXT("FSObject created")));
pNewObject->AddRef();
*ppNewObject = pNewObject;
}
else
{
DBGPRINT((DM_CONTROL, DL_MID, TEXT("FSObject create FAILED with error 0x%08X"), hr));
delete pNewObject;
pNewObject = NULL;
}
}
}
}
catch(CAllocException& e)
{
DBGERROR((TEXT("Insufficient memory exception")));
delete pNewObject; // Will also free name if necessary.
hr = E_OUTOFMEMORY;
}
return hr;
}
//
// Version to clone an existing FSObject.
//
HRESULT
FSObject::Create(
const FSObject& ObjectToClone,
FSObject **ppNewObject
)
{
return FSObject::Create(ObjectToClone.m_strFSObjName,
ObjectToClone.m_dwAccessRights,
ppNewObject);
}
///////////////////////////////////////////////////////////////////////////////
/* Function: FSObject::GetName
Description: Retrieves the file system object's name string.
Arguments:
pszBuffer - Address of buffer to accept name string.
cchBuffer - Size of destination buffer in characters.
Returns:
NOERROR - Success.
ERROR_INSUFFICIENT_BUFFER - Destination buffer is too small for name.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/24/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT FSObject::GetName(LPTSTR pszBuffer, ULONG cchBuffer) const
{
HRESULT hr = NOERROR;
DBGASSERT((NULL != pszBuffer));
if ((ULONG)m_strFSObjName.Length() < cchBuffer)
lstrcpyn(pszBuffer, m_strFSObjName, cchBuffer);
else
{
*pszBuffer = TEXT('\0');
hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
return hr;
}
//
// This function was created to fix bug 365936.
// Class FSObject has a CPath member who's constructor
// can throw CAllocException. Because of this, we
// need to isolate this construction operation from
// other FSObject code so that we don't try to delete
// an FSObject object that has already been destroyed
// by the constructor's call stack unwinding process.
//
HRESULT
FSObject_CreateLocalVolume(
LPCTSTR pszVolumeName,
FSObject **ppObject
)
{
HRESULT hr = S_OK;
*ppObject = NULL;
try
{
FSObject *pNewObject = new FSLocalVolume(pszVolumeName);
*ppObject = pNewObject;
}
catch(CAllocException& e)
{
hr = E_OUTOFMEMORY;
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: FSVolume::~FSVolume
Description: Destructor. Closes volume handle.
Arguments: None.
Returns: Nothing.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/24/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
FSVolume::~FSVolume(
VOID
)
{
DBGTRACE((DM_CONTROL, DL_MID, TEXT("FSVolume::~FSVolume")));
if (INVALID_HANDLE_VALUE != m_hVolume)
CloseHandle(m_hVolume);
}
///////////////////////////////////////////////////////////////////////////////
/* Function: FSVolume::Initialize
Description: Initializes a volume object by opening the NTFS volume.
Arguments:
dwAccess - Desired access. GENERIC_READ, GENERIC_WRITE.
Returns:
NOERROR - Success.
ERROR_ACCESS_DENIED (hr) - Insufficient access to open device.
ERROR_FILE_NOT_FOUND (hr) - Disk device not found.
ERROR_INVALID_NAME (hr) - Invalid path string.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/24/96 Initial creation. BrianAu
08/11/96 Added access right handling. BrianAu
08/16/96 Added device name formatting. BrianAu
07/03/97 Changed so caller passes in desired access. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT FSVolume::Initialize(
DWORD dwAccess
)
{
DBGTRACE((DM_CONTROL, DL_MID, TEXT("FSVolume::Initialize")));
DBGPRINT((DM_CONTROL, DL_MID, TEXT("\tdwAccess = 0x%08X"), dwAccess));
HRESULT hr = NOERROR;
//
// Close the device if it's open.
//
if (INVALID_HANDLE_VALUE != m_hVolume)
CloseHandle(m_hVolume);
//
// Create a path to the actual quota file on the volume.
// This string is appended to the existing "volume name" we already
// have.
//
CPath strQuotaFile(m_strFSObjName);
strQuotaFile.AddBackslash();
strQuotaFile += CString("$Extend\\$Quota:$Q:$INDEX_ALLOCATION");
m_hVolume = CreateFile(strQuotaFile,
dwAccess,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
if (INVALID_HANDLE_VALUE == m_hVolume)
{
//
// Couldn't open device because...
// 1. I/O error
// 2. File (device) not found.
// 3. Access denied.
//
DWORD dwErr = GetLastError();
hr = HRESULT_FROM_WIN32(dwErr);
DBGERROR((TEXT("Error %d opening quota file \"%s\""), dwErr, strQuotaFile.Cstr()));
}
else
{
//
// Save access granted to caller. Will be used to validate
// operation requests later.
//
DBGPRINT((DM_CONTROL, DL_MID, TEXT("Quota file \"%s\" open with access 0x%08X"), strQuotaFile.Cstr(), dwAccess));
m_dwAccessRights = dwAccess;
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: FSVolume::QueryObjectQuotaInformation
Description: Retrieves quota information for the volume. This includes
default quota threshold, default quota limit and system control flags.
Arguments:
poi - Address of object information buffer. This type contains
a subset of the information in FILE_FS_CONTROL_INFORMATION
(defined in ntioapi.h).
Returns:
NOERROR - Success.
ERROR_ACCESS_DENIED (hr) - No READ access to quota device.
Other - NTFS subsystem failure result.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/24/96 Initial creation. BrianAu
08/11/96 Added access control. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
FSVolume::QueryObjectQuotaInformation(
PDISKQUOTA_FSOBJECT_INFORMATION poi
)
{
DBGTRACE((DM_CONTROL, DL_MID, TEXT("FSVolume::QueryObjectQuotaInformation")));
HRESULT hr = E_FAIL;
if (!GrantedAccess(GENERIC_READ))
{
DBGPRINT((DM_CONTROL, DL_MID, TEXT("Access denied reading quota info")));
hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
}
else
{
NTSTATUS status = STATUS_SUCCESS;
IO_STATUS_BLOCK iosb;
FILE_FS_CONTROL_INFORMATION ControlInfo;
status = NtQueryVolumeInformationFile(
m_hVolume,
&iosb,
&ControlInfo,
sizeof(ControlInfo),
FileFsControlInformation);
if (STATUS_SUCCESS == status)
{
//
// Update caller's buffer with quota control data.
//
poi->DefaultQuotaThreshold = ControlInfo.DefaultQuotaThreshold.QuadPart;
poi->DefaultQuotaLimit = ControlInfo.DefaultQuotaLimit.QuadPart;
poi->FileSystemControlFlags = ControlInfo.FileSystemControlFlags;
hr = NOERROR;
}
else
{
DBGERROR((TEXT("NtQueryVolumeInformationFile failed with NTSTATUS 0x%08X"), status));
hr = HResultFromNtStatus(status);
}
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: FSVolume::SetObjectQuotaInformation
Description: Writes new quota information to the volume. This includes
default quota threshold, default quota limit and system control flags.
Arguments:
poi - Address of object information buffer. This type contains
a subset of the information in FILE_FS_CONTROL_INFORMATION
(defined in ntioapi.h).
dwChangeMask - Mask specifying which elements in *poi to write to disk.
Can be any combination of:
FSObject::ChangeState
FSObject::ChangeLogFlags
FSObject::ChangeThreshold
FSObject::ChangeLimit
Returns:
NOERROR - Success.
ERROR_ACCESS_DENIED (hr) - No WRITE access to quota device.
Other - NTFS subsystem failure result.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/24/96 Initial creation. BrianAu
08/11/96 Added access control. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
FSVolume::SetObjectQuotaInformation(
PDISKQUOTA_FSOBJECT_INFORMATION poi,
DWORD dwChangeMask
) const
{
DBGTRACE((DM_CONTROL, DL_MID, TEXT("FSVolume::SetObjectQuotaInformation")));
HRESULT hr = E_FAIL;
if (!GrantedAccess(GENERIC_WRITE))
{
DBGPRINT((DM_CONTROL, DL_MID, TEXT("Access denied setting quota info")));
hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
}
else
{
NTSTATUS status = STATUS_SUCCESS;
IO_STATUS_BLOCK iosb;
FILE_FS_CONTROL_INFORMATION ControlInfo;
//
// First read current info from disk.
// Then replace whatever we're changing.
//
status = NtQueryVolumeInformationFile(
m_hVolume,
&iosb,
&ControlInfo,
sizeof(ControlInfo),
FileFsControlInformation);
if (STATUS_SUCCESS == status)
{
//
// Only alter those values specified in dwChangeMask.
//
if (FSObject::ChangeState & dwChangeMask)
{
ControlInfo.FileSystemControlFlags &= ~DISKQUOTA_STATE_MASK;
ControlInfo.FileSystemControlFlags |= (poi->FileSystemControlFlags & DISKQUOTA_STATE_MASK);
}
if (FSObject::ChangeLogFlags & dwChangeMask)
{
ControlInfo.FileSystemControlFlags &= ~DISKQUOTA_LOGFLAG_MASK;
ControlInfo.FileSystemControlFlags |= (poi->FileSystemControlFlags & DISKQUOTA_LOGFLAG_MASK);
}
if (FSObject::ChangeThreshold & dwChangeMask)
{
ControlInfo.DefaultQuotaThreshold.QuadPart = poi->DefaultQuotaThreshold;
}
if (FSObject::ChangeLimit & dwChangeMask)
{
ControlInfo.DefaultQuotaLimit.QuadPart = poi->DefaultQuotaLimit;
}
status = NtSetVolumeInformationFile(
m_hVolume,
&iosb,
&ControlInfo,
sizeof(ControlInfo),
FileFsControlInformation);
if (STATUS_SUCCESS == status)
{
hr = NOERROR;
}
else
{
DBGERROR((TEXT("NtSetVolumeInformationFile failed with NTSTATUS = 0x%08X"), status));
hr = HResultFromNtStatus(status);
}
}
else
{
DBGERROR((TEXT("NtQueryVolumeInformationFile failed with NTSTATUS = 0x%08X"), status));
hr = HResultFromNtStatus(status);
}
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: FSVolume::QueryUserQuotaInformation
Description: Retrieves user quota information for the volume. This includes
quota threshold and quota limit. This function works like an enumerator.
Repeated calls will return multiple user records.
Arguments:
pBuffer - Address of buffer to receive quota information.
cbBuffer - Number of bytes in buffer.
bReturnSingleEntry - TRUE = Return only one record from quota file.
FALSE = Return as many whole entries as possible
in buffer.
pSidList [optional] - Address of SID list identifying users to obtain
information for. Specify NULL to include all users.
cbSidList [optional] - Number of bytes in sid list. Ignored if pSidList
is NULL.
pStartSid [optional] - Address of SID identifying which user is to start
the enumeration. Specify NULL to start with current user in
enumeration.
bRestartScan - TRUE = restart scan from first user in the SID list or
the entire file if pSidList is NULL.
FALSE = Continue enumeration from current user record.
Returns:
NOERROR - Success.
ERROR_NO_MORE_ITEMS - Read last entry in quota file.
ERROR_ACCESS_DENIED (hr) - No READ access to quota device.
Other - Quota subsystem error.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/24/96 Initial creation. BrianAu
08/11/96 Added access control. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
FSVolume::QueryUserQuotaInformation(
PVOID pBuffer,
ULONG cbBuffer,
BOOL bReturnSingleEntry,
PVOID pSidList,
ULONG cbSidList,
PSID pStartSid,
BOOL bRestartScan
)
{
DBGTRACE((DM_CONTROL, DL_MID, TEXT("FSVolume::QueryUserQuotaInformation")));
HRESULT hr = E_FAIL;
DBGASSERT((NULL != pBuffer));
if (!GrantedAccess(GENERIC_READ))
{
DBGPRINT((DM_CONTROL, DL_MID, TEXT("Access denied querying user quota info")));
hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
}
else
{
NTSTATUS status = STATUS_SUCCESS;
IO_STATUS_BLOCK iosb;
status = NtQueryQuotaInformationFile(
m_hVolume,
&iosb,
pBuffer,
cbBuffer,
(BOOLEAN)bReturnSingleEntry,
pSidList,
cbSidList,
pStartSid,
(BOOLEAN)bRestartScan);
switch(status)
{
case STATUS_SUCCESS:
hr = NOERROR;
break;
default:
DBGERROR((TEXT("NtQueryQuotaInformationFile failed with NTSTATUS 0x%08X"), status));
//
// Fall through...
//
case STATUS_NO_MORE_ENTRIES:
hr = HResultFromNtStatus(status);
break;
}
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: FSVolume::SetUserQuotaInformation
Description: Writes new user quota information to the volume. This includes
quota threshold, and quota limit.
Arguments:
pBuffer - Address of buffer containing quota information.
cbBuffer - Number of bytes of data in buffer.
Returns:
NOERROR - Success.
ERROR_ACCESS_DENIED (hr) - No WRITE access to quota device.
Or tried to set limit on Administrator.
Other - Quota subsystem error.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
05/24/96 Initial creation. BrianAu
08/11/96 Added access control. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
FSVolume::SetUserQuotaInformation(
PVOID pBuffer,
ULONG cbBuffer
) const
{
DBGTRACE((DM_CONTROL, DL_MID, TEXT("FSVolume::SetUserQuotaInformation")));
HRESULT hr = NOERROR;
DBGASSERT((NULL != pBuffer));
if (!GrantedAccess(GENERIC_WRITE))
{
DBGPRINT((DM_CONTROL, DL_MID, TEXT("Access denied setting user quota info")));
hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
}
else
{
NTSTATUS status = STATUS_SUCCESS;
IO_STATUS_BLOCK iosb;
status = NtSetQuotaInformationFile(
m_hVolume,
&iosb,
pBuffer,
cbBuffer);
if (STATUS_SUCCESS == status)
{
hr = NOERROR;
}
else
{
DBGERROR((TEXT("NtSetQuotaInformationFile failed with NTSTATUS 0x%08X"), status));
hr = HResultFromNtStatus(status);
}
}
return hr;
}
//
// Convert an NTSTATUS value to an HRESULT.
// This is a simple attempt at converting the most common NTSTATUS values that
// might be returned from NtQueryxxxxx and NTSetxxxxxx functions. If I've missed
// some obvious ones, go ahead and add them.
//
HRESULT
FSObject::HResultFromNtStatus(
NTSTATUS status
)
{
HRESULT hr = E_FAIL; // Default if none matched.
static const struct
{
NTSTATUS status;
HRESULT hr;
} rgXref[] = {
{ STATUS_SUCCESS, NOERROR },
{ STATUS_INVALID_PARAMETER, E_INVALIDARG },
{ STATUS_NO_MORE_ENTRIES, HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) },
{ STATUS_ACCESS_DENIED, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) },
{ STATUS_BUFFER_TOO_SMALL, HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) },
{ STATUS_BUFFER_OVERFLOW, HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW) },
{ STATUS_INVALID_HANDLE, HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE) },
{ STATUS_INVALID_DEVICE_REQUEST, HRESULT_FROM_WIN32(ERROR_BAD_DEVICE) },
{ STATUS_FILE_INVALID, HRESULT_FROM_WIN32(ERROR_DEVICE_NOT_AVAILABLE) }};
for (int i = 0; i < ARRAYSIZE(rgXref); i++)
{
if (rgXref[i].status == status)
{
hr = rgXref[i].hr;
break;
}
}
return hr;
}