windows-nt/Source/XPSP1/NT/shell/osshell/dskquota/control/userbat.cpp

569 lines
16 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
///////////////////////////////////////////////////////////////////////////////
/* File: userbat.cpp
Description: Contains member function definitions for class
DiskQuotaUserBatch.
The DiskQuotaUserBatch object represents a batch update mechanism for
rapid update of multiple-user-object quota information. This class
takes advantage of the batching capabilities built into the NTIOAPI.
A user batch object is obtained through
IDiskQuotaControl::CreateUserBatch().
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/06/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
#include "pch.h" // PCH
#pragma hdrstop
#include "userbat.h"
//
// Verify that build is UNICODE.
//
#if !defined(UNICODE)
# error This module must be compiled UNICODE.
#endif
//
// The NTFS quota write function can only handle a max of 64K of data
// in any one write operation. IssacHe recommended 60K as a comfortable
// limit.
//
const INT MAX_BATCH_BUFFER_BYTES = (1 << 10) * 60;
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaUserBatch::DiskQuotaUserBatch
Description: Constructor.
Arguments:
pFSObject - Address of File System object to be utilized by the
batching operations.
Returns: Nothing.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
09/03/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
DiskQuotaUserBatch::DiskQuotaUserBatch(
FSObject *pFSObject
) : m_cRef(0),
m_pFSObject(pFSObject)
{
DBGASSERT((NULL != m_pFSObject));
m_pFSObject->AddRef();
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaUserBatch::~DiskQuotaUserBatch
Description: Destructor.
Arguments: Destroys the batch object.
Returns: Nothing.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
07/26/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
DiskQuotaUserBatch::~DiskQuotaUserBatch(
VOID
)
{
Destroy();
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaUserBatch::QueryInterface
Description: Returns an interface pointer to the object's IUnknown or
IDiskQuotaUserBatch interface. Only IID_IUnknown and
IID_IDiskQuotaUserBatch are recognized. The object referenced by the
returned interface pointer is uninitialized. The recipient of the
pointer must call Initialize() before the object is usable.
Arguments:
riid - Reference to requested interface ID.
ppvOut - Address of interface pointer variable to accept interface ptr.
Returns:
NOERROR - Success.
E_NOINTERFACE - Requested interface not supported.
E_INVALIDARG - ppvOut argument was NULL.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/06/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
DiskQuotaUserBatch::QueryInterface(
REFIID riid,
LPVOID *ppvOut
)
{
HRESULT hResult = E_NOINTERFACE;
if (NULL == ppvOut)
return E_INVALIDARG;
*ppvOut = NULL;
if (IID_IUnknown == riid || IID_IDiskQuotaUserBatch == riid)
{
*ppvOut = this;
((LPUNKNOWN)*ppvOut)->AddRef();
hResult = NOERROR;
}
return hResult;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaUserBatch::AddRef
Description: Increments object reference count.
Arguments: None.
Returns: New reference count value.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/06/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP_(ULONG)
DiskQuotaUserBatch::AddRef(
VOID
)
{
ULONG ulReturn = m_cRef + 1;
DBGPRINT((DM_COM, DL_HIGH, TEXT("DiskQuotaUserBatch::AddRef, 0x%08X %d -> %d\n"),
this, ulReturn - 1, ulReturn));
InterlockedIncrement(&m_cRef);
return ulReturn;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaUserBatch::Release
Description: Decrements object reference count. If count drops to 0,
object is deleted.
Arguments: None.
Returns: New reference count value.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/06/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP_(ULONG)
DiskQuotaUserBatch::Release(
VOID
)
{
ULONG ulReturn = m_cRef - 1;
DBGPRINT((DM_COM, DL_HIGH, TEXT("DiskQuotaUserBatch::Release, 0x%08X %d -> %d\n"),
this, ulReturn + 1, ulReturn));
if (InterlockedDecrement(&m_cRef) == 0)
{
delete this;
ulReturn = 0;
}
return ulReturn;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaUserBatch::Destroy
Description: Destroys the contents of a user batch object and releases
its FSObject pointer.
Arguments: None.
Returns: Nothing.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/06/96 Initial creation. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
VOID
DiskQuotaUserBatch::Destroy(VOID)
{
//
// Remove and release all user object pointers from the batch list.
//
RemoveAll();
if (NULL != m_pFSObject)
{
//
// Release hold on File System object.
//
m_pFSObject->Release();
m_pFSObject = NULL;
}
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaUserBatch::Add
Description: Adds an IDiskQuotaUser interface pointer to the batch list.
Arguments:
pUser - Address of IDiskQuotaUser interface.
Returns:
NOERROR - Success.
E_INVALIDARG - pUser arg is NULL.
E_OUTOFMEMORY - Couldn't create new node in batch list.
E_UNEXPECTED - Unexpected exception.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/06/96 Initial creation. BrianAu
09/03/96 Add exception handling. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
DiskQuotaUserBatch::Add(
PDISKQUOTA_USER pUser
)
{
HRESULT hResult = NOERROR;
if (NULL == pUser)
return E_INVALIDARG;
try
{
m_UserList.Append(pUser);
//
// Success. Increment ref count on object.
//
pUser->AddRef();
}
catch(CAllocException& e)
{
hResult = E_OUTOFMEMORY;
}
return hResult;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaUserBatch::Remove
Description: Removes a user pointer from the batch queue.
Arguments:
pUser - Address of IDiskQuotaUser interface for the user object to
be removed.
Returns:
S_OK - Success.
S_FALSE - User not found in batch object.
E_INVALIDARG - pUser argument is NULL.
E_UNEXPECTED - Unexpected exception.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/06/96 Initial creation. BrianAu
09/03/96 Add exception handling. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
HRESULT
DiskQuotaUserBatch::Remove(
PDISKQUOTA_USER pUser
)
{
HRESULT hResult = S_FALSE; // Assume user not present.
PDISKQUOTA_USER pRemoved = NULL;
if (NULL == pUser)
return E_INVALIDARG;
m_UserList.Lock();
INT iUser = m_UserList.Find(pUser);
if (-1 != iUser)
{
try
{
DBGASSERT((NULL != m_UserList[iUser]));
m_UserList[iUser]->Release();
m_UserList.Delete(iUser);
hResult = S_OK;
}
catch(CAllocException& e)
{
hResult = E_OUTOFMEMORY;
}
}
m_UserList.ReleaseLock();
return hResult;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaUserBatch::RemoveAll
Description: Removes all user pointers from the batch
list and calling Release() through the removed pointer.
Arguments: None.
Returns:
NOERROR - Success.
E_UNEXPECTED - Unexpected exception.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/06/96 Initial creation. BrianAu
09/03/96 Add exception handling. BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
DiskQuotaUserBatch::RemoveAll(
VOID
)
{
HRESULT hResult = NOERROR;
m_UserList.Lock();
INT cUsers = m_UserList.Count();
for (INT i = 0; i < cUsers; i++)
{
try
{
DBGASSERT((NULL != m_UserList[i]));
m_UserList[i]->Release();
}
catch(CAllocException& e)
{
hResult = E_OUTOFMEMORY;
}
}
m_UserList.Clear();
m_UserList.ReleaseLock();
return hResult;
}
///////////////////////////////////////////////////////////////////////////////
/* Function: DiskQuotaUserBatch::FlushToDisk
Description: Writes data for all batched user objects to disk in a single
NTIOAPI call. This is the real worker function for the batch object.
Arguments: None.
Returns:
NOERROR - Success.
E_OUTOFMEMORY - Insufficient memory.
E_UNEXPECTED - Unexpected exception.
Revision History:
Date Description Programmer
-------- --------------------------------------------------- ----------
06/06/96 Initial creation. BrianAu
09/03/96 Add exception handling. BrianAu
02/27/97 Divided NTFS writes into max 60KB pieces. BrianAu
The quota code in NTFS couldn't handle larger
buffers. It got into an infinite loop condition
due to filling of the log.
07/01/97 Replaced use of PointerList with CArray<>. BrianAu
Now use indexes instead of iterators.
*/
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
DiskQuotaUserBatch::FlushToDisk(
VOID
)
{
HRESULT hResult = NOERROR;
PFILE_QUOTA_INFORMATION pUserInfo = NULL;
PDISKQUOTA_USER pUser = NULL;
PBYTE pbBatchBuffer = NULL;
DWORD cbMinimumSid = FIELD_OFFSET(SID, SubAuthority) + sizeof(LONG);
INT iOuter = 0;
//
// Do nothing if the batch object is empty.
//
if (0 == m_UserList.Count())
return NOERROR;
m_UserList.Lock();
try
{
//
// Process the data in 60K chunks using a nested loop.
//
while(iOuter < m_UserList.UpperBound())
{
//
// Clone the outer iterator so we can process the next 60K of data.
// Need two new iterators. One for counting the bytes and
// one for transferring data to the write buffer. They're very small
// objects and cheap to create.
//
INT iCount = iOuter;
INT iTransfer = iOuter;
DWORD cbBatchBuffer = 0;
DWORD cItemsThisBatch = 0;
while(cbBatchBuffer < MAX_BATCH_BUFFER_BYTES &&
iCount <= m_UserList.UpperBound())
{
DWORD cbSid = 0;
pUser = m_UserList[iCount++];
pUser->GetSidLength(&cbSid);
//
// Total size required for user records.
//
cbBatchBuffer += FIELD_OFFSET(FILE_QUOTA_INFORMATION, Sid) + cbSid;
//
// Ensure it's quad-word aligned.
//
if (cbBatchBuffer & 0x00000007)
cbBatchBuffer = (cbBatchBuffer & 0xFFFFFFF8) + 8;
cItemsThisBatch++;
}
//
// Allocate the buffer.
//
pbBatchBuffer = new BYTE[cbBatchBuffer];
PBYTE pbBatchBufferItem = pbBatchBuffer;
DWORD cbNextEntryOffset = 0;
//
// Now fill in the batch transaction buffer with data from
// all of the users in the batch list.
//
while(0 != cItemsThisBatch-- &&
iTransfer <= m_UserList.UpperBound())
{
pUser = m_UserList[iTransfer++];
pUserInfo = (PFILE_QUOTA_INFORMATION)pbBatchBufferItem;
pUser->GetSidLength(&pUserInfo->SidLength);
cbNextEntryOffset = FIELD_OFFSET(FILE_QUOTA_INFORMATION, Sid) + pUserInfo->SidLength;
//
// Ensure quad-word alignment.
//
if (cbNextEntryOffset & 0x00000007)
cbNextEntryOffset = (cbNextEntryOffset & 0xFFFFFFF8) + 8;
pUserInfo->NextEntryOffset = cbNextEntryOffset;
pUser->GetQuotaThreshold(&pUserInfo->QuotaThreshold.QuadPart);
pUser->GetQuotaLimit(&pUserInfo->QuotaLimit.QuadPart);
pUser->GetSid((PBYTE)&pUserInfo->Sid, pUserInfo->SidLength);
//
// These two don't get set but let's provide a known value anyway.
//
pUserInfo->ChangeTime.QuadPart = 0;
pUserInfo->QuotaUsed.QuadPart = 0;
pbBatchBufferItem += cbNextEntryOffset;
}
pUserInfo->NextEntryOffset = 0; // Last entry needs a 0 here.
//
// Submit the batch to the NTIOAPI for update.
//
hResult = m_pFSObject->SetUserQuotaInformation(pbBatchBuffer, cbBatchBuffer);
//
// Delete the data buffer.
//
delete[] pbBatchBuffer;
pbBatchBuffer = NULL;
//
// Advance the outer iterator to where the transfer iterator left off.
//
iOuter = iTransfer;
}
}
catch(CAllocException& e)
{
hResult = E_OUTOFMEMORY;
}
if (FAILED(hResult))
{
//
// Something failed. Invalid data cached in user objects.
// Next request for user data will have to read from disk.
//
iOuter = 0;
while(iOuter <= m_UserList.UpperBound())
{
pUser = m_UserList[iOuter++];
pUser->Invalidate();
}
}
m_UserList.ReleaseLock();
delete[] pbBatchBuffer;
return hResult;
}