|
|
///////////////////////////////////////////////////////////////////////////////
/* 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"))); ULONG cRef = InterlockedIncrement(&m_cRef); DBGPRINT((DM_CONTROL, DL_LOW, TEXT("\t0x%08X %d -> %d"), this, cRef - 1, cRef )); return cRef; }
///////////////////////////////////////////////////////////////////////////////
/* 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")));
ASSERT( 0 != m_cRef ); ULONG cRef = InterlockedDecrement(&m_cRef);
DBGPRINT((DM_CONTROL, DL_LOW, TEXT("\t0x%08X %d -> %d"), this, cRef + 1, cRef ));
if ( 0 == cRef ) { delete this; } return cRef; }
///////////////////////////////////////////////////////////////////////////////
/* 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; }
|