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.

366 lines
13 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. /* File: stats.cpp
  3. Description: These classes provide temporary storage of quota
  4. information for a given volume/user pair. Creation of the object
  5. automatically gathers the necessary quota and user information.
  6. Clients then query the objects to retrieve quota statistics when
  7. desired.
  8. CStatistics
  9. CStatisticsList
  10. +----------+
  11. +--->| CVolume |
  12. | +----------+
  13. +-----------------+ +-------------+<---+
  14. | CStatisticsList |<-->>| CStatistics | contains
  15. +-----------------+ +-------------+<---+
  16. | +-------------+
  17. +--->| CVolumeUser |
  18. +-------------+
  19. Revision History:
  20. Date Description Programmer
  21. -------- --------------------------------------------------- ----------
  22. 07/01/97 Initial creation. BrianAu
  23. */
  24. ///////////////////////////////////////////////////////////////////////////////
  25. #include <precomp.hxx>
  26. #pragma hdrstop
  27. #include "dskquota.h"
  28. #include "stats.h"
  29. //
  30. // Gather quota statistics for a given volume-user pair.
  31. //
  32. CStatistics::CStatistics(
  33. TCHAR chVolLetter,
  34. LPCTSTR pszVolDisplayName,
  35. LPBYTE pUserSid
  36. ) : m_vol(chVolLetter, pszVolDisplayName),
  37. m_bQuotaEnabled(FALSE),
  38. m_bWarnAtThreshold(FALSE),
  39. m_bDenyAtLimit(FALSE),
  40. m_bValid(FALSE)
  41. {
  42. //
  43. // If the volume doesn't support quotas, no use in continuing
  44. // with the expensive stuff.
  45. //
  46. if (m_vol.SupportsQuotas())
  47. {
  48. HRESULT hr;
  49. hr = OleInitialize(NULL);
  50. if (SUCCEEDED(hr))
  51. {
  52. //
  53. // Load dskquota.dll, creating a new disk quota controller object.
  54. //
  55. IDiskQuotaControl *pQC;
  56. hr = CoCreateInstance(CLSID_DiskQuotaControl,
  57. NULL,
  58. CLSCTX_INPROC_SERVER,
  59. IID_IDiskQuotaControl,
  60. (LPVOID *)&pQC);
  61. if (SUCCEEDED(hr))
  62. {
  63. //
  64. // Initialize the quota controller for this volume.
  65. // We need only read access.
  66. //
  67. TCHAR szVolume[] = TEXT("X:\\");
  68. szVolume[0] = m_vol.GetLetter();
  69. hr = IDiskQuotaControl_Initialize(pQC, szVolume, GENERIC_READ);
  70. if (SUCCEEDED(hr))
  71. {
  72. DWORD dwQuotaState = 0;
  73. DWORD dwQuotaLogFlags = 0;
  74. //
  75. // Get the volume's quota state flags.
  76. //
  77. hr = pQC->GetQuotaState(&dwQuotaState);
  78. if (SUCCEEDED(hr))
  79. {
  80. m_bQuotaEnabled = !DISKQUOTA_IS_DISABLED(dwQuotaState);
  81. m_bDenyAtLimit = DISKQUOTA_IS_ENFORCED(dwQuotaState);
  82. }
  83. //
  84. // Get the volume's quota logging flags.
  85. //
  86. hr = pQC->GetQuotaLogFlags(&dwQuotaLogFlags);
  87. if (SUCCEEDED(hr))
  88. {
  89. m_bWarnAtThreshold = m_bQuotaEnabled &&
  90. DISKQUOTA_IS_LOGGED_USER_THRESHOLD(dwQuotaLogFlags);
  91. }
  92. //
  93. // Get the quota user object for the current user.
  94. //
  95. oleauto_ptr<IDiskQuotaUser> ptrUser;
  96. hr = pQC->FindUserSid(pUserSid,
  97. ptrUser._getoutptr(),
  98. DISKQUOTA_USERNAME_RESOLVE_SYNC);
  99. if (SUCCEEDED(hr))
  100. {
  101. //
  102. // Get the user's account and friendly names.
  103. //
  104. TCHAR szUserName[MAX_PATH] = { TEXT('\0') };
  105. TCHAR szUserDomain[MAX_PATH] = { TEXT('\0') };
  106. TCHAR szUserAccount[MAX_PATH] = { TEXT('\0') };
  107. TCHAR szUserEmailName[MAX_PATH] = { TEXT('\0') };
  108. IDiskQuotaUser_GetName(ptrUser,
  109. szUserDomain, ARRAYSIZE(szUserDomain),
  110. szUserAccount, ARRAYSIZE(szUserAccount),
  111. szUserName, ARRAYSIZE(szUserName));
  112. //
  113. // Get the user's quota statistics for the volume.
  114. //
  115. DISKQUOTA_USER_INFORMATION dui;
  116. hr = ptrUser->GetQuotaInformation((LPBYTE)&dui, sizeof(dui));
  117. if (SUCCEEDED(hr))
  118. {
  119. //
  120. // Make sure we didn't enumerate a non-existant user.
  121. // This is because of the way NTFS enumerates quota records.
  122. // Even if there is no record currently in the quota file,
  123. // enumeration returns a record. This is consistent with
  124. // automatic addition of users upon first write to the
  125. // volume. We must check the quota information to
  126. // determine if it's an active user.
  127. //
  128. if (((__int64)-2) != dui.QuotaLimit.QuadPart &&
  129. (0 != dui.QuotaLimit.QuadPart ||
  130. 0 != dui.QuotaThreshold.QuadPart ||
  131. 0 != dui.QuotaUsed.QuadPart))
  132. {
  133. //
  134. // Store user-specific info.
  135. //
  136. CString strUserDisplayName;
  137. if (TEXT('\0') != szUserName[0])
  138. {
  139. //
  140. // User has a "friendly" name associated
  141. // with their account. Include it in the display
  142. // name.
  143. //
  144. strUserDisplayName.Format(TEXT("%1\\%2 (%3)"),
  145. szUserDomain,
  146. szUserAccount,
  147. szUserName);
  148. }
  149. else
  150. {
  151. //
  152. // No "friendly" name associated with user account.
  153. //
  154. strUserDisplayName.Format(TEXT("%1\\%2"),
  155. szUserDomain,
  156. szUserAccount,
  157. szUserName);
  158. }
  159. //
  160. // Store the user's information in the volume-user object.
  161. //
  162. hr = m_volUser.SetUserInfo(strUserDisplayName,
  163. szUserEmailName,
  164. dui.QuotaThreshold,
  165. dui.QuotaLimit,
  166. dui.QuotaUsed);
  167. if (FAILED(hr))
  168. {
  169. DebugMsg(DM_ERROR,
  170. TEXT("Error 0x%08X setting user info on volume \"%s\"."),
  171. hr, pszVolDisplayName);
  172. }
  173. m_bValid = SUCCEEDED(hr);
  174. }
  175. }
  176. else
  177. {
  178. DebugMsg(DM_ERROR,
  179. TEXT("Error 0x%08X getting user quota info on volume \"%s\"."),
  180. hr, pszVolDisplayName);
  181. }
  182. }
  183. else
  184. {
  185. DebugMsg(DM_ERROR,
  186. TEXT("Error 0x%08X finding quota user on volume \"%s\"."),
  187. hr, pszVolDisplayName);
  188. }
  189. }
  190. else
  191. {
  192. DebugMsg(DM_ERROR,
  193. TEXT("Error 0x%08X initializing QC for volume \"%s\"."),
  194. hr, pszVolDisplayName);
  195. }
  196. pQC->ShutdownAndRelease(TRUE);
  197. } // if SUCCEEDED(CoCreateInstance())
  198. else
  199. DebugMsg(DM_ERROR, TEXT("Failed CoCreateInstance. 0x%08X"), hr);
  200. OleUninitialize();
  201. } // if SUCCEEDED(OleInitialize())
  202. else
  203. DebugMsg(DM_ERROR, TEXT("Failed OleInitialize. 0x%08X"), hr);
  204. } // if m_vol.SupportsQuotas()
  205. else
  206. DebugMsg(DM_ERROR, TEXT("Volume \"%s\" doesn't support quotas."), pszVolDisplayName);
  207. }
  208. //
  209. // Determine if a specific statistics object contains statistics that
  210. // will generate either an email notification or a popup notification.
  211. //
  212. BOOL
  213. CStatistics::IncludeInReport(
  214. VOID
  215. ) const
  216. {
  217. BOOL bReport = FALSE;
  218. //
  219. // First check data validity and volume settings.
  220. //
  221. if (IsValid() && // Does volume support quotas and was vol opened?
  222. QuotasEnabled() && // Are quotas enabled on the volume?
  223. WarnAtThreshold()) // Is the "warn at threshold" bit set on the vol?
  224. {
  225. //
  226. // Now see if user's quota usage warrants reporting.
  227. // Report if AmtUsed > Threshold.
  228. //
  229. if (GetUserQuotaUsed().QuadPart > GetUserQuotaThreshold().QuadPart)
  230. {
  231. bReport = TRUE;
  232. }
  233. }
  234. return bReport;
  235. }
  236. CStatisticsList::CStatisticsList(
  237. VOID
  238. )
  239. {
  240. //
  241. // Nothing to do.
  242. //
  243. }
  244. CStatisticsList::~CStatisticsList(
  245. VOID
  246. )
  247. {
  248. INT cEntries = m_List.Count();
  249. for (INT i = 0; i < cEntries; i++)
  250. {
  251. delete m_List[i];
  252. }
  253. m_List.Clear();
  254. }
  255. const CStatistics *
  256. CStatisticsList::GetEntry(
  257. INT iEntry
  258. )
  259. {
  260. return m_List[iEntry];
  261. }
  262. HRESULT
  263. CStatisticsList::AddEntry(
  264. TCHAR chVolLetter,
  265. LPCTSTR pszVolDisplayName,
  266. LPBYTE pUserSid
  267. )
  268. {
  269. HRESULT hr = E_OUTOFMEMORY;
  270. CStatistics *pStats = NULL;
  271. //
  272. // Create a new statistics object.
  273. // The ctor will fill in the volume and user information.
  274. //
  275. pStats = new CStatistics(chVolLetter,
  276. pszVolDisplayName,
  277. pUserSid);
  278. if (NULL != pStats)
  279. {
  280. if (pStats->IsValid())
  281. {
  282. BOOL bDuplicate = FALSE;
  283. //
  284. // Look for a duplicate. Shouldn't be one but just to make sure.
  285. //
  286. INT cEntries = m_List.Count();
  287. for (INT i = 0; i < cEntries; i++)
  288. {
  289. const CStatistics *pEntry = m_List[i];
  290. if (pStats->SameVolume(*pEntry))
  291. bDuplicate = TRUE;
  292. }
  293. if (!bDuplicate)
  294. {
  295. hr = S_OK;
  296. try
  297. {
  298. m_List.Append(pStats);
  299. }
  300. catch(OutOfMemory)
  301. {
  302. DebugMsg(DM_ERROR, TEXT("CStatisticsList::AddEntry - Insufficient memory."));
  303. hr = E_OUTOFMEMORY;
  304. }
  305. }
  306. else
  307. {
  308. //
  309. // FEATURE: We should really assert here.
  310. //
  311. DebugMsg(DM_ERROR, TEXT("CStatisticsList::AddEntry - Duplicate entry found."));
  312. hr = S_FALSE;
  313. }
  314. }
  315. else
  316. {
  317. //
  318. // Stats object is invalid. Probably because the volume
  319. // doesn't support quotas.
  320. //
  321. DebugMsg(DM_ERROR, TEXT("CStatisticsList::AddEntry - Volume stats not valid."));
  322. hr = S_FALSE;
  323. }
  324. }
  325. if (S_OK != hr)
  326. {
  327. //
  328. // CStatistics object wasn't added to the list.
  329. // Probably was a duplicate (shouldn't happen) or the volume
  330. // didn't support quotas.
  331. //
  332. delete pStats;
  333. }
  334. return hr;
  335. }