367 lines
13 KiB
C++
367 lines
13 KiB
C++
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
/* File: stats.cpp
|
||
|
|
||
|
Description: These classes provide temporary storage of quota
|
||
|
information for a given volume/user pair. Creation of the object
|
||
|
automatically gathers the necessary quota and user information.
|
||
|
Clients then query the objects to retrieve quota statistics when
|
||
|
desired.
|
||
|
|
||
|
CStatistics
|
||
|
CStatisticsList
|
||
|
+----------+
|
||
|
+--->| CVolume |
|
||
|
| +----------+
|
||
|
+-----------------+ +-------------+<---+
|
||
|
| CStatisticsList |<-->>| CStatistics | contains
|
||
|
+-----------------+ +-------------+<---+
|
||
|
| +-------------+
|
||
|
+--->| CVolumeUser |
|
||
|
+-------------+
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
Date Description Programmer
|
||
|
-------- --------------------------------------------------- ----------
|
||
|
07/01/97 Initial creation. BrianAu
|
||
|
*/
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
#include <precomp.hxx>
|
||
|
#pragma hdrstop
|
||
|
|
||
|
#include "dskquota.h"
|
||
|
#include "stats.h"
|
||
|
|
||
|
//
|
||
|
// Gather quota statistics for a given volume-user pair.
|
||
|
//
|
||
|
CStatistics::CStatistics(
|
||
|
TCHAR chVolLetter,
|
||
|
LPCTSTR pszVolDisplayName,
|
||
|
LPBYTE pUserSid
|
||
|
) : m_vol(chVolLetter, pszVolDisplayName),
|
||
|
m_bQuotaEnabled(FALSE),
|
||
|
m_bWarnAtThreshold(FALSE),
|
||
|
m_bDenyAtLimit(FALSE),
|
||
|
m_bValid(FALSE)
|
||
|
{
|
||
|
//
|
||
|
// If the volume doesn't support quotas, no use in continuing
|
||
|
// with the expensive stuff.
|
||
|
//
|
||
|
if (m_vol.SupportsQuotas())
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
hr = OleInitialize(NULL);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
//
|
||
|
// Load dskquota.dll, creating a new disk quota controller object.
|
||
|
//
|
||
|
IDiskQuotaControl *pQC;
|
||
|
hr = CoCreateInstance(CLSID_DiskQuotaControl,
|
||
|
NULL,
|
||
|
CLSCTX_INPROC_SERVER,
|
||
|
IID_IDiskQuotaControl,
|
||
|
(LPVOID *)&pQC);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
//
|
||
|
// Initialize the quota controller for this volume.
|
||
|
// We need only read access.
|
||
|
//
|
||
|
TCHAR szVolume[] = TEXT("X:\\");
|
||
|
szVolume[0] = m_vol.GetLetter();
|
||
|
|
||
|
hr = IDiskQuotaControl_Initialize(pQC, szVolume, GENERIC_READ);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
DWORD dwQuotaState = 0;
|
||
|
DWORD dwQuotaLogFlags = 0;
|
||
|
|
||
|
//
|
||
|
// Get the volume's quota state flags.
|
||
|
//
|
||
|
hr = pQC->GetQuotaState(&dwQuotaState);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
m_bQuotaEnabled = !DISKQUOTA_IS_DISABLED(dwQuotaState);
|
||
|
m_bDenyAtLimit = DISKQUOTA_IS_ENFORCED(dwQuotaState);
|
||
|
}
|
||
|
//
|
||
|
// Get the volume's quota logging flags.
|
||
|
//
|
||
|
hr = pQC->GetQuotaLogFlags(&dwQuotaLogFlags);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
m_bWarnAtThreshold = m_bQuotaEnabled &&
|
||
|
DISKQUOTA_IS_LOGGED_USER_THRESHOLD(dwQuotaLogFlags);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get the quota user object for the current user.
|
||
|
//
|
||
|
oleauto_ptr<IDiskQuotaUser> ptrUser;
|
||
|
|
||
|
hr = pQC->FindUserSid(pUserSid,
|
||
|
ptrUser._getoutptr(),
|
||
|
DISKQUOTA_USERNAME_RESOLVE_SYNC);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
//
|
||
|
// Get the user's account and friendly names.
|
||
|
//
|
||
|
TCHAR szUserName[MAX_PATH] = { TEXT('\0') };
|
||
|
TCHAR szUserDomain[MAX_PATH] = { TEXT('\0') };
|
||
|
TCHAR szUserAccount[MAX_PATH] = { TEXT('\0') };
|
||
|
TCHAR szUserEmailName[MAX_PATH] = { TEXT('\0') };
|
||
|
|
||
|
IDiskQuotaUser_GetName(ptrUser,
|
||
|
szUserDomain, ARRAYSIZE(szUserDomain),
|
||
|
szUserAccount, ARRAYSIZE(szUserAccount),
|
||
|
szUserName, ARRAYSIZE(szUserName));
|
||
|
|
||
|
//
|
||
|
// Get the user's quota statistics for the volume.
|
||
|
//
|
||
|
DISKQUOTA_USER_INFORMATION dui;
|
||
|
|
||
|
hr = ptrUser->GetQuotaInformation((LPBYTE)&dui, sizeof(dui));
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
//
|
||
|
// Make sure we didn't enumerate a non-existant user.
|
||
|
// This is because of the way NTFS enumerates quota records.
|
||
|
// Even if there is no record currently in the quota file,
|
||
|
// enumeration returns a record. This is consistent with
|
||
|
// automatic addition of users upon first write to the
|
||
|
// volume. We must check the quota information to
|
||
|
// determine if it's an active user.
|
||
|
//
|
||
|
if (((__int64)-2) != dui.QuotaLimit.QuadPart &&
|
||
|
(0 != dui.QuotaLimit.QuadPart ||
|
||
|
0 != dui.QuotaThreshold.QuadPart ||
|
||
|
0 != dui.QuotaUsed.QuadPart))
|
||
|
{
|
||
|
//
|
||
|
// Store user-specific info.
|
||
|
//
|
||
|
CString strUserDisplayName;
|
||
|
if (TEXT('\0') != szUserName[0])
|
||
|
{
|
||
|
//
|
||
|
// User has a "friendly" name associated
|
||
|
// with their account. Include it in the display
|
||
|
// name.
|
||
|
//
|
||
|
strUserDisplayName.Format(TEXT("%1\\%2 (%3)"),
|
||
|
szUserDomain,
|
||
|
szUserAccount,
|
||
|
szUserName);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// No "friendly" name associated with user account.
|
||
|
//
|
||
|
strUserDisplayName.Format(TEXT("%1\\%2"),
|
||
|
szUserDomain,
|
||
|
szUserAccount,
|
||
|
szUserName);
|
||
|
}
|
||
|
//
|
||
|
// Store the user's information in the volume-user object.
|
||
|
//
|
||
|
hr = m_volUser.SetUserInfo(strUserDisplayName,
|
||
|
szUserEmailName,
|
||
|
dui.QuotaThreshold,
|
||
|
dui.QuotaLimit,
|
||
|
dui.QuotaUsed);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
DebugMsg(DM_ERROR,
|
||
|
TEXT("Error 0x%08X setting user info on volume \"%s\"."),
|
||
|
hr, pszVolDisplayName);
|
||
|
}
|
||
|
m_bValid = SUCCEEDED(hr);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DebugMsg(DM_ERROR,
|
||
|
TEXT("Error 0x%08X getting user quota info on volume \"%s\"."),
|
||
|
hr, pszVolDisplayName);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DebugMsg(DM_ERROR,
|
||
|
TEXT("Error 0x%08X finding quota user on volume \"%s\"."),
|
||
|
hr, pszVolDisplayName);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DebugMsg(DM_ERROR,
|
||
|
TEXT("Error 0x%08X initializing QC for volume \"%s\"."),
|
||
|
hr, pszVolDisplayName);
|
||
|
}
|
||
|
pQC->ShutdownAndRelease(TRUE);
|
||
|
} // if SUCCEEDED(CoCreateInstance())
|
||
|
else
|
||
|
DebugMsg(DM_ERROR, TEXT("Failed CoCreateInstance. 0x%08X"), hr);
|
||
|
|
||
|
OleUninitialize();
|
||
|
} // if SUCCEEDED(OleInitialize())
|
||
|
else
|
||
|
DebugMsg(DM_ERROR, TEXT("Failed OleInitialize. 0x%08X"), hr);
|
||
|
} // if m_vol.SupportsQuotas()
|
||
|
else
|
||
|
DebugMsg(DM_ERROR, TEXT("Volume \"%s\" doesn't support quotas."), pszVolDisplayName);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Determine if a specific statistics object contains statistics that
|
||
|
// will generate either an email notification or a popup notification.
|
||
|
//
|
||
|
BOOL
|
||
|
CStatistics::IncludeInReport(
|
||
|
VOID
|
||
|
) const
|
||
|
{
|
||
|
BOOL bReport = FALSE;
|
||
|
|
||
|
//
|
||
|
// First check data validity and volume settings.
|
||
|
//
|
||
|
if (IsValid() && // Does volume support quotas and was vol opened?
|
||
|
QuotasEnabled() && // Are quotas enabled on the volume?
|
||
|
WarnAtThreshold()) // Is the "warn at threshold" bit set on the vol?
|
||
|
{
|
||
|
//
|
||
|
// Now see if user's quota usage warrants reporting.
|
||
|
// Report if AmtUsed > Threshold.
|
||
|
//
|
||
|
if (GetUserQuotaUsed().QuadPart > GetUserQuotaThreshold().QuadPart)
|
||
|
{
|
||
|
bReport = TRUE;
|
||
|
}
|
||
|
}
|
||
|
return bReport;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
CStatisticsList::CStatisticsList(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
//
|
||
|
// Nothing to do.
|
||
|
//
|
||
|
}
|
||
|
|
||
|
CStatisticsList::~CStatisticsList(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
INT cEntries = m_List.Count();
|
||
|
for (INT i = 0; i < cEntries; i++)
|
||
|
{
|
||
|
delete m_List[i];
|
||
|
}
|
||
|
m_List.Clear();
|
||
|
}
|
||
|
|
||
|
|
||
|
const CStatistics *
|
||
|
CStatisticsList::GetEntry(
|
||
|
INT iEntry
|
||
|
)
|
||
|
{
|
||
|
return m_List[iEntry];
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT
|
||
|
CStatisticsList::AddEntry(
|
||
|
TCHAR chVolLetter,
|
||
|
LPCTSTR pszVolDisplayName,
|
||
|
LPBYTE pUserSid
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = E_OUTOFMEMORY;
|
||
|
|
||
|
CStatistics *pStats = NULL;
|
||
|
|
||
|
//
|
||
|
// Create a new statistics object.
|
||
|
// The ctor will fill in the volume and user information.
|
||
|
//
|
||
|
pStats = new CStatistics(chVolLetter,
|
||
|
pszVolDisplayName,
|
||
|
pUserSid);
|
||
|
if (NULL != pStats)
|
||
|
{
|
||
|
if (pStats->IsValid())
|
||
|
{
|
||
|
BOOL bDuplicate = FALSE;
|
||
|
|
||
|
//
|
||
|
// Look for a duplicate. Shouldn't be one but just to make sure.
|
||
|
//
|
||
|
INT cEntries = m_List.Count();
|
||
|
|
||
|
for (INT i = 0; i < cEntries; i++)
|
||
|
{
|
||
|
const CStatistics *pEntry = m_List[i];
|
||
|
|
||
|
if (pStats->SameVolume(*pEntry))
|
||
|
bDuplicate = TRUE;
|
||
|
}
|
||
|
if (!bDuplicate)
|
||
|
{
|
||
|
hr = S_OK;
|
||
|
try
|
||
|
{
|
||
|
m_List.Append(pStats);
|
||
|
}
|
||
|
catch(OutOfMemory)
|
||
|
{
|
||
|
DebugMsg(DM_ERROR, TEXT("CStatisticsList::AddEntry - Insufficient memory."));
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// FEATURE: We should really assert here.
|
||
|
//
|
||
|
DebugMsg(DM_ERROR, TEXT("CStatisticsList::AddEntry - Duplicate entry found."));
|
||
|
hr = S_FALSE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// Stats object is invalid. Probably because the volume
|
||
|
// doesn't support quotas.
|
||
|
//
|
||
|
DebugMsg(DM_ERROR, TEXT("CStatisticsList::AddEntry - Volume stats not valid."));
|
||
|
hr = S_FALSE;
|
||
|
}
|
||
|
}
|
||
|
if (S_OK != hr)
|
||
|
{
|
||
|
//
|
||
|
// CStatistics object wasn't added to the list.
|
||
|
// Probably was a duplicate (shouldn't happen) or the volume
|
||
|
// didn't support quotas.
|
||
|
//
|
||
|
delete pStats;
|
||
|
}
|
||
|
return hr;
|
||
|
}
|