Leaked source code of windows server 2003
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.

563 lines
17 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 cRef = InterlockedIncrement(&m_cRef);
  124. DBGPRINT((DM_COM, DL_HIGH, TEXT("DiskQuotaUserBatch::AddRef, 0x%08X %d -> %d\n"), this, cRef - 1, cRef ));
  125. return cRef;
  126. }
  127. ///////////////////////////////////////////////////////////////////////////////
  128. /* Function: DiskQuotaUserBatch::Release
  129. Description: Decrements object reference count. If count drops to 0,
  130. object is deleted.
  131. Arguments: None.
  132. Returns: New reference count value.
  133. Revision History:
  134. Date Description Programmer
  135. -------- --------------------------------------------------- ----------
  136. 06/06/96 Initial creation. BrianAu
  137. */
  138. ///////////////////////////////////////////////////////////////////////////////
  139. STDMETHODIMP_(ULONG)
  140. DiskQuotaUserBatch::Release(
  141. VOID
  142. )
  143. {
  144. ASSERT( 0 != m_cRef );
  145. ULONG cRef = InterlockedDecrement(&m_cRef);
  146. DBGPRINT((DM_COM, DL_HIGH, TEXT("DiskQuotaUserBatch::Release, 0x%08X %d -> %d\n"),
  147. this, cRef + 1, cRef));
  148. if ( 0 == cRef)
  149. {
  150. delete this;
  151. }
  152. return cRef;
  153. }
  154. ///////////////////////////////////////////////////////////////////////////////
  155. /* Function: DiskQuotaUserBatch::Destroy
  156. Description: Destroys the contents of a user batch object and releases
  157. its FSObject pointer.
  158. Arguments: None.
  159. Returns: Nothing.
  160. Revision History:
  161. Date Description Programmer
  162. -------- --------------------------------------------------- ----------
  163. 06/06/96 Initial creation. BrianAu
  164. */
  165. ///////////////////////////////////////////////////////////////////////////////
  166. VOID
  167. DiskQuotaUserBatch::Destroy(VOID)
  168. {
  169. //
  170. // Remove and release all user object pointers from the batch list.
  171. //
  172. RemoveAll();
  173. if (NULL != m_pFSObject)
  174. {
  175. //
  176. // Release hold on File System object.
  177. //
  178. m_pFSObject->Release();
  179. m_pFSObject = NULL;
  180. }
  181. }
  182. ///////////////////////////////////////////////////////////////////////////////
  183. /* Function: DiskQuotaUserBatch::Add
  184. Description: Adds an IDiskQuotaUser interface pointer to the batch list.
  185. Arguments:
  186. pUser - Address of IDiskQuotaUser interface.
  187. Returns:
  188. NOERROR - Success.
  189. E_INVALIDARG - pUser arg is NULL.
  190. E_OUTOFMEMORY - Couldn't create new node in batch list.
  191. E_UNEXPECTED - Unexpected exception.
  192. Revision History:
  193. Date Description Programmer
  194. -------- --------------------------------------------------- ----------
  195. 06/06/96 Initial creation. BrianAu
  196. 09/03/96 Add exception handling. BrianAu
  197. */
  198. ///////////////////////////////////////////////////////////////////////////////
  199. STDMETHODIMP
  200. DiskQuotaUserBatch::Add(
  201. PDISKQUOTA_USER pUser
  202. )
  203. {
  204. HRESULT hResult = NOERROR;
  205. if (NULL == pUser)
  206. return E_INVALIDARG;
  207. try
  208. {
  209. m_UserList.Append(pUser);
  210. //
  211. // Success. Increment ref count on object.
  212. //
  213. pUser->AddRef();
  214. }
  215. catch(CAllocException& e)
  216. {
  217. hResult = E_OUTOFMEMORY;
  218. }
  219. return hResult;
  220. }
  221. ///////////////////////////////////////////////////////////////////////////////
  222. /* Function: DiskQuotaUserBatch::Remove
  223. Description: Removes a user pointer from the batch queue.
  224. Arguments:
  225. pUser - Address of IDiskQuotaUser interface for the user object to
  226. be removed.
  227. Returns:
  228. S_OK - Success.
  229. S_FALSE - User not found in batch object.
  230. E_INVALIDARG - pUser argument is NULL.
  231. E_UNEXPECTED - Unexpected exception.
  232. Revision History:
  233. Date Description Programmer
  234. -------- --------------------------------------------------- ----------
  235. 06/06/96 Initial creation. BrianAu
  236. 09/03/96 Add exception handling. BrianAu
  237. */
  238. ///////////////////////////////////////////////////////////////////////////////
  239. HRESULT
  240. DiskQuotaUserBatch::Remove(
  241. PDISKQUOTA_USER pUser
  242. )
  243. {
  244. HRESULT hResult = S_FALSE; // Assume user not present.
  245. PDISKQUOTA_USER pRemoved = NULL;
  246. if (NULL == pUser)
  247. return E_INVALIDARG;
  248. m_UserList.Lock();
  249. INT iUser = m_UserList.Find(pUser);
  250. if (-1 != iUser)
  251. {
  252. try
  253. {
  254. DBGASSERT((NULL != m_UserList[iUser]));
  255. m_UserList[iUser]->Release();
  256. m_UserList.Delete(iUser);
  257. hResult = S_OK;
  258. }
  259. catch(CAllocException& e)
  260. {
  261. hResult = E_OUTOFMEMORY;
  262. }
  263. }
  264. m_UserList.ReleaseLock();
  265. return hResult;
  266. }
  267. ///////////////////////////////////////////////////////////////////////////////
  268. /* Function: DiskQuotaUserBatch::RemoveAll
  269. Description: Removes all user pointers from the batch
  270. list and calling Release() through the removed pointer.
  271. Arguments: None.
  272. Returns:
  273. NOERROR - Success.
  274. E_UNEXPECTED - Unexpected exception.
  275. Revision History:
  276. Date Description Programmer
  277. -------- --------------------------------------------------- ----------
  278. 06/06/96 Initial creation. BrianAu
  279. 09/03/96 Add exception handling. BrianAu
  280. */
  281. ///////////////////////////////////////////////////////////////////////////////
  282. STDMETHODIMP
  283. DiskQuotaUserBatch::RemoveAll(
  284. VOID
  285. )
  286. {
  287. HRESULT hResult = NOERROR;
  288. m_UserList.Lock();
  289. INT cUsers = m_UserList.Count();
  290. for (INT i = 0; i < cUsers; i++)
  291. {
  292. try
  293. {
  294. DBGASSERT((NULL != m_UserList[i]));
  295. m_UserList[i]->Release();
  296. }
  297. catch(CAllocException& e)
  298. {
  299. hResult = E_OUTOFMEMORY;
  300. }
  301. }
  302. m_UserList.Clear();
  303. m_UserList.ReleaseLock();
  304. return hResult;
  305. }
  306. ///////////////////////////////////////////////////////////////////////////////
  307. /* Function: DiskQuotaUserBatch::FlushToDisk
  308. Description: Writes data for all batched user objects to disk in a single
  309. NTIOAPI call. This is the real worker function for the batch object.
  310. Arguments: None.
  311. Returns:
  312. NOERROR - Success.
  313. E_OUTOFMEMORY - Insufficient memory.
  314. E_UNEXPECTED - Unexpected exception.
  315. Revision History:
  316. Date Description Programmer
  317. -------- --------------------------------------------------- ----------
  318. 06/06/96 Initial creation. BrianAu
  319. 09/03/96 Add exception handling. BrianAu
  320. 02/27/97 Divided NTFS writes into max 60KB pieces. BrianAu
  321. The quota code in NTFS couldn't handle larger
  322. buffers. It got into an infinite loop condition
  323. due to filling of the log.
  324. 07/01/97 Replaced use of PointerList with CArray<>. BrianAu
  325. Now use indexes instead of iterators.
  326. */
  327. ///////////////////////////////////////////////////////////////////////////////
  328. STDMETHODIMP
  329. DiskQuotaUserBatch::FlushToDisk(
  330. VOID
  331. )
  332. {
  333. HRESULT hResult = NOERROR;
  334. PFILE_QUOTA_INFORMATION pUserInfo = NULL;
  335. PDISKQUOTA_USER pUser = NULL;
  336. PBYTE pbBatchBuffer = NULL;
  337. DWORD cbMinimumSid = FIELD_OFFSET(SID, SubAuthority) + sizeof(LONG);
  338. INT iOuter = 0;
  339. //
  340. // Do nothing if the batch object is empty.
  341. //
  342. if (0 == m_UserList.Count())
  343. return NOERROR;
  344. m_UserList.Lock();
  345. try
  346. {
  347. //
  348. // Process the data in 60K chunks using a nested loop.
  349. //
  350. while(iOuter < m_UserList.UpperBound())
  351. {
  352. //
  353. // Clone the outer iterator so we can process the next 60K of data.
  354. // Need two new iterators. One for counting the bytes and
  355. // one for transferring data to the write buffer. They're very small
  356. // objects and cheap to create.
  357. //
  358. INT iCount = iOuter;
  359. INT iTransfer = iOuter;
  360. DWORD cbBatchBuffer = 0;
  361. DWORD cItemsThisBatch = 0;
  362. while(cbBatchBuffer < MAX_BATCH_BUFFER_BYTES &&
  363. iCount <= m_UserList.UpperBound())
  364. {
  365. DWORD cbSid = 0;
  366. pUser = m_UserList[iCount++];
  367. pUser->GetSidLength(&cbSid);
  368. //
  369. // Total size required for user records.
  370. //
  371. cbBatchBuffer += FIELD_OFFSET(FILE_QUOTA_INFORMATION, Sid) + cbSid;
  372. //
  373. // Ensure it's quad-word aligned.
  374. //
  375. if (cbBatchBuffer & 0x00000007)
  376. cbBatchBuffer = (cbBatchBuffer & 0xFFFFFFF8) + 8;
  377. cItemsThisBatch++;
  378. }
  379. //
  380. // Allocate the buffer.
  381. //
  382. pbBatchBuffer = new BYTE[cbBatchBuffer];
  383. PBYTE pbBatchBufferItem = pbBatchBuffer;
  384. DWORD cbNextEntryOffset = 0;
  385. //
  386. // Now fill in the batch transaction buffer with data from
  387. // all of the users in the batch list.
  388. //
  389. while(0 != cItemsThisBatch-- &&
  390. iTransfer <= m_UserList.UpperBound())
  391. {
  392. pUser = m_UserList[iTransfer++];
  393. pUserInfo = (PFILE_QUOTA_INFORMATION)pbBatchBufferItem;
  394. pUser->GetSidLength(&pUserInfo->SidLength);
  395. cbNextEntryOffset = FIELD_OFFSET(FILE_QUOTA_INFORMATION, Sid) + pUserInfo->SidLength;
  396. //
  397. // Ensure quad-word alignment.
  398. //
  399. if (cbNextEntryOffset & 0x00000007)
  400. cbNextEntryOffset = (cbNextEntryOffset & 0xFFFFFFF8) + 8;
  401. pUserInfo->NextEntryOffset = cbNextEntryOffset;
  402. pUser->GetQuotaThreshold(&pUserInfo->QuotaThreshold.QuadPart);
  403. pUser->GetQuotaLimit(&pUserInfo->QuotaLimit.QuadPart);
  404. pUser->GetSid((PBYTE)&pUserInfo->Sid, pUserInfo->SidLength);
  405. //
  406. // These two don't get set but let's provide a known value anyway.
  407. //
  408. pUserInfo->ChangeTime.QuadPart = 0;
  409. pUserInfo->QuotaUsed.QuadPart = 0;
  410. pbBatchBufferItem += cbNextEntryOffset;
  411. }
  412. pUserInfo->NextEntryOffset = 0; // Last entry needs a 0 here.
  413. //
  414. // Submit the batch to the NTIOAPI for update.
  415. //
  416. hResult = m_pFSObject->SetUserQuotaInformation(pbBatchBuffer, cbBatchBuffer);
  417. //
  418. // Delete the data buffer.
  419. //
  420. delete[] pbBatchBuffer;
  421. pbBatchBuffer = NULL;
  422. //
  423. // Advance the outer iterator to where the transfer iterator left off.
  424. //
  425. iOuter = iTransfer;
  426. }
  427. }
  428. catch(CAllocException& e)
  429. {
  430. hResult = E_OUTOFMEMORY;
  431. }
  432. if (FAILED(hResult))
  433. {
  434. //
  435. // Something failed. Invalid data cached in user objects.
  436. // Next request for user data will have to read from disk.
  437. //
  438. iOuter = 0;
  439. while(iOuter <= m_UserList.UpperBound())
  440. {
  441. pUser = m_UserList[iOuter++];
  442. pUser->Invalidate();
  443. }
  444. }
  445. m_UserList.ReleaseLock();
  446. delete[] pbBatchBuffer;
  447. return hResult;
  448. }