Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4937 lines
157 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. UsnSup.c
  5. Abstract:
  6. This module implements the Usn Journal support routines for NtOfs
  7. Author:
  8. Tom Miller [TomM] 1-Dec-1996
  9. Revision History:
  10. --*/
  11. #include "NtfsProc.h"
  12. #include "lockorder.h"
  13. //
  14. // Define a tag for general pool allocations from this module
  15. //
  16. #undef MODULE_POOL_TAG
  17. #define MODULE_POOL_TAG ('UFtN')
  18. #define GENERATE_CLOSE_RECORD_LIMIT (200)
  19. UNICODE_STRING $Max = CONSTANT_UNICODE_STRING( L"$Max" );
  20. RTL_GENERIC_COMPARE_RESULTS
  21. NtfsUsnTableCompare (
  22. IN PRTL_GENERIC_TABLE Table,
  23. PVOID FirstStruct,
  24. PVOID SecondStruct
  25. );
  26. PVOID
  27. NtfsUsnTableAllocate (
  28. IN PRTL_GENERIC_TABLE Table,
  29. CLONG ByteSize
  30. );
  31. VOID
  32. NtfsUsnTableFree (
  33. IN PRTL_GENERIC_TABLE Table,
  34. PVOID Buffer
  35. );
  36. VOID
  37. NtfsCancelReadUsnJournal (
  38. IN PDEVICE_OBJECT DeviceObject,
  39. IN PIRP Irp
  40. );
  41. VOID
  42. NtfsCancelDeleteUsnJournal (
  43. IN PDEVICE_OBJECT DeviceObject,
  44. IN PIRP Irp
  45. );
  46. NTSTATUS
  47. NtfsDeleteUsnWorker (
  48. IN PIRP_CONTEXT IrpContext,
  49. IN PFCB Fcb,
  50. IN PVOID Context
  51. );
  52. BOOLEAN
  53. NtfsValidateUsnPage (
  54. IN PUSN_RECORD UsnRecord,
  55. IN USN PageUsn,
  56. IN USN *UserStartUsn OPTIONAL,
  57. IN LONGLONG UsnFileSize,
  58. OUT PBOOLEAN ValidUserStartUsn OPTIONAL,
  59. OUT USN *NextUsn
  60. );
  61. //
  62. // VOID
  63. // NtfsAdvanceUsnJournal (
  64. // PVCB Vcb,
  65. // PUSN_JOURNAL_INSTANCE UsnJournalInstance,
  66. // LONGLONG OldSize,
  67. // PBOOLEAN NewMax
  68. // );
  69. //
  70. #define NtfsAdvanceUsnJournal(V,I,SZ,M) { \
  71. ULONG _TempUlong; \
  72. _TempUlong = USN_PAGE_BOUNDARY; \
  73. if (USN_PAGE_BOUNDARY < (V)->BytesPerCluster) { \
  74. _TempUlong = (V)->BytesPerCluster; \
  75. } \
  76. (I)->LowestValidUsn = SZ + _TempUlong - 1; \
  77. ((PLARGE_INTEGER) &(I)->LowestValidUsn)->LowPart &= ~(_TempUlong - 1); \
  78. KeQuerySystemTime( (PLARGE_INTEGER) &(I)->JournalId ); \
  79. *(M) = TRUE; \
  80. }
  81. #ifdef ALLOC_PRAGMA
  82. #pragma alloc_text(PAGE, NtfsDeleteUsnJournal)
  83. #pragma alloc_text(PAGE, NtfsDeleteUsnSpecial)
  84. #pragma alloc_text(PAGE, NtfsDeleteUsnWorker)
  85. #pragma alloc_text(PAGE, NtfsPostUsnChange)
  86. #pragma alloc_text(PAGE, NtfsQueryUsnJournal)
  87. #pragma alloc_text(PAGE, NtfsReadUsnJournal)
  88. #pragma alloc_text(PAGE, NtfsSetupUsnJournal)
  89. #pragma alloc_text(PAGE, NtfsTrimUsnJournal)
  90. #pragma alloc_text(PAGE, NtfsUsnTableCompare)
  91. #pragma alloc_text(PAGE, NtfsUsnTableAllocate)
  92. #pragma alloc_text(PAGE, NtfsUsnTableFree)
  93. #pragma alloc_text(PAGE, NtfsValidateUsnPage)
  94. #pragma alloc_text(PAGE, NtfsWriteUsnJournalChanges)
  95. #endif
  96. NTSTATUS
  97. NtfsReadUsnJournal (
  98. IN PIRP_CONTEXT IrpContext,
  99. IN PIRP Irp,
  100. IN BOOLEAN ProbeInput
  101. )
  102. /*++
  103. Routine Description:
  104. This routine reads records filtered from the Usn journal.
  105. Arguments:
  106. IrpContext - Only optional if we are being called to cancel an async
  107. request.
  108. Irp - request being serviced
  109. ProbeInput - Indicates if we should probe the user input buffer. We also
  110. call this routine internally and don't want to probe in that case.
  111. Return Value:
  112. NTSTATUS - The return status for the operation.
  113. STATUS_PENDING - if asynch Irp queued for later completion.
  114. --*/
  115. {
  116. NTSTATUS Status = STATUS_SUCCESS;
  117. PUSN_RECORD UsnRecord;
  118. USN_RECORD UNALIGNED *OutputUsnRecord;
  119. PVOID UserBuffer;
  120. LONGLONG ViewLength;
  121. ULONG RemainingUserBuffer, BytesUsed;
  122. MAP_HANDLE MapHandle;
  123. READ_USN_JOURNAL_DATA CapturedData;
  124. PSCB UsnJournal;
  125. ULONG JournalAcquired = FALSE;
  126. ULONG AccessingUserBuffer = FALSE;
  127. ULONG DecrementReferenceCount = FALSE;
  128. ULONG VcbAcquired = FALSE;
  129. ULONG Wait;
  130. ULONG OriginalWait;
  131. PIO_STACK_LOCATION IrpSp;
  132. PFILE_OBJECT FileObject;
  133. TYPE_OF_OPEN TypeOfOpen;
  134. PVCB Vcb;
  135. PFCB Fcb;
  136. PSCB Scb;
  137. PCCB Ccb;
  138. PAGED_CODE();
  139. //
  140. // Get the current Irp stack location and save some references.
  141. //
  142. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  143. //
  144. // Extract and decode the file object and check for type of open.
  145. //
  146. FileObject = IrpSp->FileObject;
  147. TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
  148. if ((Ccb == NULL) || !FlagOn( Ccb->AccessFlags, MANAGE_VOLUME_ACCESS)) {
  149. ASSERT( ProbeInput );
  150. NtfsCompleteRequest( IrpContext, Irp, STATUS_ACCESS_DENIED );
  151. return STATUS_ACCESS_DENIED;
  152. }
  153. //
  154. // This request must be able to wait for resources. Set WAIT to TRUE.
  155. //
  156. Wait = TRUE;
  157. if (ProbeInput && !FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT )) {
  158. Wait = FALSE;
  159. }
  160. NtOfsInitializeMapHandle( &MapHandle );
  161. UserBuffer = NtfsMapUserBuffer( Irp );
  162. try {
  163. //
  164. // We always want to be able to wait for resources in this routine but need to be able
  165. // to restore the original wait value in the Irp. After this the original wait will
  166. // have only the wait flag set and then only if it originally wasn't set. In clean
  167. // up we just need to clear the irp context flags using this mask.
  168. //
  169. OriginalWait = (IrpContext->State ^ IRP_CONTEXT_STATE_WAIT) & IRP_CONTEXT_STATE_WAIT;
  170. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
  171. //
  172. // Detect if we fail while accessing the input buffer.
  173. //
  174. try {
  175. AccessingUserBuffer = TRUE;
  176. //
  177. // Probe the input buffer if not in kernel mode and we haven't already done so.
  178. //
  179. if (Irp->RequestorMode != KernelMode) {
  180. if (ProbeInput) {
  181. ProbeForRead( IrpSp->Parameters.FileSystemControl.Type3InputBuffer,
  182. IrpSp->Parameters.FileSystemControl.InputBufferLength,
  183. sizeof( ULONG ));
  184. }
  185. //
  186. // Probe the output buffer if we haven't locked it down yet.
  187. // Capture the JournalData from the unsafe user buffer.
  188. //
  189. if (Irp->MdlAddress == NULL) {
  190. ProbeForWrite( UserBuffer, IrpSp->Parameters.FileSystemControl.OutputBufferLength, sizeof( ULONG ));
  191. }
  192. }
  193. //
  194. // Acquire the Vcb to serialize journal operations with delete journal and dismount.
  195. // Only do this if are being called directly by the user.
  196. //
  197. if (ProbeInput) {
  198. VcbAcquired = NtfsAcquireSharedVcb( IrpContext, Vcb, TRUE );
  199. if (!FlagOn( Vcb->VcbState, VCB_STATE_USN_JOURNAL_ACTIVE )) {
  200. UsnJournal = NULL;
  201. } else {
  202. UsnJournal = Vcb->UsnJournal;
  203. }
  204. } else {
  205. UsnJournal = Vcb->UsnJournal;
  206. }
  207. //
  208. // Make sure no one is deleting the journal.
  209. //
  210. if (FlagOn( Vcb->VcbState, VCB_STATE_USN_DELETE )) {
  211. Status = STATUS_JOURNAL_DELETE_IN_PROGRESS;
  212. leave;
  213. }
  214. //
  215. // Also check that the version is still active.
  216. //
  217. if (UsnJournal == NULL) {
  218. Status = STATUS_JOURNAL_NOT_ACTIVE;
  219. leave;
  220. }
  221. //
  222. // Check that the buffer sizes meet our minimum needs.
  223. //
  224. if (IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof( READ_USN_JOURNAL_DATA )) {
  225. Status = STATUS_INVALID_USER_BUFFER;
  226. leave;
  227. } else {
  228. RtlCopyMemory( &CapturedData,
  229. IrpSp->Parameters.FileSystemControl.Type3InputBuffer,
  230. sizeof( READ_USN_JOURNAL_DATA ));
  231. //
  232. // Check that the user is querying with the correct journal ID.
  233. //
  234. if (CapturedData.UsnJournalID != Vcb->UsnJournalInstance.JournalId) {
  235. Status = STATUS_INVALID_PARAMETER;
  236. leave;
  237. }
  238. }
  239. //
  240. // Check that the output buffer can hold at least one USN.
  241. //
  242. if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof( USN )) {
  243. Status = STATUS_BUFFER_TOO_SMALL;
  244. leave;
  245. }
  246. AccessingUserBuffer = FALSE;
  247. //
  248. // Set up for filling output records
  249. //
  250. RemainingUserBuffer = IrpSp->Parameters.FileSystemControl.OutputBufferLength - sizeof(USN);
  251. OutputUsnRecord = (PUSN_RECORD) Add2Ptr( UserBuffer, sizeof(USN) );
  252. BytesUsed = sizeof(USN);
  253. NtfsAcquireResourceShared( IrpContext, UsnJournal, TRUE );
  254. JournalAcquired = TRUE;
  255. if (VcbAcquired) {
  256. NtfsReleaseVcb( IrpContext, Vcb );
  257. VcbAcquired = FALSE;
  258. }
  259. //
  260. // Verify the volume is mounted.
  261. //
  262. if (FlagOn( UsnJournal->ScbState, SCB_STATE_VOLUME_DISMOUNTED )) {
  263. Status = STATUS_VOLUME_DISMOUNTED;
  264. leave;
  265. }
  266. //
  267. // If 0 was specified as the Usn, then translate that to the first record
  268. // in the Usn journal.
  269. //
  270. if (CapturedData.StartUsn == 0) {
  271. CapturedData.StartUsn = Vcb->FirstValidUsn;
  272. }
  273. //
  274. // Loop here until he gets some data, if that is what the caller wants.
  275. //
  276. do {
  277. //
  278. // Make sure he is within the stream.
  279. //
  280. if (CapturedData.StartUsn < Vcb->FirstValidUsn) {
  281. CapturedData.StartUsn = Vcb->FirstValidUsn;
  282. Status = STATUS_JOURNAL_ENTRY_DELETED;
  283. break;
  284. }
  285. //
  286. // Make sure he is within the stream.
  287. //
  288. if (CapturedData.StartUsn >= UsnJournal->Header.FileSize.QuadPart) {
  289. //
  290. // If he wants to wait for data, then wait here.
  291. //
  292. // If an asynchronous request has
  293. // met its wakeup condition, then this Irp will not be the same as the
  294. // Originating Irp, and we do not want to give him a second chance since
  295. // this could cause us to loop in NtOfsPostNewLength. (Basically the only
  296. // case where this could happen anyway is if he gave us a bogus StartUsn
  297. // which is too high.)
  298. //
  299. if (CapturedData.BytesToWaitFor != 0) {
  300. //
  301. // Make sure the journal doesn't get deleted while
  302. // this Irp is outstanding.
  303. //
  304. InterlockedIncrement( &UsnJournal->CloseCount );
  305. DecrementReferenceCount = TRUE;
  306. //
  307. // If the caller does not want to wait, then just queue his
  308. // Irp to be completed when sufficient bytes come in. If we were
  309. // called for another Irp, then do the same, since we know that
  310. // was another async Irp.
  311. //
  312. if (!Wait || (Irp != IrpContext->OriginatingIrp)) {
  313. //
  314. // Now set up our wait block, capturing the user's parameters.
  315. // Update the Irp to say where the input parameters are now.
  316. //
  317. Status = NtfsHoldIrpForNewLength( IrpContext,
  318. UsnJournal,
  319. Irp,
  320. CapturedData.StartUsn + CapturedData.BytesToWaitFor,
  321. NtfsCancelReadUsnJournal,
  322. &CapturedData,
  323. &IrpSp->Parameters.FileSystemControl.Type3InputBuffer,
  324. sizeof( READ_USN_JOURNAL_DATA ));
  325. //
  326. // If pending then someone else will decrement the reference count.
  327. //
  328. if (Status == STATUS_PENDING) {
  329. DecrementReferenceCount = FALSE;
  330. }
  331. leave;
  332. }
  333. //
  334. // We can safely release the resource. Our reference on the Scb above
  335. // will keep it from being deleted.
  336. //
  337. NtfsReleaseResource( IrpContext, UsnJournal );
  338. JournalAcquired = FALSE;
  339. FsRtlExitFileSystem();
  340. Status = NtOfsWaitForNewLength( UsnJournal,
  341. CapturedData.StartUsn + CapturedData.BytesToWaitFor,
  342. FALSE,
  343. Irp,
  344. NtfsCancelReadUsnJournal,
  345. ((CapturedData.Timeout != 0) ?
  346. (PLARGE_INTEGER) &CapturedData.Timeout :
  347. NULL) );
  348. FsRtlEnterFileSystem();
  349. //
  350. // Get out in the error case.
  351. //
  352. if (Status != STATUS_SUCCESS) {
  353. leave;
  354. }
  355. //
  356. // Acquire the resource to proceed with the request.
  357. //
  358. NtfsAcquireResourceShared( IrpContext, UsnJournal, TRUE );
  359. JournalAcquired = TRUE;
  360. //
  361. // Decrement our reference on the Scb.
  362. //
  363. InterlockedDecrement( &UsnJournal->CloseCount );
  364. DecrementReferenceCount = FALSE;
  365. //
  366. // The journal may have been deleted while we weren't holding
  367. // anything.
  368. //
  369. if (UsnJournal != UsnJournal->Vcb->UsnJournal) {
  370. if (FlagOn( UsnJournal->Vcb->VcbState, VCB_STATE_USN_DELETE )) {
  371. Status = STATUS_JOURNAL_DELETE_IN_PROGRESS;
  372. } else {
  373. Status = STATUS_JOURNAL_NOT_ACTIVE;
  374. }
  375. leave;
  376. }
  377. ASSERT( Status == STATUS_SUCCESS );
  378. //
  379. // **** Get out if we are shutting down the volume.
  380. //
  381. // if (ShuttingDown) {
  382. // Status = STATUS_TOO_LATE;
  383. // leave;
  384. // }
  385. //
  386. // Otherwise, get out. Note, we may have processed a number of records
  387. // that did not match his filter criteria, so we will return success, so
  388. // we can at least give him an updated Usn so we do not have to skip over
  389. // all those records again.
  390. //
  391. } else {
  392. break;
  393. }
  394. }
  395. //
  396. // Loop through as many views as required to fill the output buffer.
  397. //
  398. while ((RemainingUserBuffer != 0) && (CapturedData.StartUsn < UsnJournal->Header.FileSize.QuadPart)) {
  399. LONGLONG BiasedStartUsn;
  400. BOOLEAN ValidUserStartUsn;
  401. USN NextUsn;
  402. ULONG RecordSize;
  403. //
  404. // Calculate length to process in this view.
  405. //
  406. ViewLength = UsnJournal->Header.FileSize.QuadPart - CapturedData.StartUsn;
  407. if (ViewLength > (VACB_MAPPING_GRANULARITY - (ULONG)(CapturedData.StartUsn & (VACB_MAPPING_GRANULARITY - 1)))) {
  408. ViewLength = VACB_MAPPING_GRANULARITY - (ULONG)(CapturedData.StartUsn & (VACB_MAPPING_GRANULARITY - 1));
  409. }
  410. //
  411. // Map the view containing the desired Usn.
  412. //
  413. BiasedStartUsn = CapturedData.StartUsn - Vcb->UsnCacheBias;
  414. NtOfsMapAttribute( IrpContext, UsnJournal, BiasedStartUsn, (ULONG)ViewLength, (PVOID *)&UsnRecord, &MapHandle );
  415. //
  416. // For each page in the view we want to validate the page and return the records
  417. // within the page starting at the user's current usn.
  418. //
  419. do {
  420. //
  421. // Validate the records on the entire page are valid.
  422. //
  423. if (!NtfsValidateUsnPage( (PUSN_RECORD) BlockAlignTruncate( ((ULONG_PTR) UsnRecord), USN_PAGE_SIZE ),
  424. BlockAlignTruncate( CapturedData.StartUsn, USN_PAGE_SIZE ),
  425. &CapturedData.StartUsn,
  426. UsnJournal->Header.FileSize.QuadPart,
  427. &ValidUserStartUsn,
  428. &NextUsn )) {
  429. //
  430. // Simply fail the request with bad data.
  431. //
  432. Status = STATUS_DATA_ERROR;
  433. leave;
  434. }
  435. //
  436. // If the user gave us an incorrect Usn then fail the request.
  437. //
  438. if (!ValidUserStartUsn) {
  439. Status = STATUS_INVALID_PARAMETER;
  440. leave;
  441. }
  442. //
  443. // Now loop to process this page. We know the Usn values which exist on the page and
  444. // there are no checks for valid data needed.
  445. //
  446. while (CapturedData.StartUsn < NextUsn) {
  447. RecordSize = UsnRecord->RecordLength;
  448. //
  449. // Only recognize version 2 records.
  450. //
  451. if (FlagOn( UsnRecord->Reason, CapturedData.ReasonMask ) &&
  452. (!CapturedData.ReturnOnlyOnClose || FlagOn( UsnRecord->Reason, USN_REASON_CLOSE )) &&
  453. (UsnRecord->MajorVersion == 2)) {
  454. if (RecordSize > RemainingUserBuffer) {
  455. RemainingUserBuffer = 0;
  456. break;
  457. }
  458. //
  459. // Copy the data back to the unsafe user buffer.
  460. //
  461. AccessingUserBuffer = TRUE;
  462. //
  463. // Copy directly if the version numbers match.
  464. //
  465. RtlCopyMemory( OutputUsnRecord, UsnRecord, RecordSize );
  466. AccessingUserBuffer = FALSE;
  467. RemainingUserBuffer -= RecordSize;
  468. BytesUsed += RecordSize;
  469. OutputUsnRecord = Add2Ptr( OutputUsnRecord, RecordSize );
  470. }
  471. CapturedData.StartUsn += RecordSize;
  472. UsnRecord = Add2Ptr( UsnRecord, RecordSize );
  473. //
  474. // The view length should already account for record size.
  475. //
  476. ASSERT( ViewLength >= RecordSize );
  477. ViewLength -= RecordSize;
  478. }
  479. //
  480. // Break out if the users buffer is empty.
  481. //
  482. if (RemainingUserBuffer == 0) {
  483. break;
  484. }
  485. //
  486. // We finished the current page. Now move to the next page.
  487. // Figure out how many bytes remain on this page.
  488. // If the next offset is the start of the next page then make sure
  489. // to mask off the page size bits again.
  490. //
  491. RecordSize = BlockOffset( USN_PAGE_SIZE - BlockOffset( (ULONG) NextUsn, USN_PAGE_SIZE ),
  492. USN_PAGE_SIZE );
  493. if (RecordSize > ViewLength) {
  494. RecordSize = (ULONG) ViewLength;
  495. }
  496. UsnRecord = Add2Ptr( UsnRecord, RecordSize );
  497. CapturedData.StartUsn += RecordSize;
  498. ViewLength -= RecordSize;
  499. } while (ViewLength != 0);
  500. NtOfsReleaseMap( IrpContext, &MapHandle );
  501. }
  502. } while ((RemainingUserBuffer != 0) && (BytesUsed == sizeof(USN)));
  503. Irp->IoStatus.Information = BytesUsed;
  504. //
  505. // Set the returned Usn. Move to the start of the next page if
  506. // the next record won't fit on this page.
  507. //
  508. AccessingUserBuffer = TRUE;
  509. *(USN *)UserBuffer = CapturedData.StartUsn;
  510. AccessingUserBuffer = FALSE;
  511. } except(EXCEPTION_EXECUTE_HANDLER) {
  512. Status = GetExceptionCode();
  513. //
  514. // Restore the original wait state back into the IrpContext.
  515. //
  516. ClearFlag( IrpContext->State, OriginalWait );
  517. if (FsRtlIsNtstatusExpected( Status )) {
  518. NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
  519. } else {
  520. ExRaiseStatus( AccessingUserBuffer ? STATUS_INVALID_USER_BUFFER : Status );
  521. }
  522. }
  523. } finally {
  524. NtOfsReleaseMap( IrpContext, &MapHandle );
  525. if (JournalAcquired) {
  526. NtfsReleaseResource( IrpContext, UsnJournal );
  527. }
  528. if (DecrementReferenceCount) {
  529. InterlockedDecrement( &UsnJournal->CloseCount );
  530. }
  531. if (VcbAcquired) {
  532. NtfsReleaseVcb( IrpContext, Vcb );
  533. }
  534. }
  535. //
  536. // Complete the request, unless we've marked this Irp as pending and we plan to complete
  537. // it later. If the Irp is not the originating Irp then it belongs to another request
  538. // and we don't want to complete it.
  539. //
  540. //
  541. // Restore the original wait flag back into the IrpContext.
  542. //
  543. ClearFlag( IrpContext->State, OriginalWait );
  544. ASSERT( (Status == STATUS_PENDING) || (Irp->CancelRoutine == NULL) );
  545. NtfsCompleteRequest( (Irp == IrpContext->OriginatingIrp) ? IrpContext : NULL,
  546. (Status != STATUS_PENDING) ? Irp : NULL,
  547. Status );
  548. return Status;
  549. }
  550. ULONG
  551. NtfsPostUsnChange (
  552. IN PIRP_CONTEXT IrpContext,
  553. IN PVOID ScbOrFcb,
  554. IN ULONG Reason
  555. )
  556. /*++
  557. Routine Description:
  558. This routine is called to post a set of changes to a file. A change is
  559. only posted if at least one reason in the Reason mask is not already set
  560. in either the Fcb or the IrpContext or if we are changing the source info
  561. reasons in the Fcb.
  562. Arguments:
  563. ScbOrFcb - Supplies the file for which a change is being posted. If reason contains
  564. USN_REASON_DATA_xxx reasons, then it must be an Scb, because we transform
  565. the code for named streams and do other special handling.
  566. Reason - Supplies a mask of reasons for which a change is being posted.
  567. Return Value:
  568. Nonzero if changes are actually posted from this or a previous call
  569. --*/
  570. {
  571. PLCB Lcb;
  572. PFCB_USN_RECORD FcbUsnRecord;
  573. BOOLEAN Found;
  574. PFCB Fcb;
  575. PSCB Scb = NULL;
  576. ULONG NewReasons;
  577. ULONG RemovedSourceInfo;
  578. PUSN_FCB ThisUsn;
  579. BOOLEAN LockedFcb = FALSE;
  580. BOOLEAN AcquiredFcb = FALSE;
  581. //
  582. // Assume we got an Fcb.
  583. //
  584. Fcb = (PFCB)ScbOrFcb;
  585. ASSERT( !(Reason & (USN_REASON_DATA_OVERWRITE | USN_REASON_DATA_EXTEND | USN_REASON_DATA_TRUNCATION)) ||
  586. (NTFS_NTC_FCB != Fcb->NodeTypeCode) );
  587. //
  588. // Switch if we got an Scb
  589. //
  590. if (Fcb->NodeTypeCode != NTFS_NTC_FCB) {
  591. ASSERT_SCB(Fcb);
  592. Scb = (PSCB)ScbOrFcb;
  593. Fcb = Scb->Fcb;
  594. }
  595. //
  596. // We better be holding some resource.
  597. //
  598. ASSERT( !IsListEmpty( &IrpContext->ExclusiveFcbList ) ||
  599. ((Fcb->PagingIoResource != NULL) && NtfsIsSharedFcbPagingIo( Fcb )) ||
  600. NtfsIsSharedFcb( Fcb ) );
  601. //
  602. // If there is a Usn Journal and its not a system file setup the memory structures
  603. // to hold the usn reasons
  604. //
  605. ThisUsn = &IrpContext->Usn;
  606. if (FlagOn( Fcb->Vcb->VcbState, VCB_STATE_USN_JOURNAL_ACTIVE ) &&
  607. !FlagOn( Fcb->FcbState, FCB_STATE_SYSTEM_FILE )) {
  608. //
  609. // First see if we have a Usn record structure already for this file. We might need
  610. // the whole usn record or simply the name. If this is the RENAME_NEW_NAME record
  611. // then find then name again as well.
  612. //
  613. if ((Fcb->FcbUsnRecord == NULL) ||
  614. !FlagOn( Fcb->FcbState, FCB_STATE_VALID_USN_NAME ) ||
  615. FlagOn( Reason, USN_REASON_RENAME_NEW_NAME )) {
  616. ULONG SizeToAllocate;
  617. ATTRIBUTE_ENUMERATION_CONTEXT AttributeContext;
  618. PFILE_NAME FileName = NULL;
  619. NtfsInitializeAttributeContext( &AttributeContext );
  620. try {
  621. //
  622. // First we have to find the designated file name. If we are lucky
  623. // it is in an Lcb. We cannot do this in the case of rename, because
  624. // the in-memory stuff is not fixed up yet.
  625. //
  626. if (!FlagOn( Reason, USN_REASON_RENAME_NEW_NAME )) {
  627. Lcb = (PLCB)CONTAINING_RECORD( Fcb->LcbQueue.Flink, LCB, FcbLinks );
  628. while (&Lcb->FcbLinks.Flink != &Fcb->LcbQueue.Flink) {
  629. //
  630. // If this is the designated file name, then we can get out pointing
  631. // to the FILE_NAME in the Lcb.
  632. //
  633. if (FlagOn( Lcb->LcbState, LCB_STATE_DESIGNATED_LINK )) {
  634. FileName = (PFILE_NAME)&Lcb->ParentDirectory;
  635. break;
  636. }
  637. //
  638. // Advance to next Lcb.
  639. //
  640. Lcb = (PLCB)CONTAINING_RECORD( Lcb->FcbLinks.Flink, LCB, FcbLinks );
  641. }
  642. }
  643. //
  644. // If we did not find the file name the easy way, then we have to go
  645. // get it.
  646. //
  647. if (FileName == NULL) {
  648. //
  649. // Acquire some synchronization against the filerecord
  650. //
  651. NtfsAcquireResourceShared( IrpContext, Fcb, TRUE );
  652. AcquiredFcb = TRUE;
  653. //
  654. // Now scan for the filename attribute we need.
  655. //
  656. Found = NtfsLookupAttributeByCode( IrpContext,
  657. Fcb,
  658. &Fcb->FileReference,
  659. $FILE_NAME,
  660. &AttributeContext );
  661. while (Found) {
  662. FileName = (PFILE_NAME)NtfsAttributeValue( NtfsFoundAttribute(&AttributeContext) );
  663. if (!FlagOn(FileName->Flags, FILE_NAME_DOS) || FlagOn(FileName->Flags, FILE_NAME_NTFS)) {
  664. break;
  665. }
  666. Found = NtfsLookupNextAttributeByCode( IrpContext,
  667. Fcb,
  668. $FILE_NAME,
  669. &AttributeContext );
  670. }
  671. //
  672. // If there is no file name, raise corrupt!
  673. //
  674. if (FileName == NULL) {
  675. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  676. }
  677. }
  678. //
  679. // Lock the Fcb so the record can't go away.
  680. //
  681. NtfsLockFcb( IrpContext, Fcb );
  682. LockedFcb = TRUE;
  683. //
  684. // Now test for the need for a new record and construct one
  685. // if necc. Prev. test was unsafe for checking the Fcb->FcbUsnRecord
  686. //
  687. if ((Fcb->FcbUsnRecord == NULL) ||
  688. !FlagOn( Fcb->FcbState, FCB_STATE_VALID_USN_NAME ) ||
  689. FlagOn( Reason, USN_REASON_RENAME_NEW_NAME )) {
  690. //
  691. // Calculate the size required for the record and allocate a new record.
  692. //
  693. SizeToAllocate = sizeof( FCB_USN_RECORD ) + (FileName->FileNameLength * sizeof(WCHAR));
  694. FcbUsnRecord = NtfsAllocatePool( PagedPool, SizeToAllocate );
  695. //
  696. // Zero and initialize the new usn record.
  697. //
  698. RtlZeroMemory( FcbUsnRecord, SizeToAllocate );
  699. FcbUsnRecord->NodeTypeCode = NTFS_NTC_USN_RECORD;
  700. FcbUsnRecord->NodeByteSize = (USHORT)QuadAlign(FIELD_OFFSET( FCB_USN_RECORD, UsnRecord.FileName ) +
  701. (FileName->FileNameLength * sizeof(WCHAR)));
  702. FcbUsnRecord->Fcb = Fcb;
  703. FcbUsnRecord->UsnRecord.RecordLength = FcbUsnRecord->NodeByteSize -
  704. FIELD_OFFSET( FCB_USN_RECORD, UsnRecord );
  705. FcbUsnRecord->UsnRecord.MajorVersion = 2;
  706. FcbUsnRecord->UsnRecord.FileReferenceNumber = *(PULONGLONG)&Fcb->FileReference;
  707. FcbUsnRecord->UsnRecord.ParentFileReferenceNumber = *(PULONGLONG)&FileName->ParentDirectory;
  708. FcbUsnRecord->UsnRecord.SecurityId = Fcb->SecurityId;
  709. FcbUsnRecord->UsnRecord.FileNameLength = FileName->FileNameLength * 2;
  710. FcbUsnRecord->UsnRecord.FileNameOffset = FIELD_OFFSET( USN_RECORD, FileName );
  711. RtlCopyMemory( FcbUsnRecord->UsnRecord.FileName,
  712. FileName->FileName,
  713. FileName->FileNameLength * 2 );
  714. //
  715. // If the record is there then copy the existing reasons and source info.
  716. //
  717. if (Fcb->FcbUsnRecord != NULL) {
  718. FcbUsnRecord->UsnRecord.Reason = Fcb->FcbUsnRecord->UsnRecord.Reason;
  719. FcbUsnRecord->UsnRecord.SourceInfo = Fcb->FcbUsnRecord->UsnRecord.SourceInfo;
  720. //
  721. // Deallocate the existing block if still there.
  722. //
  723. NtfsLockFcb( IrpContext, Fcb->Vcb->UsnJournal->Fcb );
  724. //
  725. // Put the new block into the modified list if the current one is
  726. // already there.
  727. //
  728. if (Fcb->FcbUsnRecord->ModifiedOpenFilesLinks.Flink != NULL) {
  729. InsertTailList( &Fcb->FcbUsnRecord->ModifiedOpenFilesLinks,
  730. &FcbUsnRecord->ModifiedOpenFilesLinks );
  731. RemoveEntryList( &Fcb->FcbUsnRecord->ModifiedOpenFilesLinks );
  732. if (Fcb->FcbUsnRecord->TimeOutLinks.Flink != NULL) {
  733. InsertTailList( &Fcb->FcbUsnRecord->TimeOutLinks,
  734. &FcbUsnRecord->TimeOutLinks );
  735. RemoveEntryList( &Fcb->FcbUsnRecord->TimeOutLinks );
  736. }
  737. }
  738. NtfsFreePool( Fcb->FcbUsnRecord );
  739. Fcb->FcbUsnRecord = FcbUsnRecord;
  740. NtfsUnlockFcb( IrpContext, Fcb->Vcb->UsnJournal->Fcb );
  741. //
  742. // Otherwise this is a new usn structure.
  743. //
  744. } else {
  745. Fcb->FcbUsnRecord = FcbUsnRecord;
  746. }
  747. } else {
  748. //
  749. // We are going to reuse the current fcb record in this path.
  750. // This can happen in races between the write path which has only paged sharing
  751. // and the close record path which has only main exclusive. In this
  752. // case the only synchronization we have is the fcb->mutex
  753. // The old usnrecord should be identical to the current one we would have constructed
  754. //
  755. ASSERT( FileName->FileNameLength * 2 == Fcb->FcbUsnRecord->UsnRecord.FileNameLength );
  756. ASSERT( RtlEqualMemory( FileName->FileName, Fcb->FcbUsnRecord->UsnRecord.FileName, Fcb->FcbUsnRecord->UsnRecord.FileNameLength ) );
  757. }
  758. //
  759. // Set the flag indicating that the Usn name is valid.
  760. //
  761. SetFlag( Fcb->FcbState, FCB_STATE_VALID_USN_NAME );
  762. } finally {
  763. if (LockedFcb) {
  764. NtfsUnlockFcb( IrpContext, Fcb );
  765. }
  766. if (AcquiredFcb) {
  767. NtfsReleaseResource( IrpContext, Fcb );
  768. }
  769. NtfsCleanupAttributeContext( IrpContext, &AttributeContext );
  770. }
  771. }
  772. }
  773. //
  774. // If we have memory structures for the usn reasons fill in the new reasons
  775. // Note: this means that journal may not be active at this point. We will always
  776. // accumulate reasons once we have started
  777. //
  778. if (Fcb->FcbUsnRecord != NULL) {
  779. //
  780. // Scan the list to see if we already have an entry for this Fcb. If there are
  781. // no entries then use the position in the IrpContext, otherwise allocate a USN_FCB
  782. // and chain this into the IrpContext. Typical case for this is rename.
  783. //
  784. do {
  785. if (ThisUsn->CurrentUsnFcb == Fcb) { break; }
  786. //
  787. // Check if we are at the last entry then we want to use the entry in the
  788. // IrpContext.
  789. //
  790. if (ThisUsn->CurrentUsnFcb == NULL) {
  791. RtlZeroMemory( &ThisUsn->CurrentUsnFcb,
  792. sizeof( USN_FCB ) - FIELD_OFFSET( USN_FCB, CurrentUsnFcb ));
  793. ThisUsn->CurrentUsnFcb = Fcb;
  794. break;
  795. }
  796. if (ThisUsn->NextUsnFcb == NULL) {
  797. //
  798. // Allocate a new entry.
  799. //
  800. ThisUsn->NextUsnFcb = NtfsAllocatePool( PagedPool, sizeof( USN_FCB ));
  801. ThisUsn = ThisUsn->NextUsnFcb;
  802. RtlZeroMemory( ThisUsn, sizeof( USN_FCB ));
  803. ThisUsn->CurrentUsnFcb = Fcb;
  804. break;
  805. }
  806. ThisUsn = ThisUsn->NextUsnFcb;
  807. } while (TRUE);
  808. //
  809. // If the Reason is one of the data stream reasons, and this is the named data
  810. // steam, then change the code.
  811. //
  812. ASSERT(USN_REASON_NAMED_DATA_OVERWRITE == (USN_REASON_DATA_OVERWRITE << 4));
  813. ASSERT(USN_REASON_NAMED_DATA_EXTEND == (USN_REASON_DATA_EXTEND << 4));
  814. ASSERT(USN_REASON_NAMED_DATA_TRUNCATION == (USN_REASON_DATA_TRUNCATION << 4));
  815. if ((Reason & (USN_REASON_DATA_OVERWRITE | USN_REASON_DATA_EXTEND | USN_REASON_DATA_TRUNCATION)) &&
  816. (Scb->AttributeName.Length != 0)) {
  817. //
  818. // If any flag other than these three are set already, the shift will make
  819. // them look like other flags. For instance, USN_REASON_NAMED_DATA_EXTEND
  820. // will become USN_REASON_FILE_DELETE, which will cause a number of problems.
  821. //
  822. ASSERT(!FlagOn( Reason, ~(USN_REASON_DATA_OVERWRITE | USN_REASON_DATA_EXTEND | USN_REASON_DATA_TRUNCATION) ));
  823. Reason <<= 4;
  824. }
  825. //
  826. // If there are no new reasons, then we can ignore this change.
  827. //
  828. // We will generate a new record if the SourceInfo indicates some
  829. // change to the source info in the record.
  830. //
  831. NtfsLockFcb( IrpContext, Fcb );
  832. //
  833. // The rename flags are the only ones that do not accumulate until final close, since
  834. // we write records designating old and new names. So if we are writing one flag
  835. // we must clear the other.
  836. //
  837. if (FlagOn(Reason, USN_REASON_RENAME_OLD_NAME | USN_REASON_RENAME_NEW_NAME)) {
  838. ClearFlag( ThisUsn->NewReasons,
  839. (Reason ^ (USN_REASON_RENAME_OLD_NAME | USN_REASON_RENAME_NEW_NAME)) );
  840. ClearFlag( Fcb->FcbUsnRecord->UsnRecord.Reason,
  841. (Reason ^ (USN_REASON_RENAME_OLD_NAME | USN_REASON_RENAME_NEW_NAME)) );
  842. }
  843. //
  844. // Check if the reason is a new reason.
  845. //
  846. NewReasons = FlagOn( ~(Fcb->FcbUsnRecord->UsnRecord.Reason | ThisUsn->NewReasons), Reason );
  847. if (NewReasons != 0) {
  848. //
  849. // Check if we will remove a bit from the source info.
  850. //
  851. if ((Fcb->FcbUsnRecord->UsnRecord.SourceInfo != 0) &&
  852. (Fcb->FcbUsnRecord->UsnRecord.Reason != 0) &&
  853. (Reason != USN_REASON_CLOSE)) {
  854. RemovedSourceInfo = FlagOn( Fcb->FcbUsnRecord->UsnRecord.SourceInfo,
  855. ~(IrpContext->SourceInfo | ThisUsn->RemovedSourceInfo) );
  856. if (RemovedSourceInfo != 0) {
  857. SetFlag( ThisUsn->RemovedSourceInfo, RemovedSourceInfo );
  858. }
  859. }
  860. //
  861. // Post the new reasons to the IrpContext.
  862. //
  863. ThisUsn->CurrentUsnFcb = Fcb;
  864. SetFlag( ThisUsn->NewReasons, NewReasons );
  865. SetFlag( ThisUsn->UsnFcbFlags, USN_FCB_FLAG_NEW_REASON );
  866. //
  867. // Check if there is a change only to the source info.
  868. // We look to see if we would remove a bit from the
  869. // source info only if there has been at least one
  870. // usn record already.
  871. //
  872. } else if ((Fcb->FcbUsnRecord->UsnRecord.SourceInfo != 0) &&
  873. (Fcb->FcbUsnRecord->UsnRecord.Reason != 0) &&
  874. (Reason != USN_REASON_CLOSE)) {
  875. //
  876. // Remember the bit being removed.
  877. //
  878. RemovedSourceInfo = FlagOn( Fcb->FcbUsnRecord->UsnRecord.SourceInfo,
  879. ~(IrpContext->SourceInfo | ThisUsn->RemovedSourceInfo) );
  880. if (RemovedSourceInfo != 0) {
  881. SetFlag( ThisUsn->RemovedSourceInfo, RemovedSourceInfo );
  882. ThisUsn->CurrentUsnFcb = Fcb;
  883. SetFlag( ThisUsn->UsnFcbFlags, USN_FCB_FLAG_NEW_REASON );
  884. } else {
  885. Reason = 0;
  886. }
  887. //
  888. // If we did not apply the changes, then make sure we do no more special processing
  889. // below.
  890. //
  891. } else {
  892. Reason = 0;
  893. }
  894. NtfsUnlockFcb( IrpContext, Fcb );
  895. //
  896. // For data overwrites it is necessary to actually write the Usn journal now, in
  897. // case we crash before the request is completed, yet the data makes it out. Also
  898. // we need to capture the Lsn to flush to if the data is getting flushed.
  899. //
  900. // We don't need to make this call if we are doing a rename. Rename will rewrite previous
  901. // records.
  902. //
  903. if ((IrpContext->MajorFunction != IRP_MJ_SET_INFORMATION) &&
  904. FlagOn( Reason, USN_REASON_DATA_OVERWRITE | USN_REASON_NAMED_DATA_OVERWRITE )) {
  905. LSN UpdateLsn;
  906. //
  907. // For now assume we are not already a transaction, since we will be doing a
  908. // checkpoint. (If this ASSERT ever fires, verify that it is ok to checkpoint
  909. // the transaction in that case and fix the ASSERT!)
  910. //
  911. ASSERT(IrpContext->TransactionId == 0);
  912. //
  913. // Now write the journal, checkpoint the transaction, and free the UsnJournal to
  914. // reduce contention. Get rid of any pinned Mft records, because WriteUsnJournal is going
  915. // to acquire the Scb resource.
  916. //
  917. NtfsPurgeFileRecordCache( IrpContext );
  918. NtfsWriteUsnJournalChanges( IrpContext );
  919. NtfsCheckpointCurrentTransaction( IrpContext );
  920. //
  921. // Capture the Lsn to flush to *in the first thread to set one of the above bits*,
  922. // before letting any data hit the disk. Synchronize it with the Fcb lock.
  923. //
  924. UpdateLsn = LfsQueryLastLsn( Fcb->Vcb->LogHandle );
  925. NtfsLockFcb( IrpContext, Fcb );
  926. Fcb->UpdateLsn = UpdateLsn;
  927. NtfsUnlockFcb( IrpContext, Fcb );
  928. }
  929. }
  930. return ThisUsn->NewReasons;
  931. }
  932. VOID
  933. NtfsWriteUsnJournalChanges (
  934. PIRP_CONTEXT IrpContext
  935. )
  936. /*++
  937. Routine Description:
  938. This routine is called to write a set of posted changes from the IrpContext
  939. to the UsnJournal, if they have not already been posted.
  940. Arguments:
  941. Return Value:
  942. None.
  943. --*/
  944. {
  945. ATTRIBUTE_ENUMERATION_CONTEXT AttributeContext;
  946. PFCB Fcb;
  947. PVCB Vcb;
  948. PSCB UsnJournal;
  949. PUSN_FCB ThisUsn;
  950. ULONG PreserveWaitState;
  951. BOOLEAN WroteUsnRecord = FALSE;
  952. BOOLEAN ReleaseFcbs = FALSE;
  953. BOOLEAN CleanupContext = FALSE;
  954. ThisUsn = &IrpContext->Usn;
  955. do {
  956. //
  957. // Is there an Fcb with usn reasons in the current irpcontext usn_fcb structures ?
  958. // Also are there any new reasons to report for this fcb.
  959. //
  960. if ((ThisUsn->CurrentUsnFcb != NULL) &&
  961. FlagOn( ThisUsn->UsnFcbFlags, USN_FCB_FLAG_NEW_REASON )) {
  962. Fcb = ThisUsn->CurrentUsnFcb;
  963. Vcb = Fcb->Vcb;
  964. UsnJournal = Vcb->UsnJournal;
  965. //
  966. // Remember that we wrote a record.
  967. //
  968. WroteUsnRecord = TRUE;
  969. //
  970. // We better be waitable.
  971. //
  972. PreserveWaitState = (IrpContext->State ^ IRP_CONTEXT_STATE_WAIT) & IRP_CONTEXT_STATE_WAIT;
  973. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
  974. if (FlagOn( Vcb->VcbState, VCB_STATE_USN_JOURNAL_ACTIVE )) {
  975. //
  976. // Acquire the Usn journal and lock the Fcb fields.
  977. //
  978. NtfsAcquireExclusiveScb( IrpContext, Vcb->MftScb );
  979. NtfsAcquireExclusiveScb( IrpContext, UsnJournal );
  980. ReleaseFcbs = TRUE;
  981. }
  982. try {
  983. USN Usn;
  984. ULONG BytesLeftInPage;
  985. //
  986. // Make sure the changes have not already been logged. We're
  987. // looking for new reasons or a change to the source info.
  988. //
  989. NtfsLockFcb( IrpContext, Fcb );
  990. //
  991. // This is the tricky synchronization case. Assumption is
  992. // that if name goes invalid we have both resources exclusive and any writes will
  993. // be preceded by a post which will remove the invalid record
  994. // This occurs when we remove a link and generate one record under the old name
  995. // with the flag set as invalid
  996. //
  997. ASSERT( !FlagOn( Vcb->VcbState, VCB_STATE_USN_JOURNAL_ACTIVE ) ||
  998. FlagOn( Fcb->FcbState, FCB_STATE_VALID_USN_NAME ) ||
  999. (NtfsIsExclusiveFcb( Fcb ) &&
  1000. ((Fcb->PagingIoResource == NULL) || (NtfsIsExclusiveFcbPagingIo( Fcb )))) );
  1001. //
  1002. // Initialize the Fcb source info if this is our first record.
  1003. //
  1004. if (Fcb->FcbUsnRecord->UsnRecord.Reason == 0) {
  1005. Fcb->FcbUsnRecord->UsnRecord.SourceInfo = IrpContext->SourceInfo;
  1006. }
  1007. //
  1008. // Accumulate all reasons and store in the Fcb before unlocking the Fcb.
  1009. //
  1010. SetFlag( Fcb->FcbUsnRecord->UsnRecord.Reason, ThisUsn->NewReasons );
  1011. //
  1012. // Now clear the source info flags not supported by this
  1013. // caller.
  1014. //
  1015. ClearFlag( Fcb->FcbUsnRecord->UsnRecord.SourceInfo, ThisUsn->RemovedSourceInfo );
  1016. //
  1017. // Unlock Fcb now so we do not deadlock if we do a checkpoint.
  1018. //
  1019. NtfsUnlockFcb( IrpContext, Fcb );
  1020. //
  1021. // Only actually persist to disk if the journal is active
  1022. //
  1023. if (FlagOn( Vcb->VcbState, VCB_STATE_USN_JOURNAL_ACTIVE )) {
  1024. ASSERT( UsnJournal != NULL );
  1025. //
  1026. // Initialize the context structure if we are doing a close.
  1027. //
  1028. if (FlagOn( Fcb->FcbUsnRecord->UsnRecord.Reason, USN_REASON_CLOSE )) {
  1029. NtfsInitializeAttributeContext( &AttributeContext );
  1030. CleanupContext = TRUE;
  1031. }
  1032. Usn = UsnJournal->Header.FileSize.QuadPart;
  1033. BytesLeftInPage = USN_PAGE_SIZE - ((ULONG)Usn & (USN_PAGE_SIZE - 1));
  1034. //
  1035. // If there is not enough room left in this page for the
  1036. // current Usn Record, then advance to the next page boundary
  1037. // by writing 0's (these pages not zero-initialized( and update the Usn.
  1038. //
  1039. if (BytesLeftInPage < Fcb->FcbUsnRecord->UsnRecord.RecordLength) {
  1040. ASSERT( Fcb->FcbUsnRecord->UsnRecord.RecordLength <= USN_PAGE_SIZE );
  1041. NtOfsPutData( IrpContext, UsnJournal, -1, BytesLeftInPage, NULL );
  1042. Usn += BytesLeftInPage;
  1043. }
  1044. Fcb->FcbUsnRecord->UsnRecord.Usn = Usn;
  1045. //
  1046. // Build the FileAttributes from the Fcb.
  1047. //
  1048. Fcb->FcbUsnRecord->UsnRecord.FileAttributes = Fcb->Info.FileAttributes & FILE_ATTRIBUTE_VALID_FLAGS;
  1049. //
  1050. // We have to generate the DIRECTORY attribute.
  1051. //
  1052. if (IsDirectory( &Fcb->Info ) || IsViewIndex( &Fcb->Info )) {
  1053. SetFlag( Fcb->FcbUsnRecord->UsnRecord.FileAttributes, FILE_ATTRIBUTE_DIRECTORY );
  1054. }
  1055. //
  1056. // If there are no flags set then explicitly set the NORMAL flag.
  1057. //
  1058. if (Fcb->FcbUsnRecord->UsnRecord.FileAttributes == 0) {
  1059. Fcb->FcbUsnRecord->UsnRecord.FileAttributes = FILE_ATTRIBUTE_NORMAL;
  1060. }
  1061. KeQuerySystemTime( &Fcb->FcbUsnRecord->UsnRecord.TimeStamp );
  1062. //
  1063. // Append the record to the UsnJournal. We should never see a record with
  1064. // both rename flags or the close flag with the old name flag.
  1065. //
  1066. ASSERT( !FlagOn( Fcb->FcbUsnRecord->UsnRecord.Reason, USN_REASON_RENAME_OLD_NAME ) ||
  1067. !FlagOn( Fcb->FcbUsnRecord->UsnRecord.Reason,
  1068. USN_REASON_CLOSE | USN_REASON_RENAME_NEW_NAME ));
  1069. NtOfsPutData( IrpContext,
  1070. UsnJournal,
  1071. -1,
  1072. Fcb->FcbUsnRecord->UsnRecord.RecordLength,
  1073. &Fcb->FcbUsnRecord->UsnRecord );
  1074. #ifdef BRIANDBG
  1075. //
  1076. // The Usn better be in an allocated piece.
  1077. //
  1078. {
  1079. LCN Lcn;
  1080. LONGLONG ClusterCount;
  1081. if (!NtfsLookupAllocation( IrpContext,
  1082. UsnJournal,
  1083. LlClustersFromBytesTruncate( Vcb, Usn ),
  1084. &Lcn,
  1085. &ClusterCount,
  1086. NULL,
  1087. NULL ) ||
  1088. (Lcn == UNUSED_LCN)) {
  1089. ASSERT( FALSE );
  1090. }
  1091. }
  1092. #endif
  1093. //
  1094. // If this is the close record, then we must update the Usn in the file record.
  1095. //
  1096. if (!FlagOn( Fcb->FcbUsnRecord->UsnRecord.Reason, USN_REASON_FILE_DELETE ) &&
  1097. !FlagOn( Fcb->FcbState, FCB_STATE_FILE_DELETED )) {
  1098. //
  1099. // See if we need to actually grow Standard Information first.
  1100. // Do this even if we don't write the Usn record now. We may
  1101. // generate a close record for this file during mount and
  1102. // we expect the STANDARD_INFORMATION to support Usns.
  1103. //
  1104. if (!FlagOn( Fcb->FcbState, FCB_STATE_LARGE_STD_INFO )) {
  1105. //
  1106. // Grow the standard information.
  1107. //
  1108. NtfsGrowStandardInformation( IrpContext, Fcb );
  1109. }
  1110. if (FlagOn( Fcb->FcbUsnRecord->UsnRecord.Reason, USN_REASON_CLOSE )) {
  1111. //
  1112. // Locate the standard information, it must be there.
  1113. //
  1114. if (!NtfsLookupAttributeByCode( IrpContext,
  1115. Fcb,
  1116. &Fcb->FileReference,
  1117. $STANDARD_INFORMATION,
  1118. &AttributeContext )) {
  1119. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  1120. }
  1121. ASSERT(NtfsFoundAttribute( &AttributeContext )->Form.Resident.ValueLength ==
  1122. sizeof( STANDARD_INFORMATION ));
  1123. //
  1124. // Call to change the attribute value.
  1125. //
  1126. NtfsChangeAttributeValue( IrpContext,
  1127. Fcb,
  1128. FIELD_OFFSET(STANDARD_INFORMATION, Usn),
  1129. &Usn,
  1130. sizeof(Usn),
  1131. FALSE,
  1132. FALSE,
  1133. FALSE,
  1134. FALSE,
  1135. &AttributeContext );
  1136. }
  1137. }
  1138. //
  1139. // Remember to release these resources as soon as possible now.
  1140. // Note, if we are not sure that we became a transaction (else
  1141. // case below) then our finally clause will do the release.
  1142. //
  1143. // If the system has already gone through shutdown we won't be
  1144. // able to start a transaction. Test that we have a transaction
  1145. // before setting these flags.
  1146. //
  1147. if (IrpContext->TransactionId != 0) {
  1148. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_RELEASE_USN_JRNL |
  1149. IRP_CONTEXT_FLAG_RELEASE_MFT );
  1150. }
  1151. }
  1152. //
  1153. // Clear the flag indicating that there are new reasons to report.
  1154. //
  1155. ClearFlag( ThisUsn->UsnFcbFlags, USN_FCB_FLAG_NEW_REASON );
  1156. } finally {
  1157. //
  1158. // Cleanup the context structure if we are doing a close.
  1159. //
  1160. if (CleanupContext) {
  1161. NtfsCleanupAttributeContext( IrpContext, &AttributeContext );
  1162. }
  1163. if (ReleaseFcbs) {
  1164. NtfsReleaseScb( IrpContext, UsnJournal );
  1165. ASSERT( NtfsIsExclusiveScb( Vcb->MftScb ) );
  1166. NtfsReleaseScb( IrpContext, Vcb->MftScb );
  1167. }
  1168. }
  1169. ClearFlag( IrpContext->State, PreserveWaitState );
  1170. }
  1171. //
  1172. // Go to the next entry if present. If we are at the last entry then walk through all of the
  1173. // entries and clear the flag indicating we have new reasons.
  1174. //
  1175. if (ThisUsn->NextUsnFcb == NULL) {
  1176. //
  1177. // Exit if we didn't write any records.
  1178. //
  1179. if (!WroteUsnRecord) { break; }
  1180. ThisUsn = &IrpContext->Usn;
  1181. do {
  1182. ClearFlag( ThisUsn->UsnFcbFlags, USN_FCB_FLAG_NEW_REASON );
  1183. if (ThisUsn->NextUsnFcb == NULL) { break; }
  1184. ThisUsn = ThisUsn->NextUsnFcb;
  1185. } while (TRUE);
  1186. break;
  1187. }
  1188. ThisUsn = ThisUsn->NextUsnFcb;
  1189. } while (TRUE);
  1190. return;
  1191. }
  1192. VOID
  1193. NtfsSetupUsnJournal (
  1194. IN PIRP_CONTEXT IrpContext,
  1195. IN PVCB Vcb,
  1196. IN PFCB Fcb,
  1197. IN ULONG CreateIfNotExist,
  1198. IN ULONG Restamp,
  1199. IN PCREATE_USN_JOURNAL_DATA NewJournalData
  1200. )
  1201. /*++
  1202. Routine Description:
  1203. This routine is called to setup the Usn Journal - the stream may or may
  1204. not yet exist. This routine is responsible for cleaning up the disk and
  1205. in-memory structures on failure.
  1206. Arguments:
  1207. Vcb - Supplies the volume being initialized.
  1208. Fcb - Supplies the file for the Usn Journal.
  1209. CreateIfNotExist - Indicates that we should use the values in the Vcb instead of on-disk.
  1210. Restamp - Indicates if we should restamp the journal with a new Id.
  1211. NewJournalData - Allocation size and delta for Usn journal if we are not reading from disk.
  1212. Return Value:
  1213. None.
  1214. --*/
  1215. {
  1216. RTL_GENERIC_TABLE UsnControlTable;
  1217. PSCB UsnJournal;
  1218. PUSN_RECORD UsnRecord, UsnRecordInTable;
  1219. BOOLEAN CleanupControlTable = FALSE;
  1220. ATTRIBUTE_ENUMERATION_CONTEXT AttributeContext;
  1221. MAP_HANDLE MapHandle;
  1222. USN StartUsn;
  1223. LONGLONG ClusterCount;
  1224. PUSN_JOURNAL_INSTANCE UsnJournalData;
  1225. USN_JOURNAL_INSTANCE UsnJournalInstance, VcbUsnInstance;
  1226. PUSN_JOURNAL_INSTANCE InstanceToRestore;
  1227. PBCB Bcb = NULL;
  1228. LONGLONG SavedReservedSpace;
  1229. LONGLONG RequiredReserved;
  1230. BOOLEAN FoundMax;
  1231. BOOLEAN NewMax = FALSE;
  1232. BOOLEAN InsufficientReserved = FALSE;
  1233. BOOLEAN DecrementCloseCount = TRUE;
  1234. LARGE_INTEGER LastTimeStamp;
  1235. ULONG TempUlong;
  1236. LONGLONG TempLonglong;
  1237. PAGED_CODE( );
  1238. //
  1239. // Make sure we don't move to a larger page size.
  1240. //
  1241. ASSERT( USN_PAGE_BOUNDARY >= PAGE_SIZE );
  1242. //
  1243. // Open/Create the Usn Journal stream. We should never have an Scb
  1244. // if we are mounting a new volume.
  1245. //
  1246. ASSERT( (((ULONG) USN_JOURNAL_CACHE_BIAS) & (VACB_MAPPING_GRANULARITY - 1)) == 0 );
  1247. NtOfsCreateAttribute( IrpContext,
  1248. Fcb,
  1249. JournalStreamName,
  1250. CREATE_OR_OPEN,
  1251. TRUE,
  1252. &UsnJournal );
  1253. ASSERT( NtfsIsExclusiveScb( UsnJournal ) && NtfsIsExclusiveScb( Vcb->MftScb ) );
  1254. //
  1255. // Initialize the enumeration context and map handle.
  1256. //
  1257. NtfsInitializeAttributeContext( &AttributeContext );
  1258. NtOfsInitializeMapHandle( &MapHandle );
  1259. //
  1260. // Let's build the journal instance data. Assume we have current valid
  1261. // values in the Vcb for the Id and lowest valid usn.
  1262. //
  1263. UsnJournalInstance.MaximumSize = NewJournalData->MaximumSize;
  1264. UsnJournalInstance.AllocationDelta = NewJournalData->AllocationDelta;
  1265. UsnJournalInstance.JournalId = Vcb->UsnJournalInstance.JournalId;
  1266. UsnJournalInstance.LowestValidUsn = Vcb->UsnJournalInstance.LowestValidUsn;
  1267. //
  1268. // Capture the current reservation in the Journal Scb and also the
  1269. // current JournalData in the Vcb to restore on error.
  1270. //
  1271. SavedReservedSpace = UsnJournal->ScbType.Data.TotalReserved;
  1272. RtlCopyMemory( &VcbUsnInstance,
  1273. &Vcb->UsnJournalInstance,
  1274. sizeof( USN_JOURNAL_INSTANCE ));
  1275. InstanceToRestore = &VcbUsnInstance;
  1276. try {
  1277. //
  1278. // Make sure the Scb is initialized.
  1279. //
  1280. if (!FlagOn( UsnJournal->ScbState, SCB_STATE_HEADER_INITIALIZED )) {
  1281. NtfsUpdateScbFromAttribute( IrpContext, UsnJournal, NULL );
  1282. }
  1283. //
  1284. // Always create the journal non-resident. Otherwise in
  1285. // ConvertToNonResident we always need to check for this case
  1286. // which only happens once per volume.
  1287. //
  1288. if (FlagOn( UsnJournal->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
  1289. NtfsLookupAttributeForScb( IrpContext, UsnJournal, NULL, &AttributeContext );
  1290. ASSERT( NtfsIsAttributeResident( NtfsFoundAttribute( &AttributeContext )));
  1291. NtfsConvertToNonresident( IrpContext,
  1292. Fcb,
  1293. NtfsFoundAttribute( &AttributeContext ),
  1294. FALSE,
  1295. &AttributeContext );
  1296. NtfsCleanupAttributeContext( IrpContext, &AttributeContext );
  1297. }
  1298. //
  1299. // Remember to restamp if an earlier delete operation failed. This flag should
  1300. // never be set if there is a current UsnJournal Scb in the Vcb.
  1301. //
  1302. ASSERT( !FlagOn( Vcb->VcbState, VCB_STATE_INCOMPLETE_USN_DELETE ) ||
  1303. (Vcb->UsnJournal == NULL) );
  1304. if (FlagOn( Vcb->VcbState, VCB_STATE_INCOMPLETE_USN_DELETE )) {
  1305. Restamp = TRUE;
  1306. }
  1307. //
  1308. // If the $Max doesn't exist or we want to restamp then generate the
  1309. // new ID and Lowest ID.
  1310. //
  1311. if (!(FoundMax = NtfsLookupAttributeByName( IrpContext,
  1312. Fcb,
  1313. &Fcb->FileReference,
  1314. $DATA,
  1315. &$Max,
  1316. NULL,
  1317. FALSE,
  1318. &AttributeContext )) ||
  1319. Restamp ) {
  1320. NtfsAdvanceUsnJournal( Vcb, &UsnJournalInstance, UsnJournal->Header.FileSize.QuadPart, &NewMax );
  1321. //
  1322. // Examine the current $Max attribute for validity and either use the current values or
  1323. // generate new values.
  1324. //
  1325. } else {
  1326. //
  1327. // Get the size of the $Max attribute. It should always be resident but we will rewrite it in
  1328. // the case where it isn't.
  1329. //
  1330. if (NtfsIsAttributeResident( NtfsFoundAttribute( &AttributeContext ))) {
  1331. TempUlong = (NtfsFoundAttribute( &AttributeContext))->Form.Resident.ValueLength;
  1332. } else {
  1333. TempUlong = (ULONG) (NtfsFoundAttribute( &AttributeContext))->Form.Nonresident.FileSize;
  1334. NewMax = TRUE;
  1335. }
  1336. //
  1337. // Map the attribute and check it for consistency.
  1338. //
  1339. NtfsMapAttributeValue( IrpContext,
  1340. Fcb,
  1341. &UsnJournalData,
  1342. &TempUlong,
  1343. &Bcb,
  1344. &AttributeContext );
  1345. //
  1346. // Only copy over the range of values we would understand. If the size is not one
  1347. // we recognize then restamp the journal. We handle the V1 case as well as V2 case.
  1348. //
  1349. if (TempUlong == sizeof( CREATE_USN_JOURNAL_DATA )) {
  1350. UsnJournalInstance.LowestValidUsn = 0;
  1351. KeQuerySystemTime( (PLARGE_INTEGER) &UsnJournalInstance.JournalId );
  1352. //
  1353. // Put version 2 on the disk.
  1354. //
  1355. NewMax = TRUE;
  1356. //
  1357. // If this is not an overwrite then copy the size and delta from the attribute.
  1358. //
  1359. if (!CreateIfNotExist) {
  1360. //
  1361. // Assume we will use the values from the disk.
  1362. //
  1363. RtlCopyMemory( &UsnJournalInstance,
  1364. UsnJournalData,
  1365. TempUlong );
  1366. }
  1367. } else if (TempUlong == sizeof( USN_JOURNAL_INSTANCE )) {
  1368. //
  1369. // Assume we will use the values from the disk.
  1370. //
  1371. if (CreateIfNotExist) {
  1372. NewMax = TRUE;
  1373. UsnJournalInstance.LowestValidUsn = UsnJournalData->LowestValidUsn;
  1374. UsnJournalInstance.JournalId = UsnJournalData->JournalId;
  1375. } else {
  1376. //
  1377. // Get the data from the disk.
  1378. //
  1379. RtlCopyMemory( &UsnJournalInstance,
  1380. UsnJournalData,
  1381. TempUlong );
  1382. }
  1383. } else {
  1384. //
  1385. // Restamp in this case.
  1386. // We move forward in the file to the next Usn boundary.
  1387. //
  1388. NtfsAdvanceUsnJournal( Vcb, &UsnJournalInstance, UsnJournal->Header.FileSize.QuadPart, &NewMax );
  1389. }
  1390. //
  1391. // Put the Bcb back into the context if we removed it.
  1392. //
  1393. if (NtfsFoundBcb( &AttributeContext ) == NULL) {
  1394. NtfsFoundBcb( &AttributeContext ) = Bcb;
  1395. Bcb = NULL;
  1396. }
  1397. }
  1398. //
  1399. // Check that the file doesn't end on a sparse hole.
  1400. //
  1401. if (!NewMax &&
  1402. (UsnJournal->Header.AllocationSize.QuadPart != 0) &&
  1403. (UsnJournalInstance.LowestValidUsn != UsnJournal->Header.AllocationSize.QuadPart)) {
  1404. LCN Lcn;
  1405. LONGLONG ClusterCount;
  1406. if (!NtfsLookupAllocation( IrpContext,
  1407. UsnJournal,
  1408. LlClustersFromBytesTruncate( Vcb, UsnJournal->Header.AllocationSize.QuadPart - 1 ),
  1409. &Lcn,
  1410. &ClusterCount,
  1411. NULL,
  1412. NULL ) ||
  1413. (Lcn == UNUSED_LCN)) {
  1414. NtfsAdvanceUsnJournal( Vcb, &UsnJournalInstance, UsnJournal->Header.AllocationSize.QuadPart, &NewMax );
  1415. }
  1416. }
  1417. //
  1418. // Enforce minimum sizes and allocation deltas, do not let them eat the whole volume,
  1419. // and round them to a Cache Manager View Size. All of these decisions are arbitrary,
  1420. // but hopefully reasonable. An option would be to take the cases other than those
  1421. // dealing with rounding, and return an error.
  1422. //
  1423. if ((ULONGLONG) UsnJournalInstance.MaximumSize < (ULONGLONG) VcbUsnInstance.MaximumSize) {
  1424. UsnJournalInstance.MaximumSize = VcbUsnInstance.MaximumSize;
  1425. }
  1426. if (UsnJournalInstance.MaximumSize < MINIMUM_USN_JOURNAL_SIZE) {
  1427. UsnJournalInstance.MaximumSize = MINIMUM_USN_JOURNAL_SIZE;
  1428. NewMax = TRUE;
  1429. } else {
  1430. if ((ULONGLONG) UsnJournalInstance.MaximumSize > LlBytesFromClusters(Vcb, Vcb->TotalClusters) / 2) {
  1431. UsnJournalInstance.MaximumSize = LlBytesFromClusters(Vcb, Vcb->TotalClusters) / 2;
  1432. NewMax = TRUE;
  1433. }
  1434. if ((ULONGLONG) UsnJournalInstance.MaximumSize > USN_MAXIMUM_JOURNAL_SIZE) {
  1435. UsnJournalInstance.MaximumSize = USN_MAXIMUM_JOURNAL_SIZE;
  1436. NewMax = TRUE;
  1437. }
  1438. }
  1439. //
  1440. // Round this value down to a cache view boundary.
  1441. //
  1442. UsnJournalInstance.MaximumSize &= ~(LONGLONG)((VACB_MAPPING_GRANULARITY) - 1);
  1443. //
  1444. // Now do the allocation delta.
  1445. //
  1446. if ((ULONGLONG) UsnJournalInstance.AllocationDelta < (ULONGLONG) VcbUsnInstance.AllocationDelta) {
  1447. UsnJournalInstance.AllocationDelta = VcbUsnInstance.AllocationDelta;
  1448. }
  1449. if (UsnJournalInstance.AllocationDelta < (MINIMUM_USN_JOURNAL_SIZE / 4)) {
  1450. UsnJournalInstance.AllocationDelta = MINIMUM_USN_JOURNAL_SIZE / 4;
  1451. NewMax = TRUE;
  1452. } else if ((ULONGLONG) UsnJournalInstance.AllocationDelta > (UsnJournalInstance.MaximumSize / 4)) {
  1453. UsnJournalInstance.AllocationDelta = (UsnJournalInstance.MaximumSize / 4);
  1454. NewMax = TRUE;
  1455. }
  1456. //
  1457. // Round this down to a view boundary as well.
  1458. //
  1459. UsnJournalInstance.AllocationDelta &= ~(LONGLONG)((VACB_MAPPING_GRANULARITY) - 1);
  1460. //
  1461. // We now know the desired size of the journal (including allocation delta). Next
  1462. // we need to check that this space is available on disk. Otherwise we can get in
  1463. // a state where every operation on the volume will fail because we need to grow
  1464. // the journal and the space isn't available. The strategy here will be to use
  1465. // the reserved clusters in the Vcb to make sure we have enough space. If the
  1466. // journal already exists and we are simply opening it then the space should
  1467. // be available. It is possible someone could move this volume to NT4 and fill
  1468. // up the disk however. If we can't reserve the space in the current system then
  1469. // update the $Max attribute to indicate that we can't access the journal at this time.
  1470. //
  1471. //
  1472. // We need to be very precise about the initial reservation. The total allocation we allow
  1473. // ourselves is (MaxSize + Delta * 2). We will reserve the missing space now and adjust it
  1474. // during the TrimUsnJournal phase.
  1475. //
  1476. RequiredReserved = UsnJournalInstance.MaximumSize + (UsnJournalInstance.AllocationDelta * 2);
  1477. if (RequiredReserved >= UsnJournal->TotalAllocated) {
  1478. RequiredReserved -= UsnJournal->TotalAllocated;
  1479. } else {
  1480. RequiredReserved = UsnJournalInstance.AllocationDelta;
  1481. }
  1482. NtfsAcquireReservedClusters( Vcb );
  1483. //
  1484. // Check if there is more to reserve and adjust the reservation if necessary.
  1485. //
  1486. if (RequiredReserved > SavedReservedSpace) {
  1487. //
  1488. // Check that the reserved clusters are available.
  1489. //
  1490. if (LlClustersFromBytes( Vcb, (RequiredReserved - SavedReservedSpace) ) + Vcb->TotalReserved > Vcb->FreeClusters) {
  1491. //
  1492. // We can't reserve the required space. If someone is changing the journal then simply
  1493. // raise the error.
  1494. //
  1495. if (CreateIfNotExist) {
  1496. NtfsReleaseReservedClusters( Vcb );
  1497. NtfsRaiseStatus( IrpContext, STATUS_DISK_FULL, NULL, NULL );
  1498. }
  1499. //
  1500. // We are trying to open the journal but can't get the space. Update the
  1501. // $Max to indicate that the ID is changing. We will bail later in this case.
  1502. //
  1503. // We move forward in the file to the next Usn boundary.
  1504. //
  1505. TempUlong = USN_PAGE_BOUNDARY;
  1506. if (USN_PAGE_BOUNDARY < Vcb->BytesPerCluster) {
  1507. TempUlong = Vcb->BytesPerCluster;
  1508. }
  1509. UsnJournalInstance.LowestValidUsn = UsnJournal->Header.FileSize.QuadPart + TempUlong - 1;
  1510. ((PLARGE_INTEGER) &UsnJournalInstance.LowestValidUsn)->LowPart &= ~(TempUlong - 1);
  1511. //
  1512. // Generate a new journal ID.
  1513. //
  1514. KeQuerySystemTime( (PLARGE_INTEGER) &UsnJournalInstance.JournalId );
  1515. //
  1516. // Remember that we are restamping and need to rewrite the $Max attribute.
  1517. //
  1518. NewMax = TRUE;
  1519. InsufficientReserved = TRUE;
  1520. }
  1521. }
  1522. //
  1523. // Remove the current reservation and bias with the new reservation.
  1524. //
  1525. Vcb->TotalReserved -= LlClustersFromBytes( Vcb, SavedReservedSpace );
  1526. Vcb->TotalReserved += LlClustersFromBytes( Vcb, RequiredReserved );
  1527. UsnJournal->ScbType.Data.TotalReserved = RequiredReserved;
  1528. SetFlag( UsnJournal->ScbState, SCB_STATE_WRITE_ACCESS_SEEN );
  1529. NtfsReleaseReservedClusters( Vcb );
  1530. //
  1531. // Check we need to write a new $Max attribute.
  1532. //
  1533. if (NewMax) {
  1534. //
  1535. // Delete the existing $Max if present.
  1536. //
  1537. if (FoundMax) {
  1538. if (NtfsIsAttributeResident( NtfsFoundAttribute( &AttributeContext ))) {
  1539. NtfsDeleteAttributeRecord( IrpContext,
  1540. Fcb,
  1541. (DELETE_LOG_OPERATION |
  1542. DELETE_RELEASE_FILE_RECORD |
  1543. DELETE_RELEASE_ALLOCATION),
  1544. &AttributeContext );
  1545. } else {
  1546. PSCB MaxScb;
  1547. MaxScb = NtfsCreateScb( IrpContext,
  1548. Fcb,
  1549. $DATA,
  1550. &$Max,
  1551. FALSE,
  1552. NULL );
  1553. do {
  1554. NtfsDeleteAttributeRecord( IrpContext,
  1555. Fcb,
  1556. (DELETE_LOG_OPERATION |
  1557. DELETE_RELEASE_FILE_RECORD |
  1558. DELETE_RELEASE_ALLOCATION),
  1559. &AttributeContext );
  1560. } while (NtfsLookupNextAttributeForScb( IrpContext, MaxScb, &AttributeContext ));
  1561. }
  1562. }
  1563. NtfsCleanupAttributeContext( IrpContext, &AttributeContext );
  1564. //
  1565. // Create the new $MAX attribute.
  1566. //
  1567. NtfsCreateAttributeWithValue( IrpContext,
  1568. UsnJournal->Fcb,
  1569. $DATA,
  1570. &$Max,
  1571. &UsnJournalInstance,
  1572. sizeof( USN_JOURNAL_INSTANCE ),
  1573. 0, // attribute flags
  1574. NULL,
  1575. TRUE,
  1576. &AttributeContext );
  1577. }
  1578. //
  1579. // Check if we are finished with the journal because of reservation problems.
  1580. //
  1581. if (InsufficientReserved) {
  1582. //
  1583. // We want to checkpoint the request in order to leave the new $Max on disk.
  1584. //
  1585. NtfsCheckpointCurrentTransaction( IrpContext );
  1586. leave;
  1587. }
  1588. //
  1589. // Now update the Vcb with the new instance values.
  1590. //
  1591. RtlCopyMemory( &Vcb->UsnJournalInstance,
  1592. &UsnJournalInstance,
  1593. sizeof( USN_JOURNAL_INSTANCE ));
  1594. //
  1595. // We now have the correct journal values in the Vcb and the reservation in the Scb.
  1596. // The next step is to make sure that the allocation in the journal data is consistent
  1597. // with the lowest Vcn value.
  1598. //
  1599. if (UsnJournalInstance.LowestValidUsn >= UsnJournal->Header.FileSize.QuadPart) {
  1600. ASSERT( (Vcb->UsnJournal == NULL) ||
  1601. (Vcb->UsnJournal->Header.FileSize.QuadPart == 0) ||
  1602. (UsnJournalInstance.LowestValidUsn == UsnJournal->Header.FileSize.QuadPart) );
  1603. //
  1604. // Add allocation if we need to.
  1605. //
  1606. if (UsnJournalInstance.LowestValidUsn > UsnJournal->Header.AllocationSize.QuadPart) {
  1607. NtfsAddAllocation( IrpContext,
  1608. NULL,
  1609. UsnJournal,
  1610. LlClustersFromBytesTruncate( Vcb, UsnJournal->Header.AllocationSize.QuadPart ),
  1611. LlClustersFromBytes( Vcb,
  1612. UsnJournalInstance.LowestValidUsn - UsnJournal->Header.AllocationSize.QuadPart ),
  1613. FALSE,
  1614. NULL );
  1615. }
  1616. //
  1617. // Bump all of the sizes to this value.
  1618. //
  1619. UsnJournal->Header.ValidDataLength.QuadPart =
  1620. UsnJournal->Header.FileSize.QuadPart =
  1621. UsnJournal->ValidDataToDisk = UsnJournalInstance.LowestValidUsn;
  1622. NtfsWriteFileSizes( IrpContext,
  1623. UsnJournal,
  1624. &UsnJournal->Header.ValidDataLength.QuadPart,
  1625. TRUE,
  1626. TRUE,
  1627. FALSE );
  1628. //
  1629. // Throw away the allocation upto this value.
  1630. //
  1631. NtfsDeleteAllocation( IrpContext,
  1632. NULL,
  1633. UsnJournal,
  1634. 0,
  1635. LlClustersFromBytesTruncate( Vcb, UsnJournalInstance.LowestValidUsn ) - 1,
  1636. TRUE,
  1637. FALSE );
  1638. //
  1639. // Bias the Reserved space again.
  1640. //
  1641. RequiredReserved = UsnJournalInstance.MaximumSize + (UsnJournalInstance.AllocationDelta * 2);
  1642. if (RequiredReserved >= UsnJournal->TotalAllocated) {
  1643. RequiredReserved -= UsnJournal->TotalAllocated;
  1644. } else {
  1645. RequiredReserved = UsnJournalInstance.AllocationDelta;
  1646. }
  1647. NtfsAcquireReservedClusters( Vcb );
  1648. Vcb->TotalReserved -= LlClustersFromBytes( Vcb, UsnJournal->ScbType.Data.TotalReserved );
  1649. Vcb->TotalReserved += LlClustersFromBytes( Vcb, RequiredReserved );
  1650. UsnJournal->ScbType.Data.TotalReserved = RequiredReserved;
  1651. NtfsReleaseReservedClusters( Vcb );
  1652. }
  1653. //
  1654. // Make sure the stream is marked as sparse.
  1655. //
  1656. if (!FlagOn( UsnJournal->AttributeFlags, ATTRIBUTE_FLAG_SPARSE )) {
  1657. NtfsSetSparseStream( IrpContext, NULL, UsnJournal );
  1658. NtfsUpdateDuplicateInfo( IrpContext, UsnJournal->Fcb, NULL, Vcb->ExtendDirectory );
  1659. //
  1660. // No point in restoring the Vcb values now.
  1661. //
  1662. InstanceToRestore = NULL;
  1663. SavedReservedSpace = UsnJournal->ScbType.Data.TotalReserved;
  1664. }
  1665. //
  1666. // If this was just an overwrite of parameters (Journal already started), get out.
  1667. //
  1668. if (Vcb->UsnJournal != NULL) {
  1669. ASSERT( FlagOn( Vcb->UsnJournal->Fcb->FcbState, FCB_STATE_USN_JOURNAL ));
  1670. SavedReservedSpace = UsnJournal->ScbType.Data.TotalReserved;
  1671. InstanceToRestore = NULL;
  1672. leave;
  1673. }
  1674. //
  1675. // Note in the Fcb that this is a journal file.
  1676. //
  1677. SetFlag( UsnJournal->Fcb->FcbState, FCB_STATE_USN_JOURNAL );
  1678. //
  1679. // Initialize a generic table to scoreboard Fcb entries
  1680. //
  1681. RtlInitializeGenericTable( &UsnControlTable,
  1682. NtfsUsnTableCompare,
  1683. NtfsUsnTableAllocate,
  1684. NtfsUsnTableFree,
  1685. NULL );
  1686. CleanupControlTable = TRUE;
  1687. //
  1688. // Load the run information for the stream. We are looking for the first position
  1689. // to read from the disk.
  1690. //
  1691. NtfsPreloadAllocation( IrpContext, UsnJournal, 0, MAXLONGLONG );
  1692. if (UsnJournal->Header.AllocationSize.QuadPart != 0) {
  1693. VCN CurrentVcn = 0;
  1694. while (!NtfsLookupAllocation( IrpContext,
  1695. UsnJournal,
  1696. CurrentVcn,
  1697. &TempLonglong,
  1698. &ClusterCount,
  1699. NULL,
  1700. NULL )) {
  1701. //
  1702. // Check the case where we returned the maximum LCN value.
  1703. //
  1704. if (CurrentVcn + ClusterCount == MAXLONGLONG) {
  1705. Vcb->FirstValidUsn = UsnJournal->Header.FileSize.QuadPart;
  1706. break;
  1707. }
  1708. //
  1709. // Find out the number of bytes in this block and check we don't
  1710. // go beyond file size.
  1711. //
  1712. Vcb->FirstValidUsn += LlBytesFromClusters( Vcb, ClusterCount );
  1713. if (Vcb->FirstValidUsn >= UsnJournal->Header.FileSize.QuadPart) {
  1714. Vcb->FirstValidUsn = UsnJournal->Header.FileSize.QuadPart;
  1715. break;
  1716. }
  1717. CurrentVcn += ClusterCount;
  1718. }
  1719. }
  1720. //
  1721. // Skip forward if we have restamped the file.
  1722. //
  1723. if (Vcb->FirstValidUsn < UsnJournalInstance.LowestValidUsn) {
  1724. Vcb->FirstValidUsn = UsnJournalInstance.LowestValidUsn;
  1725. }
  1726. //
  1727. // Loop through as many views as required to fill the output buffer.
  1728. //
  1729. StartUsn = Vcb->LowestOpenUsn;
  1730. if (StartUsn < Vcb->FirstValidUsn) {
  1731. StartUsn = Vcb->FirstValidUsn;
  1732. }
  1733. //
  1734. // This is where we set up the bias for the Scb. Only do this for cases where
  1735. // there already isn't a data section.
  1736. //
  1737. if (UsnJournal->NonpagedScb->SegmentObject.DataSectionObject == NULL) {
  1738. Vcb->UsnCacheBias = Vcb->FirstValidUsn & ~(USN_JOURNAL_CACHE_BIAS - 1);
  1739. if (Vcb->UsnCacheBias != 0) {
  1740. Vcb->UsnCacheBias -= USN_JOURNAL_CACHE_BIAS;
  1741. }
  1742. NtfsCreateInternalAttributeStream( IrpContext, UsnJournal, TRUE, NULL );
  1743. }
  1744. while (StartUsn < UsnJournal->Header.FileSize.QuadPart) {
  1745. LONGLONG BiasedStartUsn;
  1746. //
  1747. // Calculate length to process in this view.
  1748. //
  1749. TempLonglong = UsnJournal->Header.FileSize.QuadPart - StartUsn;
  1750. if (TempLonglong > (VACB_MAPPING_GRANULARITY - (ULONG)(StartUsn & (VACB_MAPPING_GRANULARITY - 1)))) {
  1751. TempLonglong = VACB_MAPPING_GRANULARITY - (ULONG)(StartUsn & (VACB_MAPPING_GRANULARITY - 1));
  1752. }
  1753. //
  1754. // Map the view containing the desired Usn.
  1755. //
  1756. ASSERT( StartUsn >= Vcb->UsnCacheBias );
  1757. BiasedStartUsn = StartUsn - Vcb->UsnCacheBias;
  1758. NtOfsMapAttribute( IrpContext, UsnJournal, BiasedStartUsn, (ULONG)TempLonglong, &UsnRecord, &MapHandle );
  1759. //
  1760. // Now loop to process this view. TempLonglong is the space left in this view. TempUlong is
  1761. // the space for the next record.
  1762. //
  1763. while (TempLonglong != 0) {
  1764. //
  1765. // Calculate size left in current page, and see if we have to move to the
  1766. // next page.
  1767. //
  1768. // Note in this loop we are not going to trust the the contents of the
  1769. // file, so if we see anything broken we raise an error.
  1770. //
  1771. TempUlong = USN_PAGE_SIZE - (ULONG)(StartUsn & (USN_PAGE_SIZE - 1));
  1772. if ((TempUlong >= (FIELD_OFFSET(USN_RECORD, FileName) + sizeof(WCHAR))) && (UsnRecord->RecordLength != 0)) {
  1773. //
  1774. // Get the size of the current record.
  1775. //
  1776. TempUlong = UsnRecord->RecordLength;
  1777. //
  1778. // Since the Usn is embedded in the Usn record, we can do a fairly precise
  1779. // test that we got a valid Usn. Also make sure we got a valid RecordSize
  1780. // that does not go beyond FileSize or the end of the page. If we see a
  1781. // bad record, then let's just skip to the end of the page rather than
  1782. // tubing the mount process.
  1783. //
  1784. if ((TempUlong & (sizeof(ULONGLONG) - 1)) ||
  1785. (TempUlong > TempLonglong) ||
  1786. (TempUlong > (USN_PAGE_SIZE - ((ULONG)StartUsn & (USN_PAGE_SIZE - 1)))) ||
  1787. (StartUsn != UsnRecord->Usn)) {
  1788. TempUlong = (USN_PAGE_SIZE - ((ULONG)StartUsn & (USN_PAGE_SIZE - 1)));
  1789. //
  1790. // FileSize may stop before the end of the page, so check for that so
  1791. // we terminate correctly.
  1792. //
  1793. if (TempUlong > TempLonglong) {
  1794. TempUlong = (ULONG)TempLonglong;
  1795. }
  1796. //
  1797. // We have to skip over any MajorVersion we do not understand.
  1798. //
  1799. } else if ((UsnRecord->MajorVersion == 1) ||
  1800. (UsnRecord->MajorVersion == 2)) {
  1801. //
  1802. // Load up the info from this record.
  1803. //
  1804. if (!FlagOn(UsnRecord->Reason, USN_REASON_CLOSE)) {
  1805. UsnRecordInTable = RtlInsertElementGenericTable( &UsnControlTable,
  1806. UsnRecord,
  1807. UsnRecord->RecordLength,
  1808. NULL );
  1809. UsnRecordInTable->Reason = UsnRecord->Reason;
  1810. //
  1811. // If this is a close record, then we can delete our element from the
  1812. // generic table. Note if the record is not there this function returns
  1813. // FALSE, and the attempted delete is benign.
  1814. //
  1815. } else {
  1816. (VOID)RtlDeleteElementGenericTable( &UsnControlTable, UsnRecord );
  1817. }
  1818. //
  1819. // Capture each time stamp so that we can stamp our close records
  1820. // with the last one we see.
  1821. //
  1822. LastTimeStamp = UsnRecord->TimeStamp;
  1823. }
  1824. //
  1825. // Check for a bogus Usn near the end of a page that would cause us to
  1826. // decrement through length, or a RecordSize of 0, and just skip to the
  1827. // end of the page.
  1828. //
  1829. } else if ((TempUlong > TempLonglong) || (TempUlong == 0)) {
  1830. TempUlong = (USN_PAGE_SIZE - ((ULONG)StartUsn & (USN_PAGE_SIZE - 1)));
  1831. if (TempUlong > TempLonglong) {
  1832. TempUlong = (ULONG) TempLonglong;
  1833. }
  1834. }
  1835. StartUsn += TempUlong;
  1836. TempLonglong -= TempUlong;
  1837. UsnRecord = Add2Ptr( UsnRecord, TempUlong );
  1838. }
  1839. NtOfsReleaseMap( IrpContext, &MapHandle );
  1840. }
  1841. //
  1842. // Now write the close records for anyone who is left. We store a counter
  1843. // in TempUlong to limit the number of records we do at a time.
  1844. //
  1845. for (TempUlong = 0, UsnRecord = RtlEnumerateGenericTable( &UsnControlTable, TRUE );
  1846. UsnRecord != NULL;
  1847. UsnRecord = RtlEnumerateGenericTable( &UsnControlTable, TRUE )) {
  1848. ULONG UsnRecordReason;
  1849. FILE_REFERENCE UsnRecordFileReferenceNumber;
  1850. ULONG BytesLeftInPage;
  1851. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  1852. NTSTATUS Status;
  1853. StartUsn = NtOfsQueryLength( UsnJournal );
  1854. StartUsn = UsnJournal->Header.FileSize.QuadPart;
  1855. BytesLeftInPage = USN_PAGE_SIZE - ((ULONG)StartUsn & (USN_PAGE_SIZE - 1));
  1856. //
  1857. // If there is not enough room left in this page for the
  1858. // current Usn Record, then advance to the next page boundary
  1859. // by writing 0's (these pages not zero-initialized( and update the Usn.
  1860. //
  1861. if (BytesLeftInPage < UsnRecord->RecordLength) {
  1862. NtOfsPutData( IrpContext, UsnJournal, -1, BytesLeftInPage, NULL );
  1863. StartUsn += BytesLeftInPage;
  1864. }
  1865. //
  1866. // Append the record to the UsnJournal. Note that the generic table is unaligned for
  1867. // 64-bit values so we have to carefully copy the larger values.
  1868. //
  1869. *((ULONGLONG UNALIGNED *) &UsnRecord->Usn) = StartUsn;
  1870. *((ULONGLONG UNALIGNED *) &UsnRecord->TimeStamp) = *((PULONGLONG) &LastTimeStamp);
  1871. UsnRecord->Reason |= USN_REASON_CLOSE;
  1872. NtOfsPutData( IrpContext,
  1873. UsnJournal,
  1874. -1,
  1875. UsnRecord->RecordLength,
  1876. UsnRecord );
  1877. //
  1878. // Remember key fields of the Usn record.
  1879. //
  1880. UsnRecordReason = UsnRecord->Reason;
  1881. *((PULONGLONG) &UsnRecordFileReferenceNumber) = *((ULONGLONG UNALIGNED *) &UsnRecord->FileReferenceNumber);
  1882. RtlDeleteElementGenericTable( &UsnControlTable, UsnRecord );
  1883. TempUlong += 1;
  1884. //
  1885. // Now we have to update the Usn in the file record, if it is not deleted.
  1886. // Also, we use try-except to plow on in the event of any errors, so we
  1887. // do not make the volume unmountable. (One legitimate concern would be
  1888. // a hot-fix in the Mft.)
  1889. //
  1890. if (!FlagOn(UsnRecordReason, USN_REASON_FILE_DELETE)) {
  1891. //
  1892. // Start by reading the file record and performing some simple tests.
  1893. // We don't want to go down the path where we mark the volume dirty
  1894. // for a file that was already cleaned up by autochk.
  1895. //
  1896. NtfsUnpinBcb( IrpContext, &Bcb );
  1897. NtfsReadMftRecord( IrpContext,
  1898. Vcb,
  1899. &UsnRecordFileReferenceNumber,
  1900. FALSE,
  1901. &Bcb,
  1902. &FileRecord,
  1903. NULL );
  1904. //
  1905. // Proceed only if the file record passes the following tests.
  1906. //
  1907. // - FileRecord is in-use
  1908. // - Sequence numbers match
  1909. // - Standard information is the correct size (we should have done
  1910. // this when we wrote the changes)
  1911. //
  1912. if ((*(PULONG)(FileRecord)->MultiSectorHeader.Signature == *(PULONG)FileSignature) &&
  1913. FlagOn( FileRecord->Flags, FILE_RECORD_SEGMENT_IN_USE ) &&
  1914. (FileRecord->SequenceNumber == UsnRecordFileReferenceNumber.SequenceNumber)) {
  1915. //
  1916. // Locate the standard information, it must be there. This is the
  1917. // Fcb for the Usn Journal, but the lookup routine only needs to get
  1918. // the Vcb from it, and will special-case the return to us.
  1919. //
  1920. NtfsCleanupAttributeContext( IrpContext, &AttributeContext );
  1921. try {
  1922. //
  1923. // If we cannot find it for some reason, then leave.
  1924. //
  1925. if (!NtfsLookupAttributeByCode( IrpContext,
  1926. Fcb,
  1927. &UsnRecordFileReferenceNumber,
  1928. $STANDARD_INFORMATION,
  1929. &AttributeContext )) {
  1930. leave;
  1931. }
  1932. ASSERT(NtfsFoundAttribute( &AttributeContext )->Form.Resident.ValueLength ==
  1933. sizeof( STANDARD_INFORMATION ));
  1934. //
  1935. // Call to change the attribute value. Again, this is the wrong Fcb,
  1936. // but it is ok since we are not changing the attribute size and will
  1937. // only need to get the Vcb from it.
  1938. //
  1939. NtfsChangeAttributeValue( IrpContext,
  1940. Fcb,
  1941. FIELD_OFFSET(STANDARD_INFORMATION, Usn),
  1942. &StartUsn,
  1943. sizeof(StartUsn),
  1944. FALSE,
  1945. FALSE,
  1946. FALSE,
  1947. FALSE,
  1948. &AttributeContext );
  1949. } except( NtfsCleanupExceptionFilter( IrpContext, GetExceptionInformation(), &Status )) {
  1950. //
  1951. // If we get a log file full then raise this status. There
  1952. // is no reason to continue if we get a log file full.
  1953. //
  1954. if (Status == STATUS_LOG_FILE_FULL) {
  1955. NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
  1956. }
  1957. //
  1958. // OK. We are going to continue. Make sure we clean up the IrpContext.
  1959. //
  1960. IrpContext->ExceptionStatus = STATUS_SUCCESS;
  1961. }
  1962. }
  1963. NtfsUnpinBcb( IrpContext, &Bcb );
  1964. }
  1965. //
  1966. // Checkpoint the transaction periodically so we don't spin on log file full.
  1967. //
  1968. if (TempUlong > GENERATE_CLOSE_RECORD_LIMIT) {
  1969. NtfsCheckpointCurrentTransaction( IrpContext );
  1970. SavedReservedSpace = UsnJournal->ScbType.Data.TotalReserved;
  1971. InstanceToRestore = NULL;
  1972. TempUlong = 0;
  1973. }
  1974. }
  1975. //
  1976. // Everything has succeeded to this point. Now make sure the DELETE_USN flag is cleared on
  1977. // disk if present.
  1978. //
  1979. if (FlagOn( Vcb->VcbState, VCB_STATE_INCOMPLETE_USN_DELETE )) {
  1980. NtfsSetVolumeInfoFlagState( IrpContext,
  1981. Vcb,
  1982. VOLUME_DELETE_USN_UNDERWAY,
  1983. FALSE,
  1984. TRUE );
  1985. }
  1986. InstanceToRestore = NULL;
  1987. SavedReservedSpace = UsnJournal->ScbType.Data.TotalReserved;
  1988. Vcb->UsnJournal = UsnJournal;
  1989. DecrementCloseCount = FALSE;
  1990. NtfsLockVcb( IrpContext, Vcb );
  1991. SetFlag( Vcb->VcbState, VCB_STATE_USN_JOURNAL_ACTIVE );
  1992. ClearFlag( Vcb->VcbState, VCB_STATE_INCOMPLETE_USN_DELETE );
  1993. NtfsUnlockVcb( IrpContext, Vcb );
  1994. } finally {
  1995. //
  1996. // Clean up any remaining entries in the control table in case we failed
  1997. // while processing it.
  1998. //
  1999. if (CleanupControlTable) {
  2000. while ((UsnRecord = RtlEnumerateGenericTable( &UsnControlTable, TRUE )) != NULL) {
  2001. RtlDeleteElementGenericTable( &UsnControlTable, UsnRecord );
  2002. }
  2003. }
  2004. //
  2005. // Restore any changes we might have made to the Vcb.
  2006. //
  2007. if (InstanceToRestore) {
  2008. RtlCopyMemory( &Vcb->UsnJournalInstance,
  2009. InstanceToRestore,
  2010. sizeof( USN_JOURNAL_INSTANCE ));
  2011. }
  2012. //
  2013. // Back out the reservation change if necessary.
  2014. //
  2015. if (UsnJournal->ScbType.Data.TotalReserved != SavedReservedSpace) {
  2016. NtfsAcquireReservedClusters( Vcb );
  2017. Vcb->TotalReserved += LlClustersFromBytes( Vcb, SavedReservedSpace );
  2018. Vcb->TotalReserved -= LlClustersFromBytes( Vcb, UsnJournal->ScbType.Data.TotalReserved );
  2019. UsnJournal->ScbType.Data.TotalReserved = SavedReservedSpace;
  2020. NtfsReleaseReservedClusters( Vcb );
  2021. }
  2022. NtfsCleanupAttributeContext( IrpContext, &AttributeContext );
  2023. NtfsUnpinBcb( IrpContext, &Bcb );
  2024. NtOfsReleaseMap( IrpContext, &MapHandle );
  2025. //
  2026. // If we got an error and the Usn journal is not going to be created then fix up the
  2027. // new Scb so we won't access it during volume flush operations, etc. Otherwise we
  2028. // will think the volume is corrupt because there is no attribute for the Scb.
  2029. //
  2030. if (DecrementCloseCount) {
  2031. NtOfsCloseAttribute( IrpContext, UsnJournal );
  2032. }
  2033. if (Vcb->UsnJournal == NULL) {
  2034. #ifdef NTFSDBG
  2035. //
  2036. // Compensate again for misclassification of usnjournal during delete
  2037. //
  2038. if (IrpContext->OwnershipState == NtfsOwns_ExVcb_Mft_Extend_Journal) {
  2039. IrpContext->OwnershipState = NtfsOwns_ExVcb_Mft_Extend_File;
  2040. }
  2041. #endif
  2042. UsnJournal->Header.AllocationSize.QuadPart =
  2043. UsnJournal->Header.FileSize.QuadPart =
  2044. UsnJournal->ValidDataToDisk =
  2045. UsnJournal->Header.ValidDataLength.QuadPart = 0;
  2046. UsnJournal->AttributeTypeCode = $UNUSED;
  2047. SetFlag( UsnJournal->ScbState, SCB_STATE_ATTRIBUTE_DELETED );
  2048. //
  2049. // Clear the system file flag out of the Fcb.
  2050. //
  2051. ClearFlag( UsnJournal->Fcb->FcbState, FCB_STATE_SYSTEM_FILE );
  2052. ASSERT( ExIsResourceAcquiredSharedLite( &Vcb->Resource ));
  2053. NtfsTeardownStructures( IrpContext,
  2054. UsnJournal,
  2055. NULL,
  2056. TRUE,
  2057. 0,
  2058. NULL );
  2059. }
  2060. }
  2061. return;
  2062. }
  2063. VOID
  2064. NtfsTrimUsnJournal (
  2065. IN PIRP_CONTEXT IrpContext,
  2066. IN PVCB Vcb
  2067. )
  2068. /*++
  2069. Routine Description:
  2070. This routine may be called to check if the Usn Journal is beyond the designated
  2071. size goal, and if so to delete the front of the file to bring it within the goal.
  2072. This may require first generating a few close records for files that are still open
  2073. and have their last record within this range. Such files that are modified again
  2074. will simply look like they were opened again.
  2075. This routine is called with certain checkpoint flags for the volume set. This is to
  2076. serialize with the DeleteUsnJournal path. We must clear them and signal other
  2077. checkpointers to proceed.
  2078. Arguments:
  2079. Vcb - Supplies the Vcb on which the Usn Journal is to be trimmed.
  2080. Return Value:
  2081. None.
  2082. --*/
  2083. {
  2084. PFCB Fcb;
  2085. PFCB_USN_RECORD FcbUsnRecord;
  2086. PSCB UsnJournal = Vcb->UsnJournal;
  2087. USN FirstValidUsn = Vcb->FirstValidUsn;
  2088. ULONG Done = FALSE;
  2089. LONGLONG SavedReserved;
  2090. LONGLONG RequiredReserved;
  2091. LONGLONG SavedBias;
  2092. BOOLEAN AcquiredMft = FALSE;
  2093. BOOLEAN DerefFcb = FALSE;
  2094. //
  2095. // Purge file record cache - may not be necc. here, examine this post nt5
  2096. //
  2097. NtfsPurgeFileRecordCache( IrpContext );
  2098. //
  2099. // See if it is time to trim the UsnJournal.
  2100. //
  2101. NtfsAcquireResourceShared( IrpContext, UsnJournal, TRUE );
  2102. while ((USN)(FirstValidUsn +
  2103. Vcb->UsnJournalInstance.MaximumSize +
  2104. Vcb->UsnJournalInstance.AllocationDelta) < (USN)UsnJournal->Header.FileSize.QuadPart) {
  2105. FirstValidUsn += Vcb->UsnJournalInstance.AllocationDelta;
  2106. }
  2107. NtfsReleaseResource( IrpContext, UsnJournal );
  2108. //
  2109. // Get to work if we have a new Usn to trim to.
  2110. //
  2111. if (FirstValidUsn != Vcb->FirstValidUsn) {
  2112. //
  2113. // Use try-finally to catch any log file full condtions or allocation failures.
  2114. // Since these are the only possible error condition, we know what resources to
  2115. // free on exit.
  2116. //
  2117. try {
  2118. do {
  2119. Fcb = NULL;
  2120. //
  2121. // Purge file record cache before acquiring vcb everytime
  2122. //
  2123. NtfsPurgeFileRecordCache( IrpContext );
  2124. //
  2125. // Synchronize with the Fcb table and Usn Journal so that we can
  2126. // see if the next Fcb has to have a close record generated.
  2127. //
  2128. NtfsAcquireSharedVcb( IrpContext, Vcb, TRUE );
  2129. NtfsAcquireFcbTable( IrpContext, Vcb );
  2130. ExAcquireFastMutex( UsnJournal->Header.FastMutex );
  2131. if (!IsListEmpty(&Vcb->ModifiedOpenFiles)) {
  2132. FcbUsnRecord = (PFCB_USN_RECORD)CONTAINING_RECORD( Vcb->ModifiedOpenFiles.Flink,
  2133. FCB_USN_RECORD,
  2134. ModifiedOpenFilesLinks );
  2135. //
  2136. // If the Usn record for this Fcb is older than where we want to delete
  2137. // to, then reference it. Otherwise signal we are done by clearing
  2138. // the Fcb pointer.
  2139. //
  2140. if (FcbUsnRecord->Fcb->Usn < FirstValidUsn) {
  2141. Fcb = FcbUsnRecord->Fcb;
  2142. Fcb->ReferenceCount += 1;
  2143. DerefFcb = TRUE;
  2144. } else {
  2145. Fcb = NULL;
  2146. }
  2147. }
  2148. ExReleaseFastMutex( UsnJournal->Header.FastMutex );
  2149. NtfsReleaseFcbTable( IrpContext, Vcb );
  2150. //
  2151. // Do we have to generate another close record?
  2152. //
  2153. if (Fcb != NULL) {
  2154. //
  2155. // We must lock out other activity on this file since we are about
  2156. // to reset the Usn reasons.
  2157. //
  2158. if (Fcb->PagingIoResource != NULL) {
  2159. ExAcquireResourceExclusiveLite( Fcb->PagingIoResource, TRUE );
  2160. }
  2161. NtfsAcquireExclusiveFcb( IrpContext, Fcb, NULL, ACQUIRE_NO_DELETE_CHECK );
  2162. //
  2163. // Skip over system files.
  2164. //
  2165. if (!FlagOn(Fcb->FcbState, FCB_STATE_SYSTEM_FILE)) {
  2166. //
  2167. // Post the close to our IrpContext.
  2168. //
  2169. NtfsPostUsnChange( IrpContext, Fcb, USN_REASON_CLOSE );
  2170. //
  2171. // If we did not actually post a change, something is wrong,
  2172. // because when a close change is written, the Fcb is removed from
  2173. // the list.
  2174. //
  2175. ASSERT(IrpContext->Usn.CurrentUsnFcb != NULL);
  2176. //
  2177. // Now generate the close record and checkpoint the transaction.
  2178. //
  2179. NtfsWriteUsnJournalChanges( IrpContext );
  2180. NtfsCheckpointCurrentTransaction( IrpContext );
  2181. }
  2182. //
  2183. // Now we will dereference the Fcb.
  2184. //
  2185. NtfsAcquireFcbTable( IrpContext, Vcb );
  2186. Fcb->ReferenceCount -= 1;
  2187. DerefFcb = FALSE;
  2188. //
  2189. // We may be required to delete this guy. This frees the Fcb Table.
  2190. //
  2191. if (IsListEmpty( &Fcb->ScbQueue ) && (Fcb->ReferenceCount == 0) && (Fcb->CloseCount == 0)) {
  2192. BOOLEAN AcquiredFcbTable = TRUE;
  2193. NtfsDeleteFcb( IrpContext, &Fcb, &AcquiredFcbTable );
  2194. ASSERT(!AcquiredFcbTable);
  2195. Fcb = (PFCB)1;
  2196. //
  2197. // Otherwise free the table and Fcb resources.
  2198. //
  2199. } else {
  2200. NtfsReleaseFcbTable( IrpContext, Vcb );
  2201. NtfsReleaseFcb( IrpContext, Fcb );
  2202. if (Fcb->PagingIoResource != NULL) {
  2203. ExReleaseResourceLite( Fcb->PagingIoResource );
  2204. }
  2205. }
  2206. }
  2207. //
  2208. // Now we can drop the Vcb before looping back.
  2209. //
  2210. NtfsReleaseVcb( IrpContext, Vcb );
  2211. } while (Fcb != NULL);
  2212. } finally {
  2213. //
  2214. // We got an error if Fcb is not NULL
  2215. //
  2216. if (Fcb != NULL) {
  2217. if (DerefFcb) {
  2218. NtfsAcquireFcbTable( IrpContext, Vcb );
  2219. Fcb->ReferenceCount -= 1;
  2220. NtfsReleaseFcbTable( IrpContext, Vcb );
  2221. }
  2222. NtfsReleaseFcb( IrpContext, Fcb );
  2223. if (Fcb->PagingIoResource != NULL) {
  2224. ExReleaseResourceLite( Fcb->PagingIoResource );
  2225. }
  2226. NtfsReleaseVcb( IrpContext, Vcb );
  2227. }
  2228. //
  2229. // If we raised then we need to clear the checkpoint flags.
  2230. //
  2231. if (AbnormalTermination()) {
  2232. NtfsAcquireCheckpoint( IrpContext, Vcb );
  2233. ClearFlag( Vcb->CheckpointFlags,
  2234. VCB_CHECKPOINT_SYNC_FLAGS | VCB_DUMMY_CHECKPOINT_POSTED);
  2235. NtfsSetCheckpointNotify( IrpContext, Vcb );
  2236. NtfsReleaseCheckpoint( IrpContext, Vcb );
  2237. }
  2238. }
  2239. //
  2240. // Now synchronize for deleting the allocation and purging pages from
  2241. // the cache.
  2242. //
  2243. NtfsAcquireExclusiveScb( IrpContext, Vcb->MftScb );
  2244. NtfsAcquireExclusiveScb( IrpContext, UsnJournal );
  2245. //
  2246. // Clear the checkpoint flags at this point.
  2247. //
  2248. NtfsAcquireCheckpoint( IrpContext, Vcb );
  2249. ClearFlag( Vcb->CheckpointFlags,
  2250. VCB_CHECKPOINT_SYNC_FLAGS | VCB_DUMMY_CHECKPOINT_POSTED);
  2251. NtfsSetCheckpointNotify( IrpContext, Vcb );
  2252. NtfsReleaseCheckpoint( IrpContext, Vcb );
  2253. try {
  2254. LONGLONG BiasedFirstValidUsn;
  2255. LONGLONG NewBias;
  2256. SavedReserved = UsnJournal->ScbType.Data.TotalReserved;
  2257. SavedBias = Vcb->UsnCacheBias;
  2258. //
  2259. // Make sure to preserve our reservation. We need to make sure anything we
  2260. // deallocate is available to us.
  2261. //
  2262. RequiredReserved = Vcb->UsnJournalInstance.AllocationDelta * 2 + Vcb->UsnJournalInstance.MaximumSize;
  2263. if (SavedReserved < RequiredReserved) {
  2264. //
  2265. // Bias the reservation with the maximum amount.
  2266. //
  2267. NtfsAcquireReservedClusters( Vcb );
  2268. Vcb->TotalReserved -= LlClustersFromBytesTruncate( Vcb, SavedReserved );
  2269. Vcb->TotalReserved += LlClustersFromBytesTruncate( Vcb, RequiredReserved );
  2270. UsnJournal->ScbType.Data.TotalReserved = RequiredReserved;
  2271. NtfsReleaseReservedClusters( Vcb );
  2272. }
  2273. NtfsDeleteAllocation( IrpContext,
  2274. UsnJournal->FileObject,
  2275. UsnJournal,
  2276. 0,
  2277. LlClustersFromBytes(Vcb, FirstValidUsn) - 1,
  2278. TRUE,
  2279. TRUE );
  2280. //
  2281. // Do a final checkpoint, especially since this IrpContext gets reused.
  2282. //
  2283. NtfsCheckpointCurrentTransaction( IrpContext );
  2284. //
  2285. // Adjust the current reserved amount more precisely.
  2286. //
  2287. NtfsAcquireReservedClusters( Vcb );
  2288. if (UsnJournal->TotalAllocated > RequiredReserved) {
  2289. SavedReserved = Vcb->UsnJournalInstance.AllocationDelta;
  2290. } else {
  2291. SavedReserved = RequiredReserved - UsnJournal->TotalAllocated;
  2292. }
  2293. //
  2294. // Remove the current reservation and bias with the new reservation.
  2295. //
  2296. Vcb->TotalReserved -= LlClustersFromBytesTruncate( Vcb, UsnJournal->ScbType.Data.TotalReserved );
  2297. Vcb->TotalReserved += LlClustersFromBytesTruncate( Vcb, SavedReserved );
  2298. UsnJournal->ScbType.Data.TotalReserved = SavedReserved;
  2299. NtfsReleaseReservedClusters( Vcb );
  2300. //
  2301. // If the nearly impossible case that the length wraps, then our
  2302. // purge will be too small, which simply means some unused pages
  2303. // will have to leave memory on their own!
  2304. //
  2305. BiasedFirstValidUsn = Vcb->FirstValidUsn - Vcb->UsnCacheBias;
  2306. CcPurgeCacheSection( &UsnJournal->NonpagedScb->SegmentObject,
  2307. (PLARGE_INTEGER)&BiasedFirstValidUsn,
  2308. (ULONG)(FirstValidUsn - Vcb->FirstValidUsn) - 1,
  2309. FALSE );
  2310. //
  2311. // Adjust bias now if at threshold - the flush causes everything in
  2312. // cache and logfile to disk and we hold the journal exclusive. So
  2313. // all in memory stuff will now reflect the new bias
  2314. //
  2315. NewBias = FirstValidUsn & ~(USN_JOURNAL_CACHE_BIAS - 1);
  2316. if (NewBias != 0) {
  2317. NewBias -= USN_JOURNAL_CACHE_BIAS;
  2318. }
  2319. if (NewBias != Vcb->UsnCacheBias) {
  2320. //
  2321. // Flush And Purge releases all resources in exclusive list so acquire
  2322. // the mft an extra time beforehand and restore back afterwards
  2323. //
  2324. NtfsAcquireResourceExclusive( IrpContext, Vcb->MftScb, TRUE );
  2325. NtfsReleaseScb( IrpContext, Vcb->MftScb );
  2326. AcquiredMft = TRUE;
  2327. NtfsFlushAndPurgeScb( IrpContext, UsnJournal, NULL );
  2328. Vcb->UsnCacheBias = NewBias;
  2329. SavedBias = NewBias;
  2330. }
  2331. //
  2332. // If we reach here, then we can advance FirstValidUsn. (Otherwise
  2333. // any retryable conditions will just resume work on this range.
  2334. //
  2335. Vcb->FirstValidUsn = FirstValidUsn;
  2336. } finally {
  2337. //
  2338. // Restore the error if we raised while deallocating.
  2339. //
  2340. if (SavedBias != Vcb->UsnCacheBias) {
  2341. Vcb->UsnCacheBias = SavedBias;
  2342. }
  2343. if (SavedReserved != UsnJournal->ScbType.Data.TotalReserved) {
  2344. NtfsAcquireReservedClusters( Vcb );
  2345. Vcb->TotalReserved += LlClustersFromBytesTruncate( Vcb, SavedReserved );
  2346. Vcb->TotalReserved -= LlClustersFromBytesTruncate( Vcb, RequiredReserved );
  2347. UsnJournal->ScbType.Data.TotalReserved = SavedReserved;
  2348. NtfsReleaseReservedClusters( Vcb );
  2349. }
  2350. NtfsReleaseScb( IrpContext, UsnJournal );
  2351. if (AcquiredMft) {
  2352. NtfsReleaseResource( IrpContext, Vcb->MftScb );
  2353. } else {
  2354. NtfsReleaseScb( IrpContext, Vcb->MftScb );
  2355. }
  2356. }
  2357. } else {
  2358. //
  2359. // Clear the checkpoint flags at this point.
  2360. //
  2361. NtfsAcquireCheckpoint( IrpContext, Vcb );
  2362. ClearFlag( Vcb->CheckpointFlags,
  2363. VCB_CHECKPOINT_SYNC_FLAGS | VCB_DUMMY_CHECKPOINT_POSTED);
  2364. NtfsSetCheckpointNotify( IrpContext, Vcb );
  2365. NtfsReleaseCheckpoint( IrpContext, Vcb );
  2366. }
  2367. }
  2368. NTSTATUS
  2369. NtfsQueryUsnJournal (
  2370. IN PIRP_CONTEXT IrpContext,
  2371. IN PIRP Irp
  2372. )
  2373. /*++
  2374. Routine Description:
  2375. This is the worker routine which returns the information about the current instance
  2376. of the Usn journal.
  2377. Arguments:
  2378. Irp - This is the Irp for the request.
  2379. Return Value:
  2380. NTSTATUS - Result for this request.
  2381. --*/
  2382. {
  2383. PIO_STACK_LOCATION IrpSp;
  2384. TYPE_OF_OPEN TypeOfOpen;
  2385. PVCB Vcb;
  2386. PFCB Fcb;
  2387. PSCB Scb;
  2388. PCCB Ccb;
  2389. PUSN_JOURNAL_DATA JournalData;
  2390. PAGED_CODE();
  2391. //
  2392. // Always make this request synchronous.
  2393. //
  2394. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
  2395. //
  2396. // Get the current stack location and extract the output
  2397. // buffer information. The output parameter will receive
  2398. // the compressed state of the file/directory.
  2399. //
  2400. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  2401. //
  2402. // Get a pointer to the output buffer. Look at the system buffer field in th
  2403. // irp first. Then the Irp Mdl.
  2404. //
  2405. if (Irp->AssociatedIrp.SystemBuffer != NULL) {
  2406. JournalData = (PUSN_JOURNAL_DATA) Irp->AssociatedIrp.SystemBuffer;
  2407. } else if (Irp->MdlAddress != NULL) {
  2408. JournalData = MmGetSystemAddressForMdlSafe( Irp->MdlAddress, NormalPagePriority );
  2409. if (JournalData == NULL) {
  2410. NtfsCompleteRequest( IrpContext, Irp, STATUS_INSUFFICIENT_RESOURCES );
  2411. return STATUS_INSUFFICIENT_RESOURCES;
  2412. }
  2413. } else {
  2414. NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_USER_BUFFER );
  2415. return STATUS_INVALID_USER_BUFFER;
  2416. }
  2417. //
  2418. // Make sure the output buffer is large enough for the journal data.
  2419. //
  2420. if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof( USN_JOURNAL_DATA )) {
  2421. NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_USER_BUFFER );
  2422. return STATUS_INVALID_USER_BUFFER;
  2423. }
  2424. //
  2425. // Decode the file object. We only support this call for volume opens.
  2426. //
  2427. TypeOfOpen = NtfsDecodeFileObject( IrpContext, IrpSp->FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
  2428. if ((Ccb == NULL) || !FlagOn( Ccb->AccessFlags, MANAGE_VOLUME_ACCESS)) {
  2429. NtfsCompleteRequest( IrpContext, Irp, STATUS_ACCESS_DENIED );
  2430. return STATUS_ACCESS_DENIED;
  2431. }
  2432. //
  2433. // Acquire shared access to the Scb and check that the volume is still mounted.
  2434. //
  2435. NtfsAcquireSharedVcb( IrpContext, Vcb, TRUE );
  2436. if (!FlagOn(Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED)) {
  2437. NtfsReleaseVcb( IrpContext, Vcb );
  2438. NtfsCompleteRequest( IrpContext, Irp, STATUS_VOLUME_DISMOUNTED );
  2439. return STATUS_VOLUME_DISMOUNTED;
  2440. }
  2441. //
  2442. // Indicate if the journal is being deleted or has not started.
  2443. //
  2444. if (FlagOn( Vcb->VcbState, VCB_STATE_USN_DELETE )) {
  2445. NtfsReleaseVcb( IrpContext, Vcb );
  2446. NtfsCompleteRequest( IrpContext, Irp, STATUS_JOURNAL_DELETE_IN_PROGRESS );
  2447. return STATUS_JOURNAL_DELETE_IN_PROGRESS;
  2448. }
  2449. if (!FlagOn( Vcb->VcbState, VCB_STATE_USN_JOURNAL_ACTIVE )) {
  2450. NtfsReleaseVcb( IrpContext, Vcb );
  2451. NtfsCompleteRequest( IrpContext, Irp, STATUS_JOURNAL_NOT_ACTIVE );
  2452. return STATUS_JOURNAL_NOT_ACTIVE;
  2453. }
  2454. //
  2455. // Otherwise serialize with the Usn journal and copy the data from the journal Scb
  2456. // and Vcb.
  2457. //
  2458. NtfsAcquireSharedScb( IrpContext, Vcb->UsnJournal );
  2459. JournalData->UsnJournalID = Vcb->UsnJournalInstance.JournalId;
  2460. JournalData->FirstUsn = Vcb->FirstValidUsn;
  2461. JournalData->NextUsn = Vcb->UsnJournal->Header.FileSize.QuadPart;
  2462. JournalData->LowestValidUsn = Vcb->UsnJournalInstance.LowestValidUsn;
  2463. JournalData->MaxUsn = MAXFILESIZE;
  2464. JournalData->MaximumSize = Vcb->UsnJournalInstance.MaximumSize;
  2465. JournalData->AllocationDelta = Vcb->UsnJournalInstance.AllocationDelta;
  2466. NtfsReleaseScb( IrpContext, Vcb->UsnJournal );
  2467. ASSERT( JournalData->FirstUsn >= JournalData->LowestValidUsn );
  2468. NtfsReleaseVcb( IrpContext, Vcb );
  2469. Irp->IoStatus.Information = sizeof( USN_JOURNAL_DATA );
  2470. NtfsCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
  2471. return STATUS_SUCCESS;
  2472. }
  2473. NTSTATUS
  2474. NtfsDeleteUsnJournal (
  2475. IN PIRP_CONTEXT IrpContext,
  2476. IN PIRP Irp
  2477. )
  2478. /*++
  2479. Routine Description:
  2480. This routine is called when the user want to delete the current usn journal. This will
  2481. initiate the work to scan the Mft and reset all usn values to zero and remove the
  2482. UsnJournal file from the disk.
  2483. Arguments:
  2484. Irp - This is the Irp for the request.
  2485. Return Value:
  2486. NTSTATUS - Result for this request.
  2487. --*/
  2488. {
  2489. NTSTATUS Status = STATUS_SUCCESS;
  2490. PIO_STACK_LOCATION IrpSp;
  2491. PDELETE_USN_JOURNAL_DATA DeleteData;
  2492. PVCB Vcb;
  2493. PFCB Fcb;
  2494. PSCB Scb;
  2495. PCCB Ccb;
  2496. BOOLEAN VcbAcquired = FALSE;
  2497. BOOLEAN CheckpointHeld = FALSE;
  2498. BOOLEAN AcquiredNotify = FALSE;
  2499. PSCB ReleaseUsnJournal = NULL;
  2500. PLIST_ENTRY Links;
  2501. PAGED_CODE();
  2502. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  2503. //
  2504. // We always wait in this path.
  2505. //
  2506. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
  2507. //
  2508. // Perform a check on the input buffer.
  2509. //
  2510. if (Irp->AssociatedIrp.SystemBuffer != NULL) {
  2511. DeleteData = (PDELETE_USN_JOURNAL_DATA) Irp->AssociatedIrp.SystemBuffer;
  2512. } else if (Irp->MdlAddress != NULL) {
  2513. DeleteData = MmGetSystemAddressForMdlSafe( Irp->MdlAddress, NormalPagePriority );
  2514. if (DeleteData == NULL) {
  2515. NtfsCompleteRequest( IrpContext, Irp, STATUS_INSUFFICIENT_RESOURCES );
  2516. return STATUS_INSUFFICIENT_RESOURCES;
  2517. }
  2518. } else {
  2519. NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_USER_BUFFER );
  2520. return STATUS_INVALID_USER_BUFFER;
  2521. }
  2522. if (IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof( DELETE_USN_JOURNAL_DATA )) {
  2523. NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_USER_BUFFER );
  2524. return STATUS_INVALID_USER_BUFFER;
  2525. }
  2526. //
  2527. // Decode the file object type
  2528. //
  2529. NtfsDecodeFileObject( IrpContext,
  2530. IrpSp->FileObject,
  2531. &Vcb,
  2532. &Fcb,
  2533. &Scb,
  2534. &Ccb,
  2535. TRUE );
  2536. if ((Ccb == NULL) || !FlagOn( Ccb->AccessFlags, MANAGE_VOLUME_ACCESS)) {
  2537. NtfsCompleteRequest( IrpContext, Irp, STATUS_ACCESS_DENIED );
  2538. return STATUS_ACCESS_DENIED;
  2539. }
  2540. //
  2541. // We only support deleting and waiting for delete.
  2542. //
  2543. if (DeleteData->DeleteFlags == 0) {
  2544. NtfsCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
  2545. return STATUS_SUCCESS;
  2546. }
  2547. if (FlagOn( DeleteData->DeleteFlags, ~USN_DELETE_VALID_FLAGS )) {
  2548. NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
  2549. return STATUS_INVALID_PARAMETER;
  2550. }
  2551. if (NtfsIsVolumeReadOnly( Vcb )) {
  2552. NtfsCompleteRequest( IrpContext, Irp, STATUS_MEDIA_WRITE_PROTECTED );
  2553. return STATUS_MEDIA_WRITE_PROTECTED;
  2554. }
  2555. //
  2556. // Use a try-finally to facilitate cleanup.
  2557. //
  2558. try {
  2559. //
  2560. // Serialize with chkpoints and acquire the Vcb. We need to carefully remove
  2561. // the journal from the Vcb.
  2562. //
  2563. NtfsAcquireCheckpoint( IrpContext, Vcb );
  2564. while (FlagOn( Vcb->CheckpointFlags, VCB_CHECKPOINT_IN_PROGRESS )) {
  2565. //
  2566. // Release the checkpoint event because we cannot stop the log file now.
  2567. //
  2568. NtfsReleaseCheckpoint( IrpContext, Vcb );
  2569. NtfsWaitOnCheckpointNotify( IrpContext, Vcb );
  2570. NtfsAcquireCheckpoint( IrpContext, Vcb );
  2571. }
  2572. SetFlag( Vcb->CheckpointFlags, VCB_CHECKPOINT_IN_PROGRESS );
  2573. NtfsResetCheckpointNotify( IrpContext, Vcb );
  2574. NtfsReleaseCheckpoint( IrpContext, Vcb );
  2575. CheckpointHeld = TRUE;
  2576. NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
  2577. VcbAcquired = TRUE;
  2578. //
  2579. // Check that the volume is still mounted.
  2580. //
  2581. if (!FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
  2582. Status = STATUS_VOLUME_DISMOUNTED;
  2583. leave;
  2584. }
  2585. //
  2586. // If the user wants to delete the journal then make sure the delete hasn't
  2587. // already started.
  2588. //
  2589. if (FlagOn( DeleteData->DeleteFlags, USN_DELETE_FLAG_DELETE )) {
  2590. //
  2591. // If the journal is already being deleted and this caller wanted to
  2592. // do the delete then let him know it has already begun.
  2593. //
  2594. if (FlagOn( Vcb->VcbState, VCB_STATE_USN_DELETE )) {
  2595. Status = STATUS_JOURNAL_DELETE_IN_PROGRESS;
  2596. leave;
  2597. }
  2598. //
  2599. // Proceed with the delete if there is a Usn journal on disk.
  2600. //
  2601. if (FlagOn( Vcb->VcbState, VCB_STATE_USN_JOURNAL_PRESENT ) ||
  2602. (Vcb->UsnJournal != NULL)) {
  2603. PSCB UsnJournal = Vcb->UsnJournal;
  2604. //
  2605. // If the journal is running then the caller needs to match the journal ID.
  2606. //
  2607. if ((UsnJournal != NULL) &&
  2608. (DeleteData->UsnJournalID != Vcb->UsnJournalInstance.JournalId)) {
  2609. Status = STATUS_INVALID_PARAMETER;
  2610. leave;
  2611. }
  2612. //
  2613. // Write the bit to disk to indicate that the journal is being deleted.
  2614. // Checkpoint the transaction.
  2615. //
  2616. NtfsSetVolumeInfoFlagState( IrpContext,
  2617. Vcb,
  2618. VOLUME_DELETE_USN_UNDERWAY,
  2619. TRUE,
  2620. TRUE );
  2621. NtfsCheckpointCurrentTransaction( IrpContext );
  2622. //
  2623. // We are going to proceed with the delete. Clear the flag in the Vcb that
  2624. // indicates the journal is active. Then acquire and drop all of the files in
  2625. // order to serialize with anyone using the journal.
  2626. //
  2627. ClearFlag( Vcb->VcbState, VCB_STATE_USN_JOURNAL_ACTIVE );
  2628. NtfsAcquireAllFiles( IrpContext,
  2629. Vcb,
  2630. TRUE,
  2631. TRUE,
  2632. TRUE );
  2633. ReleaseUsnJournal = UsnJournal;
  2634. if (UsnJournal != NULL) {
  2635. NtfsAcquireExclusiveScb( IrpContext, UsnJournal );
  2636. }
  2637. //
  2638. // Set the delete flag in the Vcb and remove the journal from the Vcb.
  2639. //
  2640. SetFlag( Vcb->VcbState, VCB_STATE_USN_DELETE );
  2641. NtfsSetSegmentNumber( &Vcb->DeleteUsnData.DeleteUsnFileReference,
  2642. 0,
  2643. MASTER_FILE_TABLE_NUMBER );
  2644. Vcb->DeleteUsnData.DeleteUsnFileReference.SequenceNumber = 0;
  2645. Vcb->DeleteUsnData.DeleteState = 0;
  2646. Vcb->DeleteUsnData.PriorJournalScb = Vcb->UsnJournal;
  2647. Vcb->UsnJournal = NULL;
  2648. if (UsnJournal != NULL) {
  2649. //
  2650. // Let's purge the data in the Usn journal and clear the bias
  2651. // and file reference numbers in the Vcb.
  2652. //
  2653. CcPurgeCacheSection( &UsnJournal->NonpagedScb->SegmentObject,
  2654. NULL,
  2655. 0,
  2656. FALSE );
  2657. ClearFlag( UsnJournal->ScbPersist, SCB_PERSIST_USN_JOURNAL );
  2658. }
  2659. Vcb->UsnCacheBias = 0;
  2660. *((PLONGLONG) &Vcb->UsnJournalReference) = 0;
  2661. //
  2662. // Release the checkpoint if held.
  2663. //
  2664. NtfsAcquireCheckpoint( IrpContext, Vcb );
  2665. ClearFlag( Vcb->CheckpointFlags, VCB_CHECKPOINT_IN_PROGRESS );
  2666. NtfsSetCheckpointNotify( IrpContext, Vcb );
  2667. NtfsReleaseCheckpoint( IrpContext, Vcb );
  2668. CheckpointHeld = FALSE;
  2669. //
  2670. // Walk through the Irps waiting for new Usn data and cause them to be completed.
  2671. //
  2672. if (UsnJournal != NULL) {
  2673. PWAIT_FOR_NEW_LENGTH Waiter, NextWaiter;
  2674. ExAcquireFastMutex( UsnJournal->Header.FastMutex );
  2675. Waiter = (PWAIT_FOR_NEW_LENGTH) UsnJournal->ScbType.Data.WaitForNewLength.Flink;
  2676. while (Waiter != (PWAIT_FOR_NEW_LENGTH) &UsnJournal->ScbType.Data.WaitForNewLength) {
  2677. NextWaiter = (PWAIT_FOR_NEW_LENGTH) Waiter->WaitList.Flink;
  2678. //
  2679. // We want to complete all of the Irps on the waiting list. If cancel
  2680. // has already been called on the Irp we don't have to do anything.
  2681. // Otherwise complete the async Irps and signal the event on
  2682. // the sync irps.
  2683. //
  2684. if (NtfsClearCancelRoutine( Waiter->Irp )) {
  2685. //
  2686. // If this is an async request then complete the Irp.
  2687. //
  2688. if (FlagOn( Waiter->Flags, NTFS_WAIT_FLAG_ASYNC )) {
  2689. //
  2690. // Make sure we decrement the reference count in the Scb.
  2691. // Then remove the waiter from the queue and complete the Irp.
  2692. //
  2693. InterlockedDecrement( &UsnJournal->CloseCount );
  2694. RemoveEntryList( &Waiter->WaitList );
  2695. NtfsCompleteRequest( NULL, Waiter->Irp, STATUS_JOURNAL_DELETE_IN_PROGRESS );
  2696. NtfsFreePool( Waiter );
  2697. //
  2698. // This is a synch Irp. All we can do is set the event and note the status
  2699. // code.
  2700. //
  2701. } else {
  2702. Waiter->Status = STATUS_JOURNAL_DELETE_IN_PROGRESS;
  2703. KeSetEvent( &Waiter->Event, 0, FALSE );
  2704. }
  2705. }
  2706. Waiter = NextWaiter;
  2707. }
  2708. //
  2709. // Walk through all of the Fcb Usn records and deallocate them.
  2710. //
  2711. Links = Vcb->ModifiedOpenFiles.Flink;
  2712. while (Vcb->ModifiedOpenFiles.Flink != &Vcb->ModifiedOpenFiles) {
  2713. RemoveEntryList( Links );
  2714. Links->Flink = NULL;
  2715. //
  2716. // Look to see if we need to remove the TimeOut link as well.
  2717. //
  2718. Links = &(CONTAINING_RECORD( Links, FCB_USN_RECORD, ModifiedOpenFilesLinks ))->TimeOutLinks;
  2719. if (Links->Flink != NULL) {
  2720. RemoveEntryList( Links );
  2721. }
  2722. Links = Vcb->ModifiedOpenFiles.Flink;
  2723. }
  2724. ExReleaseFastMutex( UsnJournal->Header.FastMutex );
  2725. //
  2726. // Make sure remove our reference on the Usn journal.
  2727. //
  2728. NtOfsCloseAttributeSafe( IrpContext, UsnJournal );
  2729. ReleaseUsnJournal = NULL;
  2730. }
  2731. //
  2732. // If this caller wants to wait for this then acquire the notify
  2733. // mutex now.
  2734. //
  2735. if (FlagOn( DeleteData->DeleteFlags, USN_DELETE_FLAG_NOTIFY )) {
  2736. NtfsAcquireUsnNotify( Vcb );
  2737. AcquiredNotify = TRUE;
  2738. }
  2739. //
  2740. // Post the work item to do the rest of the delete.
  2741. //
  2742. NtfsPostSpecial( IrpContext, Vcb, NtfsDeleteUsnSpecial, &Vcb->DeleteUsnData );
  2743. }
  2744. }
  2745. //
  2746. // Check if our caller wants to wait for the delete to complete.
  2747. //
  2748. if (FlagOn( Vcb->VcbState, VCB_STATE_USN_DELETE ) &&
  2749. FlagOn( DeleteData->DeleteFlags, USN_DELETE_FLAG_NOTIFY )) {
  2750. if (!AcquiredNotify) {
  2751. NtfsAcquireUsnNotify( Vcb );
  2752. AcquiredNotify = TRUE;
  2753. }
  2754. Status = STATUS_PENDING;
  2755. if (!NtfsSetCancelRoutine( Irp,
  2756. NtfsCancelDeleteUsnJournal,
  2757. 0,
  2758. TRUE )) {
  2759. Status = STATUS_CANCELLED;
  2760. //
  2761. // Add it to the work queue if we were able to set the
  2762. // cancel routine.
  2763. //
  2764. } else {
  2765. InsertTailList( &Vcb->NotifyUsnDeleteIrps,
  2766. &Irp->Tail.Overlay.ListEntry );
  2767. }
  2768. NtfsReleaseUsnNotify( Vcb );
  2769. AcquiredNotify = FALSE;
  2770. }
  2771. } finally {
  2772. if (AcquiredNotify) {
  2773. NtfsReleaseUsnNotify( Vcb );
  2774. }
  2775. //
  2776. // Release the Usn journal if held.
  2777. //
  2778. if (ReleaseUsnJournal) {
  2779. NtfsReleaseScb( IrpContext, ReleaseUsnJournal );
  2780. }
  2781. //
  2782. // Release the Vcb if held.
  2783. //
  2784. if (VcbAcquired) {
  2785. NtfsReleaseVcb( IrpContext, Vcb );
  2786. }
  2787. //
  2788. // Release the checkpoint if held.
  2789. //
  2790. if (CheckpointHeld) {
  2791. NtfsAcquireCheckpoint( IrpContext, Vcb );
  2792. ClearFlag( Vcb->CheckpointFlags, VCB_CHECKPOINT_IN_PROGRESS );
  2793. NtfsSetCheckpointNotify( IrpContext, Vcb );
  2794. NtfsReleaseCheckpoint( IrpContext, Vcb );
  2795. }
  2796. }
  2797. //
  2798. // Complete the irp as appropriate.
  2799. //
  2800. NtfsCompleteRequest( IrpContext,
  2801. (Status == STATUS_PENDING) ? NULL : Irp,
  2802. Status );
  2803. return Status;
  2804. }
  2805. VOID
  2806. NtfsDeleteUsnSpecial (
  2807. IN PIRP_CONTEXT IrpContext,
  2808. IN PVOID Context
  2809. )
  2810. /*++
  2811. Routine Description:
  2812. This routine is called to perform the work of deleting a Usn journal for a volume.
  2813. It is called after the original entry point has done the preliminary work of stopping
  2814. future journal activity and cleaning up active journal requests. Once we reach this
  2815. point then this routine will make sure the Mft values are reset, delete the journal
  2816. file itself and wake up anyone waiting for the delete journal to complete.
  2817. Arguments:
  2818. IrpContext - context of the call
  2819. Context - DELETE_USN_CONTEXT structure used to manage the delete.
  2820. Return Value:
  2821. None
  2822. --*/
  2823. {
  2824. NTSTATUS Status = STATUS_SUCCESS;
  2825. PNTFS_DELETE_JOURNAL_DATA DeleteData = (PNTFS_DELETE_JOURNAL_DATA) Context;
  2826. ULONG AcquiredVcb = FALSE;
  2827. PVCB Vcb = IrpContext->Vcb;
  2828. PFCB UsnFcb = NULL;
  2829. BOOLEAN AcquiredExtendDirectory = FALSE;
  2830. PIRP UsnNotifyIrp;
  2831. PLIST_ENTRY Links;
  2832. PSCB Scb;
  2833. PFCB Fcb;
  2834. PAGED_CODE();
  2835. //
  2836. // Use a try-except to catch errors.
  2837. //
  2838. try {
  2839. if (NtfsIsVolumeReadOnly( Vcb )) {
  2840. Vcb->DeleteUsnData.FinalStatus = STATUS_MEDIA_WRITE_PROTECTED;
  2841. NtfsRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED, NULL, NULL );
  2842. }
  2843. //
  2844. // Make sure to walk the Mft to set the Usn value back to zero.
  2845. //
  2846. if (!FlagOn( DeleteData->DeleteState, DELETE_USN_RESET_MFT )) {
  2847. try {
  2848. Status = NtfsIterateMft( IrpContext,
  2849. IrpContext->Vcb,
  2850. &DeleteData->DeleteUsnFileReference,
  2851. NtfsDeleteUsnWorker,
  2852. Context );
  2853. } except (NtfsCleanupExceptionFilter( IrpContext, GetExceptionInformation(), &Status )) {
  2854. NOTHING;
  2855. }
  2856. if (!NT_SUCCESS( Status ) && (Status != STATUS_END_OF_FILE)) {
  2857. //
  2858. // If the operation is going to fail then decide if this is retryable.
  2859. //
  2860. if (Status == STATUS_VOLUME_DISMOUNTED) {
  2861. Vcb->DeleteUsnData.FinalStatus = STATUS_VOLUME_DISMOUNTED;
  2862. } else if ((Status != STATUS_LOG_FILE_FULL) &&
  2863. (Status != STATUS_CANT_WAIT)) {
  2864. Vcb->DeleteUsnData.FinalStatus = Status;
  2865. //
  2866. // Set all the flags for delete operations so we stop at this point.
  2867. //
  2868. SetFlag( DeleteData->DeleteState,
  2869. DELETE_USN_RESET_MFT | DELETE_USN_REMOVE_JOURNAL | DELETE_USN_FINAL_CLEANUP );
  2870. Status = STATUS_CANT_WAIT;
  2871. }
  2872. NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
  2873. }
  2874. Status = STATUS_SUCCESS;
  2875. NtfsPurgeFileRecordCache( IrpContext );
  2876. NtfsCheckpointCurrentTransaction( IrpContext );
  2877. SetFlag( DeleteData->DeleteState, DELETE_USN_RESET_MFT );
  2878. }
  2879. NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
  2880. AcquiredVcb = TRUE;
  2881. //
  2882. // If the volume is no longer available then raise STATUS_VOLUME_DISMOUNTED. Someone
  2883. // else will find all of the waiters.
  2884. //
  2885. if (!NtfsIsVcbAvailable( Vcb )) {
  2886. Vcb->DeleteUsnData.FinalStatus = STATUS_VOLUME_DISMOUNTED;
  2887. NtfsRaiseStatus( IrpContext, STATUS_VOLUME_DISMOUNTED, NULL, NULL );
  2888. }
  2889. //
  2890. // The next step is to remove the file if present.
  2891. //
  2892. if (!FlagOn( DeleteData->DeleteState, DELETE_USN_REMOVE_JOURNAL )) {
  2893. try {
  2894. if (Vcb->ExtendDirectory != NULL) {
  2895. NtfsAcquireExclusiveScb( IrpContext, Vcb->ExtendDirectory );
  2896. AcquiredExtendDirectory = TRUE;
  2897. UsnFcb = NtfsInitializeFileInExtendDirectory( IrpContext,
  2898. Vcb,
  2899. &NtfsUsnJrnlName,
  2900. FALSE,
  2901. FALSE );
  2902. if (UsnFcb != NULL) {
  2903. //
  2904. // For lock order acquire in canonical order after unsafe try
  2905. //
  2906. if (!NtfsAcquireExclusiveFcb( IrpContext, UsnFcb, NULL, ACQUIRE_NO_DELETE_CHECK | ACQUIRE_DONT_WAIT)) {
  2907. NtfsReleaseScb( IrpContext, Vcb->ExtendDirectory );
  2908. NtfsAcquireExclusiveFcb( IrpContext, UsnFcb, NULL, ACQUIRE_NO_DELETE_CHECK );
  2909. NtfsAcquireExclusiveScb( IrpContext, Vcb->ExtendDirectory );
  2910. }
  2911. NtfsDeleteFile( IrpContext,
  2912. UsnFcb,
  2913. Vcb->ExtendDirectory,
  2914. &AcquiredExtendDirectory,
  2915. NULL,
  2916. NULL );
  2917. NtfsPurgeFileRecordCache( IrpContext );
  2918. NtfsCheckpointCurrentTransaction( IrpContext );
  2919. ClearFlag( UsnFcb->FcbState, FCB_STATE_SYSTEM_FILE );
  2920. SetFlag( UsnFcb->FcbState, FCB_STATE_FILE_DELETED );
  2921. //
  2922. // Walk all of the Scbs for this file and mark them
  2923. // deleted. This will keep the lazy writer from trying to
  2924. // flush them.
  2925. //
  2926. Links = UsnFcb->ScbQueue.Flink;
  2927. while (Links != &UsnFcb->ScbQueue) {
  2928. Scb = CONTAINING_RECORD( Links, SCB, FcbLinks );
  2929. //
  2930. // Recover the reservation for the Scb now instead of waiting for it
  2931. // to go away.
  2932. //
  2933. if ((Scb->AttributeTypeCode == $DATA) &&
  2934. (Scb->ScbType.Data.TotalReserved != 0)) {
  2935. NtfsAcquireReservedClusters( Vcb );
  2936. Vcb->TotalReserved -= LlClustersFromBytes( Vcb,
  2937. Scb->ScbType.Data.TotalReserved );
  2938. Scb->ScbType.Data.TotalReserved = 0;
  2939. NtfsReleaseReservedClusters( Vcb );
  2940. }
  2941. Scb->ValidDataToDisk =
  2942. Scb->Header.AllocationSize.QuadPart =
  2943. Scb->Header.FileSize.QuadPart =
  2944. Scb->Header.ValidDataLength.QuadPart = 0;
  2945. Scb->AttributeTypeCode = $UNUSED;
  2946. SetFlag( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED );
  2947. Links = Links->Flink;
  2948. }
  2949. //
  2950. // Now teardown the Fcb.
  2951. //
  2952. NtfsTeardownStructures( IrpContext,
  2953. UsnFcb,
  2954. NULL,
  2955. FALSE,
  2956. ACQUIRE_NO_DELETE_CHECK,
  2957. NULL );
  2958. }
  2959. }
  2960. } except (NtfsCleanupExceptionFilter( IrpContext, GetExceptionInformation(), &Status )) {
  2961. //
  2962. // We hit some failure and can't complete the operation.
  2963. // Remember the error, set the flags in the delete Usn structure
  2964. // and raise CANT_WAIT so we can abort and then do the final cleanup.
  2965. //
  2966. Vcb->DeleteUsnData.FinalStatus = Status;
  2967. //
  2968. // Set all the flags for delete operations so we stop at this point.
  2969. //
  2970. SetFlag( DeleteData->DeleteState,
  2971. DELETE_USN_RESET_MFT | DELETE_USN_REMOVE_JOURNAL | DELETE_USN_FINAL_CLEANUP );
  2972. Status = STATUS_CANT_WAIT;
  2973. }
  2974. if (!NT_SUCCESS( Status )) {
  2975. NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
  2976. }
  2977. SetFlag( DeleteData->DeleteState, DELETE_USN_REMOVE_JOURNAL );
  2978. }
  2979. if (!FlagOn( DeleteData->DeleteState, DELETE_USN_FINAL_CLEANUP )) {
  2980. //
  2981. // Clear the on-disk flag indicating the delete is in progress.
  2982. //
  2983. try {
  2984. NtfsSetVolumeInfoFlagState( IrpContext,
  2985. Vcb,
  2986. VOLUME_DELETE_USN_UNDERWAY,
  2987. FALSE,
  2988. TRUE );
  2989. } except (NtfsCleanupExceptionFilter( IrpContext, GetExceptionInformation(), &Status )) {
  2990. //
  2991. // We hit some failure and can't complete the operation.
  2992. // Remember the error, set the flags in the delete Usn structure
  2993. // and raise CANT_WAIT so we can abort and then do the final cleanup.
  2994. //
  2995. Vcb->DeleteUsnData.FinalStatus = Status;
  2996. //
  2997. // Set all the flags for delete operations so we stop at this point.
  2998. //
  2999. SetFlag( DeleteData->DeleteState,
  3000. DELETE_USN_RESET_MFT | DELETE_USN_REMOVE_JOURNAL | DELETE_USN_FINAL_CLEANUP );
  3001. Status = STATUS_CANT_WAIT;
  3002. }
  3003. if (!NT_SUCCESS( Status )) {
  3004. NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
  3005. }
  3006. }
  3007. //
  3008. // Make sure we don't own any resources at this point.
  3009. //
  3010. NtfsPurgeFileRecordCache( IrpContext );
  3011. NtfsCheckpointCurrentTransaction( IrpContext );
  3012. //
  3013. // Finally, now that we have written the forget record, we can free
  3014. // any exclusive Scbs that we have been holding.
  3015. //
  3016. while (!IsListEmpty(&IrpContext->ExclusiveFcbList)) {
  3017. Fcb = (PFCB)CONTAINING_RECORD(IrpContext->ExclusiveFcbList.Flink,
  3018. FCB,
  3019. ExclusiveFcbLinks );
  3020. NtfsReleaseFcb( IrpContext, Fcb );
  3021. }
  3022. //
  3023. // Remember any saved status code.
  3024. //
  3025. if (Vcb->DeleteUsnData.FinalStatus != STATUS_SUCCESS) {
  3026. Status = Vcb->DeleteUsnData.FinalStatus;
  3027. //
  3028. // Since we failed make sure to leave the flag set in the Vcb which indicates the
  3029. // incomplete delete.
  3030. //
  3031. SetFlag( Vcb->VcbState, VCB_STATE_INCOMPLETE_USN_DELETE );
  3032. }
  3033. //
  3034. // Cleanup the context and flags in the Vcb.
  3035. //
  3036. RtlZeroMemory( &Vcb->DeleteUsnData, sizeof( NTFS_DELETE_JOURNAL_DATA ));
  3037. RtlZeroMemory( &Vcb->UsnJournalInstance, sizeof( USN_JOURNAL_INSTANCE ));
  3038. Vcb->FirstValidUsn = 0;
  3039. Vcb->LowestOpenUsn = 0;
  3040. ClearFlag( Vcb->VcbState, VCB_STATE_USN_JOURNAL_PRESENT | VCB_STATE_USN_DELETE );
  3041. //
  3042. // Finally complete all of the waiting Irps in the Usn notify queue.
  3043. //
  3044. NtfsAcquireUsnNotify( Vcb );
  3045. Links = Vcb->NotifyUsnDeleteIrps.Flink;
  3046. while (Links != &Vcb->NotifyUsnDeleteIrps) {
  3047. UsnNotifyIrp = CONTAINING_RECORD( Links,
  3048. IRP,
  3049. Tail.Overlay.ListEntry );
  3050. //
  3051. // Remember to move forward in any case.
  3052. //
  3053. Links = Links->Flink;
  3054. //
  3055. // Clear the notify routine and detect if cancel has
  3056. // already been called.
  3057. //
  3058. if (NtfsClearCancelRoutine( UsnNotifyIrp )) {
  3059. RemoveEntryList( &UsnNotifyIrp->Tail.Overlay.ListEntry );
  3060. NtfsCompleteRequest( NULL, UsnNotifyIrp, Status );
  3061. }
  3062. }
  3063. NtfsReleaseUsnNotify( Vcb );
  3064. } except( NtfsExceptionFilter( IrpContext, GetExceptionInformation())) {
  3065. Status = IrpContext->TopLevelIrpContext->ExceptionStatus;
  3066. }
  3067. if (AcquiredVcb) {
  3068. NtfsReleaseVcb( IrpContext, Vcb );
  3069. }
  3070. //
  3071. // If this is a fatal failure then do any final cleanup.
  3072. //
  3073. if (!NT_SUCCESS( Status )) {
  3074. NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
  3075. }
  3076. return;
  3077. UNREFERENCED_PARAMETER( Context );
  3078. }
  3079. //
  3080. // Local support routine
  3081. //
  3082. RTL_GENERIC_COMPARE_RESULTS
  3083. NtfsUsnTableCompare (
  3084. IN PRTL_GENERIC_TABLE Table,
  3085. PVOID FirstStruct,
  3086. PVOID SecondStruct
  3087. )
  3088. /*++
  3089. Routine Description:
  3090. This is a generic table support routine to compare two File References
  3091. in Usn Records.
  3092. Arguments:
  3093. Table - Supplies the generic table being queried. Not used.
  3094. FirstStruct - Supplies the first Usn Record to compare
  3095. SecondStruct - Supplies the second Usn Record to compare
  3096. Return Value:
  3097. RTL_GENERIC_COMPARE_RESULTS - The results of comparing the two
  3098. input structures
  3099. --*/
  3100. {
  3101. PAGED_CODE();
  3102. if (*((PLONGLONG) &((PUSN_RECORD) FirstStruct)->FileReferenceNumber) <
  3103. *((PLONGLONG) &((PUSN_RECORD) SecondStruct)->FileReferenceNumber)) {
  3104. return GenericLessThan;
  3105. }
  3106. if (*((PLONGLONG) &((PUSN_RECORD) FirstStruct)->FileReferenceNumber) >
  3107. *((PLONGLONG) &((PUSN_RECORD) SecondStruct)->FileReferenceNumber)) {
  3108. return GenericGreaterThan;
  3109. }
  3110. return GenericEqual;
  3111. UNREFERENCED_PARAMETER( Table );
  3112. }
  3113. //
  3114. // Local support routine
  3115. //
  3116. PVOID
  3117. NtfsUsnTableAllocate (
  3118. IN PRTL_GENERIC_TABLE Table,
  3119. CLONG ByteSize
  3120. )
  3121. /*++
  3122. Routine Description:
  3123. This is a generic table support routine to allocate memory
  3124. Arguments:
  3125. Table - Supplies the generic table being used
  3126. ByteSize - Supplies the number of bytes to allocate
  3127. Return Value:
  3128. PVOID - Returns a pointer to the allocated data
  3129. --*/
  3130. {
  3131. UNREFERENCED_PARAMETER( Table );
  3132. PAGED_CODE();
  3133. return NtfsAllocatePool( PagedPool, ByteSize );
  3134. }
  3135. //
  3136. // Local support routine
  3137. //
  3138. VOID
  3139. NtfsUsnTableFree (
  3140. IN PRTL_GENERIC_TABLE Table,
  3141. IN PVOID Buffer
  3142. )
  3143. /*++
  3144. Routine Description:
  3145. This is a generic table support routine to free memory
  3146. Arguments:
  3147. Table - Supplies the generic table being used
  3148. Buffer - Supplies pointer to the buffer to be freed
  3149. Return Value:
  3150. None
  3151. --*/
  3152. {
  3153. UNREFERENCED_PARAMETER( Table );
  3154. PAGED_CODE();
  3155. NtfsFreePool( Buffer );
  3156. }
  3157. //
  3158. // Local support routine
  3159. //
  3160. VOID
  3161. NtfsCancelReadUsnJournal (
  3162. IN PDEVICE_OBJECT DeviceObject,
  3163. IN PIRP Irp
  3164. )
  3165. /*++
  3166. Routine Description:
  3167. This routine may be called by the I/O system to cancel an outstanding
  3168. Irp in NtfsReadUsnJournal.
  3169. Arguments:
  3170. DeviceObject - DeviceObject from I/O system
  3171. Irp - Supplies the pointer to the Irp being canceled.
  3172. Return Value:
  3173. None
  3174. --*/
  3175. {
  3176. PWAIT_FOR_NEW_LENGTH WaitForNewLength;
  3177. IoSetCancelRoutine( Irp, NULL );
  3178. IoReleaseCancelSpinLock( Irp->CancelIrql );
  3179. //
  3180. // Capture the Wait block out of the Status field. We know the Irp can't
  3181. // go away at this point.
  3182. //
  3183. WaitForNewLength = (PWAIT_FOR_NEW_LENGTH) Irp->IoStatus.Information;
  3184. Irp->IoStatus.Information = 0;
  3185. //
  3186. // Take a different action depending on whether we are completing the irp
  3187. // or simply signaling the cancel.
  3188. //
  3189. //
  3190. // This is the async case. We can simply complete this irp.
  3191. //
  3192. if (FlagOn( WaitForNewLength->Flags, NTFS_WAIT_FLAG_ASYNC )) {
  3193. //
  3194. // Acquire the mutex in order to remove this from the list and complete
  3195. // the Irp.
  3196. //
  3197. ExAcquireFastMutex( WaitForNewLength->Stream->Header.FastMutex );
  3198. RemoveEntryList( &WaitForNewLength->WaitList );
  3199. ExReleaseFastMutex( WaitForNewLength->Stream->Header.FastMutex );
  3200. InterlockedDecrement( &WaitForNewLength->Stream->CloseCount );
  3201. NtfsCompleteRequest( NULL, Irp, STATUS_CANCELLED );
  3202. NtfsFreePool( WaitForNewLength );
  3203. //
  3204. // If there is not an Irp we simply signal the event and let someone else
  3205. // do the work. This is the synchronous case.
  3206. //
  3207. } else {
  3208. WaitForNewLength->Status = STATUS_CANCELLED;
  3209. KeSetEvent( &WaitForNewLength->Event, 0, FALSE );
  3210. }
  3211. return;
  3212. UNREFERENCED_PARAMETER( DeviceObject );
  3213. }
  3214. //
  3215. // Local support routine
  3216. //
  3217. VOID
  3218. NtfsCancelDeleteUsnJournal (
  3219. IN PDEVICE_OBJECT DeviceObject,
  3220. IN PIRP Irp
  3221. )
  3222. /*++
  3223. Routine Description:
  3224. This routine may be called by the I/O system to cancel an outstanding
  3225. Irp waiting for the usn journal to be deleted.
  3226. Arguments:
  3227. DeviceObject - DeviceObject from I/O system
  3228. Irp - Supplies the pointer to the Irp being canceled.
  3229. Return Value:
  3230. None
  3231. --*/
  3232. {
  3233. PIO_STACK_LOCATION IrpSp;
  3234. PFILE_OBJECT FileObject;
  3235. PVCB Vcb;
  3236. //
  3237. // Block out future cancels.
  3238. //
  3239. IoSetCancelRoutine( Irp, NULL );
  3240. IoReleaseCancelSpinLock( Irp->CancelIrql );
  3241. //
  3242. // Get the current Irp stack location and save some references.
  3243. //
  3244. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  3245. //
  3246. // Capture the Vcb so we can do the necessary synchronization.
  3247. //
  3248. FileObject = IrpSp->FileObject;
  3249. Vcb = ((PSCB)(FileObject->FsContext))->Vcb;
  3250. //
  3251. // Acquire the list and remove the Irp. Complete the Irp with
  3252. // STATUS_CANCELLED.
  3253. //
  3254. NtfsAcquireUsnNotify( Vcb );
  3255. RemoveEntryList( &Irp->Tail.Overlay.ListEntry );
  3256. NtfsReleaseUsnNotify( Vcb );
  3257. Irp->IoStatus.Information = 0;
  3258. NtfsCompleteRequest( NULL, Irp, STATUS_CANCELLED );
  3259. return;
  3260. UNREFERENCED_PARAMETER( DeviceObject );
  3261. }
  3262. //
  3263. // Local support routine
  3264. //
  3265. NTSTATUS
  3266. NtfsDeleteUsnWorker (
  3267. IN PIRP_CONTEXT IrpContext,
  3268. IN PFCB Fcb,
  3269. IN PVOID Context
  3270. )
  3271. /*++
  3272. Routine Description:
  3273. This routines resets the Usn in the file record for the Fcb to zero.
  3274. Arguments:
  3275. IrpContext - context of the call
  3276. Fcb - Fcb for the file record to clear
  3277. Context - Unused
  3278. Return Value:
  3279. NTSTATUS - The return status for the operation
  3280. --*/
  3281. {
  3282. NTSTATUS Status = STATUS_SUCCESS;
  3283. PVOID UsnRecord;
  3284. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  3285. PATTRIBUTE_RECORD_HEADER Attribute;
  3286. STANDARD_INFORMATION NewStandardInformation;
  3287. USN Usn = 0;
  3288. PAGED_CODE();
  3289. //
  3290. // Initialize the search context.
  3291. //
  3292. NtfsInitializeAttributeContext( &AttrContext );
  3293. //
  3294. // Use a try-except to catch all of the errors.
  3295. //
  3296. try {
  3297. //
  3298. // Use a try-finally to facilitate cleanup.
  3299. //
  3300. try {
  3301. //
  3302. // Look up the standard information attribute and modify the usn field if
  3303. // the attribute is found and it is a large standard attribute.
  3304. //
  3305. if (FlagOn( Fcb->FcbState, FCB_STATE_LARGE_STD_INFO ) &&
  3306. NtfsLookupAttributeByCode( IrpContext,
  3307. Fcb,
  3308. &Fcb->FileReference,
  3309. $STANDARD_INFORMATION,
  3310. &AttrContext )) {
  3311. Attribute = NtfsFoundAttribute( &AttrContext );
  3312. if (((PSTANDARD_INFORMATION) NtfsAttributeValue( Attribute ))->Usn != 0) {
  3313. RtlCopyMemory( &NewStandardInformation,
  3314. NtfsAttributeValue( Attribute ),
  3315. sizeof( STANDARD_INFORMATION ));
  3316. NewStandardInformation.Usn = 0;
  3317. NtfsChangeAttributeValue( IrpContext,
  3318. Fcb,
  3319. 0,
  3320. &NewStandardInformation,
  3321. sizeof( STANDARD_INFORMATION ),
  3322. FALSE,
  3323. FALSE,
  3324. FALSE,
  3325. FALSE,
  3326. &AttrContext );
  3327. }
  3328. }
  3329. //
  3330. // Make sure the Fcb reflects this change.
  3331. //
  3332. NtfsLockFcb( IrpContext, Fcb );
  3333. Fcb->Usn = 0;
  3334. UsnRecord = Fcb->FcbUsnRecord;
  3335. Fcb->FcbUsnRecord = NULL;
  3336. NtfsUnlockFcb( IrpContext, Fcb );
  3337. if (UsnRecord != NULL) {
  3338. NtfsFreePool( UsnRecord );
  3339. }
  3340. } finally {
  3341. //
  3342. // Be sure to clean up the context.
  3343. //
  3344. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  3345. }
  3346. //
  3347. // We want to swallow any expected errors except LOG_FILE_FULL and CANT_WAIT.
  3348. //
  3349. } except ((FsRtlIsNtstatusExpected( Status = GetExceptionCode()) &&
  3350. (Status != STATUS_LOG_FILE_FULL) &&
  3351. (Status != STATUS_CANT_WAIT)) ?
  3352. EXCEPTION_EXECUTE_HANDLER :
  3353. EXCEPTION_CONTINUE_SEARCH) {
  3354. NOTHING;
  3355. }
  3356. //
  3357. // Always return success from this routine.
  3358. //
  3359. IrpContext->ExceptionStatus = STATUS_SUCCESS;
  3360. return STATUS_SUCCESS;
  3361. UNREFERENCED_PARAMETER( Context );
  3362. }
  3363. //
  3364. // Local support routine
  3365. //
  3366. BOOLEAN
  3367. NtfsValidateUsnPage (
  3368. IN PUSN_RECORD UsnRecord,
  3369. IN USN PageUsn,
  3370. IN USN *UserStartUsn OPTIONAL,
  3371. IN LONGLONG UsnFileSize,
  3372. OUT PBOOLEAN ValidUserStartUsn OPTIONAL,
  3373. OUT USN *NextUsn
  3374. )
  3375. /*++
  3376. Routine Description:
  3377. This routine checks the offsets within a single page of the usn journal. This allows the caller to
  3378. then walk safely through the page.
  3379. Arguments:
  3380. UsnRecord - Pointer to the start of the Usn page.
  3381. PageUsn - This is the Usn for the first record of the page.
  3382. UserStartUsn - If specified then do an additional check that the user's specified usn in fact
  3383. lies correctly on this page. The output boolean must also be specified if this is.
  3384. UsnFileSize - This is the current size of the usn journal. If we are looking at the last page then
  3385. we only check to this point.
  3386. ValidUserStartUsn - Address to result of check on user specified start Usn.
  3387. NextUsn - This is the Usn past the valid portion of the page. It will point to a position on the
  3388. current page unless the last record on the page completely fills the page. If the page isn't valid
  3389. then it points to the position where the invalid record was detected.
  3390. Return Value:
  3391. BOOLEAN - TRUE if the page is valid until a legal terminating condition. FALSE if there is internal
  3392. corruption on the page.
  3393. --*/
  3394. {
  3395. ULONG RemainingPageBytes;
  3396. ULONG RecordLength;
  3397. BOOLEAN ValidPage = TRUE;
  3398. BOOLEAN FoundEntry = FALSE;
  3399. PAGED_CODE();
  3400. //
  3401. // Verify a few input values.
  3402. //
  3403. ASSERT( UsnFileSize > PageUsn );
  3404. ASSERT( !FlagOn( *((PULONG) &UsnRecord), USN_PAGE_SIZE - 1 ));
  3405. ASSERT( !ARGUMENT_PRESENT( UserStartUsn ) || ARGUMENT_PRESENT( ValidUserStartUsn ));
  3406. ASSERT( !ARGUMENT_PRESENT( ValidUserStartUsn ) || ARGUMENT_PRESENT( UserStartUsn ));
  3407. //
  3408. // Compute the Usn past the valid data on this page. It is either the end of the journal or
  3409. // the next page of the journal.
  3410. //
  3411. RemainingPageBytes = USN_PAGE_SIZE;
  3412. if (UsnFileSize < (PageUsn + USN_PAGE_SIZE)) {
  3413. RemainingPageBytes = (ULONG) (UsnFileSize - PageUsn);
  3414. }
  3415. //
  3416. // Assume the user's Usn is invalid unless it wasn't specified.
  3417. //
  3418. if (!ARGUMENT_PRESENT( ValidUserStartUsn )) {
  3419. ValidUserStartUsn = (PBOOLEAN) NtfsAllocateFromStack( sizeof( BOOLEAN ));
  3420. *ValidUserStartUsn = TRUE;
  3421. } else {
  3422. *ValidUserStartUsn = FALSE;
  3423. }
  3424. //
  3425. // Keep track of our current position in the page with the user's pointer.
  3426. //
  3427. *NextUsn = PageUsn;
  3428. //
  3429. // Check each entry in the page for the following.
  3430. //
  3431. // 1 - Fixed portion of the header won't fit within the remaining bytes on the page.
  3432. // 2 - Record header is zeroed.
  3433. // 3 - Record length is not quad-aligned.
  3434. // 4 - Record length is larger than the remaining bytes on the page.
  3435. // 5 - Usn on the page doesn't match the computed value.
  3436. //
  3437. while (RemainingPageBytes != 0) {
  3438. //
  3439. // Not enough bytes even for the full Usn header.
  3440. //
  3441. if (RemainingPageBytes < (FIELD_OFFSET( USN_RECORD, FileName ) + sizeof( WCHAR ))) {
  3442. //
  3443. // If there is at least a ulong it better be zeroed.
  3444. //
  3445. if ((RemainingPageBytes >= sizeof( ULONG )) &&
  3446. (UsnRecord->RecordLength != 0)) {
  3447. ValidPage = FALSE;
  3448. //
  3449. // If the user's Usn points to this offset then it is valid.
  3450. //
  3451. } else if (!(*ValidUserStartUsn) &&
  3452. (*NextUsn == *UserStartUsn)) {
  3453. *ValidUserStartUsn = TRUE;
  3454. }
  3455. break;
  3456. }
  3457. //
  3458. // There should be at least one entry on the page. We attempt to detect
  3459. // a local loss of data through zeroing but won't check to the end of
  3460. // the page.
  3461. //
  3462. RecordLength = UsnRecord->RecordLength;
  3463. if (RecordLength == 0) {
  3464. //
  3465. // Fail if we haven't found at least one entry.
  3466. //
  3467. if (!FoundEntry) {
  3468. ValidPage = FALSE;
  3469. //
  3470. // We know we should be dealing with the tail of the page. It should
  3471. // be zeroed through the fixed portion of a Usn record. Theoretically
  3472. // it should be zeroed to the end of the page but we will assume that we
  3473. // are only looking for local corruption. If we lost data through the
  3474. // end of the page we can't detect it anyway.
  3475. //
  3476. } else {
  3477. PCHAR CurrentByte = (PCHAR) UsnRecord;
  3478. ULONG Count = FIELD_OFFSET( USN_RECORD, FileName ) + sizeof( WCHAR );
  3479. while (Count != 0) {
  3480. if (*CurrentByte != 0) {
  3481. ValidPage = FALSE;
  3482. break;
  3483. }
  3484. Count -= 1;
  3485. CurrentByte += 1;
  3486. }
  3487. //
  3488. // If the page is valid then check if the user's Usn is at this point. It is
  3489. // legal for him to specify the point where the zeroes begin.
  3490. //
  3491. if (ValidPage &&
  3492. !(*ValidUserStartUsn) &&
  3493. (*NextUsn == *UserStartUsn)) {
  3494. *ValidUserStartUsn = TRUE;
  3495. }
  3496. }
  3497. break;
  3498. }
  3499. //
  3500. // Invalid if record length is not-quad aligned or is larger than
  3501. // remaining bytes on the page.
  3502. //
  3503. if (FlagOn( RecordLength, sizeof( ULONGLONG ) - 1 ) ||
  3504. (RecordLength > RemainingPageBytes)) {
  3505. ValidPage = FALSE;
  3506. break;
  3507. }
  3508. //
  3509. // Now check that the Usn is the expected value.
  3510. //
  3511. if (UsnRecord->Usn != *NextUsn) {
  3512. ValidPage = FALSE;
  3513. break;
  3514. }
  3515. //
  3516. // Remember that we found a valid entry.
  3517. //
  3518. FoundEntry = TRUE;
  3519. //
  3520. // If the user's Usn matches this one then remember his is valid.
  3521. //
  3522. if (!(*ValidUserStartUsn) &&
  3523. (*NextUsn == *UserStartUsn)) {
  3524. *ValidUserStartUsn = TRUE;
  3525. }
  3526. //
  3527. // Advance to the next record in the page.
  3528. //
  3529. UsnRecord = Add2Ptr( UsnRecord, RecordLength );
  3530. RemainingPageBytes -= RecordLength;
  3531. *NextUsn += RecordLength;
  3532. }
  3533. return ValidPage;
  3534. }