/////////////////////////////////////////////////////////////////////////////// /* 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 #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 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; }