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.

568 lines
16 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. /* File: userbat.cpp
  3. Description: Contains member function definitions for class
  4. DiskQuotaUserBatch.
  5. The DiskQuotaUserBatch object represents a batch update mechanism for
  6. rapid update of multiple-user-object quota information. This class
  7. takes advantage of the batching capabilities built into the NTIOAPI.
  8. A user batch object is obtained through
  9. IDiskQuotaControl::CreateUserBatch().
  10. Revision History:
  11. Date Description Programmer
  12. -------- --------------------------------------------------- ----------
  13. 06/06/96 Initial creation. BrianAu
  14. */
  15. ///////////////////////////////////////////////////////////////////////////////
  16. #include "pch.h" // PCH
  17. #pragma hdrstop
  18. #include "userbat.h"
  19. //
  20. // Verify that build is UNICODE.
  21. //
  22. #if !defined(UNICODE)
  23. # error This module must be compiled UNICODE.
  24. #endif
  25. //
  26. // The NTFS quota write function can only handle a max of 64K of data
  27. // in any one write operation. IssacHe recommended 60K as a comfortable
  28. // limit.
  29. //
  30. const INT MAX_BATCH_BUFFER_BYTES = (1 << 10) * 60;
  31. ///////////////////////////////////////////////////////////////////////////////
  32. /* Function: DiskQuotaUserBatch::DiskQuotaUserBatch
  33. Description: Constructor.
  34. Arguments:
  35. pFSObject - Address of File System object to be utilized by the
  36. batching operations.
  37. Returns: Nothing.
  38. Revision History:
  39. Date Description Programmer
  40. -------- --------------------------------------------------- ----------
  41. 09/03/96 Initial creation. BrianAu
  42. */
  43. ///////////////////////////////////////////////////////////////////////////////
  44. DiskQuotaUserBatch::DiskQuotaUserBatch(
  45. FSObject *pFSObject
  46. ) : m_cRef(0),
  47. m_pFSObject(pFSObject)
  48. {
  49. DBGASSERT((NULL != m_pFSObject));
  50. m_pFSObject->AddRef();
  51. }
  52. ///////////////////////////////////////////////////////////////////////////////
  53. /* Function: DiskQuotaUserBatch::~DiskQuotaUserBatch
  54. Description: Destructor.
  55. Arguments: Destroys the batch object.
  56. Returns: Nothing.
  57. Revision History:
  58. Date Description Programmer
  59. -------- --------------------------------------------------- ----------
  60. 07/26/96 Initial creation. BrianAu
  61. */
  62. ///////////////////////////////////////////////////////////////////////////////
  63. DiskQuotaUserBatch::~DiskQuotaUserBatch(
  64. VOID
  65. )
  66. {
  67. Destroy();
  68. }
  69. ///////////////////////////////////////////////////////////////////////////////
  70. /* Function: DiskQuotaUserBatch::QueryInterface
  71. Description: Returns an interface pointer to the object's IUnknown or
  72. IDiskQuotaUserBatch interface. Only IID_IUnknown and
  73. IID_IDiskQuotaUserBatch are recognized. The object referenced by the
  74. returned interface pointer is uninitialized. The recipient of the
  75. pointer must call Initialize() before the object is usable.
  76. Arguments:
  77. riid - Reference to requested interface ID.
  78. ppvOut - Address of interface pointer variable to accept interface ptr.
  79. Returns:
  80. NOERROR - Success.
  81. E_NOINTERFACE - Requested interface not supported.
  82. E_INVALIDARG - ppvOut argument was NULL.
  83. Revision History:
  84. Date Description Programmer
  85. -------- --------------------------------------------------- ----------
  86. 06/06/96 Initial creation. BrianAu
  87. */
  88. ///////////////////////////////////////////////////////////////////////////////
  89. STDMETHODIMP
  90. DiskQuotaUserBatch::QueryInterface(
  91. REFIID riid,
  92. LPVOID *ppvOut
  93. )
  94. {
  95. HRESULT hResult = E_NOINTERFACE;
  96. if (NULL == ppvOut)
  97. return E_INVALIDARG;
  98. *ppvOut = NULL;
  99. if (IID_IUnknown == riid || IID_IDiskQuotaUserBatch == riid)
  100. {
  101. *ppvOut = this;
  102. ((LPUNKNOWN)*ppvOut)->AddRef();
  103. hResult = NOERROR;
  104. }
  105. return hResult;
  106. }
  107. ///////////////////////////////////////////////////////////////////////////////
  108. /* Function: DiskQuotaUserBatch::AddRef
  109. Description: Increments object reference count.
  110. Arguments: None.
  111. Returns: New reference count value.
  112. Revision History:
  113. Date Description Programmer
  114. -------- --------------------------------------------------- ----------
  115. 06/06/96 Initial creation. BrianAu
  116. */
  117. ///////////////////////////////////////////////////////////////////////////////
  118. STDMETHODIMP_(ULONG)
  119. DiskQuotaUserBatch::AddRef(
  120. VOID
  121. )
  122. {
  123. ULONG ulReturn = m_cRef + 1;
  124. DBGPRINT((DM_COM, DL_HIGH, TEXT("DiskQuotaUserBatch::AddRef, 0x%08X %d -> %d\n"),
  125. this, ulReturn - 1, ulReturn));
  126. InterlockedIncrement(&m_cRef);
  127. return ulReturn;
  128. }
  129. ///////////////////////////////////////////////////////////////////////////////
  130. /* Function: DiskQuotaUserBatch::Release
  131. Description: Decrements object reference count. If count drops to 0,
  132. object is deleted.
  133. Arguments: None.
  134. Returns: New reference count value.
  135. Revision History:
  136. Date Description Programmer
  137. -------- --------------------------------------------------- ----------
  138. 06/06/96 Initial creation. BrianAu
  139. */
  140. ///////////////////////////////////////////////////////////////////////////////
  141. STDMETHODIMP_(ULONG)
  142. DiskQuotaUserBatch::Release(
  143. VOID
  144. )
  145. {
  146. ULONG ulReturn = m_cRef - 1;
  147. DBGPRINT((DM_COM, DL_HIGH, TEXT("DiskQuotaUserBatch::Release, 0x%08X %d -> %d\n"),
  148. this, ulReturn + 1, ulReturn));
  149. if (InterlockedDecrement(&m_cRef) == 0)
  150. {
  151. delete this;
  152. ulReturn = 0;
  153. }
  154. return ulReturn;
  155. }
  156. ///////////////////////////////////////////////////////////////////////////////
  157. /* Function: DiskQuotaUserBatch::Destroy
  158. Description: Destroys the contents of a user batch object and releases
  159. its FSObject pointer.
  160. Arguments: None.
  161. Returns: Nothing.
  162. Revision History:
  163. Date Description Programmer
  164. -------- --------------------------------------------------- ----------
  165. 06/06/96 Initial creation. BrianAu
  166. */
  167. ///////////////////////////////////////////////////////////////////////////////
  168. VOID
  169. DiskQuotaUserBatch::Destroy(VOID)
  170. {
  171. //
  172. // Remove and release all user object pointers from the batch list.
  173. //
  174. RemoveAll();
  175. if (NULL != m_pFSObject)
  176. {
  177. //
  178. // Release hold on File System object.
  179. //
  180. m_pFSObject->Release();
  181. m_pFSObject = NULL;
  182. }
  183. }
  184. ///////////////////////////////////////////////////////////////////////////////
  185. /* Function: DiskQuotaUserBatch::Add
  186. Description: Adds an IDiskQuotaUser interface pointer to the batch list.
  187. Arguments:
  188. pUser - Address of IDiskQuotaUser interface.
  189. Returns:
  190. NOERROR - Success.
  191. E_INVALIDARG - pUser arg is NULL.
  192. E_OUTOFMEMORY - Couldn't create new node in batch list.
  193. E_UNEXPECTED - Unexpected exception.
  194. Revision History:
  195. Date Description Programmer
  196. -------- --------------------------------------------------- ----------
  197. 06/06/96 Initial creation. BrianAu
  198. 09/03/96 Add exception handling. BrianAu
  199. */
  200. ///////////////////////////////////////////////////////////////////////////////
  201. STDMETHODIMP
  202. DiskQuotaUserBatch::Add(
  203. PDISKQUOTA_USER pUser
  204. )
  205. {
  206. HRESULT hResult = NOERROR;
  207. if (NULL == pUser)
  208. return E_INVALIDARG;
  209. try
  210. {
  211. m_UserList.Append(pUser);
  212. //
  213. // Success. Increment ref count on object.
  214. //
  215. pUser->AddRef();
  216. }
  217. catch(CAllocException& e)
  218. {
  219. hResult = E_OUTOFMEMORY;
  220. }
  221. return hResult;
  222. }
  223. ///////////////////////////////////////////////////////////////////////////////
  224. /* Function: DiskQuotaUserBatch::Remove
  225. Description: Removes a user pointer from the batch queue.
  226. Arguments:
  227. pUser - Address of IDiskQuotaUser interface for the user object to
  228. be removed.
  229. Returns:
  230. S_OK - Success.
  231. S_FALSE - User not found in batch object.
  232. E_INVALIDARG - pUser argument is NULL.
  233. E_UNEXPECTED - Unexpected exception.
  234. Revision History:
  235. Date Description Programmer
  236. -------- --------------------------------------------------- ----------
  237. 06/06/96 Initial creation. BrianAu
  238. 09/03/96 Add exception handling. BrianAu
  239. */
  240. ///////////////////////////////////////////////////////////////////////////////
  241. HRESULT
  242. DiskQuotaUserBatch::Remove(
  243. PDISKQUOTA_USER pUser
  244. )
  245. {
  246. HRESULT hResult = S_FALSE; // Assume user not present.
  247. PDISKQUOTA_USER pRemoved = NULL;
  248. if (NULL == pUser)
  249. return E_INVALIDARG;
  250. m_UserList.Lock();
  251. INT iUser = m_UserList.Find(pUser);
  252. if (-1 != iUser)
  253. {
  254. try
  255. {
  256. DBGASSERT((NULL != m_UserList[iUser]));
  257. m_UserList[iUser]->Release();
  258. m_UserList.Delete(iUser);
  259. hResult = S_OK;
  260. }
  261. catch(CAllocException& e)
  262. {
  263. hResult = E_OUTOFMEMORY;
  264. }
  265. }
  266. m_UserList.ReleaseLock();
  267. return hResult;
  268. }
  269. ///////////////////////////////////////////////////////////////////////////////
  270. /* Function: DiskQuotaUserBatch::RemoveAll
  271. Description: Removes all user pointers from the batch
  272. list and calling Release() through the removed pointer.
  273. Arguments: None.
  274. Returns:
  275. NOERROR - Success.
  276. E_UNEXPECTED - Unexpected exception.
  277. Revision History:
  278. Date Description Programmer
  279. -------- --------------------------------------------------- ----------
  280. 06/06/96 Initial creation. BrianAu
  281. 09/03/96 Add exception handling. BrianAu
  282. */
  283. ///////////////////////////////////////////////////////////////////////////////
  284. STDMETHODIMP
  285. DiskQuotaUserBatch::RemoveAll(
  286. VOID
  287. )
  288. {
  289. HRESULT hResult = NOERROR;
  290. m_UserList.Lock();
  291. INT cUsers = m_UserList.Count();
  292. for (INT i = 0; i < cUsers; i++)
  293. {
  294. try
  295. {
  296. DBGASSERT((NULL != m_UserList[i]));
  297. m_UserList[i]->Release();
  298. }
  299. catch(CAllocException& e)
  300. {
  301. hResult = E_OUTOFMEMORY;
  302. }
  303. }
  304. m_UserList.Clear();
  305. m_UserList.ReleaseLock();
  306. return hResult;
  307. }
  308. ///////////////////////////////////////////////////////////////////////////////
  309. /* Function: DiskQuotaUserBatch::FlushToDisk
  310. Description: Writes data for all batched user objects to disk in a single
  311. NTIOAPI call. This is the real worker function for the batch object.
  312. Arguments: None.
  313. Returns:
  314. NOERROR - Success.
  315. E_OUTOFMEMORY - Insufficient memory.
  316. E_UNEXPECTED - Unexpected exception.
  317. Revision History:
  318. Date Description Programmer
  319. -------- --------------------------------------------------- ----------
  320. 06/06/96 Initial creation. BrianAu
  321. 09/03/96 Add exception handling. BrianAu
  322. 02/27/97 Divided NTFS writes into max 60KB pieces. BrianAu
  323. The quota code in NTFS couldn't handle larger
  324. buffers. It got into an infinite loop condition
  325. due to filling of the log.
  326. 07/01/97 Replaced use of PointerList with CArray<>. BrianAu
  327. Now use indexes instead of iterators.
  328. */
  329. ///////////////////////////////////////////////////////////////////////////////
  330. STDMETHODIMP
  331. DiskQuotaUserBatch::FlushToDisk(
  332. VOID
  333. )
  334. {
  335. HRESULT hResult = NOERROR;
  336. PFILE_QUOTA_INFORMATION pUserInfo = NULL;
  337. PDISKQUOTA_USER pUser = NULL;
  338. PBYTE pbBatchBuffer = NULL;
  339. DWORD cbMinimumSid = FIELD_OFFSET(SID, SubAuthority) + sizeof(LONG);
  340. INT iOuter = 0;
  341. //
  342. // Do nothing if the batch object is empty.
  343. //
  344. if (0 == m_UserList.Count())
  345. return NOERROR;
  346. m_UserList.Lock();
  347. try
  348. {
  349. //
  350. // Process the data in 60K chunks using a nested loop.
  351. //
  352. while(iOuter < m_UserList.UpperBound())
  353. {
  354. //
  355. // Clone the outer iterator so we can process the next 60K of data.
  356. // Need two new iterators. One for counting the bytes and
  357. // one for transferring data to the write buffer. They're very small
  358. // objects and cheap to create.
  359. //
  360. INT iCount = iOuter;
  361. INT iTransfer = iOuter;
  362. DWORD cbBatchBuffer = 0;
  363. DWORD cItemsThisBatch = 0;
  364. while(cbBatchBuffer < MAX_BATCH_BUFFER_BYTES &&
  365. iCount <= m_UserList.UpperBound())
  366. {
  367. DWORD cbSid = 0;
  368. pUser = m_UserList[iCount++];
  369. pUser->GetSidLength(&cbSid);
  370. //
  371. // Total size required for user records.
  372. //
  373. cbBatchBuffer += FIELD_OFFSET(FILE_QUOTA_INFORMATION, Sid) + cbSid;
  374. //
  375. // Ensure it's quad-word aligned.
  376. //
  377. if (cbBatchBuffer & 0x00000007)
  378. cbBatchBuffer = (cbBatchBuffer & 0xFFFFFFF8) + 8;
  379. cItemsThisBatch++;
  380. }
  381. //
  382. // Allocate the buffer.
  383. //
  384. pbBatchBuffer = new BYTE[cbBatchBuffer];
  385. PBYTE pbBatchBufferItem = pbBatchBuffer;
  386. DWORD cbNextEntryOffset = 0;
  387. //
  388. // Now fill in the batch transaction buffer with data from
  389. // all of the users in the batch list.
  390. //
  391. while(0 != cItemsThisBatch-- &&
  392. iTransfer <= m_UserList.UpperBound())
  393. {
  394. pUser = m_UserList[iTransfer++];
  395. pUserInfo = (PFILE_QUOTA_INFORMATION)pbBatchBufferItem;
  396. pUser->GetSidLength(&pUserInfo->SidLength);
  397. cbNextEntryOffset = FIELD_OFFSET(FILE_QUOTA_INFORMATION, Sid) + pUserInfo->SidLength;
  398. //
  399. // Ensure quad-word alignment.
  400. //
  401. if (cbNextEntryOffset & 0x00000007)
  402. cbNextEntryOffset = (cbNextEntryOffset & 0xFFFFFFF8) + 8;
  403. pUserInfo->NextEntryOffset = cbNextEntryOffset;
  404. pUser->GetQuotaThreshold(&pUserInfo->QuotaThreshold.QuadPart);
  405. pUser->GetQuotaLimit(&pUserInfo->QuotaLimit.QuadPart);
  406. pUser->GetSid((PBYTE)&pUserInfo->Sid, pUserInfo->SidLength);
  407. //
  408. // These two don't get set but let's provide a known value anyway.
  409. //
  410. pUserInfo->ChangeTime.QuadPart = 0;
  411. pUserInfo->QuotaUsed.QuadPart = 0;
  412. pbBatchBufferItem += cbNextEntryOffset;
  413. }
  414. pUserInfo->NextEntryOffset = 0; // Last entry needs a 0 here.
  415. //
  416. // Submit the batch to the NTIOAPI for update.
  417. //
  418. hResult = m_pFSObject->SetUserQuotaInformation(pbBatchBuffer, cbBatchBuffer);
  419. //
  420. // Delete the data buffer.
  421. //
  422. delete[] pbBatchBuffer;
  423. pbBatchBuffer = NULL;
  424. //
  425. // Advance the outer iterator to where the transfer iterator left off.
  426. //
  427. iOuter = iTransfer;
  428. }
  429. }
  430. catch(CAllocException& e)
  431. {
  432. hResult = E_OUTOFMEMORY;
  433. }
  434. if (FAILED(hResult))
  435. {
  436. //
  437. // Something failed. Invalid data cached in user objects.
  438. // Next request for user data will have to read from disk.
  439. //
  440. iOuter = 0;
  441. while(iOuter <= m_UserList.UpperBound())
  442. {
  443. pUser = m_UserList[iOuter++];
  444. pUser->Invalidate();
  445. }
  446. }
  447. m_UserList.ReleaseLock();
  448. delete[] pbBatchBuffer;
  449. return hResult;
  450. }