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.

5580 lines
152 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. Quota.c
  5. Abstract:
  6. This module implements the quota support routines for Ntfs
  7. Author:
  8. Jeff Havens [JHavens] 29-Feb-1996
  9. Revision History:
  10. --*/
  11. #include "NtfsProc.h"
  12. #define Dbg DEBUG_TRACE_QUOTA
  13. //
  14. // Define a tag for general pool allocations from this module
  15. //
  16. #undef MODULE_POOL_TAG
  17. #define MODULE_POOL_TAG ('QFtN')
  18. #define MAXIMUM_SID_LENGTH \
  19. (FIELD_OFFSET( SID, SubAuthority ) + sizeof( ULONG ) * SID_MAX_SUB_AUTHORITIES)
  20. #define MAXIMUM_QUOTA_ROW (SIZEOF_QUOTA_USER_DATA + MAXIMUM_SID_LENGTH + sizeof( ULONG ))
  21. //
  22. // Local quota support routines.
  23. //
  24. VOID
  25. NtfsClearAndVerifyQuotaIndex (
  26. IN PIRP_CONTEXT IrpContext,
  27. IN PVCB Vcb
  28. );
  29. NTSTATUS
  30. NtfsClearPerFileQuota (
  31. IN PIRP_CONTEXT IrpContext,
  32. IN PFCB Fcb,
  33. IN PVOID Context
  34. );
  35. VOID
  36. NtfsDeleteUnsedIds (
  37. IN PIRP_CONTEXT IrpContext,
  38. IN PVCB Vcb
  39. );
  40. VOID
  41. NtfsMarkUserLimit (
  42. IN PIRP_CONTEXT IrpContext,
  43. IN PVOID Context
  44. );
  45. NTSTATUS
  46. NtfsPackQuotaInfo (
  47. IN PSID Sid,
  48. IN PQUOTA_USER_DATA QuotaUserData OPTIONAL,
  49. IN PFILE_QUOTA_INFORMATION OutBuffer,
  50. IN OUT PULONG OutBufferSize
  51. );
  52. VOID
  53. NtfsPostUserLimit (
  54. IN PIRP_CONTEXT IrpContext,
  55. IN PVCB Vcb,
  56. IN PQUOTA_CONTROL_BLOCK QuotaControl
  57. );
  58. NTSTATUS
  59. NtfsPrepareForDelete (
  60. IN PIRP_CONTEXT IrpContext,
  61. IN PVCB Vcb,
  62. IN PSID Sid
  63. );
  64. VOID
  65. NtfsRepairQuotaIndex (
  66. IN PIRP_CONTEXT IrpContext,
  67. IN PVOID Context
  68. );
  69. NTSTATUS
  70. NtfsRepairPerFileQuota (
  71. IN PIRP_CONTEXT IrpContext,
  72. IN PFCB Fcb,
  73. IN PVOID Context
  74. );
  75. VOID
  76. NtfsSaveQuotaFlags (
  77. IN PIRP_CONTEXT IrpContext,
  78. IN PVCB Vcb
  79. );
  80. VOID
  81. NtfsSaveQuotaFlagsSafe (
  82. IN PIRP_CONTEXT IrpContext,
  83. IN PVCB Vcb
  84. );
  85. NTSTATUS
  86. NtfsVerifyOwnerIndex (
  87. IN PIRP_CONTEXT IrpContext,
  88. IN PVCB Vcb
  89. );
  90. RTL_GENERIC_COMPARE_RESULTS
  91. NtfsQuotaTableCompare (
  92. IN PRTL_GENERIC_TABLE Table,
  93. PVOID FirstStruct,
  94. PVOID SecondStruct
  95. );
  96. PVOID
  97. NtfsQuotaTableAllocate (
  98. IN PRTL_GENERIC_TABLE Table,
  99. CLONG ByteSize
  100. );
  101. VOID
  102. NtfsQuotaTableFree (
  103. IN PRTL_GENERIC_TABLE Table,
  104. PVOID Buffer
  105. );
  106. #if (DBG || defined( NTFS_FREE_ASSERTS ) || defined( NTFSDBG ))
  107. BOOLEAN NtfsAllowFixups = 1;
  108. BOOLEAN NtfsCheckQuota = 0;
  109. #endif // DBG
  110. #ifdef ALLOC_PRAGMA
  111. #pragma alloc_text(PAGE, NtfsAcquireQuotaControl)
  112. #pragma alloc_text(PAGE, NtfsCalculateQuotaAdjustment)
  113. #pragma alloc_text(PAGE, NtfsClearAndVerifyQuotaIndex)
  114. #pragma alloc_text(PAGE, NtfsClearPerFileQuota)
  115. #pragma alloc_text(PAGE, NtfsDeleteUnsedIds)
  116. #pragma alloc_text(PAGE, NtfsDereferenceQuotaControlBlock)
  117. #pragma alloc_text(PAGE, NtfsFixupQuota)
  118. #pragma alloc_text(PAGE, NtfsFsQuotaQueryInfo)
  119. #pragma alloc_text(PAGE, NtfsFsQuotaSetInfo)
  120. #pragma alloc_text(PAGE, NtfsGetCallersUserId)
  121. #pragma alloc_text(PAGE, NtfsGetOwnerId)
  122. #pragma alloc_text(PAGE, NtfsGetRemainingQuota)
  123. #pragma alloc_text(PAGE, NtfsInitializeQuotaControlBlock)
  124. #pragma alloc_text(PAGE, NtfsInitializeQuotaIndex)
  125. #pragma alloc_text(PAGE, NtfsMarkQuotaCorrupt)
  126. #pragma alloc_text(PAGE, NtfsMarkUserLimit)
  127. #pragma alloc_text(PAGE, NtfsMoveQuotaOwner)
  128. #pragma alloc_text(PAGE, NtfsPackQuotaInfo)
  129. #pragma alloc_text(PAGE, NtfsPostUserLimit)
  130. #pragma alloc_text(PAGE, NtfsPostRepairQuotaIndex)
  131. #pragma alloc_text(PAGE, NtfsPrepareForDelete)
  132. #pragma alloc_text(PAGE, NtfsReleaseQuotaControl)
  133. #pragma alloc_text(PAGE, NtfsRepairQuotaIndex)
  134. #pragma alloc_text(PAGE, NtfsSaveQuotaFlags)
  135. #pragma alloc_text(PAGE, NtfsSaveQuotaFlagsSafe)
  136. #pragma alloc_text(PAGE, NtfsQueryQuotaUserSidList)
  137. #pragma alloc_text(PAGE, NtfsQuotaTableCompare)
  138. #pragma alloc_text(PAGE, NtfsQuotaTableAllocate)
  139. #pragma alloc_text(PAGE, NtfsQuotaTableFree)
  140. #pragma alloc_text(PAGE, NtfsUpdateFileQuota)
  141. #pragma alloc_text(PAGE, NtfsUpdateQuotaDefaults)
  142. #pragma alloc_text(PAGE, NtfsVerifyOwnerIndex)
  143. #pragma alloc_text(PAGE, NtfsRepairPerFileQuota)
  144. #endif
  145. VOID
  146. NtfsAcquireQuotaControl (
  147. IN PIRP_CONTEXT IrpContext,
  148. IN PQUOTA_CONTROL_BLOCK QuotaControl
  149. )
  150. /*++
  151. Routine Description:
  152. Acquire the quota control block and quota index for shared update. Multiple
  153. transactions can update then index, but only one thread can update a
  154. particular index.
  155. Arguments:
  156. QuotaControl - Quota control block to be acquired.
  157. Return Value:
  158. None.
  159. --*/
  160. {
  161. PVOID *Position;
  162. PVOID *ScbArray;
  163. ULONG Count;
  164. PAGED_CODE();
  165. ASSERT( QuotaControl->ReferenceCount > 0 );
  166. //
  167. // Make sure we have a free spot in the Scb array in the IrpContext.
  168. //
  169. if (IrpContext->SharedScb == NULL) {
  170. Position = &IrpContext->SharedScb;
  171. IrpContext->SharedScbSize = 1;
  172. //
  173. // Too bad the first one is not available. If the current size is one then allocate a
  174. // new block and copy the existing value to it.
  175. //
  176. } else if (IrpContext->SharedScbSize == 1) {
  177. if (IrpContext->SharedScb == QuotaControl) {
  178. //
  179. // The quota block has already been aquired.
  180. //
  181. return;
  182. }
  183. ScbArray = NtfsAllocatePool( PagedPool, sizeof( PVOID ) * 4 );
  184. RtlZeroMemory( ScbArray, sizeof( PVOID ) * 4 );
  185. *ScbArray = IrpContext->SharedScb;
  186. IrpContext->SharedScb = ScbArray;
  187. IrpContext->SharedScbSize = 4;
  188. Position = ScbArray + 1;
  189. //
  190. // Otherwise look through the existing array and look for a free spot. Allocate a larger
  191. // array if we need to grow it.
  192. //
  193. } else {
  194. Position = IrpContext->SharedScb;
  195. Count = IrpContext->SharedScbSize;
  196. do {
  197. if (*Position == NULL) {
  198. break;
  199. }
  200. if (*Position == QuotaControl) {
  201. //
  202. // The quota block has already been aquired.
  203. //
  204. return;
  205. }
  206. Count -= 1;
  207. Position += 1;
  208. } while (Count != 0);
  209. //
  210. // If we didn't find one then allocate a new structure.
  211. //
  212. if (Count == 0) {
  213. ScbArray = NtfsAllocatePool( PagedPool, sizeof( PVOID ) * IrpContext->SharedScbSize * 2 );
  214. RtlZeroMemory( ScbArray, sizeof( PVOID ) * IrpContext->SharedScbSize * 2 );
  215. RtlCopyMemory( ScbArray,
  216. IrpContext->SharedScb,
  217. sizeof( PVOID ) * IrpContext->SharedScbSize );
  218. NtfsFreePool( IrpContext->SharedScb );
  219. IrpContext->SharedScb = ScbArray;
  220. Position = ScbArray + IrpContext->SharedScbSize;
  221. IrpContext->SharedScbSize *= 2;
  222. }
  223. }
  224. //
  225. // The following assert is bougus, but I want know if we hit the case
  226. // where create is acquiring the scb stream shared.
  227. // Then make sure that the resource is released in create.c
  228. //
  229. ASSERT( IrpContext->MajorFunction != IRP_MJ_CREATE || IrpContext->OriginatingIrp != NULL || NtfsIsExclusiveScb( IrpContext->Vcb->QuotaTableScb ));
  230. //
  231. // Increase the reference count so the quota control block is not deleted
  232. // while it is in the shared list.
  233. //
  234. ASSERT( QuotaControl->ReferenceCount > 0 );
  235. InterlockedIncrement( &QuotaControl->ReferenceCount );
  236. //
  237. // The quota index must be acquired before the mft scb is acquired.
  238. //
  239. ASSERT(!NtfsIsExclusiveScb( IrpContext->Vcb->MftScb ) ||
  240. ExIsResourceAcquiredSharedLite( IrpContext->Vcb->QuotaTableScb->Header.Resource ));
  241. NtfsAcquireResourceShared( IrpContext, IrpContext->Vcb->QuotaTableScb, TRUE );
  242. ExAcquireFastMutexUnsafe( QuotaControl->QuotaControlLock );
  243. *Position = QuotaControl;
  244. return;
  245. }
  246. VOID
  247. NtfsCalculateQuotaAdjustment (
  248. IN PIRP_CONTEXT IrpContext,
  249. IN PFCB Fcb,
  250. OUT PLONGLONG Delta
  251. )
  252. /*++
  253. Routine Description:
  254. This routine scans the user data streams in a file and determines
  255. by how much the quota needs to be adjusted.
  256. Arguments:
  257. Fcb - Fcb whose quota usage is being modified.
  258. Delta - Returns the amount of quota adjustment required for the file.
  259. Return Value:
  260. None
  261. --*/
  262. {
  263. ATTRIBUTE_ENUMERATION_CONTEXT Context;
  264. PATTRIBUTE_RECORD_HEADER Attribute;
  265. VCN StartVcn = 0;
  266. PAGED_CODE();
  267. ASSERT_EXCLUSIVE_FCB( Fcb );
  268. //
  269. // There is nothing to do if the standard infor has not been
  270. // expanded yet.
  271. //
  272. if (!FlagOn( Fcb->FcbState, FCB_STATE_LARGE_STD_INFO )) {
  273. *Delta = 0;
  274. return;
  275. }
  276. NtfsInitializeAttributeContext( &Context );
  277. //
  278. // Use a try-finally to cleanup the enumeration structure.
  279. //
  280. try {
  281. //
  282. // Start with the $STANDARD_INFORMATION. This must be the first one found.
  283. //
  284. if (!NtfsLookupAttribute( IrpContext, Fcb, &Fcb->FileReference, &Context )) {
  285. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  286. }
  287. Attribute = NtfsFoundAttribute( &Context );
  288. if (Attribute->TypeCode != $STANDARD_INFORMATION) {
  289. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  290. }
  291. //
  292. // Initialize quota amount to the value current in the standard information structure.
  293. //
  294. *Delta = -(LONGLONG) ((PSTANDARD_INFORMATION) NtfsAttributeValue( Attribute ))->QuotaCharged;
  295. //
  296. // Now continue while there are more attributes to find.
  297. //
  298. while (NtfsLookupNextAttributeByVcn( IrpContext, Fcb, &StartVcn, &Context )) {
  299. //
  300. // Point to the current attribute.
  301. //
  302. Attribute = NtfsFoundAttribute( &Context );
  303. //
  304. // For all user data streams charge for a file record plus any non-resident allocation.
  305. // For index streams charge for a file record for the INDEX_ROOT.
  306. //
  307. // For user data look for a resident attribute or the first attribute of a non-resident stream.
  308. // Otherwise look for a $I30 stream.
  309. //
  310. if (NtfsIsTypeCodeSubjectToQuota( Attribute->TypeCode ) ||
  311. ((Attribute->TypeCode == $INDEX_ROOT) &&
  312. ((Attribute->NameLength * sizeof( WCHAR )) == NtfsFileNameIndex.Length) &&
  313. RtlEqualMemory( Add2Ptr( Attribute, Attribute->NameOffset ),
  314. NtfsFileNameIndex.Buffer,
  315. NtfsFileNameIndex.Length ))) {
  316. //
  317. // Always charge for at least one file record.
  318. //
  319. *Delta += NtfsResidentStreamQuota( Fcb->Vcb );
  320. //
  321. // Charge for the allocated length for non-resident.
  322. //
  323. if (!NtfsIsAttributeResident( Attribute )) {
  324. *Delta += Attribute->Form.Nonresident.AllocatedLength;
  325. }
  326. }
  327. }
  328. } finally {
  329. NtfsCleanupAttributeContext( IrpContext, &Context );
  330. }
  331. return;
  332. }
  333. VOID
  334. NtfsClearAndVerifyQuotaIndex (
  335. IN PIRP_CONTEXT IrpContext,
  336. IN PVCB Vcb
  337. )
  338. /*++
  339. Routine Description:
  340. This routine iterates over the quota user data index and verifies the back
  341. pointer to the owner id index. It also zeros the quota used field for
  342. each owner.
  343. Arguments:
  344. Vcb - Pointer to the volume control block whose index is to be operated
  345. on.
  346. Return Value:
  347. None
  348. --*/
  349. {
  350. INDEX_KEY IndexKey;
  351. INDEX_ROW OwnerRow;
  352. MAP_HANDLE MapHandle;
  353. PQUOTA_USER_DATA UserData;
  354. PINDEX_ROW QuotaRow;
  355. PVOID RowBuffer;
  356. NTSTATUS Status;
  357. ULONG OwnerId;
  358. ULONG Count;
  359. ULONG i;
  360. PSCB QuotaScb = Vcb->QuotaTableScb;
  361. PSCB OwnerIdScb = Vcb->OwnerIdTableScb;
  362. PINDEX_ROW IndexRow = NULL;
  363. PREAD_CONTEXT ReadContext = NULL;
  364. BOOLEAN IndexAcquired = FALSE;
  365. NtOfsInitializeMapHandle( &MapHandle );
  366. //
  367. // Allocate a buffer lager enough for several rows.
  368. //
  369. RowBuffer = NtfsAllocatePool( PagedPool, PAGE_SIZE );
  370. try {
  371. //
  372. // Allocate a bunch of index row entries.
  373. //
  374. Count = PAGE_SIZE / sizeof( QUOTA_USER_DATA );
  375. IndexRow = NtfsAllocatePool( PagedPool,
  376. Count * sizeof( INDEX_ROW ) );
  377. //
  378. // Iterate through the quota entries. Start where we left off.
  379. //
  380. OwnerId = Vcb->QuotaFileReference.SegmentNumberLowPart;
  381. IndexKey.KeyLength = sizeof( OwnerId );
  382. IndexKey.Key = &OwnerId;
  383. Status = NtOfsReadRecords( IrpContext,
  384. QuotaScb,
  385. &ReadContext,
  386. &IndexKey,
  387. NtOfsMatchAll,
  388. NULL,
  389. &Count,
  390. IndexRow,
  391. PAGE_SIZE,
  392. RowBuffer );
  393. while (NT_SUCCESS( Status )) {
  394. NtfsAcquireSharedVcb( IrpContext, Vcb, TRUE );
  395. //
  396. // Acquire the VCB shared and check whether we should
  397. // continue.
  398. //
  399. if (!NtfsIsVcbAvailable( Vcb )) {
  400. //
  401. // The volume is going away, bail out.
  402. //
  403. NtfsReleaseVcb( IrpContext, Vcb );
  404. Status = STATUS_VOLUME_DISMOUNTED;
  405. leave;
  406. }
  407. NtfsAcquireExclusiveScb( IrpContext, QuotaScb );
  408. NtfsAcquireExclusiveScb( IrpContext, OwnerIdScb );
  409. IndexAcquired = TRUE;
  410. //
  411. // The following assert must be done while the quota resource
  412. // held; otherwise a lingering transaction may cause it to
  413. //
  414. ASSERT( RtlIsGenericTableEmpty( &Vcb->QuotaControlTable ));
  415. QuotaRow = IndexRow;
  416. for (i = 0; i < Count; i += 1, QuotaRow += 1) {
  417. UserData = QuotaRow->DataPart.Data;
  418. //
  419. // Validate the record is long enough for the Sid.
  420. //
  421. IndexKey.KeyLength = RtlLengthSid( &UserData->QuotaSid );
  422. if ((IndexKey.KeyLength + SIZEOF_QUOTA_USER_DATA > QuotaRow->DataPart.DataLength) ||
  423. !RtlValidSid( &UserData->QuotaSid )) {
  424. ASSERT( FALSE );
  425. //
  426. // The sid is bad delete the record.
  427. //
  428. NtOfsDeleteRecords( IrpContext,
  429. QuotaScb,
  430. 1,
  431. &QuotaRow->KeyPart );
  432. continue;
  433. }
  434. IndexKey.Key = &UserData->QuotaSid;
  435. //
  436. // Look up the Sid is in the owner id index.
  437. //
  438. Status = NtOfsFindRecord( IrpContext,
  439. OwnerIdScb,
  440. &IndexKey,
  441. &OwnerRow,
  442. &MapHandle,
  443. NULL );
  444. ASSERT( NT_SUCCESS( Status ));
  445. if (!NT_SUCCESS( Status )) {
  446. //
  447. // The owner id entry is missing. Add one back in.
  448. //
  449. OwnerRow.KeyPart = IndexKey;
  450. OwnerRow.DataPart.DataLength = QuotaRow->KeyPart.KeyLength;
  451. OwnerRow.DataPart.Data = QuotaRow->KeyPart.Key;
  452. NtOfsAddRecords( IrpContext,
  453. OwnerIdScb,
  454. 1,
  455. &OwnerRow,
  456. FALSE );
  457. } else {
  458. //
  459. // Verify that the owner id's match.
  460. //
  461. if (*((PULONG) QuotaRow->KeyPart.Key) != *((PULONG) OwnerRow.DataPart.Data)) {
  462. ASSERT( FALSE );
  463. //
  464. // Keep the quota record with the lower
  465. // quota id. Delete the one with the higher
  466. // quota id. Note this is the simple approach
  467. // and not best case of the lower id does not
  468. // exist. In that case a user entry will be delete
  469. // and be reassigned a default quota.
  470. //
  471. if (*((PULONG) QuotaRow->KeyPart.Key) < *((PULONG) OwnerRow.DataPart.Data)) {
  472. //
  473. // Make the ownid's match.
  474. //
  475. OwnerRow.KeyPart = IndexKey;
  476. OwnerRow.DataPart.DataLength = QuotaRow->KeyPart.KeyLength;
  477. OwnerRow.DataPart.Data = QuotaRow->KeyPart.Key;
  478. NtOfsUpdateRecord( IrpContext,
  479. OwnerIdScb,
  480. 1,
  481. &OwnerRow,
  482. NULL,
  483. NULL );
  484. } else {
  485. //
  486. // Delete this record and proceed.
  487. //
  488. NtOfsDeleteRecords( IrpContext,
  489. QuotaScb,
  490. 1,
  491. &QuotaRow->KeyPart );
  492. NtOfsReleaseMap( IrpContext, &MapHandle );
  493. continue;
  494. }
  495. }
  496. NtOfsReleaseMap( IrpContext, &MapHandle );
  497. }
  498. //
  499. // Set the quota used to zero.
  500. //
  501. UserData->QuotaUsed = 0;
  502. QuotaRow->DataPart.DataLength = SIZEOF_QUOTA_USER_DATA;
  503. NtOfsUpdateRecord( IrpContext,
  504. QuotaScb,
  505. 1,
  506. QuotaRow,
  507. NULL,
  508. NULL );
  509. }
  510. //
  511. // Release the indexes and commit what has been done so far.
  512. //
  513. NtfsReleaseScb( IrpContext, QuotaScb );
  514. NtfsReleaseScb( IrpContext, OwnerIdScb );
  515. NtfsReleaseVcb( IrpContext, Vcb );
  516. IndexAcquired = FALSE;
  517. //
  518. // Complete the request which commits the pending
  519. // transaction if there is one and releases of the
  520. // acquired resources. The IrpContext will not
  521. // be deleted because the no delete flag is set.
  522. //
  523. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DONT_DELETE | IRP_CONTEXT_FLAG_RETAIN_FLAGS );
  524. NtfsCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
  525. //
  526. // Remember how far we got so we can restart correctly.
  527. //
  528. Vcb->QuotaFileReference.SegmentNumberLowPart = *((PULONG) IndexRow[Count - 1].KeyPart.Key);
  529. //
  530. // Make sure the next free id is beyond the current ids.
  531. //
  532. if (Vcb->QuotaOwnerId <= Vcb->QuotaFileReference.SegmentNumberLowPart) {
  533. ASSERT( Vcb->QuotaOwnerId > Vcb->QuotaFileReference.SegmentNumberLowPart );
  534. Vcb->QuotaOwnerId = Vcb->QuotaFileReference.SegmentNumberLowPart + 1;
  535. }
  536. //
  537. // Look up the next set of entries in the quota index.
  538. //
  539. Count = PAGE_SIZE / sizeof( QUOTA_USER_DATA );
  540. Status = NtOfsReadRecords( IrpContext,
  541. QuotaScb,
  542. &ReadContext,
  543. NULL,
  544. NtOfsMatchAll,
  545. NULL,
  546. &Count,
  547. IndexRow,
  548. PAGE_SIZE,
  549. RowBuffer );
  550. }
  551. ASSERT( (Status == STATUS_NO_MORE_MATCHES) || (Status == STATUS_NO_MATCH) );
  552. } finally {
  553. NtfsFreePool( RowBuffer );
  554. NtOfsReleaseMap( IrpContext, &MapHandle );
  555. if (IndexAcquired) {
  556. NtfsReleaseScb( IrpContext, QuotaScb );
  557. NtfsReleaseScb( IrpContext, OwnerIdScb );
  558. NtfsReleaseVcb( IrpContext, Vcb );
  559. }
  560. if (IndexRow != NULL) {
  561. NtfsFreePool( IndexRow );
  562. }
  563. if (ReadContext != NULL) {
  564. NtOfsFreeReadContext( ReadContext );
  565. }
  566. }
  567. return;
  568. }
  569. NTSTATUS
  570. NtfsClearPerFileQuota (
  571. IN PIRP_CONTEXT IrpContext,
  572. IN PFCB Fcb,
  573. IN PVOID Context
  574. )
  575. /*++
  576. Routine Description:
  577. This routine clears the quota charged field in each file on the volume. The
  578. Quata control block is also released in fcb.
  579. Arguments:
  580. Fcb - Fcb for the file to be processed.
  581. Context - Unsed.
  582. Return Value:
  583. STATUS_SUCCESS
  584. --*/
  585. {
  586. ULONGLONG NewQuota;
  587. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  588. PSTANDARD_INFORMATION StandardInformation;
  589. PQUOTA_CONTROL_BLOCK QuotaControl = Fcb->QuotaControl;
  590. PVCB Vcb = Fcb->Vcb;
  591. UNREFERENCED_PARAMETER( Context);
  592. PAGED_CODE();
  593. //
  594. // There is nothing to do if the standard info has not been
  595. // expanded yet.
  596. //
  597. if (!FlagOn( Fcb->FcbState, FCB_STATE_LARGE_STD_INFO )) {
  598. return STATUS_SUCCESS;
  599. }
  600. //
  601. // Use a try-finally to cleanup the attribute context.
  602. //
  603. try {
  604. //
  605. // Initialize the context structure.
  606. //
  607. NtfsInitializeAttributeContext( &AttrContext );
  608. //
  609. // Locate the standard information, it must be there.
  610. //
  611. if (!NtfsLookupAttributeByCode( IrpContext,
  612. Fcb,
  613. &Fcb->FileReference,
  614. $STANDARD_INFORMATION,
  615. &AttrContext )) {
  616. DebugTrace( 0, Dbg, ("Can't find standard information\n") );
  617. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  618. }
  619. StandardInformation = (PSTANDARD_INFORMATION) NtfsAttributeValue( NtfsFoundAttribute( &AttrContext ));
  620. ASSERT( NtfsFoundAttribute( &AttrContext )->Form.Resident.ValueLength == sizeof( STANDARD_INFORMATION ));
  621. NewQuota = 0;
  622. //
  623. // Call to change the attribute value.
  624. //
  625. NtfsChangeAttributeValue( IrpContext,
  626. Fcb,
  627. FIELD_OFFSET( STANDARD_INFORMATION, QuotaCharged ),
  628. &NewQuota,
  629. sizeof( StandardInformation->QuotaCharged ),
  630. FALSE,
  631. FALSE,
  632. FALSE,
  633. FALSE,
  634. &AttrContext );
  635. //
  636. // Release the quota control block for this fcb.
  637. //
  638. if (QuotaControl != NULL) {
  639. NtfsDereferenceQuotaControlBlock( Vcb, &Fcb->QuotaControl );
  640. }
  641. } finally {
  642. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  643. }
  644. return STATUS_SUCCESS;
  645. }
  646. VOID
  647. NtfsDeleteUnsedIds (
  648. IN PIRP_CONTEXT IrpContext,
  649. IN PVCB Vcb
  650. )
  651. /*++
  652. Routine Description:
  653. This routine iterates over the quota user data index and removes any
  654. entries still marked as deleted.
  655. Arguments:
  656. Vcb - Pointer to the volume control block whoes index is to be operated
  657. on.
  658. Return Value:
  659. None
  660. --*/
  661. {
  662. INDEX_KEY IndexKey;
  663. PINDEX_KEY KeyPtr;
  664. PQUOTA_USER_DATA UserData;
  665. PINDEX_ROW QuotaRow;
  666. PVOID RowBuffer;
  667. NTSTATUS Status = STATUS_SUCCESS;
  668. ULONG OwnerId;
  669. ULONG Count;
  670. ULONG i;
  671. PSCB QuotaScb = Vcb->QuotaTableScb;
  672. PSCB OwnerIdScb = Vcb->OwnerIdTableScb;
  673. PINDEX_ROW IndexRow = NULL;
  674. PREAD_CONTEXT ReadContext = NULL;
  675. BOOLEAN IndexAcquired = FALSE;
  676. //
  677. // Allocate a buffer large enough for several rows.
  678. //
  679. RowBuffer = NtfsAllocatePool( PagedPool, PAGE_SIZE );
  680. try {
  681. //
  682. // Allocate a bunch of index row entries.
  683. //
  684. Count = PAGE_SIZE / sizeof( QUOTA_USER_DATA );
  685. IndexRow = NtfsAllocatePool( PagedPool,
  686. Count * sizeof( INDEX_ROW ) );
  687. //
  688. // Iterate through the quota entries. Start where we left off.
  689. //
  690. OwnerId = Vcb->QuotaFileReference.SegmentNumberLowPart;
  691. IndexKey.KeyLength = sizeof( OwnerId );
  692. IndexKey.Key = &OwnerId;
  693. KeyPtr = &IndexKey;
  694. while (NT_SUCCESS( Status )) {
  695. NtfsAcquireSharedVcb( IrpContext, Vcb, TRUE );
  696. //
  697. // Acquire the VCB shared and check whether we should
  698. // continue.
  699. //
  700. if (!NtfsIsVcbAvailable( Vcb )) {
  701. //
  702. // The volume is going away, bail out.
  703. //
  704. NtfsReleaseVcb( IrpContext, Vcb );
  705. Status = STATUS_VOLUME_DISMOUNTED;
  706. leave;
  707. }
  708. NtfsAcquireExclusiveScb( IrpContext, QuotaScb );
  709. NtfsAcquireExclusiveScb( IrpContext, OwnerIdScb );
  710. ExAcquireFastMutexUnsafe( &Vcb->QuotaControlLock );
  711. IndexAcquired = TRUE;
  712. //
  713. // Make sure the delete secquence number has not changed since
  714. // the scan was delete.
  715. //
  716. if (ULongToPtr( Vcb->QuotaDeleteSecquence ) != IrpContext->Union.NtfsIoContext) {
  717. //
  718. // The scan needs to be restarted. Set the state to posted
  719. // and raise status can not wait which will cause us to retry.
  720. //
  721. ClearFlag( Vcb->QuotaState, VCB_QUOTA_REPAIR_RUNNING );
  722. SetFlag( Vcb->QuotaState, VCB_QUOTA_REPAIR_POSTED );
  723. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  724. }
  725. Status = NtOfsReadRecords( IrpContext,
  726. QuotaScb,
  727. &ReadContext,
  728. KeyPtr,
  729. NtOfsMatchAll,
  730. NULL,
  731. &Count,
  732. IndexRow,
  733. PAGE_SIZE,
  734. RowBuffer );
  735. if (!NT_SUCCESS( Status )) {
  736. break;
  737. }
  738. QuotaRow = IndexRow;
  739. for (i = 0; i < Count; i += 1, QuotaRow += 1) {
  740. PQUOTA_CONTROL_BLOCK QuotaControl;
  741. UserData = QuotaRow->DataPart.Data;
  742. if (!FlagOn( UserData->QuotaFlags, QUOTA_FLAG_ID_DELETED )) {
  743. continue;
  744. }
  745. //
  746. // Check to see if there is a quota control entry
  747. // for this id.
  748. //
  749. ASSERT( FIELD_OFFSET( QUOTA_CONTROL_BLOCK, OwnerId ) <= FIELD_OFFSET( INDEX_ROW, KeyPart.Key ));
  750. QuotaControl = RtlLookupElementGenericTable( &Vcb->QuotaControlTable,
  751. CONTAINING_RECORD( &QuotaRow->KeyPart.Key,
  752. QUOTA_CONTROL_BLOCK,
  753. OwnerId ));
  754. //
  755. // If there is a quota control entry or there is now
  756. // some quota charged, then clear the deleted flag
  757. // and update the entry.
  758. //
  759. if ((QuotaControl != NULL) || (UserData->QuotaUsed != 0)) {
  760. ASSERT( (QuotaControl == NULL) && (UserData->QuotaUsed == 0) );
  761. ClearFlag( UserData->QuotaFlags, QUOTA_FLAG_ID_DELETED );
  762. QuotaRow->DataPart.DataLength = SIZEOF_QUOTA_USER_DATA;
  763. IndexKey.KeyLength = sizeof( OwnerId );
  764. IndexKey.Key = &OwnerId;
  765. NtOfsUpdateRecord( IrpContext,
  766. QuotaScb,
  767. 1,
  768. QuotaRow,
  769. NULL,
  770. NULL );
  771. continue;
  772. }
  773. //
  774. // Delete the user quota data record.
  775. //
  776. IndexKey.KeyLength = sizeof( OwnerId );
  777. IndexKey.Key = &OwnerId;
  778. NtOfsDeleteRecords( IrpContext,
  779. QuotaScb,
  780. 1,
  781. &QuotaRow->KeyPart );
  782. //
  783. // Delete the owner id record.
  784. //
  785. IndexKey.Key = &UserData->QuotaSid;
  786. IndexKey.KeyLength = RtlLengthSid( &UserData->QuotaSid );
  787. NtOfsDeleteRecords( IrpContext,
  788. OwnerIdScb,
  789. 1,
  790. &IndexKey );
  791. }
  792. //
  793. // Release the indexes and commit what has been done so far.
  794. //
  795. ExReleaseFastMutexUnsafe( &Vcb->QuotaControlLock );
  796. NtfsReleaseScb( IrpContext, QuotaScb );
  797. NtfsReleaseScb( IrpContext, OwnerIdScb );
  798. NtfsReleaseVcb( IrpContext, Vcb );
  799. IndexAcquired = FALSE;
  800. //
  801. // Complete the request which commits the pending
  802. // transaction if there is one and releases of the
  803. // acquired resources. The IrpContext will not
  804. // be deleted because the no delete flag is set.
  805. //
  806. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DONT_DELETE | IRP_CONTEXT_FLAG_RETAIN_FLAGS );
  807. NtfsCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
  808. //
  809. // Remember how far we got so we can restart correctly.
  810. //
  811. Vcb->QuotaFileReference.SegmentNumberLowPart = *((PULONG) IndexRow[Count - 1].KeyPart.Key);
  812. KeyPtr = NULL;
  813. }
  814. ASSERT( (Status == STATUS_NO_MORE_MATCHES) || (Status == STATUS_NO_MATCH) );
  815. } finally {
  816. NtfsFreePool( RowBuffer );
  817. if (IndexAcquired) {
  818. ExReleaseFastMutexUnsafe( &Vcb->QuotaControlLock );
  819. NtfsReleaseScb( IrpContext, QuotaScb );
  820. NtfsReleaseScb( IrpContext, OwnerIdScb );
  821. NtfsReleaseVcb( IrpContext, Vcb );
  822. }
  823. if (IndexRow != NULL) {
  824. NtfsFreePool( IndexRow );
  825. }
  826. if (ReadContext != NULL) {
  827. NtOfsFreeReadContext( ReadContext );
  828. }
  829. }
  830. return;
  831. }
  832. VOID
  833. NtfsDereferenceQuotaControlBlock (
  834. IN PVCB Vcb,
  835. IN PQUOTA_CONTROL_BLOCK *QuotaControl
  836. )
  837. /*++
  838. Routine Description:
  839. This routine dereferences the quota control block.
  840. If reference count is now zero the block will be deallocated.
  841. Arguments:
  842. Vcb - Vcb for the volume that own the quota contorl block.
  843. QuotaControl - Quota control block to be derefernece.
  844. Return Value:
  845. None.
  846. --*/
  847. {
  848. PQUOTA_CONTROL_BLOCK TempQuotaControl;
  849. LONG ReferenceCount;
  850. ULONG OwnerId;
  851. ULONG QuotaControlDeleteCount;
  852. PAGED_CODE();
  853. //
  854. // Capture the owner id and delete count;
  855. //
  856. OwnerId = (*QuotaControl)->OwnerId;
  857. QuotaControlDeleteCount = Vcb->QuotaControlDeleteCount;
  858. //
  859. // Update the reference count.
  860. //
  861. ReferenceCount = InterlockedDecrement( &(*QuotaControl)->ReferenceCount );
  862. ASSERT( ReferenceCount >= 0 );
  863. //
  864. // If the reference count is not zero we are done.
  865. //
  866. if (ReferenceCount != 0) {
  867. //
  868. // Clear the pointer from the FCB and return.
  869. //
  870. *QuotaControl = NULL;
  871. return;
  872. }
  873. //
  874. // Lock the quota table.
  875. //
  876. ExAcquireFastMutexUnsafe( &Vcb->QuotaControlLock );
  877. try {
  878. //
  879. // Now things get messy. Check the delete count.
  880. //
  881. if (QuotaControlDeleteCount != Vcb->QuotaControlDeleteCount) {
  882. //
  883. // This is a bogus assert, but I want to see if this ever occurs.
  884. //
  885. ASSERT( QuotaControlDeleteCount != Vcb->QuotaControlDeleteCount );
  886. //
  887. // Something has already been deleted, the old quota control
  888. // block may have been deleted already. Look it up again.
  889. //
  890. TempQuotaControl = RtlLookupElementGenericTable( &Vcb->QuotaControlTable,
  891. CONTAINING_RECORD( &OwnerId,
  892. QUOTA_CONTROL_BLOCK,
  893. OwnerId ));
  894. //
  895. // The block was already deleted we are done.
  896. //
  897. if (TempQuotaControl == NULL) {
  898. leave;
  899. }
  900. } else {
  901. TempQuotaControl = *QuotaControl;
  902. ASSERT( TempQuotaControl == RtlLookupElementGenericTable( &Vcb->QuotaControlTable,
  903. CONTAINING_RECORD( &OwnerId,
  904. QUOTA_CONTROL_BLOCK,
  905. OwnerId )));
  906. }
  907. //
  908. // Verify the reference count is still zero. The reference count
  909. // cannot transision from zero to one while the quota table lock is
  910. // held.
  911. //
  912. if (TempQuotaControl->ReferenceCount != 0) {
  913. leave;
  914. }
  915. //
  916. // Increment the delete count.
  917. //
  918. InterlockedIncrement( &Vcb->QuotaControlDeleteCount );
  919. NtfsFreePool( TempQuotaControl->QuotaControlLock );
  920. RtlDeleteElementGenericTable( &Vcb->QuotaControlTable,
  921. TempQuotaControl );
  922. } finally {
  923. ExReleaseFastMutexUnsafe( &Vcb->QuotaControlLock );
  924. *QuotaControl = NULL;
  925. }
  926. return;
  927. }
  928. VOID
  929. NtfsFixupQuota (
  930. IN PIRP_CONTEXT IrpContext,
  931. IN PFCB Fcb
  932. )
  933. /*++
  934. Routine Description:
  935. This routine ensures that the charged field is correct in the
  936. standard information attribute of a file. If there is a problem
  937. the it is fixed.
  938. Arguments:
  939. Fcb - Pointer to the FCB of the file being opened.
  940. Return Value:
  941. NONE
  942. --*/
  943. {
  944. LONGLONG Delta = 0;
  945. PAGED_CODE();
  946. ASSERT( FlagOn( Fcb->Vcb->QuotaFlags, QUOTA_FLAG_TRACKING_ENABLED ));
  947. ASSERT( NtfsIsExclusiveFcb( Fcb ));
  948. if (Fcb->OwnerId != QUOTA_INVALID_ID) {
  949. ASSERT( Fcb->QuotaControl == NULL );
  950. Fcb->QuotaControl = NtfsInitializeQuotaControlBlock( Fcb->Vcb, Fcb->OwnerId );
  951. }
  952. if ((NtfsPerformQuotaOperation( Fcb )) && (!NtfsIsVolumeReadOnly( Fcb->Vcb ))) {
  953. NtfsCalculateQuotaAdjustment( IrpContext, Fcb, &Delta );
  954. ASSERT( NtfsAllowFixups || FlagOn( Fcb->Vcb->QuotaState, VCB_QUOTA_REPAIR_RUNNING ) || (Delta == 0) );
  955. if (Delta != 0) {
  956. #if DBG
  957. if (IrpContext->OriginatingIrp != NULL ) {
  958. PFILE_OBJECT FileObject;
  959. FileObject = IoGetCurrentIrpStackLocation(
  960. IrpContext->OriginatingIrp )->FileObject;
  961. if (FileObject != NULL && FileObject->FileName.Buffer != NULL) {
  962. DebugTrace( 0, Dbg, ( "NtfsFixupQuota: Quota fix up required on %Z of %I64x bytes\n",
  963. &FileObject->FileName,
  964. Delta ));
  965. }
  966. }
  967. #endif
  968. NtfsUpdateFileQuota( IrpContext, Fcb, &Delta, TRUE, FALSE );
  969. }
  970. }
  971. return;
  972. }
  973. NTSTATUS
  974. NtfsFsQuotaQueryInfo (
  975. IN PIRP_CONTEXT IrpContext,
  976. IN PVCB Vcb,
  977. IN ULONG StartingId,
  978. IN BOOLEAN ReturnSingleEntry,
  979. IN OUT PFILE_QUOTA_INFORMATION *QuotaInfoOutBuffer,
  980. IN OUT PULONG Length,
  981. IN OUT PCCB Ccb OPTIONAL
  982. )
  983. /*++
  984. Routine Description:
  985. This routine returns the quota information for the volume.
  986. Arguments:
  987. Vcb - Volume control block for the volume to be quered.
  988. StartingId - Owner Id after which to start the listing.
  989. ReturnSingleEntry - Indicates only one entry should be returned.
  990. QuotaInfoOutBuffer - Buffer to return the data. On return, points at the
  991. last good entry copied.
  992. Length - In the size of the buffer. Out the amount of space remaining.
  993. Ccb - Optional Ccb which is updated with the last returned owner id.
  994. Return Value:
  995. Returns the status of the operation.
  996. --*/
  997. {
  998. INDEX_ROW IndexRow;
  999. INDEX_KEY IndexKey;
  1000. PINDEX_KEY KeyPtr;
  1001. PQUOTA_USER_DATA UserData;
  1002. PVOID RowBuffer;
  1003. NTSTATUS Status;
  1004. ULONG OwnerId;
  1005. ULONG Count = 1;
  1006. PREAD_CONTEXT ReadContext = NULL;
  1007. ULONG UserBufferLength = *Length;
  1008. PFILE_QUOTA_INFORMATION OutBuffer = *QuotaInfoOutBuffer;
  1009. PAGED_CODE();
  1010. if (UserBufferLength < sizeof(FILE_QUOTA_INFORMATION)) {
  1011. //
  1012. // The user buffer is way too small.
  1013. //
  1014. return STATUS_BUFFER_TOO_SMALL;
  1015. }
  1016. //
  1017. // Return nothing if quotas are not enabled.
  1018. //
  1019. if (Vcb->QuotaTableScb == NULL) {
  1020. return STATUS_SUCCESS;
  1021. }
  1022. //
  1023. // Allocate a buffer large enough for the largest quota entry and key.
  1024. //
  1025. RowBuffer = NtfsAllocatePool( PagedPool, MAXIMUM_QUOTA_ROW );
  1026. //
  1027. // Look up each entry in the quota index start with the next
  1028. // requested owner id.
  1029. //
  1030. OwnerId = StartingId + 1;
  1031. if (OwnerId < QUOTA_FISRT_USER_ID) {
  1032. OwnerId = QUOTA_FISRT_USER_ID;
  1033. }
  1034. IndexKey.KeyLength = sizeof( OwnerId );
  1035. IndexKey.Key = &OwnerId;
  1036. KeyPtr = &IndexKey;
  1037. try {
  1038. while (NT_SUCCESS( Status = NtOfsReadRecords( IrpContext,
  1039. Vcb->QuotaTableScb,
  1040. &ReadContext,
  1041. KeyPtr,
  1042. NtOfsMatchAll,
  1043. NULL,
  1044. &Count,
  1045. &IndexRow,
  1046. MAXIMUM_QUOTA_ROW,
  1047. RowBuffer ))) {
  1048. ASSERT( Count == 1 );
  1049. KeyPtr = NULL;
  1050. UserData = IndexRow.DataPart.Data;
  1051. //
  1052. // Skip this entry if it has been deleted.
  1053. //
  1054. if (FlagOn( UserData->QuotaFlags, QUOTA_FLAG_ID_DELETED )) {
  1055. continue;
  1056. }
  1057. if (!NT_SUCCESS( Status = NtfsPackQuotaInfo(&UserData->QuotaSid,
  1058. UserData,
  1059. OutBuffer,
  1060. &UserBufferLength ))) {
  1061. break;
  1062. }
  1063. //
  1064. // Remember the owner id of the last entry returned.
  1065. //
  1066. OwnerId = *((PULONG) IndexRow.KeyPart.Key);
  1067. if (ReturnSingleEntry) {
  1068. break;
  1069. }
  1070. *QuotaInfoOutBuffer = OutBuffer;
  1071. OutBuffer = Add2Ptr( OutBuffer, OutBuffer->NextEntryOffset );
  1072. }
  1073. //
  1074. // If we're returning at least one entry, it's a SUCCESS.
  1075. //
  1076. if (UserBufferLength != *Length) {
  1077. Status = STATUS_SUCCESS;
  1078. //
  1079. // Set the next entry offset to zero to
  1080. // indicate list termination. If we are only returning a
  1081. // single entry, it makes more sense to let the caller
  1082. // take care of it.
  1083. //
  1084. if (!ReturnSingleEntry) {
  1085. (*QuotaInfoOutBuffer)->NextEntryOffset = 0;
  1086. }
  1087. if (Ccb != NULL) {
  1088. Ccb->LastOwnerId = OwnerId;
  1089. }
  1090. //
  1091. // Return how much of the buffer was used up.
  1092. // QuotaInfoOutBuffer already points at the last good entry.
  1093. //
  1094. *Length = UserBufferLength;
  1095. } else if (Status != STATUS_BUFFER_OVERFLOW) {
  1096. //
  1097. // We return NO_MORE_ENTRIES if we aren't returning any
  1098. // entries (even when the buffer was large enough).
  1099. //
  1100. Status = STATUS_NO_MORE_ENTRIES;
  1101. }
  1102. } finally {
  1103. NtfsFreePool( RowBuffer );
  1104. if (ReadContext != NULL) {
  1105. NtOfsFreeReadContext( ReadContext );
  1106. }
  1107. }
  1108. return Status;
  1109. }
  1110. NTSTATUS
  1111. NtfsFsQuotaSetInfo (
  1112. IN PIRP_CONTEXT IrpContext,
  1113. IN PVCB Vcb,
  1114. IN PFILE_QUOTA_INFORMATION FileQuotaInfo,
  1115. IN ULONG Length
  1116. )
  1117. /*++
  1118. Routine Description:
  1119. This routine sets the quota information on the volume for the
  1120. owner pasted in from the user buffer.
  1121. Arguments:
  1122. Vcb - Volume control block for the volume to be changed.
  1123. FileQuotaInfo - Buffer to return the data.
  1124. Length - The size of the buffer in bytes.
  1125. Return Value:
  1126. Returns the status of the operation.
  1127. --*/
  1128. {
  1129. NTSTATUS Status = STATUS_SUCCESS;
  1130. ULONG LengthUsed = 0;
  1131. PAGED_CODE();
  1132. //
  1133. // Return nothing if quotas are not enabled.
  1134. //
  1135. if (Vcb->QuotaTableScb == NULL) {
  1136. return STATUS_INVALID_DEVICE_REQUEST;
  1137. }
  1138. //
  1139. // Validate the entire buffer before doing any work.
  1140. //
  1141. Status = IoCheckQuotaBufferValidity( FileQuotaInfo,
  1142. Length,
  1143. &LengthUsed );
  1144. IrpContext->OriginatingIrp->IoStatus.Information = LengthUsed;
  1145. if (!NT_SUCCESS(Status)) {
  1146. return Status;
  1147. }
  1148. LengthUsed = 0;
  1149. //
  1150. // Perform the requested updates.
  1151. //
  1152. while (TRUE) {
  1153. //
  1154. // Make sure that the administrator limit is not being changed.
  1155. //
  1156. if (RtlEqualSid( SeExports->SeAliasAdminsSid, &FileQuotaInfo->Sid ) &&
  1157. (FileQuotaInfo->QuotaLimit.QuadPart != -1)) {
  1158. //
  1159. // Reject the request with access denied.
  1160. //
  1161. NtfsRaiseStatus( IrpContext, STATUS_ACCESS_DENIED, NULL, NULL );
  1162. }
  1163. if (FileQuotaInfo->QuotaLimit.QuadPart == -2) {
  1164. Status = NtfsPrepareForDelete( IrpContext,
  1165. Vcb,
  1166. &FileQuotaInfo->Sid );
  1167. if (!NT_SUCCESS( Status )) {
  1168. break;
  1169. }
  1170. } else {
  1171. NtfsGetOwnerId( IrpContext,
  1172. &FileQuotaInfo->Sid,
  1173. TRUE,
  1174. FileQuotaInfo );
  1175. }
  1176. if (FileQuotaInfo->NextEntryOffset == 0) {
  1177. break;
  1178. }
  1179. //
  1180. // Advance to the next entry.
  1181. //
  1182. FileQuotaInfo = Add2Ptr( FileQuotaInfo, FileQuotaInfo->NextEntryOffset);
  1183. }
  1184. //
  1185. // If the quota tracking has been requested and the quotas need to be
  1186. // repaired then try to repair them now.
  1187. //
  1188. if (FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_TRACKING_REQUESTED ) &&
  1189. FlagOn( Vcb->QuotaFlags,
  1190. (QUOTA_FLAG_OUT_OF_DATE |
  1191. QUOTA_FLAG_CORRUPT |
  1192. QUOTA_FLAG_PENDING_DELETES) )) {
  1193. NtfsPostRepairQuotaIndex( IrpContext, Vcb );
  1194. }
  1195. return Status;
  1196. }
  1197. NTSTATUS
  1198. NtfsQueryQuotaUserSidList (
  1199. IN PIRP_CONTEXT IrpContext,
  1200. IN PVCB Vcb,
  1201. IN PFILE_GET_QUOTA_INFORMATION SidList,
  1202. IN OUT PFILE_QUOTA_INFORMATION QuotaInfoOutBuffer,
  1203. IN OUT PULONG BufferLength,
  1204. IN BOOLEAN ReturnSingleEntry
  1205. )
  1206. /*++
  1207. Routine Description:
  1208. This routine query for the quota data for each user specified in the
  1209. user provided sid list.
  1210. Arguments:
  1211. Vcb - Supplies a pointer to the volume control block.
  1212. SidList - Supplies a pointer to the Sid list. The list has already
  1213. been validated.
  1214. QuotaInfoOutBuffer - Indicates where the retrived query data should be placed.
  1215. BufferLength - Indicates that size of the buffer, and is updated with the
  1216. amount of data actually placed in the buffer.
  1217. ReturnSingleEntry - Indicates if just one entry should be returned.
  1218. Return Value:
  1219. Returns the status of the operation.
  1220. --*/
  1221. {
  1222. NTSTATUS Status = STATUS_SUCCESS;
  1223. ULONG BytesRemaining = *BufferLength;
  1224. PFILE_QUOTA_INFORMATION LastEntry = QuotaInfoOutBuffer;
  1225. ULONG OwnerId;
  1226. PAGED_CODE( );
  1227. //
  1228. // Loop through each of the entries.
  1229. //
  1230. while (TRUE) {
  1231. //
  1232. // Get the owner id.
  1233. //
  1234. OwnerId = NtfsGetOwnerId( IrpContext,
  1235. &SidList->Sid,
  1236. FALSE,
  1237. NULL );
  1238. if (OwnerId != QUOTA_INVALID_ID) {
  1239. //
  1240. // Send ownerid and ask for a single entry.
  1241. //
  1242. Status = NtfsFsQuotaQueryInfo( IrpContext,
  1243. Vcb,
  1244. OwnerId - 1,
  1245. TRUE,
  1246. &QuotaInfoOutBuffer,
  1247. &BytesRemaining,
  1248. NULL );
  1249. } else {
  1250. //
  1251. // Send back zeroed data alongwith the Sid.
  1252. //
  1253. Status = NtfsPackQuotaInfo( &SidList->Sid,
  1254. NULL,
  1255. QuotaInfoOutBuffer,
  1256. &BytesRemaining );
  1257. }
  1258. //
  1259. // Bail out if we got a real error.
  1260. //
  1261. if (!NT_SUCCESS( Status ) && (Status != STATUS_NO_MORE_ENTRIES)) {
  1262. break;
  1263. }
  1264. if (ReturnSingleEntry) {
  1265. break;
  1266. }
  1267. //
  1268. // Make a note of the last entry filled in.
  1269. //
  1270. LastEntry = QuotaInfoOutBuffer;
  1271. //
  1272. // If we've exhausted the SidList, we're done
  1273. //
  1274. if (SidList->NextEntryOffset == 0) {
  1275. break;
  1276. }
  1277. SidList = Add2Ptr( SidList, SidList->NextEntryOffset );
  1278. ASSERT(QuotaInfoOutBuffer->NextEntryOffset > 0);
  1279. QuotaInfoOutBuffer = Add2Ptr( QuotaInfoOutBuffer,
  1280. QuotaInfoOutBuffer->NextEntryOffset );
  1281. }
  1282. //
  1283. // Set the next entry offset to zero to
  1284. // indicate list termination.
  1285. //
  1286. if (BytesRemaining != *BufferLength) {
  1287. LastEntry->NextEntryOffset = 0;
  1288. Status = STATUS_SUCCESS;
  1289. }
  1290. //
  1291. // Update the buffer length to reflect what's left.
  1292. // If we've copied anything at all, we must return SUCCESS.
  1293. //
  1294. ASSERT( (BytesRemaining == *BufferLength) || (Status == STATUS_SUCCESS ) );
  1295. *BufferLength = BytesRemaining;
  1296. return Status;
  1297. }
  1298. NTSTATUS
  1299. NtfsPackQuotaInfo (
  1300. IN PSID Sid,
  1301. IN PQUOTA_USER_DATA QuotaUserData OPTIONAL,
  1302. IN PFILE_QUOTA_INFORMATION OutBuffer,
  1303. IN OUT PULONG OutBufferSize
  1304. )
  1305. /*++
  1306. Routine Description:
  1307. This is an internal routine that fills a given FILE_QUOTA_INFORMATION
  1308. structure with information from a given QUOTA_USER_DATA structure.
  1309. Arguments:
  1310. Sid - SID to be copied. Same as the one embedded inside the USER_DATA struct.
  1311. This routine doesn't care if it's a valid sid.
  1312. QuotaUserData - Source of data
  1313. QuotaInfoBufferPtr - Buffer to have user data copied in to.
  1314. OutBufferSize - IN size of the buffer, OUT size of the remaining buffer.
  1315. --*/
  1316. {
  1317. ULONG SidLength;
  1318. ULONG NextOffset;
  1319. ULONG EntrySize;
  1320. SidLength = RtlLengthSid( Sid );
  1321. EntrySize = SidLength + FIELD_OFFSET( FILE_QUOTA_INFORMATION, Sid );
  1322. //
  1323. // Abort if this entry won't fit in the buffer.
  1324. //
  1325. if (*OutBufferSize < EntrySize) {
  1326. return STATUS_BUFFER_OVERFLOW;
  1327. }
  1328. if (ARGUMENT_PRESENT(QuotaUserData)) {
  1329. //
  1330. // Fill in the user buffer for this entry.
  1331. //
  1332. OutBuffer->ChangeTime.QuadPart = QuotaUserData->QuotaChangeTime;
  1333. OutBuffer->QuotaUsed.QuadPart = QuotaUserData->QuotaUsed;
  1334. OutBuffer->QuotaThreshold.QuadPart = QuotaUserData->QuotaThreshold;
  1335. OutBuffer->QuotaLimit.QuadPart = QuotaUserData->QuotaLimit;
  1336. } else {
  1337. //
  1338. // Return all zeros for the data, up until the Sid.
  1339. //
  1340. RtlZeroMemory( OutBuffer, FIELD_OFFSET(FILE_QUOTA_INFORMATION, Sid) );
  1341. }
  1342. OutBuffer->SidLength = SidLength;
  1343. RtlCopyMemory( &OutBuffer->Sid,
  1344. Sid,
  1345. SidLength );
  1346. //
  1347. // Calculate the next offset.
  1348. //
  1349. NextOffset = QuadAlign( EntrySize );
  1350. //
  1351. // Add the offset to the amount used.
  1352. // NextEntryOffset may be sligthly larger than Length due to
  1353. // rounding of the previous entry size to longlong.
  1354. //
  1355. if (*OutBufferSize > NextOffset) {
  1356. *OutBufferSize -= NextOffset;
  1357. OutBuffer->NextEntryOffset = NextOffset;
  1358. } else {
  1359. //
  1360. // We did have enough room for this entry, but quad-alignment made
  1361. // it look like we didn't. Return the last few bytes left
  1362. // (what we lost in rounding up) just for correctness, although
  1363. // those really won't be of much use. The NextEntryOffset will be
  1364. // zeroed subsequently by the caller.
  1365. // Note that the OutBuffer is pointing at the _beginning_ of the
  1366. // last entry returned in this case.
  1367. //
  1368. ASSERT( *OutBufferSize >= EntrySize );
  1369. *OutBufferSize -= EntrySize;
  1370. OutBuffer->NextEntryOffset = EntrySize;
  1371. }
  1372. return STATUS_SUCCESS;
  1373. }
  1374. ULONG
  1375. NtfsGetOwnerId (
  1376. IN PIRP_CONTEXT IrpContext,
  1377. IN PSID Sid,
  1378. IN BOOLEAN CreateNew,
  1379. IN PFILE_QUOTA_INFORMATION FileQuotaInfo OPTIONAL
  1380. )
  1381. /*++
  1382. Routine Description:
  1383. This routine determines the owner id for the requested SID. First the
  1384. Sid is looked up in the Owner Id index. If the entry exists, then that
  1385. owner id is returned. If the sid does not exist then new entry is
  1386. created in the owner id index.
  1387. Arguments:
  1388. Sid - Security id to determine the owner id.
  1389. CreateNew - Create a new id if necessary.
  1390. FileQuotaInfo - Optional quota data to update quota index with.
  1391. Return Value:
  1392. ULONG - Owner Id for the security id. QUOTA_INVALID_ID is returned if id
  1393. did not exist and CreateNew was FALSE.
  1394. --*/
  1395. {
  1396. ULONG OwnerId;
  1397. ULONG DefaultId;
  1398. ULONG SidLength;
  1399. NTSTATUS Status;
  1400. INDEX_ROW IndexRow;
  1401. INDEX_KEY IndexKey;
  1402. MAP_HANDLE MapHandle;
  1403. PQUOTA_USER_DATA NewQuotaData = NULL;
  1404. QUICK_INDEX_HINT QuickIndexHint;
  1405. PSCB QuotaScb;
  1406. PVCB Vcb = IrpContext->Vcb;
  1407. PSCB OwnerIdScb = Vcb->OwnerIdTableScb;
  1408. BOOLEAN ExistingRecord;
  1409. PAGED_CODE();
  1410. //
  1411. // Determine the Sid length.
  1412. //
  1413. SidLength = RtlLengthSid( Sid );
  1414. IndexKey.KeyLength = SidLength;
  1415. IndexKey.Key = Sid;
  1416. //
  1417. // If there is quota information to update or there are pending deletes
  1418. // then long path must be taken where the user quota entry is found.
  1419. //
  1420. if (FileQuotaInfo == NULL) {
  1421. //
  1422. // Acquire the owner id index shared.
  1423. //
  1424. NtfsAcquireSharedScb( IrpContext, OwnerIdScb );
  1425. try {
  1426. //
  1427. // Assume the Sid is in the index.
  1428. //
  1429. Status = NtOfsFindRecord( IrpContext,
  1430. OwnerIdScb,
  1431. &IndexKey,
  1432. &IndexRow,
  1433. &MapHandle,
  1434. NULL );
  1435. //
  1436. // If the sid was found then capture is value.
  1437. //
  1438. if (NT_SUCCESS( Status )) {
  1439. ASSERT( IndexRow.DataPart.DataLength == sizeof( ULONG ));
  1440. OwnerId = *((PULONG) IndexRow.DataPart.Data);
  1441. //
  1442. // Release the index map handle.
  1443. //
  1444. NtOfsReleaseMap( IrpContext, &MapHandle );
  1445. }
  1446. } finally {
  1447. NtfsReleaseScb( IrpContext, OwnerIdScb );
  1448. }
  1449. //
  1450. // If the sid was found and there are no pending deletes, we are done.
  1451. //
  1452. if (NT_SUCCESS(Status)) {
  1453. if (!FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_PENDING_DELETES )) {
  1454. return OwnerId;
  1455. }
  1456. //
  1457. // Look up the actual record to see if it is deleted.
  1458. //
  1459. QuotaScb = Vcb->QuotaTableScb;
  1460. NtfsAcquireSharedScb( IrpContext, QuotaScb );
  1461. try {
  1462. IndexKey.KeyLength = sizeof(ULONG);
  1463. IndexKey.Key = &OwnerId;
  1464. Status = NtOfsFindRecord( IrpContext,
  1465. QuotaScb,
  1466. &IndexKey,
  1467. &IndexRow,
  1468. &MapHandle,
  1469. NULL );
  1470. if (!NT_SUCCESS( Status )) {
  1471. ASSERT( NT_SUCCESS( Status ));
  1472. NtfsMarkQuotaCorrupt( IrpContext, Vcb );
  1473. OwnerId = QUOTA_INVALID_ID;
  1474. leave;
  1475. }
  1476. if (FlagOn( ((PQUOTA_USER_DATA) IndexRow.DataPart.Data)->QuotaFlags,
  1477. QUOTA_FLAG_ID_DELETED )) {
  1478. //
  1479. // Return invalid user.
  1480. //
  1481. OwnerId = QUOTA_INVALID_ID;
  1482. }
  1483. //
  1484. // Release the index map handle.
  1485. //
  1486. NtOfsReleaseMap( IrpContext, &MapHandle );
  1487. } finally {
  1488. NtfsReleaseScb( IrpContext, QuotaScb );
  1489. }
  1490. //
  1491. // If an active id was found or caller does not want a new
  1492. // created then return.
  1493. //
  1494. if ((OwnerId != QUOTA_INVALID_ID) || !CreateNew) {
  1495. return OwnerId;
  1496. }
  1497. } else if (!CreateNew) {
  1498. //
  1499. // Just return QUOTA_INVALID_ID.
  1500. //
  1501. return QUOTA_INVALID_ID;
  1502. }
  1503. }
  1504. //
  1505. // If we have the quotatable resource, we should have it exclusively.
  1506. //
  1507. ASSERT( CreateNew );
  1508. ASSERT( !ExIsResourceAcquiredSharedLite( Vcb->QuotaTableScb->Fcb->Resource ) ||
  1509. ExIsResourceAcquiredExclusiveLite( Vcb->QuotaTableScb->Fcb->Resource ));
  1510. //
  1511. // Acquire Owner id and quota index exclusive.
  1512. //
  1513. QuotaScb = Vcb->QuotaTableScb;
  1514. NtfsAcquireExclusiveScb( IrpContext, QuotaScb );
  1515. NtfsAcquireExclusiveScb( IrpContext, OwnerIdScb );
  1516. NtOfsInitializeMapHandle( &MapHandle );
  1517. try {
  1518. //
  1519. // Verify that the sid is still not in the index.
  1520. //
  1521. IndexKey.KeyLength = SidLength;
  1522. IndexKey.Key = Sid;
  1523. Status = NtOfsFindRecord( IrpContext,
  1524. OwnerIdScb,
  1525. &IndexKey,
  1526. &IndexRow,
  1527. &MapHandle,
  1528. NULL );
  1529. //
  1530. // If the sid was found then capture the owner id.
  1531. //
  1532. ExistingRecord = NT_SUCCESS(Status);
  1533. if (ExistingRecord) {
  1534. ASSERT( IndexRow.DataPart.DataLength == sizeof( ULONG ));
  1535. OwnerId = *((PULONG) IndexRow.DataPart.Data);
  1536. if ((FileQuotaInfo == NULL) &&
  1537. !FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_PENDING_DELETES )) {
  1538. leave;
  1539. }
  1540. //
  1541. // Release the index map handle.
  1542. //
  1543. NtOfsReleaseMap( IrpContext, &MapHandle );
  1544. } else {
  1545. //
  1546. // Allocate a new owner id and update the owner index.
  1547. //
  1548. OwnerId = Vcb->QuotaOwnerId;
  1549. Vcb->QuotaOwnerId += 1;
  1550. IndexRow.KeyPart.KeyLength = SidLength;
  1551. IndexRow.KeyPart.Key = Sid;
  1552. IndexRow.DataPart.Data = &OwnerId;
  1553. IndexRow.DataPart.DataLength = sizeof(OwnerId);
  1554. NtOfsAddRecords( IrpContext,
  1555. OwnerIdScb,
  1556. 1,
  1557. &IndexRow,
  1558. FALSE );
  1559. }
  1560. //
  1561. // Allocate space for the new quota user data.
  1562. //
  1563. NewQuotaData = NtfsAllocatePool( PagedPool,
  1564. SIZEOF_QUOTA_USER_DATA + SidLength);
  1565. if (ExistingRecord) {
  1566. //
  1567. // Find the existing record and update it.
  1568. //
  1569. IndexKey.KeyLength = sizeof( ULONG );
  1570. IndexKey.Key = &OwnerId;
  1571. RtlZeroMemory( &QuickIndexHint, sizeof( QuickIndexHint ));
  1572. Status = NtOfsFindRecord( IrpContext,
  1573. QuotaScb,
  1574. &IndexKey,
  1575. &IndexRow,
  1576. &MapHandle,
  1577. &QuickIndexHint );
  1578. if (!NT_SUCCESS( Status )) {
  1579. ASSERT( NT_SUCCESS( Status ));
  1580. NtfsMarkQuotaCorrupt( IrpContext, Vcb );
  1581. OwnerId = QUOTA_INVALID_ID;
  1582. leave;
  1583. }
  1584. ASSERT( IndexRow.DataPart.DataLength == SIZEOF_QUOTA_USER_DATA + SidLength );
  1585. RtlCopyMemory( NewQuotaData, IndexRow.DataPart.Data, IndexRow.DataPart.DataLength );
  1586. ASSERT( RtlEqualMemory( &NewQuotaData->QuotaSid, Sid, SidLength ));
  1587. //
  1588. // Update the changed fields in the record.
  1589. //
  1590. if (FileQuotaInfo != NULL) {
  1591. ClearFlag( NewQuotaData->QuotaFlags, QUOTA_FLAG_DEFAULT_LIMITS );
  1592. NewQuotaData->QuotaThreshold = FileQuotaInfo->QuotaThreshold.QuadPart;
  1593. NewQuotaData->QuotaLimit = FileQuotaInfo->QuotaLimit.QuadPart;
  1594. KeQuerySystemTime( (PLARGE_INTEGER) &NewQuotaData->QuotaChangeTime );
  1595. } else if (!FlagOn( NewQuotaData->QuotaFlags, QUOTA_FLAG_ID_DELETED )) {
  1596. //
  1597. // There is nothing to update just return.
  1598. //
  1599. leave;
  1600. }
  1601. //
  1602. // Always clear the deleted flag.
  1603. //
  1604. ClearFlag( NewQuotaData->QuotaFlags, QUOTA_FLAG_ID_DELETED );
  1605. ASSERT( (OwnerId != Vcb->AdministratorId) || (NewQuotaData->QuotaLimit == -1) );
  1606. //
  1607. // The key length does not change.
  1608. //
  1609. IndexRow.KeyPart.Key = &OwnerId;
  1610. ASSERT( IndexRow.KeyPart.KeyLength == sizeof( ULONG ));
  1611. IndexRow.DataPart.Data = NewQuotaData;
  1612. IndexRow.DataPart.DataLength = SIZEOF_QUOTA_USER_DATA;
  1613. NtOfsUpdateRecord( IrpContext,
  1614. QuotaScb,
  1615. 1,
  1616. &IndexRow,
  1617. &QuickIndexHint,
  1618. &MapHandle );
  1619. leave;
  1620. }
  1621. if (FileQuotaInfo == NULL) {
  1622. //
  1623. // Look up the default quota limits.
  1624. //
  1625. DefaultId = QUOTA_DEFAULTS_ID;
  1626. IndexKey.KeyLength = sizeof( ULONG );
  1627. IndexKey.Key = &DefaultId;
  1628. Status = NtOfsFindRecord( IrpContext,
  1629. QuotaScb,
  1630. &IndexKey,
  1631. &IndexRow,
  1632. &MapHandle,
  1633. NULL );
  1634. if (!NT_SUCCESS( Status )) {
  1635. ASSERT( NT_SUCCESS( Status ));
  1636. NtfsRaiseStatus( IrpContext,
  1637. STATUS_QUOTA_LIST_INCONSISTENT,
  1638. NULL,
  1639. Vcb->QuotaTableScb->Fcb );
  1640. }
  1641. ASSERT( IndexRow.DataPart.DataLength >= SIZEOF_QUOTA_USER_DATA );
  1642. //
  1643. // Initialize the new quota entry with the defaults.
  1644. //
  1645. RtlCopyMemory( NewQuotaData,
  1646. IndexRow.DataPart.Data,
  1647. SIZEOF_QUOTA_USER_DATA );
  1648. ClearFlag( NewQuotaData->QuotaFlags, ~QUOTA_FLAG_USER_MASK );
  1649. } else {
  1650. //
  1651. // Initialize the new record with the new data.
  1652. //
  1653. RtlZeroMemory( NewQuotaData, SIZEOF_QUOTA_USER_DATA );
  1654. NewQuotaData->QuotaVersion = QUOTA_USER_VERSION;
  1655. NewQuotaData->QuotaThreshold = FileQuotaInfo->QuotaThreshold.QuadPart;
  1656. NewQuotaData->QuotaLimit = FileQuotaInfo->QuotaLimit.QuadPart;
  1657. }
  1658. ASSERT( !RtlEqualSid( SeExports->SeAliasAdminsSid, Sid ) ||
  1659. (NewQuotaData->QuotaThreshold == -1) );
  1660. //
  1661. // Copy the Sid into the new record.
  1662. //
  1663. RtlCopyMemory( &NewQuotaData->QuotaSid, Sid, SidLength );
  1664. KeQuerySystemTime( (PLARGE_INTEGER) &NewQuotaData->QuotaChangeTime );
  1665. //
  1666. // Add the new quota data record to the index.
  1667. //
  1668. IndexRow.KeyPart.KeyLength = sizeof( ULONG );
  1669. IndexRow.KeyPart.Key = &OwnerId;
  1670. IndexRow.DataPart.Data = NewQuotaData;
  1671. IndexRow.DataPart.DataLength = SIZEOF_QUOTA_USER_DATA + SidLength;
  1672. NtOfsAddRecords( IrpContext,
  1673. QuotaScb,
  1674. 1,
  1675. &IndexRow,
  1676. TRUE );
  1677. } finally {
  1678. if (NewQuotaData != NULL) {
  1679. NtfsFreePool( NewQuotaData );
  1680. }
  1681. //
  1682. // Release the index map handle and index resources.
  1683. //
  1684. NtOfsReleaseMap( IrpContext, &MapHandle );
  1685. NtfsReleaseScb( IrpContext, QuotaScb );
  1686. NtfsReleaseScb( IrpContext, OwnerIdScb );
  1687. }
  1688. return OwnerId;
  1689. }
  1690. VOID
  1691. NtfsGetRemainingQuota (
  1692. IN PIRP_CONTEXT IrpContext,
  1693. IN ULONG OwnerId,
  1694. OUT PULONGLONG RemainingQuota,
  1695. OUT PULONGLONG TotalQuota,
  1696. IN OUT PQUICK_INDEX_HINT QuickIndexHint OPTIONAL
  1697. )
  1698. /*++
  1699. Routine Description:
  1700. This routine returns the remaining amount of quota a user has before a
  1701. the quota limit is reached.
  1702. Arguments:
  1703. Fcb - Fcb whose quota usage is being checked.
  1704. OwnerId - Supplies the owner id to look up.
  1705. RemainingQuota - Returns the remaining amount of quota in bytes.
  1706. TotalQuota - Returns the total amount of quota in bytes for the given sid.
  1707. QuickIndexHint - Supplies an optional hint where to look of the value.
  1708. Return Value:
  1709. None
  1710. --*/
  1711. {
  1712. PQUOTA_USER_DATA UserData;
  1713. INDEX_ROW IndexRow;
  1714. INDEX_KEY IndexKey;
  1715. MAP_HANDLE MapHandle;
  1716. NTSTATUS Status;
  1717. PVCB Vcb = IrpContext->Vcb;
  1718. PAGED_CODE();
  1719. //
  1720. // Initialize the map handle.
  1721. //
  1722. NtOfsInitializeMapHandle( &MapHandle );
  1723. NtfsAcquireSharedScb( IrpContext, Vcb->QuotaTableScb );
  1724. try {
  1725. IndexKey.KeyLength = sizeof(ULONG);
  1726. IndexKey.Key = &OwnerId;
  1727. Status = NtOfsFindRecord( IrpContext,
  1728. Vcb->QuotaTableScb,
  1729. &IndexKey,
  1730. &IndexRow,
  1731. &MapHandle,
  1732. QuickIndexHint );
  1733. if (!NT_SUCCESS( Status )) {
  1734. //
  1735. // This look up should not fail.
  1736. //
  1737. ASSERT( NT_SUCCESS( Status ));
  1738. //
  1739. // There is one case where this could occur. That is a
  1740. // owner id could be deleted while this ccb was in use.
  1741. //
  1742. *RemainingQuota = 0;
  1743. *TotalQuota = 0;
  1744. leave;
  1745. }
  1746. UserData = IndexRow.DataPart.Data;
  1747. if (UserData->QuotaUsed >= UserData->QuotaLimit) {
  1748. *RemainingQuota = 0;
  1749. } else {
  1750. *RemainingQuota = UserData->QuotaLimit - UserData->QuotaUsed;
  1751. }
  1752. *TotalQuota = UserData->QuotaLimit;
  1753. } finally {
  1754. NtOfsReleaseMap( IrpContext, &MapHandle );
  1755. NtfsReleaseScb( IrpContext, Vcb->QuotaTableScb );
  1756. }
  1757. return;
  1758. }
  1759. PQUOTA_CONTROL_BLOCK
  1760. NtfsInitializeQuotaControlBlock (
  1761. IN PVCB Vcb,
  1762. IN ULONG OwnerId
  1763. )
  1764. /*++
  1765. Routine Description:
  1766. This routine returns the quota control block field specified owner. First
  1767. a lookup is done in the quota control table for an existing quota control
  1768. block. If there is no quota control block, then a new one is created.
  1769. Arguments:
  1770. Vcb - Supplies the volume control block.
  1771. OwnerId - Supplies the requested owner id.
  1772. Return Value:
  1773. Returns a quota control block for the owner.
  1774. --*/
  1775. {
  1776. PQUOTA_CONTROL_BLOCK QuotaControl;
  1777. BOOLEAN NewEntry;
  1778. PQUOTA_CONTROL_BLOCK InitQuotaControl;
  1779. PFAST_MUTEX Lock = NULL;
  1780. PVOID NodeOrParent;
  1781. TABLE_SEARCH_RESULT SearchResult;
  1782. PAGED_CODE();
  1783. ASSERT( OwnerId != 0 );
  1784. //
  1785. // Lock the quota table.
  1786. //
  1787. ExAcquireFastMutexUnsafe( &Vcb->QuotaControlLock );
  1788. try {
  1789. InitQuotaControl = Vcb->QuotaControlTemplate;
  1790. InitQuotaControl->OwnerId = OwnerId;
  1791. QuotaControl = RtlLookupElementGenericTableFull( &Vcb->QuotaControlTable,
  1792. InitQuotaControl,
  1793. &NodeOrParent,
  1794. &SearchResult );
  1795. if (QuotaControl == NULL) {
  1796. //
  1797. // Allocate and initialize the lock.
  1798. //
  1799. Lock = NtfsAllocatePoolWithTag( NonPagedPool,
  1800. sizeof( FAST_MUTEX ),
  1801. 'QftN' );
  1802. ExInitializeFastMutex( Lock );
  1803. //
  1804. // Insert table element into table.
  1805. //
  1806. QuotaControl = RtlInsertElementGenericTableFull( &Vcb->QuotaControlTable,
  1807. InitQuotaControl,
  1808. sizeof( QUOTA_CONTROL_BLOCK ) + SIZEOF_QUOTA_USER_DATA,
  1809. &NewEntry,
  1810. NodeOrParent,
  1811. SearchResult );
  1812. ASSERT( IsQuadAligned( &QuotaControl->QuickIndexHint ));
  1813. QuotaControl->QuotaControlLock = Lock;
  1814. Lock = NULL;
  1815. }
  1816. //
  1817. // Update the reference count and add set the pointer in the Fcb.
  1818. //
  1819. InterlockedIncrement( &QuotaControl->ReferenceCount );
  1820. ASSERT( OwnerId == QuotaControl->OwnerId );
  1821. } finally {
  1822. //
  1823. // Clean up.
  1824. //
  1825. if (Lock != NULL) {
  1826. NtfsFreePool( Lock );
  1827. }
  1828. ExReleaseFastMutexUnsafe( &Vcb->QuotaControlLock );
  1829. }
  1830. return QuotaControl;
  1831. }
  1832. VOID
  1833. NtfsInitializeQuotaIndex (
  1834. IN PIRP_CONTEXT IrpContext,
  1835. IN PFCB Fcb,
  1836. IN PVCB Vcb
  1837. )
  1838. /*++
  1839. Routine Description:
  1840. This routine opens the quota index for the volume. If the index does not
  1841. exist it is created and initialized.
  1842. Arguments:
  1843. Fcb - Pointer to Fcb for the quota file.
  1844. Vcb - Volume control block for volume be mounted.
  1845. Return Value:
  1846. None
  1847. --*/
  1848. {
  1849. ULONG Key;
  1850. NTSTATUS Status;
  1851. INDEX_ROW IndexRow;
  1852. MAP_HANDLE MapHandle;
  1853. QUOTA_USER_DATA QuotaData;
  1854. UNICODE_STRING IndexName = CONSTANT_UNICODE_STRING( L"$Q" );
  1855. PAGED_CODE();
  1856. //
  1857. // Initialize quota table and fast mutex.
  1858. //
  1859. ExInitializeFastMutex( &Vcb->QuotaControlLock );
  1860. RtlInitializeGenericTable( &Vcb->QuotaControlTable,
  1861. NtfsQuotaTableCompare,
  1862. NtfsQuotaTableAllocate,
  1863. NtfsQuotaTableFree,
  1864. NULL );
  1865. ReInitializeQuotaIndex:
  1866. NtOfsCreateIndex( IrpContext,
  1867. Fcb,
  1868. IndexName,
  1869. CREATE_OR_OPEN,
  1870. 0,
  1871. COLLATION_NTOFS_ULONG,
  1872. NtOfsCollateUlong,
  1873. NULL,
  1874. &Vcb->QuotaTableScb );
  1875. IndexName.Buffer = L"$O";
  1876. NtOfsCreateIndex( IrpContext,
  1877. Fcb,
  1878. IndexName,
  1879. CREATE_OR_OPEN,
  1880. 0,
  1881. COLLATION_NTOFS_SID,
  1882. NtOfsCollateSid,
  1883. NULL,
  1884. &Vcb->OwnerIdTableScb );
  1885. //
  1886. // Find the next owner id to allocate.
  1887. //
  1888. NtfsAcquireExclusiveScb( IrpContext, Vcb->QuotaTableScb );
  1889. try {
  1890. //
  1891. // Initialize quota delete secquence number.
  1892. //
  1893. Vcb->QuotaDeleteSecquence = 1;
  1894. //
  1895. // Load the quota flags.
  1896. //
  1897. Key = QUOTA_DEFAULTS_ID;
  1898. IndexRow.KeyPart.KeyLength = sizeof( ULONG );
  1899. IndexRow.KeyPart.Key = &Key;
  1900. Status = NtOfsFindRecord( IrpContext,
  1901. Vcb->QuotaTableScb,
  1902. &IndexRow.KeyPart,
  1903. &IndexRow,
  1904. &MapHandle,
  1905. NULL);
  1906. if (NT_SUCCESS( Status )) {
  1907. //
  1908. // Make sure this is the correct version.
  1909. //
  1910. if (((PQUOTA_USER_DATA) IndexRow.DataPart.Data)->QuotaVersion > QUOTA_USER_VERSION) {
  1911. //
  1912. // Release the index map handle.
  1913. //
  1914. NtOfsReleaseMap( IrpContext, &MapHandle );
  1915. //
  1916. // Wrong version close the quota index this will
  1917. // pervent use from doing anything with quotas.
  1918. //
  1919. NtOfsCloseIndex( IrpContext, Vcb->QuotaTableScb );
  1920. Vcb->QuotaTableScb = NULL;
  1921. leave;
  1922. }
  1923. //
  1924. // If this is an old version delete it.
  1925. //
  1926. if (((PQUOTA_USER_DATA) IndexRow.DataPart.Data)->QuotaVersion < QUOTA_USER_VERSION) {
  1927. DebugTrace( 0, Dbg, ( "NtfsInitializeQuotaIndex: Deleting version 1 quota index\n" ));
  1928. //
  1929. // Release the index map handle.
  1930. //
  1931. NtOfsReleaseMap( IrpContext, &MapHandle );
  1932. //
  1933. // Increment the cleanup count so the FCB does not
  1934. // go away.
  1935. //
  1936. Fcb->CleanupCount += 1;
  1937. //
  1938. // This is an old version of the quota file
  1939. // delete it the owner id index and start over again.
  1940. //
  1941. NtOfsDeleteIndex( IrpContext, Fcb, Vcb->QuotaTableScb );
  1942. NtOfsCloseIndex( IrpContext, Vcb->QuotaTableScb );
  1943. Vcb->QuotaTableScb = NULL;
  1944. //
  1945. // Delete the owner index too.
  1946. //
  1947. NtOfsDeleteIndex( IrpContext, Fcb, Vcb->OwnerIdTableScb );
  1948. NtOfsCloseIndex( IrpContext, Vcb->OwnerIdTableScb );
  1949. Vcb->OwnerIdTableScb = NULL;
  1950. NtfsCommitCurrentTransaction( IrpContext );
  1951. //
  1952. // Restore the cleanup count
  1953. //
  1954. Fcb->CleanupCount -= 1;
  1955. IndexName.Buffer = L"$Q";
  1956. goto ReInitializeQuotaIndex;
  1957. }
  1958. //
  1959. // The index already exists, just initialize the quota
  1960. // fields in the VCB.
  1961. //
  1962. Vcb->QuotaFlags = ((PQUOTA_USER_DATA) IndexRow.DataPart.Data)->QuotaFlags;
  1963. //
  1964. // Release the index map handle.
  1965. //
  1966. NtOfsReleaseMap( IrpContext, &MapHandle );
  1967. } else if (Status == STATUS_NO_MATCH) {
  1968. //
  1969. // The index was newly created.
  1970. // Create a default quota data row.
  1971. //
  1972. Key = QUOTA_DEFAULTS_ID;
  1973. RtlZeroMemory( &QuotaData, sizeof( QUOTA_USER_DATA ));
  1974. //
  1975. // Indicate that the quota needs to be rebuilt.
  1976. //
  1977. QuotaData.QuotaVersion = QUOTA_USER_VERSION;
  1978. QuotaData.QuotaFlags = QUOTA_FLAG_DEFAULT_LIMITS;
  1979. QuotaData.QuotaThreshold = MAXULONGLONG;
  1980. QuotaData.QuotaLimit = MAXULONGLONG;
  1981. KeQuerySystemTime( (PLARGE_INTEGER) &QuotaData.QuotaChangeTime );
  1982. IndexRow.KeyPart.KeyLength = sizeof( ULONG );
  1983. IndexRow.KeyPart.Key = &Key;
  1984. IndexRow.DataPart.DataLength = SIZEOF_QUOTA_USER_DATA;
  1985. IndexRow.DataPart.Data = &QuotaData;
  1986. NtOfsAddRecords( IrpContext,
  1987. Vcb->QuotaTableScb,
  1988. 1,
  1989. &IndexRow,
  1990. TRUE );
  1991. Vcb->QuotaOwnerId = QUOTA_FISRT_USER_ID;
  1992. Vcb->QuotaFlags = QuotaData.QuotaFlags;
  1993. }
  1994. Key = MAXULONG;
  1995. IndexRow.KeyPart.KeyLength = sizeof( ULONG );
  1996. IndexRow.KeyPart.Key = &Key;
  1997. Status = NtOfsFindLastRecord( IrpContext,
  1998. Vcb->QuotaTableScb,
  1999. &IndexRow.KeyPart,
  2000. &IndexRow,
  2001. &MapHandle );
  2002. if (!NT_SUCCESS( Status )) {
  2003. //
  2004. // This call should never fail.
  2005. //
  2006. ASSERT( NT_SUCCESS( Status) );
  2007. SetFlag( Vcb->QuotaFlags, QUOTA_FLAG_CORRUPT);
  2008. leave;
  2009. }
  2010. Key = *((PULONG) IndexRow.KeyPart.Key) + 1;
  2011. if (Key < QUOTA_FISRT_USER_ID) {
  2012. Key = QUOTA_FISRT_USER_ID;
  2013. }
  2014. Vcb->QuotaOwnerId = Key;
  2015. //
  2016. // Release the index map handle.
  2017. //
  2018. NtOfsReleaseMap( IrpContext, &MapHandle );
  2019. //
  2020. // Get the administrator ID so it can be protected from quota
  2021. // limits.
  2022. //
  2023. Vcb->AdministratorId = NtfsGetOwnerId( IrpContext,
  2024. SeExports->SeAliasAdminsSid,
  2025. TRUE,
  2026. NULL );
  2027. if (FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_TRACKING_REQUESTED )) {
  2028. //
  2029. // Allocate and initialize the template control block.
  2030. // Allocate enough space in the quota control block for the index
  2031. // data part. This is used as the new record when calling update
  2032. // record. This template is only allocated once and then it is
  2033. // saved in the vcb.
  2034. //
  2035. Vcb->QuotaControlTemplate = NtfsAllocatePoolWithTag( PagedPool,
  2036. sizeof( QUOTA_CONTROL_BLOCK ) + SIZEOF_QUOTA_USER_DATA,
  2037. 'QftN' );
  2038. RtlZeroMemory( Vcb->QuotaControlTemplate,
  2039. sizeof( QUOTA_CONTROL_BLOCK ) +
  2040. SIZEOF_QUOTA_USER_DATA );
  2041. Vcb->QuotaControlTemplate->NodeTypeCode = NTFS_NTC_QUOTA_CONTROL;
  2042. Vcb->QuotaControlTemplate->NodeByteSize = sizeof( QUOTA_CONTROL_BLOCK ) + SIZEOF_QUOTA_USER_DATA;
  2043. }
  2044. //
  2045. // Fix up the quota on the root directory.
  2046. //
  2047. NtfsConditionallyFixupQuota( IrpContext, Vcb->RootIndexScb->Fcb );
  2048. } finally {
  2049. if (Vcb->QuotaTableScb != NULL) {
  2050. NtfsReleaseScb( IrpContext, Vcb->QuotaTableScb );
  2051. }
  2052. }
  2053. //
  2054. // If the quota tracking has been requested and the quotas need to be
  2055. // repaired then try to repair them now.
  2056. //
  2057. if (FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_TRACKING_REQUESTED) &&
  2058. FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_OUT_OF_DATE | QUOTA_FLAG_CORRUPT | QUOTA_FLAG_PENDING_DELETES )) {
  2059. NtfsPostRepairQuotaIndex( IrpContext, Vcb );
  2060. }
  2061. return;
  2062. }
  2063. VOID
  2064. NtfsMarkUserLimit (
  2065. IN PIRP_CONTEXT IrpContext,
  2066. IN PVOID Context
  2067. )
  2068. /*++
  2069. Routine Description:
  2070. This routine marks a user's quota data entry to indicate that the user
  2071. has exceeded quota. The event is also logged.
  2072. Arguments:
  2073. Context - Supplies a pointer to the referenced quota control block.
  2074. Return Value:
  2075. None.
  2076. --*/
  2077. {
  2078. PQUOTA_CONTROL_BLOCK QuotaControl = Context;
  2079. PVCB Vcb = IrpContext->Vcb;
  2080. LARGE_INTEGER CurrentTime;
  2081. PQUOTA_USER_DATA UserData;
  2082. INDEX_ROW IndexRow;
  2083. INDEX_KEY IndexKey;
  2084. MAP_HANDLE MapHandle;
  2085. NTSTATUS Status;
  2086. BOOLEAN QuotaTableAcquired = FALSE;
  2087. PAGED_CODE();
  2088. DebugTrace( 0, Dbg, ( "NtfsMarkUserLimit: Quota limit called for owner id = %lx\n", QuotaControl->OwnerId ));
  2089. NtOfsInitializeMapHandle( &MapHandle );
  2090. //
  2091. // Acquire the VCB shared and check whether we should
  2092. // continue.
  2093. //
  2094. NtfsAcquireSharedVcb( IrpContext, Vcb, TRUE );
  2095. try {
  2096. if (!NtfsIsVcbAvailable( Vcb )) {
  2097. //
  2098. // The volume is going away, bail out.
  2099. //
  2100. Status = STATUS_VOLUME_DISMOUNTED;
  2101. leave;
  2102. }
  2103. NtfsAcquireExclusiveScb( IrpContext, Vcb->QuotaTableScb );
  2104. QuotaTableAcquired = TRUE;
  2105. //
  2106. // Get the user's quota data entry.
  2107. //
  2108. IndexKey.KeyLength = sizeof( ULONG );
  2109. IndexKey.Key = &QuotaControl->OwnerId;
  2110. Status = NtOfsFindRecord( IrpContext,
  2111. Vcb->QuotaTableScb,
  2112. &IndexKey,
  2113. &IndexRow,
  2114. &MapHandle,
  2115. &QuotaControl->QuickIndexHint );
  2116. if (!NT_SUCCESS( Status ) ||
  2117. (IndexRow.DataPart.DataLength < SIZEOF_QUOTA_USER_DATA + FIELD_OFFSET( SID, SubAuthority )) ||
  2118. ((ULONG) SeLengthSid( &(((PQUOTA_USER_DATA) (IndexRow.DataPart.Data))->QuotaSid)) + SIZEOF_QUOTA_USER_DATA !=
  2119. IndexRow.DataPart.DataLength)) {
  2120. //
  2121. // This look up should not fail.
  2122. //
  2123. ASSERT( NT_SUCCESS( Status ));
  2124. ASSERTMSG(( "NTFS: corrupt quotasid\n" ), FALSE);
  2125. NtfsMarkQuotaCorrupt( IrpContext, IrpContext->Vcb );
  2126. leave;
  2127. }
  2128. //
  2129. // Space is allocated for the new record after the quota control
  2130. // block.
  2131. //
  2132. UserData = (PQUOTA_USER_DATA) (QuotaControl + 1);
  2133. ASSERT( IndexRow.DataPart.DataLength >= SIZEOF_QUOTA_USER_DATA );
  2134. RtlCopyMemory( UserData,
  2135. IndexRow.DataPart.Data,
  2136. SIZEOF_QUOTA_USER_DATA );
  2137. KeQuerySystemTime( &CurrentTime );
  2138. UserData->QuotaChangeTime = CurrentTime.QuadPart;
  2139. //
  2140. // Indicate that user exceeded quota.
  2141. //
  2142. UserData->QuotaExceededTime = CurrentTime.QuadPart;
  2143. SetFlag( UserData->QuotaFlags, QUOTA_FLAG_LIMIT_REACHED );
  2144. //
  2145. // Log the limit event. If this fails then leave.
  2146. //
  2147. if (!NtfsLogEvent( IrpContext,
  2148. IndexRow.DataPart.Data,
  2149. IO_FILE_QUOTA_LIMIT,
  2150. STATUS_DISK_FULL )) {
  2151. leave;
  2152. }
  2153. //
  2154. // The key length does not change.
  2155. //
  2156. IndexRow.KeyPart.Key = &QuotaControl->OwnerId;
  2157. ASSERT( IndexRow.KeyPart.KeyLength == sizeof( ULONG ));
  2158. IndexRow.DataPart.Data = UserData;
  2159. IndexRow.DataPart.DataLength = SIZEOF_QUOTA_USER_DATA;
  2160. NtOfsUpdateRecord( IrpContext,
  2161. Vcb->QuotaTableScb,
  2162. 1,
  2163. &IndexRow,
  2164. &QuotaControl->QuickIndexHint,
  2165. &MapHandle );
  2166. } except( NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
  2167. Status = IrpContext->TopLevelIrpContext->ExceptionStatus;
  2168. }
  2169. //
  2170. // The request will be retied if the status is can't wait or log file full.
  2171. //
  2172. if ((Status != STATUS_CANT_WAIT) && (Status != STATUS_LOG_FILE_FULL)) {
  2173. //
  2174. // If we will not be called back, then no matter what happened
  2175. // dereference the quota control block and clear the post flag.
  2176. //
  2177. ExAcquireFastMutexUnsafe( &Vcb->QuotaControlLock );
  2178. ASSERT( FlagOn( QuotaControl->Flags, QUOTA_FLAG_LIMIT_POSTED ));
  2179. ClearFlag( QuotaControl->Flags, QUOTA_FLAG_LIMIT_POSTED );
  2180. ExReleaseFastMutexUnsafe( &Vcb->QuotaControlLock );
  2181. NtfsDereferenceQuotaControlBlock( Vcb, &QuotaControl );
  2182. }
  2183. //
  2184. // Release the index map handle.
  2185. //
  2186. NtOfsReleaseMap( IrpContext, &MapHandle );
  2187. if (QuotaTableAcquired) {
  2188. NtfsReleaseScb( IrpContext, Vcb->QuotaTableScb );
  2189. }
  2190. NtfsReleaseVcb( IrpContext, Vcb );
  2191. if (!NT_SUCCESS( Status )) {
  2192. NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
  2193. }
  2194. return;
  2195. }
  2196. VOID
  2197. NtfsMoveQuotaOwner (
  2198. IN PIRP_CONTEXT IrpContext,
  2199. IN PFCB Fcb,
  2200. IN PSECURITY_DESCRIPTOR Security
  2201. )
  2202. /*++
  2203. Routine Description:
  2204. This routine changes the owner id and quota charged for a file when the
  2205. file owner is changed.
  2206. Arguments:
  2207. Fcb - Pointer to fcb being opened.
  2208. Security - Pointer to the new security descriptor
  2209. Return Value:
  2210. None.
  2211. --*/
  2212. {
  2213. LONGLONG QuotaCharged;
  2214. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  2215. PSTANDARD_INFORMATION StandardInformation;
  2216. PSID Sid = NULL;
  2217. ULONG OwnerId;
  2218. NTSTATUS Status;
  2219. BOOLEAN OwnerDefaulted;
  2220. PAGED_CODE();
  2221. if (!NtfsPerformQuotaOperation(Fcb)) {
  2222. return;
  2223. }
  2224. //
  2225. // Extract the security id from the security descriptor.
  2226. //
  2227. Status = RtlGetOwnerSecurityDescriptor( Security,
  2228. &Sid,
  2229. &OwnerDefaulted );
  2230. if (!NT_SUCCESS( Status )) {
  2231. NtfsRaiseStatus( IrpContext, Status, NULL, Fcb );
  2232. }
  2233. //
  2234. // If we didn't get a SID then we can't move the owner.
  2235. //
  2236. if (Sid == NULL) {
  2237. return;
  2238. }
  2239. //
  2240. // Generate a owner id for the Fcb.
  2241. //
  2242. OwnerId = NtfsGetOwnerId( IrpContext, Sid, TRUE, NULL );
  2243. if (OwnerId == Fcb->OwnerId) {
  2244. //
  2245. // The owner is not changing so just return.
  2246. //
  2247. return;
  2248. }
  2249. //
  2250. // Initialize the context structure and map handle.
  2251. //
  2252. NtfsInitializeAttributeContext( &AttrContext );
  2253. //
  2254. // Preacquire the quota index exclusive since an entry may need to
  2255. // be added.
  2256. //
  2257. NtfsAcquireExclusiveScb( IrpContext, Fcb->Vcb->QuotaTableScb );
  2258. try {
  2259. //
  2260. // Locate the standard information, it must be there.
  2261. //
  2262. if (!NtfsLookupAttributeByCode( IrpContext,
  2263. Fcb,
  2264. &Fcb->FileReference,
  2265. $STANDARD_INFORMATION,
  2266. &AttrContext )) {
  2267. DebugTrace( 0, Dbg, ("Can't find standard information\n") );
  2268. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  2269. }
  2270. StandardInformation = (PSTANDARD_INFORMATION) NtfsAttributeValue( NtfsFoundAttribute( &AttrContext ));
  2271. QuotaCharged = -((LONGLONG) StandardInformation->QuotaCharged);
  2272. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  2273. //
  2274. // Remove the quota from the old owner.
  2275. //
  2276. NtfsUpdateFileQuota( IrpContext,
  2277. Fcb,
  2278. &QuotaCharged,
  2279. TRUE,
  2280. FALSE );
  2281. //
  2282. // Set the new owner id.
  2283. //
  2284. Fcb->OwnerId = OwnerId;
  2285. //
  2286. // Note the old quota block is kept around until the operation is
  2287. // complete. This is so the recovery code does not have allocate
  2288. // a memory if the old quota block is needed. This is done in
  2289. // NtfsCommonSetSecurityInfo.
  2290. //
  2291. Fcb->QuotaControl = NtfsInitializeQuotaControlBlock( Fcb->Vcb, OwnerId );
  2292. QuotaCharged = -QuotaCharged;
  2293. //
  2294. // Try to charge the quota to the new owner.
  2295. //
  2296. NtfsUpdateFileQuota( IrpContext,
  2297. Fcb,
  2298. &QuotaCharged,
  2299. TRUE,
  2300. TRUE );
  2301. SetFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  2302. } finally {
  2303. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  2304. NtfsReleaseScb( IrpContext, Fcb->Vcb->QuotaTableScb );
  2305. }
  2306. return;
  2307. }
  2308. VOID
  2309. NtfsMarkQuotaCorrupt (
  2310. IN PIRP_CONTEXT IrpContext,
  2311. IN PVCB Vcb
  2312. )
  2313. /*++
  2314. Routine Description:
  2315. This routine attempts to mark the quota index corrupt. It will
  2316. also attempt post a request to rebuild the quota index.
  2317. Arguments:
  2318. Vcb - Supplies a pointer the the volume who quota data is corrupt.
  2319. Return Value:
  2320. None
  2321. --*/
  2322. {
  2323. DebugTrace( 0, Dbg, ( "NtfsMarkQuotaCorrupt: Marking quota dirty on Vcb = %lx\n", Vcb));
  2324. if (!FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_CORRUPT )) {
  2325. //
  2326. // If the quota were not previous corrupt then log an event
  2327. // so others know this occured.
  2328. //
  2329. NtfsLogEvent( IrpContext,
  2330. NULL,
  2331. IO_FILE_QUOTA_CORRUPT,
  2332. STATUS_FILE_CORRUPT_ERROR );
  2333. }
  2334. ExAcquireFastMutexUnsafe( &Vcb->QuotaControlLock );
  2335. SetFlag( Vcb->QuotaFlags, QUOTA_FLAG_CORRUPT );
  2336. SetFlag( Vcb->QuotaState, VCB_QUOTA_SAVE_QUOTA_FLAGS );
  2337. //
  2338. // Since the index is corrupt there is no point in tracking the
  2339. // quota usage.
  2340. //
  2341. ClearFlag( Vcb->QuotaFlags, QUOTA_FLAG_TRACKING_ENABLED );
  2342. ExReleaseFastMutexUnsafe( &Vcb->QuotaControlLock );
  2343. //
  2344. // Do not save the flags here since the quota scb may be acquired
  2345. // shared. The repair will save the flags when it runs.
  2346. // Try to fix the problems.
  2347. //
  2348. NtfsPostRepairQuotaIndex( IrpContext, Vcb );
  2349. return;
  2350. }
  2351. VOID
  2352. NtfsPostRepairQuotaIndex (
  2353. IN PIRP_CONTEXT IrpContext,
  2354. IN PVCB Vcb
  2355. )
  2356. /*++
  2357. Routine Description:
  2358. This routine posts a request to recalculate all of the user quota data.
  2359. Arguments:
  2360. Vcb - Volume control block for volume whos quota needs to be fixed.
  2361. Return Value:
  2362. None
  2363. --*/
  2364. {
  2365. PAGED_CODE();
  2366. try {
  2367. ExAcquireFastMutexUnsafe( &Vcb->QuotaControlLock );
  2368. if (FlagOn( Vcb->QuotaState, VCB_QUOTA_REPAIR_RUNNING)) {
  2369. ExReleaseFastMutexUnsafe( &Vcb->QuotaControlLock );
  2370. leave;
  2371. }
  2372. if (Vcb->QuotaControlTemplate == NULL) {
  2373. //
  2374. // Allocate and initialize the template control block.
  2375. // Allocate enough space in the quota control block for the index
  2376. // data part. This is used as the new record when calling update
  2377. // record. This template is only allocated once and then it is
  2378. // saved in the vcb.
  2379. //
  2380. Vcb->QuotaControlTemplate = NtfsAllocatePoolWithTag( PagedPool,
  2381. sizeof( QUOTA_CONTROL_BLOCK ) + SIZEOF_QUOTA_USER_DATA,
  2382. 'QftN' );
  2383. RtlZeroMemory( Vcb->QuotaControlTemplate,
  2384. sizeof( QUOTA_CONTROL_BLOCK ) + SIZEOF_QUOTA_USER_DATA );
  2385. Vcb->QuotaControlTemplate->NodeTypeCode = NTFS_NTC_QUOTA_CONTROL;
  2386. Vcb->QuotaControlTemplate->NodeByteSize = sizeof( QUOTA_CONTROL_BLOCK ) + SIZEOF_QUOTA_USER_DATA;
  2387. }
  2388. SetFlag( Vcb->QuotaState, VCB_QUOTA_REPAIR_POSTED );
  2389. ExReleaseFastMutexUnsafe( &Vcb->QuotaControlLock );
  2390. //
  2391. // Post this special request.
  2392. //
  2393. NtfsPostSpecial( IrpContext,
  2394. Vcb,
  2395. NtfsRepairQuotaIndex,
  2396. NULL );
  2397. } finally {
  2398. if (AbnormalTermination()) {
  2399. ExAcquireFastMutexUnsafe( &Vcb->QuotaControlLock );
  2400. ClearFlag( Vcb->QuotaState, VCB_QUOTA_REPAIR_POSTED);
  2401. ExReleaseFastMutexUnsafe( &Vcb->QuotaControlLock );
  2402. }
  2403. }
  2404. return;
  2405. }
  2406. VOID
  2407. NtfsPostUserLimit (
  2408. IN PIRP_CONTEXT IrpContext,
  2409. IN PVCB Vcb,
  2410. IN PQUOTA_CONTROL_BLOCK QuotaControl
  2411. )
  2412. /*++
  2413. Routine Description:
  2414. This routine posts a request to save the fact that the user has exceeded
  2415. their limit.
  2416. Arguments:
  2417. Vcb - Volume control block for volume whos quota needs to be fixed.
  2418. QuotaControl - Quota control block for the user.
  2419. Return Value:
  2420. None
  2421. --*/
  2422. {
  2423. PAGED_CODE();
  2424. try {
  2425. ExAcquireFastMutexUnsafe( &Vcb->QuotaControlLock );
  2426. if (FlagOn( QuotaControl->Flags, QUOTA_FLAG_LIMIT_POSTED )) {
  2427. ExReleaseFastMutexUnsafe( &Vcb->QuotaControlLock );
  2428. leave;
  2429. }
  2430. SetFlag( QuotaControl->Flags, QUOTA_FLAG_LIMIT_POSTED );
  2431. //
  2432. // Reference the quota control block so it does not go away.
  2433. //
  2434. ASSERT( QuotaControl->ReferenceCount > 0 );
  2435. InterlockedIncrement( &QuotaControl->ReferenceCount );
  2436. ExReleaseFastMutexUnsafe( &Vcb->QuotaControlLock );
  2437. //
  2438. // Post this special request.
  2439. //
  2440. NtfsPostSpecial( IrpContext,
  2441. Vcb,
  2442. NtfsMarkUserLimit,
  2443. QuotaControl );
  2444. } finally {
  2445. if (AbnormalTermination()) {
  2446. ExAcquireFastMutexUnsafe( &Vcb->QuotaControlLock );
  2447. ClearFlag( QuotaControl->Flags, QUOTA_FLAG_LIMIT_POSTED );
  2448. ExReleaseFastMutexUnsafe( &Vcb->QuotaControlLock );
  2449. }
  2450. }
  2451. return;
  2452. }
  2453. NTSTATUS
  2454. NtfsPrepareForDelete (
  2455. IN PIRP_CONTEXT IrpContext,
  2456. IN PVCB Vcb,
  2457. IN PSID Sid
  2458. )
  2459. /*++
  2460. Routine Description:
  2461. This routine determines if an owner id is a candidate for deletion. If
  2462. the id appears deletable its user data is reset to the defaults and the
  2463. entry is marked as deleted. Later a worker thread will do the actual
  2464. deletion.
  2465. Arguments:
  2466. Vcb - Supplies a pointer to the volume containing the entry to be deleted.
  2467. Sid - Security id to to be deleted.
  2468. Return Value:
  2469. Returns a status indicating of the id was deletable at this time.
  2470. --*/
  2471. {
  2472. ULONG OwnerId;
  2473. ULONG DefaultOwnerId;
  2474. NTSTATUS Status = STATUS_SUCCESS;
  2475. INDEX_ROW IndexRow;
  2476. INDEX_ROW NewIndexRow;
  2477. INDEX_KEY IndexKey;
  2478. MAP_HANDLE MapHandle;
  2479. PQUOTA_CONTROL_BLOCK QuotaControl;
  2480. QUOTA_USER_DATA NewQuotaData;
  2481. PSCB QuotaScb = Vcb->QuotaTableScb;
  2482. PSCB OwnerIdScb = Vcb->OwnerIdTableScb;
  2483. PAGED_CODE();
  2484. //
  2485. // Determine the Sid length.
  2486. //
  2487. IndexKey.KeyLength = RtlLengthSid( Sid );
  2488. IndexKey.Key = Sid;
  2489. //
  2490. // Acquire Owner id and quota index exclusive.
  2491. //
  2492. NtfsAcquireExclusiveScb( IrpContext, QuotaScb );
  2493. NtfsAcquireExclusiveScb( IrpContext, OwnerIdScb );
  2494. ExAcquireFastMutexUnsafe( &Vcb->QuotaControlLock );
  2495. NtOfsInitializeMapHandle( &MapHandle );
  2496. try {
  2497. //
  2498. // Look up the SID in the owner index.
  2499. //
  2500. Status = NtOfsFindRecord( IrpContext,
  2501. OwnerIdScb,
  2502. &IndexKey,
  2503. &IndexRow,
  2504. &MapHandle,
  2505. NULL );
  2506. if (!NT_SUCCESS( Status )) {
  2507. leave;
  2508. }
  2509. //
  2510. // If the sid was found then capture the owner id.
  2511. //
  2512. ASSERT( IndexRow.DataPart.DataLength == sizeof( ULONG ));
  2513. OwnerId = *((PULONG) IndexRow.DataPart.Data);
  2514. //
  2515. // Release the index map handle.
  2516. //
  2517. NtOfsReleaseMap( IrpContext, &MapHandle );
  2518. //
  2519. // Find the existing record and update it.
  2520. //
  2521. IndexKey.KeyLength = sizeof( ULONG );
  2522. IndexKey.Key = &OwnerId;
  2523. Status = NtOfsFindRecord( IrpContext,
  2524. QuotaScb,
  2525. &IndexKey,
  2526. &IndexRow,
  2527. &MapHandle,
  2528. NULL );
  2529. if (!NT_SUCCESS( Status )) {
  2530. ASSERT( NT_SUCCESS( Status ));
  2531. NtfsMarkQuotaCorrupt( IrpContext, Vcb );
  2532. leave;
  2533. }
  2534. RtlCopyMemory( &NewQuotaData, IndexRow.DataPart.Data, SIZEOF_QUOTA_USER_DATA );
  2535. //
  2536. // Check to see if there is a quota control entry
  2537. // for this id.
  2538. //
  2539. ASSERT( FIELD_OFFSET( QUOTA_CONTROL_BLOCK, OwnerId ) <= FIELD_OFFSET( INDEX_ROW, KeyPart.Key ));
  2540. QuotaControl = RtlLookupElementGenericTable( &Vcb->QuotaControlTable,
  2541. CONTAINING_RECORD( &IndexRow.KeyPart.Key,
  2542. QUOTA_CONTROL_BLOCK,
  2543. OwnerId ));
  2544. //
  2545. // If there is a quota control entry or there is now
  2546. // some quota charged, then the entry cannot be deleted.
  2547. //
  2548. if ((QuotaControl != NULL) || (NewQuotaData.QuotaUsed != 0)) {
  2549. Status = STATUS_CANNOT_DELETE;
  2550. leave;
  2551. }
  2552. //
  2553. // Find the default quota record.
  2554. //
  2555. DefaultOwnerId = QUOTA_DEFAULTS_ID;
  2556. IndexKey.KeyLength = sizeof( ULONG );
  2557. IndexKey.Key = &DefaultOwnerId;
  2558. NtOfsReleaseMap( IrpContext, &MapHandle );
  2559. Status = NtOfsFindRecord( IrpContext,
  2560. QuotaScb,
  2561. &IndexKey,
  2562. &IndexRow,
  2563. &MapHandle,
  2564. NULL );
  2565. if (!NT_SUCCESS( Status )) {
  2566. NtfsRaiseStatus( IrpContext, STATUS_QUOTA_LIST_INCONSISTENT, NULL, QuotaScb->Fcb );
  2567. }
  2568. //
  2569. // Set the user entry to the current defaults. Then if the entry
  2570. // is really inuse it will appear that is came back after the delete.
  2571. //
  2572. RtlCopyMemory( &NewQuotaData,
  2573. IndexRow.DataPart.Data,
  2574. SIZEOF_QUOTA_USER_DATA );
  2575. ClearFlag( NewQuotaData.QuotaFlags, ~QUOTA_FLAG_USER_MASK );
  2576. //
  2577. // Set the deleted flag.
  2578. //
  2579. SetFlag( NewQuotaData.QuotaFlags, QUOTA_FLAG_ID_DELETED );
  2580. //
  2581. // The key length does not change.
  2582. //
  2583. NewIndexRow.KeyPart.Key = &OwnerId;
  2584. NewIndexRow.KeyPart.KeyLength = sizeof( ULONG );
  2585. NewIndexRow.DataPart.Data = &NewQuotaData;
  2586. NewIndexRow.DataPart.DataLength = SIZEOF_QUOTA_USER_DATA;
  2587. NtOfsUpdateRecord( IrpContext,
  2588. QuotaScb,
  2589. 1,
  2590. &NewIndexRow,
  2591. NULL,
  2592. NULL );
  2593. //
  2594. // Update the delete secquence number this is used to indicate
  2595. // another id has been deleted. If the repair code is in the
  2596. // middle of its scan it must restart the scan.
  2597. //
  2598. Vcb->QuotaDeleteSecquence += 1;
  2599. //
  2600. // Indicate there are pending deletes.
  2601. //
  2602. if (!FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_PENDING_DELETES )) {
  2603. SetFlag( Vcb->QuotaFlags, QUOTA_FLAG_PENDING_DELETES );
  2604. ASSERT( IndexRow.DataPart.DataLength <= sizeof( QUOTA_USER_DATA ));
  2605. RtlCopyMemory( &NewQuotaData,
  2606. IndexRow.DataPart.Data,
  2607. IndexRow.DataPart.DataLength );
  2608. //
  2609. // Update the changed fields in the record.
  2610. //
  2611. NewQuotaData.QuotaFlags = Vcb->QuotaFlags;
  2612. //
  2613. // Note the sizes in the IndexRow stay the same.
  2614. //
  2615. IndexRow.KeyPart.Key = &DefaultOwnerId;
  2616. ASSERT( IndexRow.KeyPart.KeyLength == sizeof( ULONG ));
  2617. IndexRow.DataPart.Data = &NewQuotaData;
  2618. NtOfsUpdateRecord( IrpContext,
  2619. QuotaScb,
  2620. 1,
  2621. &IndexRow,
  2622. NULL,
  2623. NULL );
  2624. }
  2625. } finally {
  2626. //
  2627. // Release the index map handle and index resources.
  2628. //
  2629. NtOfsReleaseMap( IrpContext, &MapHandle );
  2630. ExReleaseFastMutexUnsafe( &Vcb->QuotaControlLock );
  2631. NtfsReleaseScb( IrpContext, QuotaScb );
  2632. }
  2633. return Status;
  2634. }
  2635. VOID
  2636. NtfsRepairQuotaIndex (
  2637. IN PIRP_CONTEXT IrpContext,
  2638. IN PVOID Context
  2639. )
  2640. /*++
  2641. Routine Description:
  2642. This routine is called by a worker thread to fix the quota indexes
  2643. and recalculate all of the quota values.
  2644. Arguments:
  2645. Context - Unused.
  2646. Return Value:
  2647. None
  2648. --*/
  2649. {
  2650. PVCB Vcb = IrpContext->Vcb;
  2651. ULONG State;
  2652. NTSTATUS Status;
  2653. ULONG RetryCount = 0;
  2654. PAGED_CODE();
  2655. UNREFERENCED_PARAMETER( Context );
  2656. try {
  2657. DebugTrace( 0, Dbg, ( "NtfsRepairQuotaIndex: Starting quota repair. Vcb = %lx\n", Vcb ));
  2658. //
  2659. // The volume could've gotten write-protected by now.
  2660. //
  2661. if (NtfsIsVolumeReadOnly( Vcb )) {
  2662. NtfsRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED, NULL, NULL );
  2663. }
  2664. //
  2665. // Acquire the volume exclusive and the quota lock.
  2666. //
  2667. NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
  2668. ExAcquireFastMutexUnsafe( &Vcb->QuotaControlLock );
  2669. Status = STATUS_SUCCESS;
  2670. if (!FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_TRACKING_REQUESTED )) {
  2671. //
  2672. // There is no point in doing any of this work if tracking
  2673. // is not requested.
  2674. //
  2675. Status = STATUS_INVALID_PARAMETER;
  2676. } else if (FlagOn( Vcb->QuotaState, VCB_QUOTA_REPAIR_RUNNING ) == VCB_QUOTA_REPAIR_POSTED) {
  2677. if (FlagOn( Vcb->QuotaFlags,
  2678. (QUOTA_FLAG_OUT_OF_DATE |
  2679. QUOTA_FLAG_CORRUPT |
  2680. QUOTA_FLAG_PENDING_DELETES) ) == QUOTA_FLAG_PENDING_DELETES) {
  2681. //
  2682. // Only the last to phases need to be run.
  2683. //
  2684. ClearFlag( Vcb->QuotaState, VCB_QUOTA_REPAIR_RUNNING );
  2685. SetFlag( Vcb->QuotaState, VCB_QUOTA_RECALC_STARTED );
  2686. State = VCB_QUOTA_RECALC_STARTED;
  2687. //
  2688. // Capture the delete secquence number. If it changes
  2689. // before the actual deletes are done then we have to
  2690. // start over.
  2691. //
  2692. IrpContext->Union.NtfsIoContext = ULongToPtr( Vcb->QuotaDeleteSecquence );
  2693. } else {
  2694. //
  2695. // We are starting just starting. Clear the quota tracking
  2696. // flags and indicate the current state.
  2697. //
  2698. ClearFlag( Vcb->QuotaState, VCB_QUOTA_REPAIR_RUNNING );
  2699. SetFlag( Vcb->QuotaState, VCB_QUOTA_CLEAR_RUNNING | VCB_QUOTA_SAVE_QUOTA_FLAGS);
  2700. ClearFlag( Vcb->QuotaFlags, QUOTA_FLAG_TRACKING_ENABLED );
  2701. SetFlag( Vcb->QuotaFlags, QUOTA_FLAG_OUT_OF_DATE );
  2702. State = VCB_QUOTA_CLEAR_RUNNING;
  2703. }
  2704. //
  2705. // Initialize the File reference to the root index.
  2706. //
  2707. NtfsSetSegmentNumber( &Vcb->QuotaFileReference,
  2708. 0,
  2709. ROOT_FILE_NAME_INDEX_NUMBER );
  2710. Vcb->QuotaFileReference.SequenceNumber = 0;
  2711. NtfsLogEvent( IrpContext,
  2712. NULL,
  2713. IO_FILE_QUOTA_STARTED,
  2714. STATUS_SUCCESS );
  2715. } else {
  2716. State = FlagOn( Vcb->QuotaState, VCB_QUOTA_REPAIR_RUNNING);
  2717. }
  2718. ExReleaseFastMutexUnsafe( &Vcb->QuotaControlLock );
  2719. NtfsReleaseVcb( IrpContext, Vcb );
  2720. if (FlagOn( Vcb->QuotaState, VCB_QUOTA_SAVE_QUOTA_FLAGS )) {
  2721. NtfsSaveQuotaFlagsSafe( IrpContext, Vcb );
  2722. }
  2723. if (!NT_SUCCESS( Status )) {
  2724. NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
  2725. }
  2726. //
  2727. // Determine the current state
  2728. //
  2729. switch (State) {
  2730. case VCB_QUOTA_CLEAR_RUNNING:
  2731. DebugTrace( 4, Dbg, ( "NtfsRepairQuotaIndex: Starting clear per file quota.\n" ));
  2732. //
  2733. // Clear the quota charged field in each file and clear
  2734. // all of the quota control blocks from the fcbs.
  2735. //
  2736. Status = NtfsIterateMft( IrpContext,
  2737. Vcb,
  2738. &Vcb->QuotaFileReference,
  2739. NtfsClearPerFileQuota,
  2740. NULL );
  2741. if (Status == STATUS_END_OF_FILE) {
  2742. Status = STATUS_SUCCESS;
  2743. }
  2744. if (!NT_SUCCESS( Status )) {
  2745. NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
  2746. }
  2747. RestartVerifyQuotaIndex:
  2748. //
  2749. // Update the state to the next phase.
  2750. //
  2751. ExAcquireFastMutexUnsafe( &Vcb->QuotaControlLock );
  2752. ClearFlag( Vcb->QuotaState, VCB_QUOTA_REPAIR_RUNNING );
  2753. SetFlag( Vcb->QuotaState, VCB_QUOTA_INDEX_REPAIR);
  2754. ExReleaseFastMutexUnsafe( &Vcb->QuotaControlLock );
  2755. //
  2756. // NtfsClearAndVerifyQuotaIndex uses the low part of the
  2757. // file reference to store the current owner id.
  2758. // Intialize this to the first user id.
  2759. //
  2760. Vcb->QuotaFileReference.SegmentNumberLowPart = QUOTA_FISRT_USER_ID;
  2761. //
  2762. // Fall through.
  2763. //
  2764. case VCB_QUOTA_INDEX_REPAIR:
  2765. DebugTrace( 4, Dbg, ( "NtfsRepairQuotaIndex: Starting clear quota index.\n" ));
  2766. //
  2767. // Clear the quota used for each owner id.
  2768. //
  2769. NtfsClearAndVerifyQuotaIndex( IrpContext, Vcb );
  2770. //
  2771. // Update the state to the next phase.
  2772. //
  2773. ExAcquireFastMutexUnsafe( &Vcb->QuotaControlLock );
  2774. ClearFlag( Vcb->QuotaState, VCB_QUOTA_REPAIR_RUNNING );
  2775. SetFlag( Vcb->QuotaState, VCB_QUOTA_OWNER_VERIFY);
  2776. ExReleaseFastMutexUnsafe( &Vcb->QuotaControlLock );
  2777. //
  2778. // Note NtfsVerifyOwnerIndex does not use any restart state,
  2779. // since it normally does not preform any transactions.
  2780. //
  2781. //
  2782. // Fall through.
  2783. //
  2784. case VCB_QUOTA_OWNER_VERIFY:
  2785. DebugTrace( 4, Dbg, ( "NtfsRepairQuotaIndex: Starting verify owner index.\n" ));
  2786. //
  2787. // Verify the owner's id points to quota user data.
  2788. //
  2789. Status = NtfsVerifyOwnerIndex( IrpContext, Vcb );
  2790. //
  2791. // Restart the rebuild with the quota index phase.
  2792. //
  2793. if (!NT_SUCCESS( Status ) ) {
  2794. if (RetryCount < 2) {
  2795. RetryCount += 1;
  2796. goto RestartVerifyQuotaIndex;
  2797. } else {
  2798. NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
  2799. }
  2800. }
  2801. //
  2802. // Update the state to the next phase.
  2803. // Start tracking quota and do enforcement as requested.
  2804. //
  2805. NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
  2806. ExAcquireFastMutexUnsafe( &Vcb->QuotaControlLock );
  2807. ClearFlag( Vcb->QuotaState, VCB_QUOTA_REPAIR_RUNNING );
  2808. SetFlag( Vcb->QuotaState, VCB_QUOTA_RECALC_STARTED | VCB_QUOTA_SAVE_QUOTA_FLAGS);
  2809. if (FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_TRACKING_REQUESTED)) {
  2810. SetFlag( Vcb->QuotaFlags, QUOTA_FLAG_TRACKING_ENABLED);
  2811. Status = STATUS_SUCCESS;
  2812. } else {
  2813. //
  2814. // There is no point in doing any of this work if tracking
  2815. // is not requested.
  2816. //
  2817. Status = STATUS_INVALID_PARAMETER;
  2818. }
  2819. //
  2820. // Capture the delete secquence number. If it changes
  2821. // before the actual deletes are done then we have to
  2822. // start over.
  2823. //
  2824. IrpContext->Union.NtfsIoContext = ULongToPtr( Vcb->QuotaDeleteSecquence );
  2825. ExReleaseFastMutexUnsafe( &Vcb->QuotaControlLock );
  2826. NtfsReleaseVcb( IrpContext, Vcb );
  2827. if (FlagOn( Vcb->QuotaState, VCB_QUOTA_SAVE_QUOTA_FLAGS )) {
  2828. NtfsSaveQuotaFlagsSafe( IrpContext, Vcb );
  2829. }
  2830. if (!NT_SUCCESS( Status )) {
  2831. NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
  2832. }
  2833. //
  2834. // Initialize the File reference to the first user file.
  2835. //
  2836. NtfsSetSegmentNumber( &Vcb->QuotaFileReference,
  2837. 0,
  2838. ROOT_FILE_NAME_INDEX_NUMBER );
  2839. Vcb->QuotaFileReference.SequenceNumber = 0;
  2840. //
  2841. // Fall through.
  2842. //
  2843. case VCB_QUOTA_RECALC_STARTED:
  2844. DebugTrace( 4, Dbg, ( "NtfsRepairQuotaIndex: Starting per file quota usage.\n" ));
  2845. //
  2846. // Fix the user files.
  2847. //
  2848. Status = NtfsIterateMft( IrpContext,
  2849. Vcb,
  2850. &Vcb->QuotaFileReference,
  2851. NtfsRepairPerFileQuota,
  2852. NULL );
  2853. if (Status == STATUS_END_OF_FILE) {
  2854. Status = STATUS_SUCCESS;
  2855. }
  2856. //
  2857. // Everything is done indicate we are up to date.
  2858. //
  2859. ExAcquireFastMutexUnsafe( &Vcb->QuotaControlLock );
  2860. ClearFlag( Vcb->QuotaState, VCB_QUOTA_REPAIR_RUNNING );
  2861. SetFlag( Vcb->QuotaState, VCB_QUOTA_SAVE_QUOTA_FLAGS);
  2862. if (FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_PENDING_DELETES )) {
  2863. //
  2864. // Need to actually delete the ids.
  2865. //
  2866. SetFlag( Vcb->QuotaState, VCB_QUOTA_DELETEING_IDS );
  2867. State = VCB_QUOTA_DELETEING_IDS;
  2868. //
  2869. // NtfsDeleteUnsedIds uses the low part of the
  2870. // file reference to store the current owner id.
  2871. // Intialize this to the first user id.
  2872. //
  2873. Vcb->QuotaFileReference.SegmentNumberLowPart = QUOTA_FISRT_USER_ID;
  2874. }
  2875. ClearFlag( Vcb->QuotaFlags, QUOTA_FLAG_OUT_OF_DATE | QUOTA_FLAG_CORRUPT );
  2876. ExReleaseFastMutexUnsafe( &Vcb->QuotaControlLock );
  2877. if (FlagOn( Vcb->QuotaState, VCB_QUOTA_SAVE_QUOTA_FLAGS )) {
  2878. NtfsSaveQuotaFlagsSafe( IrpContext, Vcb );
  2879. }
  2880. if (State != VCB_QUOTA_DELETEING_IDS) {
  2881. break;
  2882. }
  2883. case VCB_QUOTA_DELETEING_IDS:
  2884. //
  2885. // Remove and ids which are marked for deletion.
  2886. //
  2887. NtfsDeleteUnsedIds( IrpContext, Vcb );
  2888. ExAcquireFastMutexUnsafe( &Vcb->QuotaControlLock );
  2889. ClearFlag( Vcb->QuotaState, VCB_QUOTA_REPAIR_RUNNING );
  2890. SetFlag( Vcb->QuotaState, VCB_QUOTA_SAVE_QUOTA_FLAGS);
  2891. ClearFlag( Vcb->QuotaFlags, QUOTA_FLAG_PENDING_DELETES );
  2892. ExReleaseFastMutexUnsafe( &Vcb->QuotaControlLock );
  2893. if (FlagOn( Vcb->QuotaState, VCB_QUOTA_SAVE_QUOTA_FLAGS )) {
  2894. NtfsSaveQuotaFlagsSafe( IrpContext, Vcb );
  2895. }
  2896. break;
  2897. default:
  2898. ASSERT( FALSE );
  2899. Status = STATUS_INVALID_PARAMETER;
  2900. NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
  2901. }
  2902. if (NT_SUCCESS( Status )) {
  2903. NtfsLogEvent( IrpContext,
  2904. NULL,
  2905. IO_FILE_QUOTA_SUCCEEDED,
  2906. Status );
  2907. }
  2908. } except(NtfsExceptionFilter(IrpContext, GetExceptionInformation())) {
  2909. Status = IrpContext->TopLevelIrpContext->ExceptionStatus;
  2910. }
  2911. DebugTrace( 0, Dbg, ( "NtfsRepairQuotaIndex: Quota repair done. Status = %8lx Context = %lx\n", Status, (ULONG) NtfsSegmentNumber( &Vcb->QuotaFileReference )));
  2912. if (!NT_SUCCESS( Status )) {
  2913. //
  2914. // If we will not be called back then clear the running state bits.
  2915. //
  2916. if ((Status != STATUS_CANT_WAIT) && (Status != STATUS_LOG_FILE_FULL)) {
  2917. ExAcquireFastMutexUnsafe( &Vcb->QuotaControlLock );
  2918. ClearFlag( Vcb->QuotaState, VCB_QUOTA_REPAIR_RUNNING );
  2919. ExReleaseFastMutexUnsafe( &Vcb->QuotaControlLock );
  2920. //
  2921. // Only log if we attempted to do work - which is only the case
  2922. // if tracking is on
  2923. //
  2924. if (FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_TRACKING_REQUESTED)) {
  2925. NtfsLogEvent( IrpContext, NULL, IO_FILE_QUOTA_FAILED, Status );
  2926. }
  2927. }
  2928. NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
  2929. }
  2930. return;
  2931. }
  2932. VOID
  2933. NtfsReleaseQuotaControl (
  2934. IN PIRP_CONTEXT IrpContext,
  2935. IN PQUOTA_CONTROL_BLOCK QuotaControl
  2936. )
  2937. /*++
  2938. Routine Description:
  2939. This function is called by transcation control to release the quota control
  2940. block and quota index after a transcation has been completed.
  2941. Arguments:
  2942. QuotaControl - Quota control block to be released.
  2943. Return Value:
  2944. None.
  2945. --*/
  2946. {
  2947. PVCB Vcb = IrpContext->Vcb;
  2948. PAGED_CODE();
  2949. ExReleaseFastMutexUnsafe( QuotaControl->QuotaControlLock );
  2950. NtfsReleaseResource( IrpContext, Vcb->QuotaTableScb );
  2951. NtfsDereferenceQuotaControlBlock( Vcb, &QuotaControl );
  2952. return;
  2953. }
  2954. NTSTATUS
  2955. NtfsRepairPerFileQuota (
  2956. IN PIRP_CONTEXT IrpContext,
  2957. IN PFCB Fcb,
  2958. IN PVOID Context
  2959. )
  2960. /*++
  2961. Routine Description:
  2962. This routine calculate the quota used by a file and update the
  2963. update QuotaCharged field in the standard info as well as QuotaUsed
  2964. in the user's index structure. If the owner id is not set this is
  2965. also updated at this time.
  2966. Arguments:
  2967. Fcb - Fcb for the file to be processed.
  2968. Context - Unsed.
  2969. Return Value:
  2970. STATUS_SUCCESS
  2971. --*/
  2972. {
  2973. LONGLONG Delta;
  2974. INDEX_KEY IndexKey;
  2975. INDEX_ROW IndexRow;
  2976. PREAD_CONTEXT ReadContext = NULL;
  2977. ULONG Count;
  2978. PSID Sid;
  2979. PVCB Vcb = Fcb->Vcb;
  2980. NTSTATUS Status;
  2981. BOOLEAN OwnerDefaulted;
  2982. BOOLEAN SetOwnerId = FALSE;
  2983. BOOLEAN StdInfoGrown = FALSE;
  2984. PAGED_CODE( );
  2985. UNREFERENCED_PARAMETER( Context);
  2986. //
  2987. // Preacquire the security stream and quota index in case the
  2988. // mft has to be grown.
  2989. //
  2990. ASSERT(!NtfsIsExclusiveScb( Vcb->MftScb ) || NtfsIsExclusiveScb( Vcb->QuotaTableScb ));
  2991. NtfsAcquireExclusiveScb( IrpContext, Vcb->QuotaTableScb );
  2992. try {
  2993. //
  2994. // Always clear the owner ID so that the SID is retrived from
  2995. // the security descriptor.
  2996. //
  2997. Fcb->OwnerId = QUOTA_INVALID_ID;
  2998. if (Fcb->QuotaControl != NULL) {
  2999. //
  3000. // If there is a quota control block it is now bougus
  3001. // Free it up a new one will be generated below.
  3002. //
  3003. NtfsDereferenceQuotaControlBlock( Vcb, &Fcb->QuotaControl );
  3004. }
  3005. if (Fcb->OwnerId != QUOTA_INVALID_ID) {
  3006. //
  3007. // Verify the id actually exists in the index.
  3008. //
  3009. Count = 0;
  3010. IndexKey.Key = &Fcb->OwnerId;
  3011. IndexKey.KeyLength = sizeof( Fcb->OwnerId );
  3012. Status = NtOfsReadRecords( IrpContext,
  3013. Vcb->QuotaTableScb,
  3014. &ReadContext,
  3015. &IndexKey,
  3016. NtOfsMatchUlongExact,
  3017. &IndexKey,
  3018. &Count,
  3019. &IndexRow,
  3020. 0,
  3021. NULL );
  3022. if (!NT_SUCCESS( Status )) {
  3023. ASSERT( NT_SUCCESS( Status ));
  3024. //
  3025. // There is no user quota data for this id assign a
  3026. // new one to the file.
  3027. //
  3028. Fcb->OwnerId = QUOTA_INVALID_ID;
  3029. if (Fcb->QuotaControl != NULL) {
  3030. //
  3031. // If there is a quota control block it is now bougus
  3032. // Free it up a new one will be generated below.
  3033. //
  3034. NtfsDereferenceQuotaControlBlock( Vcb, &Fcb->QuotaControl );
  3035. }
  3036. }
  3037. NtOfsFreeReadContext( ReadContext );
  3038. }
  3039. if (Fcb->OwnerId == QUOTA_INVALID_ID) {
  3040. if (Fcb->SharedSecurity == NULL) {
  3041. NtfsLoadSecurityDescriptor ( IrpContext, Fcb );
  3042. }
  3043. ASSERT( Fcb->SharedSecurity != NULL );
  3044. //
  3045. // Extract the security id from the security descriptor.
  3046. //
  3047. Status = RtlGetOwnerSecurityDescriptor( Fcb->SharedSecurity->SecurityDescriptor,
  3048. &Sid,
  3049. &OwnerDefaulted );
  3050. if (!NT_SUCCESS(Status)) {
  3051. NtfsRaiseStatus( IrpContext, Status, NULL, Fcb);
  3052. }
  3053. //
  3054. // Generate a owner id for the Fcb.
  3055. //
  3056. Fcb->OwnerId = NtfsGetOwnerId( IrpContext,
  3057. Sid,
  3058. TRUE,
  3059. NULL );
  3060. SetOwnerId = TRUE;
  3061. SetFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  3062. if (FlagOn( Fcb->FcbState, FCB_STATE_LARGE_STD_INFO )) {
  3063. NtfsUpdateStandardInformation( IrpContext, Fcb );
  3064. } else {
  3065. //
  3066. // Grow the standard information.
  3067. //
  3068. StdInfoGrown = TRUE;
  3069. NtfsGrowStandardInformation( IrpContext, Fcb );
  3070. }
  3071. }
  3072. //
  3073. // Initialize the quota control block.
  3074. //
  3075. if (Fcb->QuotaControl == NULL) {
  3076. Fcb->QuotaControl = NtfsInitializeQuotaControlBlock( Vcb, Fcb->OwnerId );
  3077. }
  3078. NtfsCalculateQuotaAdjustment( IrpContext, Fcb, &Delta );
  3079. ASSERT( NtfsAllowFixups || FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_OUT_OF_DATE ) || (Delta == 0));
  3080. if ((Delta != 0) || FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_PENDING_DELETES )) {
  3081. NtfsUpdateFileQuota( IrpContext, Fcb, &Delta, TRUE, FALSE );
  3082. }
  3083. if (SetOwnerId) {
  3084. //
  3085. // If the owner id was set then commit the transaction now.
  3086. // That way if a raise occurs the OwnerId can be cleared before
  3087. // the function returns. No resources are released.
  3088. //
  3089. NtfsCheckpointCurrentTransaction( IrpContext );
  3090. }
  3091. } finally {
  3092. //
  3093. // Clear any Fcb changes if the operation failed.
  3094. // This is so when a retry occurs the necessary
  3095. // operations are done.
  3096. //
  3097. if (AbnormalTermination()) {
  3098. if (StdInfoGrown) {
  3099. ClearFlag( Fcb->FcbState, FCB_STATE_LARGE_STD_INFO );
  3100. }
  3101. if (SetOwnerId) {
  3102. Fcb->OwnerId = QUOTA_INVALID_ID;
  3103. if (Fcb->QuotaControl != NULL) {
  3104. NtfsDereferenceQuotaControlBlock( Vcb, &Fcb->QuotaControl );
  3105. }
  3106. }
  3107. }
  3108. NtfsReleaseScb( IrpContext, Vcb->QuotaTableScb );
  3109. }
  3110. return STATUS_SUCCESS;
  3111. }
  3112. VOID
  3113. NtfsUpdateFileQuota (
  3114. IN PIRP_CONTEXT IrpContext,
  3115. IN PFCB Fcb,
  3116. IN PLONGLONG Delta,
  3117. IN LOGICAL LogIt,
  3118. IN LOGICAL CheckQuota
  3119. )
  3120. /*++
  3121. Routine Description:
  3122. This routine updates the quota amount for a file and owner by the
  3123. requested amount. If quota is being increated and the CheckQuota is true
  3124. than the new quota amount will be tested for quota violations. If the
  3125. hard limit is exceeded an error is raised. If the LogIt flags is not set
  3126. then changes to the standard information structure are not logged.
  3127. Changes to the user quota data are always logged.
  3128. Arguments:
  3129. Fcb - Fcb whose quota usage is being modified.
  3130. Delta - Supplies the signed amount to change the quota for the file.
  3131. LogIt - Indicates whether we should log this change.
  3132. CheckQuota - Indicates whether we should check for quota violations.
  3133. Return Value:
  3134. None.
  3135. --*/
  3136. {
  3137. ULONGLONG NewQuota;
  3138. LARGE_INTEGER CurrentTime;
  3139. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  3140. PSTANDARD_INFORMATION StandardInformation;
  3141. PQUOTA_USER_DATA UserData;
  3142. INDEX_ROW IndexRow;
  3143. INDEX_KEY IndexKey;
  3144. MAP_HANDLE MapHandle;
  3145. NTSTATUS Status;
  3146. PQUOTA_CONTROL_BLOCK QuotaControl = Fcb->QuotaControl;
  3147. PVCB Vcb = Fcb->Vcb;
  3148. ULONG Length;
  3149. PAGED_CODE();
  3150. DebugTrace( +1, Dbg, ("NtfsUpdateFileQuota: Entered\n") );
  3151. ASSERT( FlagOn( Fcb->FcbState, FCB_STATE_LARGE_STD_INFO ));
  3152. //
  3153. // Readonly volumes shouldn't proceed.
  3154. //
  3155. if (NtfsIsVolumeReadOnly( Vcb )) {
  3156. ASSERT( FALSE );
  3157. NtfsRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED, NULL, NULL );
  3158. }
  3159. //
  3160. // Use a try-finally to cleanup the attribute context.
  3161. //
  3162. try {
  3163. //
  3164. // Initialize the context structure and map handle.
  3165. //
  3166. NtfsInitializeAttributeContext( &AttrContext );
  3167. NtOfsInitializeMapHandle( &MapHandle );
  3168. //
  3169. // Locate the standard information, it must be there.
  3170. //
  3171. if (!NtfsLookupAttributeByCode( IrpContext,
  3172. Fcb,
  3173. &Fcb->FileReference,
  3174. $STANDARD_INFORMATION,
  3175. &AttrContext )) {
  3176. DebugTrace( 0, Dbg, ("Can't find standard information\n") );
  3177. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  3178. }
  3179. StandardInformation = (PSTANDARD_INFORMATION) NtfsAttributeValue( NtfsFoundAttribute( &AttrContext ));
  3180. ASSERT( NtfsFoundAttribute( &AttrContext )->Form.Resident.ValueLength == sizeof( STANDARD_INFORMATION ));
  3181. NewQuota = StandardInformation->QuotaCharged + *Delta;
  3182. SetFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  3183. if ((LONGLONG) NewQuota < 0) {
  3184. //
  3185. // Do not let the quota data go negitive.
  3186. //
  3187. NewQuota = 0;
  3188. }
  3189. if (LogIt) {
  3190. //
  3191. // Call to change the attribute value.
  3192. //
  3193. NtfsChangeAttributeValue( IrpContext,
  3194. Fcb,
  3195. FIELD_OFFSET(STANDARD_INFORMATION, QuotaCharged),
  3196. &NewQuota,
  3197. sizeof( StandardInformation->QuotaCharged),
  3198. FALSE,
  3199. FALSE,
  3200. FALSE,
  3201. FALSE,
  3202. &AttrContext );
  3203. } else {
  3204. //
  3205. // Just update the value in the standard information
  3206. // it will be logged later.
  3207. //
  3208. StandardInformation->QuotaCharged = NewQuota;
  3209. }
  3210. //
  3211. // Update the quota information block.
  3212. //
  3213. NtfsAcquireQuotaControl( IrpContext, QuotaControl );
  3214. IndexKey.KeyLength = sizeof(ULONG);
  3215. IndexKey.Key = &QuotaControl->OwnerId;
  3216. Status = NtOfsFindRecord( IrpContext,
  3217. Vcb->QuotaTableScb,
  3218. &IndexKey,
  3219. &IndexRow,
  3220. &MapHandle,
  3221. &QuotaControl->QuickIndexHint );
  3222. if (!(NT_SUCCESS( Status )) ||
  3223. (IndexRow.DataPart.DataLength < SIZEOF_QUOTA_USER_DATA + FIELD_OFFSET( SID, SubAuthority )) ||
  3224. ((ULONG)SeLengthSid( &(((PQUOTA_USER_DATA)(IndexRow.DataPart.Data))->QuotaSid)) + SIZEOF_QUOTA_USER_DATA !=
  3225. IndexRow.DataPart.DataLength)) {
  3226. //
  3227. // This look up should not fail.
  3228. //
  3229. ASSERT( NT_SUCCESS( Status ));
  3230. ASSERTMSG(( "NTFS: corrupt quotasid\n" ), FALSE);
  3231. NtfsMarkQuotaCorrupt( IrpContext, IrpContext->Vcb );
  3232. leave;
  3233. }
  3234. //
  3235. // Space is allocated for the new record after the quota control
  3236. // block.
  3237. //
  3238. UserData = (PQUOTA_USER_DATA) (QuotaControl + 1);
  3239. ASSERT( IndexRow.DataPart.DataLength >= SIZEOF_QUOTA_USER_DATA );
  3240. RtlCopyMemory( UserData,
  3241. IndexRow.DataPart.Data,
  3242. SIZEOF_QUOTA_USER_DATA );
  3243. ASSERT( (LONGLONG) UserData->QuotaUsed >= -*Delta );
  3244. UserData->QuotaUsed += *Delta;
  3245. if ((LONGLONG) UserData->QuotaUsed < 0) {
  3246. //
  3247. // Do not let the quota data go negative.
  3248. //
  3249. UserData->QuotaUsed = 0;
  3250. }
  3251. //
  3252. // Indicate only the quota used field has been set so far.
  3253. //
  3254. Length = FIELD_OFFSET( QUOTA_USER_DATA, QuotaChangeTime );
  3255. //
  3256. // Only update the quota modified time if this is the last cleanup
  3257. // for the owner.
  3258. //
  3259. if (IrpContext->MajorFunction == IRP_MJ_CLEANUP) {
  3260. KeQuerySystemTime( &CurrentTime );
  3261. UserData->QuotaChangeTime = CurrentTime.QuadPart;
  3262. ASSERT( Length <= FIELD_OFFSET( QUOTA_USER_DATA, QuotaThreshold ));
  3263. Length = FIELD_OFFSET( QUOTA_USER_DATA, QuotaThreshold );
  3264. }
  3265. if (CheckQuota && (*Delta > 0)) {
  3266. if ((UserData->QuotaUsed > UserData->QuotaLimit) &&
  3267. (UserData->QuotaUsed >= (UserData->QuotaLimit + Vcb->BytesPerCluster))) {
  3268. KeQuerySystemTime( &CurrentTime );
  3269. UserData->QuotaChangeTime = CurrentTime.QuadPart;
  3270. if (FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_LOG_LIMIT ) &&
  3271. (!FlagOn( UserData->QuotaFlags, QUOTA_FLAG_LIMIT_REACHED ) ||
  3272. ((ULONGLONG) CurrentTime.QuadPart > UserData->QuotaExceededTime + NtfsMaxQuotaNotifyRate))) {
  3273. if (FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_ENFORCEMENT_ENABLED) &&
  3274. (Vcb->AdministratorId != QuotaControl->OwnerId)) {
  3275. //
  3276. // The operation to mark the user's quota data entry
  3277. // must be posted since any changes to the entry
  3278. // will be undone by the following raise.
  3279. //
  3280. NtfsPostUserLimit( IrpContext, Vcb, QuotaControl );
  3281. NtfsRaiseStatus( IrpContext, STATUS_DISK_FULL, NULL, Fcb );
  3282. } else {
  3283. //
  3284. // Log the fact that quota was exceeded.
  3285. //
  3286. if (NtfsLogEvent( IrpContext,
  3287. IndexRow.DataPart.Data,
  3288. IO_FILE_QUOTA_LIMIT,
  3289. STATUS_SUCCESS )) {
  3290. //
  3291. // The event was successfuly logged. Do not log
  3292. // another for a while.
  3293. //
  3294. DebugTrace( 0, Dbg, ("NtfsUpdateFileQuota: Quota Limit exceeded. OwnerId = %lx\n", QuotaControl->OwnerId));
  3295. UserData->QuotaExceededTime = CurrentTime.QuadPart;
  3296. SetFlag( UserData->QuotaFlags, QUOTA_FLAG_LIMIT_REACHED );
  3297. //
  3298. // Log all of the changed data.
  3299. //
  3300. Length = SIZEOF_QUOTA_USER_DATA;
  3301. }
  3302. }
  3303. } else if (FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_ENFORCEMENT_ENABLED) &&
  3304. (Vcb->AdministratorId != QuotaControl->OwnerId)) {
  3305. NtfsRaiseStatus( IrpContext, STATUS_DISK_FULL, NULL, Fcb );
  3306. }
  3307. }
  3308. if (UserData->QuotaUsed > UserData->QuotaThreshold) {
  3309. KeQuerySystemTime( &CurrentTime );
  3310. UserData->QuotaChangeTime = CurrentTime.QuadPart;
  3311. if ((ULONGLONG) CurrentTime.QuadPart >
  3312. (UserData->QuotaExceededTime + NtfsMaxQuotaNotifyRate)) {
  3313. if (FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_LOG_THRESHOLD)) {
  3314. if (NtfsLogEvent( IrpContext,
  3315. IndexRow.DataPart.Data,
  3316. IO_FILE_QUOTA_THRESHOLD,
  3317. STATUS_SUCCESS )) {
  3318. //
  3319. // The event was successfuly logged. Do not log
  3320. // another for a while.
  3321. //
  3322. DebugTrace( 0, Dbg, ("NtfsUpdateFileQuota: Quota threshold exceeded. OwnerId = %lx\n", QuotaControl->OwnerId));
  3323. UserData->QuotaExceededTime = CurrentTime.QuadPart;
  3324. //
  3325. // Log all of the changed data.
  3326. //
  3327. Length = SIZEOF_QUOTA_USER_DATA;
  3328. }
  3329. }
  3330. //
  3331. // Now is a good time to clear the limit reached flag.
  3332. //
  3333. ClearFlag( UserData->QuotaFlags, QUOTA_FLAG_LIMIT_REACHED );
  3334. }
  3335. }
  3336. }
  3337. //
  3338. // Always clear the deleted flag.
  3339. //
  3340. ClearFlag( UserData->QuotaFlags, QUOTA_FLAG_ID_DELETED );
  3341. //
  3342. // Only log the part that changed.
  3343. //
  3344. IndexRow.KeyPart.Key = &QuotaControl->OwnerId;
  3345. ASSERT( IndexRow.KeyPart.KeyLength == sizeof(ULONG) );
  3346. IndexRow.DataPart.Data = UserData;
  3347. IndexRow.DataPart.DataLength = Length;
  3348. NtOfsUpdateRecord( IrpContext,
  3349. Vcb->QuotaTableScb,
  3350. 1,
  3351. &IndexRow,
  3352. &QuotaControl->QuickIndexHint,
  3353. &MapHandle );
  3354. } finally {
  3355. DebugUnwind( NtfsUpdateFileQuota );
  3356. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  3357. NtOfsReleaseMap( IrpContext, &MapHandle );
  3358. DebugTrace( -1, Dbg, ("NtfsUpdateFileQuota: Exit\n") );
  3359. }
  3360. return;
  3361. }
  3362. VOID
  3363. NtfsSaveQuotaFlags (
  3364. IN PIRP_CONTEXT IrpContext,
  3365. IN PVCB Vcb
  3366. )
  3367. /*++
  3368. Routine Description:
  3369. This routine saves the quota flags in the defaults quota entry.
  3370. Arguments:
  3371. Vcb - Volume control block for volume be query.
  3372. Return Value:
  3373. None.
  3374. --*/
  3375. {
  3376. ULONG OwnerId;
  3377. NTSTATUS Status;
  3378. INDEX_ROW IndexRow;
  3379. INDEX_KEY IndexKey;
  3380. MAP_HANDLE MapHandle;
  3381. QUICK_INDEX_HINT QuickIndexHint;
  3382. QUOTA_USER_DATA NewQuotaData;
  3383. PSCB QuotaScb;
  3384. PAGED_CODE();
  3385. //
  3386. // Acquire quota index exclusive.
  3387. //
  3388. QuotaScb = Vcb->QuotaTableScb;
  3389. NtfsAcquireExclusiveScb( IrpContext, QuotaScb );
  3390. NtOfsInitializeMapHandle( &MapHandle );
  3391. ExAcquireFastMutexUnsafe( &Vcb->QuotaControlLock );
  3392. try {
  3393. //
  3394. // Find the default quota record and update it.
  3395. //
  3396. OwnerId = QUOTA_DEFAULTS_ID;
  3397. IndexKey.KeyLength = sizeof(ULONG);
  3398. IndexKey.Key = &OwnerId;
  3399. RtlZeroMemory( &QuickIndexHint, sizeof( QuickIndexHint ));
  3400. Status = NtOfsFindRecord( IrpContext,
  3401. QuotaScb,
  3402. &IndexKey,
  3403. &IndexRow,
  3404. &MapHandle,
  3405. &QuickIndexHint );
  3406. if (!NT_SUCCESS( Status )) {
  3407. NtfsRaiseStatus( IrpContext, STATUS_QUOTA_LIST_INCONSISTENT, NULL, QuotaScb->Fcb );
  3408. }
  3409. ASSERT( IndexRow.DataPart.DataLength <= sizeof( QUOTA_USER_DATA ));
  3410. RtlCopyMemory( &NewQuotaData,
  3411. IndexRow.DataPart.Data,
  3412. IndexRow.DataPart.DataLength );
  3413. //
  3414. // Update the changed fields in the record.
  3415. //
  3416. NewQuotaData.QuotaFlags = Vcb->QuotaFlags;
  3417. //
  3418. // Note the sizes in the IndexRow stay the same.
  3419. //
  3420. IndexRow.KeyPart.Key = &OwnerId;
  3421. ASSERT( IndexRow.KeyPart.KeyLength == sizeof(ULONG) );
  3422. IndexRow.DataPart.Data = &NewQuotaData;
  3423. NtOfsUpdateRecord( IrpContext,
  3424. QuotaScb,
  3425. 1,
  3426. &IndexRow,
  3427. &QuickIndexHint,
  3428. &MapHandle );
  3429. ClearFlag( Vcb->QuotaState, VCB_QUOTA_SAVE_QUOTA_FLAGS);
  3430. } finally {
  3431. //
  3432. // Release the index map handle and scb.
  3433. //
  3434. ExReleaseFastMutexUnsafe( &Vcb->QuotaControlLock );
  3435. NtOfsReleaseMap( IrpContext, &MapHandle );
  3436. NtfsReleaseScb( IrpContext, QuotaScb );
  3437. }
  3438. return;
  3439. }
  3440. VOID
  3441. NtfsSaveQuotaFlagsSafe (
  3442. IN PIRP_CONTEXT IrpContext,
  3443. IN PVCB Vcb
  3444. )
  3445. /*++
  3446. Routine Description:
  3447. This routine safely saves the quota flags in the defaults quota entry.
  3448. It acquires the volume shared, checks to see if it is ok to write,
  3449. updates the flags and finally commits the transaction.
  3450. Arguments:
  3451. Vcb - Volume control block for volume be query.
  3452. Return Value:
  3453. None.
  3454. --*/
  3455. {
  3456. PAGED_CODE();
  3457. ASSERT( IrpContext->TransactionId == 0);
  3458. NtfsAcquireSharedVcb( IrpContext, Vcb, TRUE );
  3459. try {
  3460. //
  3461. // Acquire the VCB shared and check whether we should
  3462. // continue.
  3463. //
  3464. if (!NtfsIsVcbAvailable( Vcb )) {
  3465. //
  3466. // The volume is going away, bail out.
  3467. //
  3468. NtfsRaiseStatus( IrpContext, STATUS_VOLUME_DISMOUNTED, NULL, NULL );
  3469. }
  3470. //
  3471. // Do the work.
  3472. //
  3473. NtfsSaveQuotaFlags( IrpContext, Vcb );
  3474. //
  3475. // Set the irp context flags to indicate that we are in the
  3476. // fsp and that the irp context should not be deleted when
  3477. // complete request or process exception are called. The in
  3478. // fsp flag keeps us from raising in a few places. These
  3479. // flags must be set inside the loop since they are cleared
  3480. // under certain conditions.
  3481. //
  3482. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DONT_DELETE | IRP_CONTEXT_FLAG_RETAIN_FLAGS );
  3483. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_IN_FSP);
  3484. NtfsCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
  3485. } finally {
  3486. NtfsReleaseVcb( IrpContext, Vcb );
  3487. }
  3488. return;
  3489. }
  3490. VOID
  3491. NtfsUpdateQuotaDefaults (
  3492. IN PIRP_CONTEXT IrpContext,
  3493. IN PVCB Vcb,
  3494. IN PFILE_FS_CONTROL_INFORMATION FileControlInfo
  3495. )
  3496. /*++
  3497. Routine Description:
  3498. This function updates the default settings index entry for quotas.
  3499. Arguments:
  3500. Vcb - Volume control block for volume be query.
  3501. FileQuotaInfo - Optional quota data to update quota index with.
  3502. Return Value:
  3503. None.
  3504. --*/
  3505. {
  3506. ULONG OwnerId;
  3507. NTSTATUS Status;
  3508. INDEX_ROW IndexRow;
  3509. INDEX_KEY IndexKey;
  3510. MAP_HANDLE MapHandle;
  3511. QUOTA_USER_DATA NewQuotaData;
  3512. QUICK_INDEX_HINT QuickIndexHint;
  3513. ULONG Flags;
  3514. PSCB QuotaScb;
  3515. PAGED_CODE();
  3516. //
  3517. // Acquire quota index exclusive.
  3518. //
  3519. QuotaScb = Vcb->QuotaTableScb;
  3520. NtfsAcquireExclusiveScb( IrpContext, QuotaScb );
  3521. NtOfsInitializeMapHandle( &MapHandle );
  3522. ExAcquireFastMutexUnsafe( &Vcb->QuotaControlLock );
  3523. try {
  3524. //
  3525. // Find the default quota record and update it.
  3526. //
  3527. OwnerId = QUOTA_DEFAULTS_ID;
  3528. IndexKey.KeyLength = sizeof( ULONG );
  3529. IndexKey.Key = &OwnerId;
  3530. RtlZeroMemory( &QuickIndexHint, sizeof( QuickIndexHint ));
  3531. Status = NtOfsFindRecord( IrpContext,
  3532. QuotaScb,
  3533. &IndexKey,
  3534. &IndexRow,
  3535. &MapHandle,
  3536. &QuickIndexHint );
  3537. if (!NT_SUCCESS( Status )) {
  3538. NtfsRaiseStatus( IrpContext, STATUS_QUOTA_LIST_INCONSISTENT, NULL, QuotaScb->Fcb );
  3539. }
  3540. ASSERT( IndexRow.DataPart.DataLength == SIZEOF_QUOTA_USER_DATA );
  3541. RtlCopyMemory( &NewQuotaData,
  3542. IndexRow.DataPart.Data,
  3543. IndexRow.DataPart.DataLength );
  3544. //
  3545. // Update the changed fields in the record.
  3546. //
  3547. NewQuotaData.QuotaThreshold = FileControlInfo->DefaultQuotaThreshold.QuadPart;
  3548. NewQuotaData.QuotaLimit = FileControlInfo->DefaultQuotaLimit.QuadPart;
  3549. KeQuerySystemTime( (PLARGE_INTEGER) &NewQuotaData.QuotaChangeTime );
  3550. //
  3551. // Update the quota flags.
  3552. //
  3553. Flags = FlagOn( FileControlInfo->FileSystemControlFlags,
  3554. FILE_VC_QUOTA_MASK );
  3555. switch (Flags) {
  3556. case FILE_VC_QUOTA_NONE:
  3557. //
  3558. // Disable quotas
  3559. //
  3560. ClearFlag( Vcb->QuotaFlags,
  3561. (QUOTA_FLAG_TRACKING_ENABLED |
  3562. QUOTA_FLAG_ENFORCEMENT_ENABLED |
  3563. QUOTA_FLAG_TRACKING_REQUESTED) );
  3564. break;
  3565. case FILE_VC_QUOTA_TRACK:
  3566. //
  3567. // Clear the enforment flags.
  3568. //
  3569. ClearFlag( Vcb->QuotaFlags, QUOTA_FLAG_ENFORCEMENT_ENABLED );
  3570. //
  3571. // Request tracking be enabled.
  3572. //
  3573. SetFlag( Vcb->QuotaFlags, QUOTA_FLAG_TRACKING_REQUESTED );
  3574. break;
  3575. case FILE_VC_QUOTA_ENFORCE:
  3576. //
  3577. // Set the enforcement and tracking enabled flags.
  3578. //
  3579. SetFlag( Vcb->QuotaFlags,
  3580. QUOTA_FLAG_ENFORCEMENT_ENABLED | QUOTA_FLAG_TRACKING_REQUESTED);
  3581. break;
  3582. }
  3583. //
  3584. // If quota tracking is not now
  3585. // enabled then the quota data will need
  3586. // to be rebuild so indicate quotas are out of date.
  3587. // Note the out of date flags always set of quotas
  3588. // are disabled.
  3589. //
  3590. if (!FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_TRACKING_ENABLED )) {
  3591. SetFlag( Vcb->QuotaFlags, QUOTA_FLAG_OUT_OF_DATE );
  3592. }
  3593. //
  3594. // Track the logging flags.
  3595. //
  3596. ClearFlag( Vcb->QuotaFlags,
  3597. QUOTA_FLAG_LOG_THRESHOLD | QUOTA_FLAG_LOG_LIMIT );
  3598. if (FlagOn( FileControlInfo->FileSystemControlFlags, FILE_VC_LOG_QUOTA_THRESHOLD )) {
  3599. SetFlag( Vcb->QuotaFlags, QUOTA_FLAG_LOG_THRESHOLD );
  3600. }
  3601. if (FlagOn( FileControlInfo->FileSystemControlFlags, FILE_VC_LOG_QUOTA_LIMIT )) {
  3602. SetFlag( Vcb->QuotaFlags, QUOTA_FLAG_LOG_LIMIT );
  3603. }
  3604. SetFlag( Vcb->QuotaState, VCB_QUOTA_SAVE_QUOTA_FLAGS );
  3605. //
  3606. // Save the new flags in the new index entry.
  3607. //
  3608. NewQuotaData.QuotaFlags = Vcb->QuotaFlags;
  3609. //
  3610. // Note the sizes in the IndexRow stays the same.
  3611. //
  3612. IndexRow.KeyPart.Key = &OwnerId;
  3613. ASSERT( IndexRow.KeyPart.KeyLength == sizeof( ULONG ));
  3614. IndexRow.DataPart.Data = &NewQuotaData;
  3615. NtOfsUpdateRecord( IrpContext,
  3616. QuotaScb,
  3617. 1,
  3618. &IndexRow,
  3619. &QuickIndexHint,
  3620. &MapHandle );
  3621. ClearFlag( Vcb->QuotaState, VCB_QUOTA_SAVE_QUOTA_FLAGS );
  3622. } finally {
  3623. //
  3624. // Release the index map handle and scb.
  3625. //
  3626. ExReleaseFastMutexUnsafe( &Vcb->QuotaControlLock );
  3627. NtOfsReleaseMap( IrpContext, &MapHandle );
  3628. NtfsReleaseScb( IrpContext, QuotaScb );
  3629. }
  3630. //
  3631. // If the quota tracking has been requested and the quotas need to be
  3632. // repaired then try to repair them now.
  3633. //
  3634. if (FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_TRACKING_REQUESTED ) &&
  3635. FlagOn( Vcb->QuotaFlags,
  3636. QUOTA_FLAG_OUT_OF_DATE | QUOTA_FLAG_CORRUPT | QUOTA_FLAG_PENDING_DELETES )) {
  3637. NtfsPostRepairQuotaIndex( IrpContext, Vcb );
  3638. }
  3639. return;
  3640. }
  3641. NTSTATUS
  3642. NtfsVerifyOwnerIndex (
  3643. IN PIRP_CONTEXT IrpContext,
  3644. IN PVCB Vcb
  3645. )
  3646. /*++
  3647. Routine Description:
  3648. This routine iterates over the owner id index and verifies the pointer
  3649. to the quota user data index.
  3650. Arguments:
  3651. Vcb - Pointer to the volume control block whoes index is to be operated
  3652. on.
  3653. Return Value:
  3654. Returns a status indicating if the owner index was ok.
  3655. --*/
  3656. {
  3657. INDEX_KEY IndexKey;
  3658. INDEX_ROW QuotaRow;
  3659. MAP_HANDLE MapHandle;
  3660. PQUOTA_USER_DATA UserData;
  3661. PINDEX_ROW OwnerRow;
  3662. PVOID RowBuffer;
  3663. NTSTATUS Status;
  3664. NTSTATUS ReturnStatus = STATUS_SUCCESS;
  3665. ULONG Count;
  3666. ULONG i;
  3667. PSCB QuotaScb = Vcb->QuotaTableScb;
  3668. PSCB OwnerIdScb = Vcb->OwnerIdTableScb;
  3669. PINDEX_ROW IndexRow = NULL;
  3670. PREAD_CONTEXT ReadContext = NULL;
  3671. BOOLEAN IndexAcquired = FALSE;
  3672. NtOfsInitializeMapHandle( &MapHandle );
  3673. //
  3674. // Allocate a buffer lager enough for several rows.
  3675. //
  3676. RowBuffer = NtfsAllocatePool( PagedPool, PAGE_SIZE );
  3677. try {
  3678. //
  3679. // Allocate a bunch of index row entries.
  3680. //
  3681. Count = PAGE_SIZE / sizeof( SID );
  3682. IndexRow = NtfsAllocatePool( PagedPool,
  3683. Count * sizeof( INDEX_ROW ));
  3684. //
  3685. // Iterate through the owner id entries. Start with a zero sid.
  3686. //
  3687. RtlZeroMemory( IndexRow, sizeof( SID ));
  3688. IndexKey.KeyLength = sizeof( SID );
  3689. IndexKey.Key = IndexRow;
  3690. Status = NtOfsReadRecords( IrpContext,
  3691. OwnerIdScb,
  3692. &ReadContext,
  3693. &IndexKey,
  3694. NtOfsMatchAll,
  3695. NULL,
  3696. &Count,
  3697. IndexRow,
  3698. PAGE_SIZE,
  3699. RowBuffer );
  3700. while (NT_SUCCESS( Status )) {
  3701. //
  3702. // Acquire the VCB shared and check whether we should
  3703. // continue.
  3704. //
  3705. NtfsAcquireSharedVcb( IrpContext, Vcb, TRUE );
  3706. if (!NtfsIsVcbAvailable( Vcb )) {
  3707. //
  3708. // The volume is going away, bail out.
  3709. //
  3710. NtfsReleaseVcb( IrpContext, Vcb );
  3711. Status = STATUS_VOLUME_DISMOUNTED;
  3712. leave;
  3713. }
  3714. NtfsAcquireExclusiveScb( IrpContext, QuotaScb );
  3715. NtfsAcquireExclusiveScb( IrpContext, OwnerIdScb );
  3716. IndexAcquired = TRUE;
  3717. OwnerRow = IndexRow;
  3718. for (i = 0; i < Count; i += 1, OwnerRow += 1) {
  3719. IndexKey.KeyLength = OwnerRow->DataPart.DataLength;
  3720. IndexKey.Key = OwnerRow->DataPart.Data;
  3721. //
  3722. // Look up the Owner id in the quota index.
  3723. //
  3724. Status = NtOfsFindRecord( IrpContext,
  3725. QuotaScb,
  3726. &IndexKey,
  3727. &QuotaRow,
  3728. &MapHandle,
  3729. NULL );
  3730. ASSERT( NT_SUCCESS( Status ));
  3731. if (!NT_SUCCESS( Status )) {
  3732. //
  3733. // The quota entry is missing just delete this row;
  3734. //
  3735. NtOfsDeleteRecords( IrpContext,
  3736. OwnerIdScb,
  3737. 1,
  3738. &OwnerRow->KeyPart );
  3739. continue;
  3740. }
  3741. UserData = QuotaRow.DataPart.Data;
  3742. ASSERT( (OwnerRow->KeyPart.KeyLength == QuotaRow.DataPart.DataLength - SIZEOF_QUOTA_USER_DATA) &&
  3743. RtlEqualMemory( OwnerRow->KeyPart.Key, &UserData->QuotaSid, OwnerRow->KeyPart.KeyLength ));
  3744. if ((OwnerRow->KeyPart.KeyLength != QuotaRow.DataPart.DataLength - SIZEOF_QUOTA_USER_DATA) ||
  3745. !RtlEqualMemory( OwnerRow->KeyPart.Key,
  3746. &UserData->QuotaSid,
  3747. OwnerRow->KeyPart.KeyLength )) {
  3748. NtOfsReleaseMap( IrpContext, &MapHandle );
  3749. //
  3750. // The Sids do not match delete both of these records.
  3751. // This causes the user whatever their Sid is to get
  3752. // the defaults.
  3753. //
  3754. NtOfsDeleteRecords( IrpContext,
  3755. OwnerIdScb,
  3756. 1,
  3757. &OwnerRow->KeyPart );
  3758. NtOfsDeleteRecords( IrpContext,
  3759. QuotaScb,
  3760. 1,
  3761. &IndexKey );
  3762. ReturnStatus = STATUS_QUOTA_LIST_INCONSISTENT;
  3763. }
  3764. NtOfsReleaseMap( IrpContext, &MapHandle );
  3765. }
  3766. //
  3767. // Release the indexes and commit what has been done so far.
  3768. //
  3769. NtfsReleaseScb( IrpContext, QuotaScb );
  3770. NtfsReleaseScb( IrpContext, OwnerIdScb );
  3771. NtfsReleaseVcb( IrpContext, Vcb );
  3772. IndexAcquired = FALSE;
  3773. //
  3774. // Complete the request which commits the pending
  3775. // transaction if there is one and releases of the
  3776. // acquired resources. The IrpContext will not
  3777. // be deleted because the no delete flag is set.
  3778. //
  3779. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DONT_DELETE | IRP_CONTEXT_FLAG_RETAIN_FLAGS );
  3780. NtfsCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
  3781. //
  3782. // Look up the next set of entries in the quota index.
  3783. //
  3784. Count = PAGE_SIZE / sizeof( SID );
  3785. Status = NtOfsReadRecords( IrpContext,
  3786. OwnerIdScb,
  3787. &ReadContext,
  3788. NULL,
  3789. NtOfsMatchAll,
  3790. NULL,
  3791. &Count,
  3792. IndexRow,
  3793. PAGE_SIZE,
  3794. RowBuffer );
  3795. }
  3796. ASSERT( (Status == STATUS_NO_MORE_MATCHES) || (Status == STATUS_NO_MATCH) );
  3797. } finally {
  3798. NtfsFreePool( RowBuffer );
  3799. NtOfsReleaseMap( IrpContext, &MapHandle );
  3800. if (IndexAcquired) {
  3801. NtfsReleaseScb( IrpContext, QuotaScb );
  3802. NtfsReleaseScb( IrpContext, OwnerIdScb );
  3803. NtfsReleaseVcb( IrpContext, Vcb );
  3804. }
  3805. if (IndexRow != NULL) {
  3806. NtfsFreePool( IndexRow );
  3807. }
  3808. if (ReadContext != NULL) {
  3809. NtOfsFreeReadContext( ReadContext );
  3810. }
  3811. }
  3812. return ReturnStatus;
  3813. }
  3814. RTL_GENERIC_COMPARE_RESULTS
  3815. NtfsQuotaTableCompare (
  3816. IN PRTL_GENERIC_TABLE Table,
  3817. PVOID FirstStruct,
  3818. PVOID SecondStruct
  3819. )
  3820. /*++
  3821. Routine Description:
  3822. This is a generic table support routine to compare two quota table elements
  3823. Arguments:
  3824. Table - Supplies the generic table being queried. Not used.
  3825. FirstStruct - Supplies the first quota table element to compare
  3826. SecondStruct - Supplies the second quota table element to compare
  3827. Return Value:
  3828. RTL_GENERIC_COMPARE_RESULTS - The results of comparing the two
  3829. input structures
  3830. --*/
  3831. {
  3832. ULONG Key1 = ((PQUOTA_CONTROL_BLOCK) FirstStruct)->OwnerId;
  3833. ULONG Key2 = ((PQUOTA_CONTROL_BLOCK) SecondStruct)->OwnerId;
  3834. PAGED_CODE();
  3835. if (Key1 < Key2) {
  3836. return GenericLessThan;
  3837. }
  3838. if (Key1 > Key2) {
  3839. return GenericGreaterThan;
  3840. }
  3841. return GenericEqual;
  3842. UNREFERENCED_PARAMETER( Table );
  3843. }
  3844. PVOID
  3845. NtfsQuotaTableAllocate (
  3846. IN PRTL_GENERIC_TABLE Table,
  3847. CLONG ByteSize
  3848. )
  3849. /*++
  3850. Routine Description:
  3851. This is a generic table support routine to allocate memory
  3852. Arguments:
  3853. Table - Supplies the generic table being used
  3854. ByteSize - Supplies the number of bytes to allocate
  3855. Return Value:
  3856. PVOID - Returns a pointer to the allocated data
  3857. --*/
  3858. {
  3859. PAGED_CODE();
  3860. return NtfsAllocatePoolWithTag( PagedPool, ByteSize, 'QftN' );
  3861. UNREFERENCED_PARAMETER( Table );
  3862. }
  3863. VOID
  3864. NtfsQuotaTableFree (
  3865. IN PRTL_GENERIC_TABLE Table,
  3866. IN PVOID Buffer
  3867. )
  3868. /*++
  3869. Routine Description:
  3870. This is a generic table support routine to free memory
  3871. Arguments:
  3872. Table - Supplies the generic table being used
  3873. Buffer - Supplies pointer to the buffer to be freed
  3874. Return Value:
  3875. None
  3876. --*/
  3877. {
  3878. PAGED_CODE();
  3879. NtfsFreePool( Buffer );
  3880. UNREFERENCED_PARAMETER( Table );
  3881. }
  3882. ULONG
  3883. NtfsGetCallersUserId (
  3884. IN PIRP_CONTEXT IrpContext
  3885. )
  3886. /*++
  3887. Routine Description:
  3888. This routine finds the calling thread's SID and translates it to an
  3889. owner id.
  3890. Arguments:
  3891. Return Value:
  3892. Returns the owner id.
  3893. --*/
  3894. {
  3895. SECURITY_SUBJECT_CONTEXT SubjectContext;
  3896. PACCESS_TOKEN Token;
  3897. PTOKEN_USER UserToken = NULL;
  3898. NTSTATUS Status;
  3899. ULONG OwnerId;
  3900. PAGED_CODE();
  3901. SeCaptureSubjectContext( &SubjectContext );
  3902. try {
  3903. Token = SeQuerySubjectContextToken( &SubjectContext );
  3904. Status = SeQueryInformationToken( Token, TokenOwner, &UserToken );
  3905. if (!NT_SUCCESS( Status )) {
  3906. NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
  3907. }
  3908. OwnerId = NtfsGetOwnerId( IrpContext, UserToken->User.Sid, FALSE, NULL );
  3909. if (OwnerId == QUOTA_INVALID_ID) {
  3910. //
  3911. // If the user does not currently have an id on this
  3912. // system just use the current defaults.
  3913. //
  3914. OwnerId = QUOTA_DEFAULTS_ID;
  3915. }
  3916. } finally {
  3917. if (UserToken != NULL) {
  3918. NtfsFreePool( UserToken);
  3919. }
  3920. SeReleaseSubjectContext( &SubjectContext );
  3921. }
  3922. return OwnerId;
  3923. }