Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3924 lines
149 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. Write.c
  5. Abstract:
  6. This module implements the File Write routine for Ntfs called by the
  7. dispatch driver.
  8. Author:
  9. Brian Andrew BrianAn 19-Aug-1991
  10. Revision History:
  11. --*/
  12. #include "NtfsProc.h"
  13. //
  14. // The local debug trace level
  15. //
  16. #define Dbg (DEBUG_TRACE_WRITE)
  17. #ifdef NTFS_RWC_DEBUG
  18. PRWC_HISTORY_ENTRY
  19. NtfsGetHistoryEntry (
  20. IN PSCB Scb
  21. );
  22. #endif
  23. //
  24. // Define a tag for general pool allocations from this module
  25. //
  26. #undef MODULE_POOL_TAG
  27. #define MODULE_POOL_TAG ('WFtN')
  28. #define OVERFLOW_WRITE_THRESHHOLD (0x1a00)
  29. #define CollectWriteStats(VCB,OPEN_TYPE,SCB,FCB,BYTE_COUNT,IRP_CONTEXT,TLIC) { \
  30. PFILE_SYSTEM_STATISTICS FsStats = &(VCB)->Statistics[KeGetCurrentProcessorNumber()]; \
  31. if (!FlagOn( (FCB)->FcbState, FCB_STATE_SYSTEM_FILE )) { \
  32. if (NtfsIsTypeCodeUserData( (SCB)->AttributeTypeCode )) { \
  33. FsStats->Common.UserFileWrites += 1; \
  34. FsStats->Common.UserFileWriteBytes += (ULONG)(BYTE_COUNT); \
  35. } else { \
  36. FsStats->Ntfs.UserIndexWrites += 1; \
  37. FsStats->Ntfs.UserIndexWriteBytes += (ULONG)(BYTE_COUNT); \
  38. } \
  39. } else { \
  40. if ((SCB) != (VCB)->LogFileScb) { \
  41. FsStats->Common.MetaDataWrites += 1; \
  42. FsStats->Common.MetaDataWriteBytes += (ULONG)(BYTE_COUNT); \
  43. } else { \
  44. FsStats->Ntfs.LogFileWrites += 1; \
  45. FsStats->Ntfs.LogFileWriteBytes += (ULONG)(BYTE_COUNT); \
  46. } \
  47. \
  48. if ((SCB) == (VCB)->MftScb) { \
  49. FsStats->Ntfs.MftWrites += 1; \
  50. FsStats->Ntfs.MftWriteBytes += (ULONG)(BYTE_COUNT); \
  51. \
  52. if ((IRP_CONTEXT) == (TLIC)) { \
  53. FsStats->Ntfs.MftWritesLazyWriter += 1; \
  54. } else if ((TLIC)->LastRestartArea.QuadPart != 0) { \
  55. FsStats->Ntfs.MftWritesFlushForLogFileFull += 1; \
  56. } else { \
  57. FsStats->Ntfs.MftWritesUserRequest += 1; \
  58. \
  59. switch ((TLIC)->MajorFunction) { \
  60. case IRP_MJ_WRITE: \
  61. FsStats->Ntfs.MftWritesUserLevel.Write += 1; \
  62. break; \
  63. case IRP_MJ_CREATE: \
  64. FsStats->Ntfs.MftWritesUserLevel.Create += 1; \
  65. break; \
  66. case IRP_MJ_SET_INFORMATION: \
  67. FsStats->Ntfs.MftWritesUserLevel.SetInfo += 1; \
  68. break; \
  69. case IRP_MJ_FLUSH_BUFFERS: \
  70. FsStats->Ntfs.MftWritesUserLevel.Flush += 1; \
  71. break; \
  72. default: \
  73. break; \
  74. } \
  75. } \
  76. } else if ((SCB) == (VCB)->Mft2Scb) { \
  77. FsStats->Ntfs.Mft2Writes += 1; \
  78. FsStats->Ntfs.Mft2WriteBytes += (ULONG)(BYTE_COUNT); \
  79. \
  80. if ((IRP_CONTEXT) == (TLIC)) { \
  81. FsStats->Ntfs.Mft2WritesLazyWriter += 1; \
  82. } else if ((TLIC)->LastRestartArea.QuadPart != 0) { \
  83. FsStats->Ntfs.Mft2WritesFlushForLogFileFull += 1; \
  84. } else { \
  85. FsStats->Ntfs.Mft2WritesUserRequest += 1; \
  86. \
  87. switch ((TLIC)->MajorFunction) { \
  88. case IRP_MJ_WRITE: \
  89. FsStats->Ntfs.Mft2WritesUserLevel.Write += 1; \
  90. break; \
  91. case IRP_MJ_CREATE: \
  92. FsStats->Ntfs.Mft2WritesUserLevel.Create += 1; \
  93. break; \
  94. case IRP_MJ_SET_INFORMATION: \
  95. FsStats->Ntfs.Mft2WritesUserLevel.SetInfo += 1; \
  96. break; \
  97. case IRP_MJ_FLUSH_BUFFERS: \
  98. FsStats->Ntfs.Mft2WritesUserLevel.Flush += 1; \
  99. break; \
  100. default: \
  101. break; \
  102. } \
  103. } \
  104. } else if ((SCB) == (VCB)->RootIndexScb) { \
  105. FsStats->Ntfs.RootIndexWrites += 1; \
  106. FsStats->Ntfs.RootIndexWriteBytes += (ULONG)(BYTE_COUNT); \
  107. } else if ((SCB) == (VCB)->BitmapScb) { \
  108. FsStats->Ntfs.BitmapWrites += 1; \
  109. FsStats->Ntfs.BitmapWriteBytes += (ULONG)(BYTE_COUNT); \
  110. \
  111. if ((IRP_CONTEXT) == (TLIC)) { \
  112. FsStats->Ntfs.BitmapWritesLazyWriter += 1; \
  113. } else if ((TLIC)->LastRestartArea.QuadPart != 0) { \
  114. FsStats->Ntfs.BitmapWritesFlushForLogFileFull += 1; \
  115. } else { \
  116. FsStats->Ntfs.BitmapWritesUserRequest += 1; \
  117. \
  118. switch ((TLIC)->MajorFunction) { \
  119. case IRP_MJ_WRITE: \
  120. FsStats->Ntfs.BitmapWritesUserLevel.Write += 1; \
  121. break; \
  122. case IRP_MJ_CREATE: \
  123. FsStats->Ntfs.BitmapWritesUserLevel.Create += 1; \
  124. break; \
  125. case IRP_MJ_SET_INFORMATION: \
  126. FsStats->Ntfs.BitmapWritesUserLevel.SetInfo += 1; \
  127. break; \
  128. default: \
  129. break; \
  130. } \
  131. } \
  132. } else if ((SCB) == (VCB)->MftBitmapScb) { \
  133. FsStats->Ntfs.MftBitmapWrites += 1; \
  134. FsStats->Ntfs.MftBitmapWriteBytes += (ULONG)(BYTE_COUNT); \
  135. \
  136. if ((IRP_CONTEXT) == (TLIC)) { \
  137. FsStats->Ntfs.MftBitmapWritesLazyWriter += 1; \
  138. } else if ((TLIC)->LastRestartArea.QuadPart != 0) { \
  139. FsStats->Ntfs.MftBitmapWritesFlushForLogFileFull += 1; \
  140. } else { \
  141. FsStats->Ntfs.MftBitmapWritesUserRequest += 1; \
  142. \
  143. switch ((TLIC)->MajorFunction) { \
  144. case IRP_MJ_WRITE: \
  145. FsStats->Ntfs.MftBitmapWritesUserLevel.Write += 1; \
  146. break; \
  147. case IRP_MJ_CREATE: \
  148. FsStats->Ntfs.MftBitmapWritesUserLevel.Create += 1; \
  149. break; \
  150. case IRP_MJ_SET_INFORMATION: \
  151. FsStats->Ntfs.MftBitmapWritesUserLevel.SetInfo += 1; \
  152. break; \
  153. default: \
  154. break; \
  155. } \
  156. } \
  157. } \
  158. } \
  159. }
  160. #define WriteToEof (StartingVbo < 0)
  161. #ifdef SYSCACHE_DEBUG
  162. #define CalculateSyscacheFlags( IRPCONTEXT, FLAG, INITIAL_VALUE ) \
  163. FLAG = INITIAL_VALUE; \
  164. if (PagingIo) { \
  165. FLAG |= SCE_FLAG_PAGING; \
  166. } \
  167. if (!SynchronousIo) { \
  168. FLAG |= SCE_FLAG_ASYNC; \
  169. } \
  170. if (SynchPagingIo) { \
  171. FLAG |= SCE_FLAG_SYNC_PAGING; \
  172. } \
  173. if (FlagOn( (IRPCONTEXT)->State, IRP_CONTEXT_STATE_LAZY_WRITE )) { \
  174. FLAG |= SCE_FLAG_LAZY_WRITE; \
  175. } \
  176. if (RecursiveWriteThrough) { \
  177. FLAG |= SCE_FLAG_RECURSIVE; \
  178. } \
  179. if (NonCachedIo) { \
  180. FLAG |= SCE_FLAG_NON_CACHED; \
  181. } \
  182. if (Scb->CompressionUnit) { \
  183. FLAG |= SCE_FLAG_COMPRESSED; \
  184. }
  185. #endif
  186. NTSTATUS
  187. NtfsFsdWrite (
  188. IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
  189. IN PIRP Irp
  190. )
  191. /*++
  192. Routine Description:
  193. This routine implements the FSD entry part of Write.
  194. Arguments:
  195. IrpContext - If present, a pointer to an IrpContext
  196. on the caller's stack.
  197. Irp - Supplies the Irp being processed
  198. Return Value:
  199. NTSTATUS - The FSD status for the IRP
  200. --*/
  201. {
  202. TOP_LEVEL_CONTEXT TopLevelContext;
  203. PTOP_LEVEL_CONTEXT ThreadTopLevelContext;
  204. NTSTATUS Status = STATUS_SUCCESS;
  205. PIRP_CONTEXT IrpContext = NULL;
  206. ULONG Retries = 0;
  207. ASSERT_IRP( Irp );
  208. DebugTrace( +1, Dbg, ("NtfsFsdWrite\n") );
  209. //
  210. // Call the common Write routine
  211. //
  212. FsRtlEnterFileSystem();
  213. ThreadTopLevelContext = NtfsInitializeTopLevelIrp( &TopLevelContext, FALSE, FALSE );
  214. do {
  215. try {
  216. //
  217. // We are either initiating this request or retrying it.
  218. //
  219. if (IrpContext == NULL) {
  220. PSCB Scb = IoGetCurrentIrpStackLocation( Irp )->FileObject->FsContext;
  221. PFCB Fcb;
  222. BOOLEAN PagingFileIo = FALSE;
  223. if (Scb != NULL) {
  224. Fcb = Scb->Fcb;
  225. PagingFileIo = FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE ) && FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA );
  226. }
  227. //
  228. // Allocate synchronous paging io on the stack to avoid allocation
  229. // failures. All paging file i/o can also be on the stack even async
  230. //
  231. if (PagingFileIo || (CanFsdWait( Irp ) && FlagOn( Irp->Flags, IRP_PAGING_IO ))) {
  232. //
  233. // AllocateFromStack is only called in the first pass of the
  234. // loop. Once the IrpContext exists we don't call this again.
  235. //
  236. IrpContext = (PIRP_CONTEXT) NtfsAllocateFromStack( sizeof( IRP_CONTEXT ));
  237. }
  238. NtfsInitializeIrpContext( Irp, CanFsdWait( Irp ), &IrpContext );
  239. if (ThreadTopLevelContext->ScbBeingHotFixed != NULL) {
  240. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_HOTFIX_UNDERWAY );
  241. }
  242. //
  243. // If this is an MDL_WRITE then the Mdl in the Irp should
  244. // be NULL.
  245. //
  246. if (FlagOn( IrpContext->MinorFunction, IRP_MN_MDL ) &&
  247. !FlagOn( IrpContext->MinorFunction, IRP_MN_COMPLETE )) {
  248. Irp->MdlAddress = NULL;
  249. }
  250. //
  251. // Initialize the thread top level structure, if needed.
  252. //
  253. NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext );
  254. } else if (Status == STATUS_LOG_FILE_FULL) {
  255. NtfsCheckpointForLogFileFull( IrpContext );
  256. } else if (Status == STATUS_WAIT_FOR_OPLOCK) {
  257. Status = KeWaitForSingleObject( &IrpContext->Union.NtfsIoContext->Wait.SyncEvent,
  258. Executive,
  259. KernelMode,
  260. FALSE,
  261. NULL );
  262. ASSERT( Status == STATUS_SUCCESS );
  263. //
  264. // Cleanup the iocontext directly
  265. //
  266. if (FlagOn( IrpContext->Union.NtfsIoContext->Flags, NTFS_IO_CONTEXT_ALLOCATED )) {
  267. ExFreeToNPagedLookasideList( &NtfsIoContextLookasideList, IrpContext->Union.NtfsIoContext );
  268. }
  269. IrpContext->Union.NtfsIoContext = NULL;
  270. //
  271. // If we had any failures i.e the irp was cancelled - leave
  272. //
  273. Status = Irp->IoStatus.Status;
  274. if (!NT_SUCCESS( Status )) {
  275. NtfsCompleteRequest( IrpContext, Irp, Status );
  276. break;
  277. }
  278. }
  279. //
  280. // If this is an Mdl complete request, don't go through
  281. // common write.
  282. //
  283. ASSERT( !FlagOn( IrpContext->MinorFunction, IRP_MN_DPC ) );
  284. if (FlagOn( IrpContext->MinorFunction, IRP_MN_COMPLETE )) {
  285. DebugTrace( 0, Dbg, ("Calling NtfsCompleteMdl\n") );
  286. Status = NtfsCompleteMdl( IrpContext, Irp );
  287. //
  288. // Identify write requests which can't wait and post them to the
  289. // Fsp.
  290. //
  291. } else {
  292. #ifdef COMPRESS_ON_WRITE
  293. //
  294. // Capture the auxiliary buffer and clear its address if it
  295. // is not supposed to be deleted by the I/O system on I/O completion.
  296. //
  297. if (Irp->Tail.Overlay.AuxiliaryBuffer != NULL) {
  298. IrpContext->Union.AuxiliaryBuffer =
  299. (PFSRTL_AUXILIARY_BUFFER)Irp->Tail.Overlay.AuxiliaryBuffer;
  300. if (!FlagOn(IrpContext->Union.AuxiliaryBuffer->Flags,
  301. FSRTL_AUXILIARY_FLAG_DEALLOCATE)) {
  302. Irp->Tail.Overlay.AuxiliaryBuffer = NULL;
  303. }
  304. }
  305. #endif
  306. Status = NtfsCommonWrite( IrpContext, Irp );
  307. }
  308. } except( NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
  309. NTSTATUS ExceptionCode;
  310. //
  311. // We had some trouble trying to perform the requested
  312. // operation, so we'll abort the I/O request with
  313. // the error status that we get back from the
  314. // execption code
  315. //
  316. ExceptionCode = GetExceptionCode();
  317. if (ExceptionCode == STATUS_FILE_DELETED) {
  318. if (!FlagOn( IrpContext->MinorFunction, IRP_MN_MDL ) ||
  319. FlagOn( IrpContext->MinorFunction, IRP_MN_COMPLETE )) {
  320. IrpContext->ExceptionStatus = ExceptionCode = STATUS_SUCCESS;
  321. }
  322. } else if ((ExceptionCode == STATUS_VOLUME_DISMOUNTED) &&
  323. FlagOn( Irp->Flags, IRP_PAGING_IO )) {
  324. IrpContext->ExceptionStatus = ExceptionCode = STATUS_SUCCESS;
  325. }
  326. Status = NtfsProcessException( IrpContext,
  327. Irp,
  328. ExceptionCode );
  329. }
  330. ASSERT( (Status != STATUS_WAIT_FOR_OPLOCK) || (ThreadTopLevelContext == &TopLevelContext) );
  331. Retries++;
  332. } while ((Status == STATUS_CANT_WAIT || Status == STATUS_LOG_FILE_FULL || Status == STATUS_WAIT_FOR_OPLOCK) &&
  333. (ThreadTopLevelContext == &TopLevelContext));
  334. ASSERT( IoGetTopLevelIrp() != (PIRP) &TopLevelContext );
  335. FsRtlExitFileSystem();
  336. //
  337. // And return to our caller
  338. //
  339. DebugTrace( -1, Dbg, ("NtfsFsdWrite -> %08lx\n", Status) );
  340. return Status;
  341. UNREFERENCED_PARAMETER( VolumeDeviceObject );
  342. }
  343. NTSTATUS
  344. NtfsCommonWrite (
  345. IN PIRP_CONTEXT IrpContext,
  346. IN PIRP Irp
  347. )
  348. /*++
  349. Routine Description:
  350. This is the common routine for Write called by both the fsd and fsp
  351. threads.
  352. Arguments:
  353. Irp - Supplies the Irp to process
  354. Return Value:
  355. NTSTATUS - The return status for the operation
  356. --*/
  357. {
  358. NTSTATUS Status;
  359. PIO_STACK_LOCATION IrpSp;
  360. PFILE_OBJECT FileObject;
  361. PFILE_OBJECT UserFileObject;
  362. TYPE_OF_OPEN TypeOfOpen;
  363. PVCB Vcb;
  364. PFCB Fcb;
  365. PSCB Scb;
  366. PCCB Ccb;
  367. #ifdef COMPRESS_ON_WIRE
  368. PCOMPRESSION_SYNC CompressionSync = NULL;
  369. PCOMPRESSED_DATA_INFO CompressedDataInfo;
  370. ULONG EngineMatches;
  371. ULONG CompressionUnitSize, ChunkSize;
  372. #endif
  373. PNTFS_ADVANCED_FCB_HEADER Header;
  374. BOOLEAN OplockPostIrp = FALSE;
  375. BOOLEAN PostIrp = FALSE;
  376. PVOID SystemBuffer = NULL;
  377. PVOID SafeBuffer = NULL;
  378. BOOLEAN RecursiveWriteThrough = FALSE;
  379. BOOLEAN ScbAcquired = FALSE;
  380. BOOLEAN PagingIoAcquired = FALSE;
  381. BOOLEAN UpdateMft = FALSE;
  382. BOOLEAN DoingIoAtEof = FALSE;
  383. BOOLEAN SetWriteSeen = FALSE;
  384. BOOLEAN RestoreValidDataToDisk = FALSE;
  385. BOOLEAN Wait;
  386. BOOLEAN OriginalTopLevel;
  387. BOOLEAN PagingIo;
  388. BOOLEAN NonCachedIo;
  389. BOOLEAN SynchronousIo;
  390. ULONG PagingFileIo;
  391. BOOLEAN SynchPagingIo;
  392. BOOLEAN RawEncryptedWrite = FALSE;
  393. NTFS_IO_CONTEXT LocalContext;
  394. VBO StartingVbo;
  395. LONGLONG ByteCount;
  396. LONGLONG ByteRange;
  397. LONGLONG OldFileSize;
  398. PVOID NewBuffer;
  399. PMDL NewMdl;
  400. PMDL OriginalMdl;
  401. PVOID OriginalBuffer;
  402. ULONG TempLength;
  403. PATTRIBUTE_RECORD_HEADER Attribute;
  404. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  405. BOOLEAN CleanupAttributeContext = FALSE;
  406. LONGLONG LlTemp1;
  407. LONGLONG LlTemp2;
  408. LONGLONG ZeroStart;
  409. LONGLONG ZeroLength;
  410. #ifdef SYSCACHE_DEBUG
  411. BOOLEAN PurgeResult;
  412. LONG TempEntry;
  413. ULONG Flags;
  414. #endif
  415. ASSERT_IRP_CONTEXT( IrpContext );
  416. ASSERT_IRP( Irp );
  417. ASSERT( FlagOn( IrpContext->TopLevelIrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL ));
  418. //
  419. // Get the current Irp stack location
  420. //
  421. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  422. DebugTrace( +1, Dbg, ("NtfsCommonWrite\n") );
  423. DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) );
  424. DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) );
  425. //
  426. // Extract and decode the file object
  427. //
  428. UserFileObject = FileObject = IrpSp->FileObject;
  429. TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, TRUE );
  430. //
  431. // Let's kill invalid write requests.
  432. //
  433. if ((TypeOfOpen != UserFileOpen) &&
  434. (TypeOfOpen != StreamFileOpen) &&
  435. (TypeOfOpen != UserVolumeOpen)) {
  436. DebugTrace( 0, Dbg, ("Invalid file object for write\n") );
  437. DebugTrace( -1, Dbg, ("NtfsCommonWrite: Exit -> %08lx\n", STATUS_INVALID_DEVICE_REQUEST) );
  438. NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST );
  439. return STATUS_INVALID_DEVICE_REQUEST;
  440. }
  441. //
  442. // If this is a recursive request which has already failed then
  443. // complete this request with STATUS_FILE_LOCK_CONFLICT. Always let the
  444. // log file requests go through though since Cc won't get a chance to
  445. // retry.
  446. //
  447. if (!FlagOn( Scb->ScbState, SCB_STATE_RESTORE_UNDERWAY ) &&
  448. !NT_SUCCESS( IrpContext->TopLevelIrpContext->ExceptionStatus ) &&
  449. (Scb != Vcb->LogFileScb)) {
  450. NtfsCompleteRequest( IrpContext, Irp, STATUS_FILE_LOCK_CONFLICT );
  451. return STATUS_FILE_LOCK_CONFLICT;
  452. }
  453. //
  454. // Check if this volume has already been shut down. If it has, fail
  455. // this write request.
  456. //
  457. //**** ASSERT( !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_SHUTDOWN) );
  458. if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_SHUTDOWN)) {
  459. Irp->IoStatus.Information = 0;
  460. DebugTrace( 0, Dbg, ("Write for volume that is already shutdown.\n") );
  461. DebugTrace( -1, Dbg, ("NtfsCommonWrite: Exit -> %08lx\n", STATUS_TOO_LATE) );
  462. NtfsCompleteRequest( IrpContext, Irp, STATUS_TOO_LATE );
  463. return STATUS_TOO_LATE;
  464. }
  465. //
  466. // Fail if the volume is mounted read only.
  467. //
  468. if (NtfsIsVolumeReadOnly( Vcb )) {
  469. Irp->IoStatus.Information = 0;
  470. DebugTrace( -1, Dbg, ("NtfsCommonWrite: Exit -> %08lx\n", STATUS_MEDIA_WRITE_PROTECTED) );
  471. NtfsCompleteRequest( IrpContext, Irp, STATUS_MEDIA_WRITE_PROTECTED );
  472. return STATUS_MEDIA_WRITE_PROTECTED;
  473. }
  474. //
  475. // Initialize the appropriate local variables.
  476. //
  477. Wait = (BOOLEAN) FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
  478. PagingIo = BooleanFlagOn( Irp->Flags, IRP_PAGING_IO );
  479. NonCachedIo = BooleanFlagOn( Irp->Flags,IRP_NOCACHE );
  480. SynchronousIo = BooleanFlagOn( FileObject->Flags, FO_SYNCHRONOUS_IO );
  481. PagingFileIo = FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE ) && FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA );
  482. SynchPagingIo = (BOOLEAN) FlagOn( Irp->Flags, IRP_SYNCHRONOUS_PAGING_IO );
  483. OriginalTopLevel = NtfsIsTopLevelRequest( IrpContext );
  484. //
  485. // If this is async paging io then check if we are being called by the mapped page writer.
  486. // Convert it back to synchronous if not.
  487. //
  488. if (!Wait && PagingIo && !PagingFileIo) {
  489. if ((IrpContext->TopLevelIrpContext != IrpContext) ||
  490. (NtfsGetTopLevelContext()->SavedTopLevelIrp != (PIRP) FSRTL_MOD_WRITE_TOP_LEVEL_IRP)) {
  491. Wait = TRUE;
  492. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
  493. }
  494. }
  495. DebugTrace( 0, Dbg, ("PagingIo -> %04x\n", PagingIo) );
  496. DebugTrace( 0, Dbg, ("NonCachedIo -> %04x\n", NonCachedIo) );
  497. DebugTrace( 0, Dbg, ("SynchronousIo -> %04x\n", SynchronousIo) );
  498. //
  499. // Extract starting Vbo and offset. Restore back write to eof if the
  500. // flag was set that we came through and adjusted for it and now the filesize
  501. // has shrunk due to a failure to adjust size or an intervening seteof
  502. // it should be safe to add the irp params since we validated for overflows when
  503. // we set the writing_at_eof flag
  504. //
  505. if (FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WRITING_AT_EOF ) &&
  506. (Scb->Header.FileSize.QuadPart < IrpSp->Parameters.Write.ByteOffset.QuadPart + IrpSp->Parameters.Write.Length)) {
  507. ClearFlag( IrpContext->State, IRP_CONTEXT_STATE_WRITING_AT_EOF );
  508. IrpSp->Parameters.Write.ByteOffset.LowPart = FILE_WRITE_TO_END_OF_FILE;
  509. IrpSp->Parameters.Write.ByteOffset.HighPart = -1;
  510. }
  511. StartingVbo = IrpSp->Parameters.Write.ByteOffset.QuadPart;
  512. ByteCount = (LONGLONG) IrpSp->Parameters.Write.Length;
  513. //
  514. // Check for overflows. However, 0xFFFFFFFF is a valid value
  515. // when we are appending at EOF.
  516. //
  517. ASSERT( !WriteToEof ||
  518. (IrpSp->Parameters.Write.ByteOffset.HighPart == -1 &&
  519. IrpSp->Parameters.Write.ByteOffset.LowPart == FILE_WRITE_TO_END_OF_FILE));
  520. if ((MAXLONGLONG - StartingVbo < ByteCount) && (!WriteToEof)) {
  521. ASSERT( !PagingIo );
  522. NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
  523. return STATUS_INVALID_PARAMETER;
  524. }
  525. ByteRange = StartingVbo + ByteCount;
  526. DebugTrace( 0, Dbg, ("StartingVbo -> %016I64x\n", StartingVbo) );
  527. //
  528. // If this is a null request, return immediately.
  529. //
  530. if ((ULONG)ByteCount == 0) {
  531. Irp->IoStatus.Information = 0;
  532. DebugTrace( 0, Dbg, ("No bytes to write\n") );
  533. DebugTrace( -1, Dbg, ("NtfsCommonWrite: Exit -> %08lx\n", STATUS_SUCCESS) );
  534. NtfsCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
  535. return STATUS_SUCCESS;
  536. }
  537. #if DBG
  538. if (PagingIo &&
  539. NtfsIsTypeCodeEncryptible( Scb->AttributeTypeCode ) &&
  540. Scb->Header.PagingIoResource != NULL &&
  541. NtfsIsSharedScbPagingIo( Scb ) &&
  542. FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_ENCRYPTED ) &&
  543. Scb->EncryptionContext == NULL) {
  544. //
  545. // We're in trouble if we can't encrypt the data in the pages before writing
  546. // it out. Naturally, if this is a directory or some other unencryptible
  547. // attribute type, we don't care, since we weren't going to encrypt the data
  548. // anyway. It is valid to do raw writes to an encypted stream without an
  549. // encryption context, but raw encrypted writes shouldn't look like paging io.
  550. //
  551. ASSERTMSG( "Encrypted file without an encryption context -- can't do paging io", FALSE );
  552. }
  553. #endif
  554. //
  555. // If this is async Io to a compressed stream
  556. // then we will make this look synchronous.
  557. //
  558. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  559. Wait = TRUE;
  560. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
  561. }
  562. //
  563. // See if we have to defer the write.
  564. //
  565. if (!PagingIo &&
  566. !NonCachedIo &&
  567. !FlagOn( FileObject->Flags, FO_WRITE_THROUGH ) &&
  568. !CcCanIWrite( FileObject,
  569. (ULONG)ByteCount,
  570. (BOOLEAN)(FlagOn( IrpContext->State,
  571. IRP_CONTEXT_STATE_WAIT | IRP_CONTEXT_STATE_IN_FSP ) == IRP_CONTEXT_STATE_WAIT),
  572. BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_DEFERRED_WRITE))) {
  573. BOOLEAN Retrying = BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_DEFERRED_WRITE);
  574. NtfsPrePostIrp( IrpContext, Irp );
  575. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DEFERRED_WRITE );
  576. CcDeferWrite( FileObject,
  577. (PCC_POST_DEFERRED_WRITE)NtfsAddToWorkque,
  578. IrpContext,
  579. Irp,
  580. (ULONG)ByteCount,
  581. Retrying );
  582. return STATUS_PENDING;
  583. }
  584. //
  585. // Use a local pointer to the Scb header for convenience.
  586. //
  587. Header = &Scb->Header;
  588. //
  589. // Make sure there is an initialized NtfsIoContext block.
  590. // If there is a context pointer, we need to make sure it was
  591. // allocated and not a stale stack pointer.
  592. //
  593. if (!PagingFileIo) {
  594. NtfsInitializeIoContext( IrpContext, &LocalContext, PagingIo );
  595. }
  596. DebugTrace( 0, Dbg, ("PagingIo -> %04x\n", PagingIo) );
  597. DebugTrace( 0, Dbg, ("NonCachedIo -> %04x\n", NonCachedIo) );
  598. DebugTrace( 0, Dbg, ("SynchronousIo -> %04x\n", SynchronousIo) );
  599. DebugTrace( 0, Dbg, ("WriteToEof -> %04x\n", WriteToEof) );
  600. //
  601. // Handle volume Dasd here.
  602. //
  603. if (TypeOfOpen == UserVolumeOpen) {
  604. //
  605. // If the caller has not asked for extended DASD IO access then
  606. // limit with the volume size.
  607. //
  608. if (!FlagOn( Ccb->Flags, CCB_FLAG_ALLOW_XTENDED_DASD_IO )) {
  609. //
  610. // If this is a volume file, we cannot write past the current
  611. // end of file (volume). We check here now before continueing.
  612. //
  613. // If the starting vbo is past the end of the volume, we are done.
  614. //
  615. if (WriteToEof || (Scb->Header.FileSize.QuadPart <= StartingVbo)) {
  616. DebugTrace( 0, Dbg, ("No bytes to write\n") );
  617. DebugTrace( -1, Dbg, ("NtfsCommonWrite: Exit -> %08lx\n", STATUS_SUCCESS) );
  618. NtfsCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
  619. return STATUS_SUCCESS;
  620. //
  621. // If the write extends beyond the end of the volume, truncate the
  622. // bytes to write.
  623. //
  624. } else if (Scb->Header.FileSize.QuadPart < ByteRange) {
  625. ByteCount = Scb->Header.FileSize.QuadPart - StartingVbo;
  626. }
  627. }
  628. //
  629. // Set the io context async if necc. before doing the i/o
  630. //
  631. if (!Wait) {
  632. NtfsSetIoContextAsync( IrpContext, NULL, (ULONG)ByteCount );
  633. }
  634. SetFlag( UserFileObject->Flags, FO_FILE_MODIFIED );
  635. Status = NtfsVolumeDasdIo( IrpContext,
  636. Irp,
  637. Scb,
  638. Ccb,
  639. StartingVbo,
  640. (ULONG)ByteCount );
  641. //
  642. // If the volume was opened for Synchronous IO, update the current
  643. // file position.
  644. //
  645. if (SynchronousIo && !PagingIo && NT_SUCCESS( Status )) {
  646. UserFileObject->CurrentByteOffset.QuadPart = StartingVbo + (LONGLONG) Irp->IoStatus.Information;
  647. }
  648. DebugTrace( 0, Dbg, ("Complete with %08lx bytes written\n", Irp->IoStatus.Information) );
  649. DebugTrace( -1, Dbg, ("NtfsCommonWrite: Exit -> %08lx\n", Status) );
  650. if (Wait) {
  651. NtfsCompleteRequest( IrpContext, Irp, Status );
  652. }
  653. return Status;
  654. }
  655. //
  656. // If this is a paging file, just send it to the device driver.
  657. // We assume Mm is a good citizen.
  658. //
  659. if (PagingFileIo != 0) {
  660. if (FlagOn( Fcb->FcbState, FCB_STATE_FILE_DELETED )) {
  661. NtfsRaiseStatus( IrpContext, STATUS_FILE_DELETED, NULL, NULL );
  662. }
  663. //
  664. // Do the usual STATUS_PENDING things.
  665. //
  666. IoMarkIrpPending( Irp );
  667. //
  668. // Perform the actual IO, it will be completed when the io finishes.
  669. //
  670. NtfsPagingFileIo( IrpContext,
  671. Irp,
  672. Scb,
  673. StartingVbo,
  674. (ULONG)ByteCount );
  675. //
  676. // We, nor anybody else, need the IrpContext any more.
  677. //
  678. NtfsCompleteRequest( IrpContext, NULL, 0 );
  679. return STATUS_PENDING;
  680. }
  681. //
  682. // Special processing for paging io.
  683. //
  684. if (PagingIo) {
  685. //
  686. // If this is the Usn Journal then bias the Io to the correct location in the
  687. // file.
  688. //
  689. if (FlagOn( Scb->ScbPersist, SCB_PERSIST_USN_JOURNAL )) {
  690. StartingVbo += Vcb->UsnCacheBias;
  691. ByteRange = StartingVbo + (LONGLONG) IrpSp->Parameters.Write.Length;
  692. }
  693. //
  694. // Gather statistics on this IO.
  695. //
  696. CollectWriteStats( Vcb, TypeOfOpen, Scb, Fcb, ByteCount, IrpContext,
  697. IrpContext->TopLevelIrpContext );
  698. }
  699. //
  700. // Use a try-finally to free Scb and buffers on the way out.
  701. // At this point we can treat all requests identically since we
  702. // have a usable Scb for each of them. (Volume, User or Stream file)
  703. //
  704. Status = STATUS_SUCCESS;
  705. try {
  706. //
  707. // If this is a noncached transfer and is not a paging I/O, and
  708. // the file has been opened cached, then we will do a flush here
  709. // to avoid stale data problems. Note that we must flush before
  710. // acquiring the Fcb shared since the write may try to acquire
  711. // it exclusive.
  712. //
  713. // CcFlushCache may not raise.
  714. //
  715. // The Purge following the flush will guarantee cache coherency.
  716. //
  717. //
  718. // If this request is paging IO then check if our caller already
  719. // owns any of the resources for this file. If so then we don't
  720. // want to perform a log file full in this thread.
  721. //
  722. if (!PagingIo) {
  723. //
  724. // Capture the source information.
  725. //
  726. IrpContext->SourceInfo = Ccb->UsnSourceInfo;
  727. //
  728. // Check for rawencryptedwrite
  729. //
  730. if (NonCachedIo &&
  731. !NtfsIsTopLevelNtfs( IrpContext )) {
  732. #if DBG || defined( NTFS_FREE_ASSERT )
  733. ASSERT( (IrpContext->TopLevelIrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) &&
  734. (IoGetCurrentIrpStackLocation( IrpContext->TopLevelIrpContext->OriginatingIrp )->Parameters.FileSystemControl.FsControlCode == FSCTL_WRITE_RAW_ENCRYPTED ));
  735. #endif
  736. RawEncryptedWrite = TRUE;
  737. }
  738. if (NonCachedIo &&
  739. (TypeOfOpen != StreamFileOpen) &&
  740. (FileObject->SectionObjectPointer->DataSectionObject != NULL)) {
  741. //
  742. // Acquire the paging io resource to test the compression state. If the
  743. // file is compressed this will add serialization up to the point where
  744. // CcCopyWrite flushes the data, but those flushes will be serialized
  745. // anyway. Uncompressed files will need the paging io resource
  746. // exclusive to do the flush.
  747. //
  748. NtfsAcquirePagingResourceExclusive( IrpContext, Scb, TRUE );
  749. PagingIoAcquired = TRUE;
  750. if (!FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  751. if (WriteToEof) {
  752. FsRtlLockFsRtlHeader( Header );
  753. IrpContext->CleanupStructure = Scb;
  754. }
  755. #ifdef SYSCACHE_DEBUG
  756. if (ScbIsBeingLogged( Scb )) {
  757. CalculateSyscacheFlags( IrpContext, Flags, SCE_FLAG_WRITE );
  758. TempEntry = FsRtlLogSyscacheEvent( Scb, SCE_CC_FLUSH, Flags, WriteToEof ? Header->FileSize.QuadPart : StartingVbo, ByteCount, -1 );
  759. }
  760. #endif
  761. CcFlushCache( &Scb->NonpagedScb->SegmentObject,
  762. WriteToEof ? &Header->FileSize : (PLARGE_INTEGER)&StartingVbo,
  763. (ULONG)ByteCount,
  764. &Irp->IoStatus );
  765. #ifdef SYSCACHE_DEBUG
  766. if (ScbIsBeingLogged( Scb )) {
  767. FsRtlUpdateSyscacheEvent( Scb, TempEntry, Irp->IoStatus.Status, 0 );
  768. }
  769. #endif
  770. if (WriteToEof) {
  771. FsRtlUnlockFsRtlHeader( Header );
  772. IrpContext->CleanupStructure = NULL;
  773. }
  774. //
  775. // Make sure there was no error in the flush path.
  776. //
  777. if (!NT_SUCCESS( IrpContext->TopLevelIrpContext->ExceptionStatus ) ||
  778. !NT_SUCCESS( Irp->IoStatus.Status )) {
  779. NtfsNormalizeAndCleanupTransaction( IrpContext,
  780. &Irp->IoStatus.Status,
  781. TRUE,
  782. STATUS_UNEXPECTED_IO_ERROR );
  783. }
  784. //
  785. // Now purge the data for this range.
  786. //
  787. NtfsDeleteInternalAttributeStream( Scb, FALSE, FALSE );
  788. #ifdef SYSCACHE_DEBUG
  789. PurgeResult =
  790. #endif
  791. CcPurgeCacheSection( &Scb->NonpagedScb->SegmentObject,
  792. (PLARGE_INTEGER)&StartingVbo,
  793. (ULONG)ByteCount,
  794. FALSE );
  795. #ifdef SYSCACHE_DEBUG
  796. if (ScbIsBeingLogged( Scb ) && !PurgeResult) {
  797. KdPrint( ("NTFS: Failed Purge 0x%x 0x%I64x 0x%x\n", Scb, StartingVbo, ByteCount) );
  798. DbgBreakPoint();
  799. //
  800. // Repeat attempt so we can watch
  801. //
  802. PurgeResult = CcPurgeCacheSection( &Scb->NonpagedScb->SegmentObject,
  803. (PLARGE_INTEGER)&StartingVbo,
  804. (ULONG)ByteCount,
  805. FALSE );
  806. }
  807. #endif
  808. }
  809. //
  810. // If not paging I/O, then we must acquire a resource, and do some
  811. // other initialization. We already have the resource if we performed
  812. // the coherency flush above.
  813. //
  814. } else {
  815. // We want to acquire the paging io resource if not already acquired.
  816. // Acquire exclusive if we failed a previous convert to non-resident because
  817. // of a possible deadlock. Otherwise get it shared.
  818. //
  819. if (FlagOn( IrpContext->State, IRP_CONTEXT_STATE_ACQUIRE_EX )) {
  820. if (!NtfsAcquirePagingResourceExclusive( IrpContext, Scb, Wait )) {
  821. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  822. }
  823. } else {
  824. if (!NtfsAcquirePagingResourceSharedWaitForExclusive( IrpContext, Scb, Wait )) {
  825. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  826. }
  827. }
  828. PagingIoAcquired = TRUE;
  829. }
  830. //
  831. // Check if we have already gone through cleanup on this handle.
  832. //
  833. if (FlagOn( Ccb->Flags, CCB_FLAG_CLEANUP )) {
  834. NtfsRaiseStatus( IrpContext, STATUS_FILE_CLOSED, NULL, NULL );
  835. }
  836. //
  837. // Now check if the attribute has been deleted or is on a dismounted volume.
  838. //
  839. if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED | SCB_STATE_VOLUME_DISMOUNTED)) {
  840. if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED )) {
  841. NtfsRaiseStatus( IrpContext, STATUS_FILE_DELETED, NULL, NULL );
  842. } else {
  843. NtfsRaiseStatus( IrpContext, STATUS_VOLUME_DISMOUNTED, NULL, NULL );
  844. }
  845. }
  846. //
  847. // Now synchronize with the FsRtl Header
  848. //
  849. NtfsAcquireFsrtlHeader( Scb );
  850. //
  851. // Now see if we will change FileSize. We have to do it now
  852. // so that our reads are not nooped.
  853. //
  854. if ((ByteRange > Header->ValidDataLength.QuadPart) || WriteToEof) {
  855. if ((IrpContext->TopLevelIrpContext->CleanupStructure == Fcb) ||
  856. (IrpContext->TopLevelIrpContext->CleanupStructure == Scb)) {
  857. DoingIoAtEof = TRUE;
  858. OldFileSize = Header->FileSize.QuadPart;
  859. } else {
  860. ASSERT( IrpContext->TopLevelIrpContext->CleanupStructure == NULL );
  861. DoingIoAtEof = !FlagOn( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE ) ||
  862. NtfsWaitForIoAtEof( Header, (PLARGE_INTEGER)&StartingVbo, (ULONG)ByteCount );
  863. //
  864. // Set the Flag if we are changing FileSize or ValidDataLength,
  865. // and save current values.
  866. //
  867. if (DoingIoAtEof) {
  868. SetFlag( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE );
  869. #if (DBG || defined( NTFS_FREE_ASSERTS ))
  870. ((PSCB) Header)->IoAtEofThread = (PERESOURCE_THREAD) ExGetCurrentResourceThread();
  871. #endif
  872. //
  873. // Store this in the IrpContext until commit or post
  874. //
  875. IrpContext->CleanupStructure = Scb;
  876. OldFileSize = Header->FileSize.QuadPart;
  877. //
  878. // Check for writing to end of File. If we are, then we have to
  879. // recalculate the byte range.
  880. //
  881. if (WriteToEof) {
  882. //
  883. // Mark the in irp context that the write is at eof and change its paramters
  884. // to reflect where the end of the file is.
  885. //
  886. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_WRITING_AT_EOF );
  887. IrpSp->Parameters.Write.ByteOffset.QuadPart = Header->FileSize.QuadPart;
  888. StartingVbo = Header->FileSize.QuadPart;
  889. ByteRange = StartingVbo + ByteCount;
  890. //
  891. // If the ByteRange now exceeds our maximum value, then
  892. // return an error.
  893. //
  894. if (ByteRange < StartingVbo) {
  895. NtfsReleaseFsrtlHeader( Scb );
  896. try_return( Status = STATUS_INVALID_PARAMETER );
  897. }
  898. }
  899. #if (DBG || defined( NTFS_FREE_ASSERTS ))
  900. } else {
  901. ASSERT( ((PSCB) Header)->IoAtEofThread != (PERESOURCE_THREAD) ExGetCurrentResourceThread() );
  902. #endif
  903. }
  904. }
  905. //
  906. // Make sure the user isn't writing past our maximum file size.
  907. //
  908. if ((ULONGLONG)ByteRange > MAXFILESIZE) {
  909. NtfsReleaseFsrtlHeader( Scb );
  910. try_return( Status = STATUS_INVALID_PARAMETER );
  911. }
  912. }
  913. NtfsReleaseFsrtlHeader( Scb );
  914. //
  915. // We cannot handle user noncached I/Os to compressed files, so we always
  916. // divert them through the cache with write through.
  917. //
  918. // The reason that we always handle the user requests through the cache,
  919. // is that there is no other safe way to deal with alignment issues, for
  920. // the frequent case where the user noncached I/O is not an integral of
  921. // the Compression Unit. We cannot, for example, read the rest of the
  922. // compression unit into a scratch buffer, because we are not synchronized
  923. // with anyone mapped to the file and modifying the other data. If we
  924. // try to assemble the data in the cache in the noncached path, to solve
  925. // the above problem, then we have to somehow purge these pages away
  926. // to solve cache coherency problems, but then the pages could be modified
  927. // by a file mapper and that would be wrong, too.
  928. //
  929. // Bottom line is we can only really support cached writes to compresed
  930. // files.
  931. //
  932. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK ) && NonCachedIo) {
  933. NonCachedIo = FALSE;
  934. if (Scb->FileObject == NULL) {
  935. //
  936. // Make sure we are serialized with the FileSizes, and
  937. // will remove this condition if we abort.
  938. //
  939. if (!DoingIoAtEof) {
  940. FsRtlLockFsRtlHeader( Header );
  941. IrpContext->CleanupStructure = Scb;
  942. }
  943. NtfsCreateInternalAttributeStream( IrpContext, Scb, FALSE, NULL );
  944. if (!DoingIoAtEof) {
  945. FsRtlUnlockFsRtlHeader( Header );
  946. IrpContext->CleanupStructure = NULL;
  947. }
  948. }
  949. FileObject = Scb->FileObject;
  950. SetFlag( FileObject->Flags, FO_WRITE_THROUGH );
  951. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_WRITE_THROUGH );
  952. }
  953. //
  954. // Set the flag in our IrpContext to indicate that we have entered
  955. // write.
  956. //
  957. ASSERT( !FlagOn( IrpContext->TopLevelIrpContext->Flags,
  958. IRP_CONTEXT_FLAG_WRITE_SEEN ));
  959. SetFlag( IrpContext->TopLevelIrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_SEEN );
  960. SetWriteSeen = TRUE;
  961. //
  962. // Now post any Usn changes. We will blindly make the call here, because
  963. // usually all but the first call is in the fast path anyway.
  964. // Checkpoint the transaction to reduce resource contention of the UsnJournal
  965. // and Mft.
  966. //
  967. if (FlagOn( Vcb->VcbState, VCB_STATE_USN_JOURNAL_ACTIVE )) {
  968. ULONG Reason = 0;
  969. ASSERT( Vcb->UsnJournal != NULL );
  970. if (ByteRange > Header->FileSize.QuadPart) {
  971. Reason |= USN_REASON_DATA_EXTEND;
  972. }
  973. if (StartingVbo < Header->FileSize.QuadPart) {
  974. Reason |= USN_REASON_DATA_OVERWRITE;
  975. }
  976. NtfsPostUsnChange( IrpContext, Scb, Reason );
  977. if (IrpContext->TransactionId != 0) {
  978. NtfsCheckpointCurrentTransaction( IrpContext );
  979. }
  980. }
  981. } else {
  982. //
  983. // Only do the check if we are the top-level Ntfs case. In any
  984. // recursive Ntfs case we don't perform a log-file full.
  985. //
  986. if (NtfsIsTopLevelRequest( IrpContext )) {
  987. if (NtfsIsSharedScb( Scb ) ||
  988. ((Scb->Header.PagingIoResource != NULL) &&
  989. NtfsIsSharedScbPagingIo( Scb ))) {
  990. //
  991. // Don't try to do a clean checkpoint in this thread.
  992. //
  993. NtfsGetTopLevelContext()->TopLevelRequest = FALSE;
  994. }
  995. }
  996. //
  997. // For all paging I/O, the correct resource has already been
  998. // acquired shared - PagingIoResource if it exists, or else
  999. // main Resource. In some rare cases this is not currently
  1000. // true (shutdown & segment dereference thread), so we acquire
  1001. // shared here, but we starve exclusive in these rare cases
  1002. // to be a little more resilient to deadlocks! Most of the
  1003. // time all we do is the test.
  1004. //
  1005. if ((Header->PagingIoResource != NULL) &&
  1006. !NtfsIsSharedScbPagingIo( (PSCB) Header ) &&
  1007. !NtfsIsSharedScb( (PSCB) Header ) ) {
  1008. ExAcquireSharedStarveExclusive( Header->PagingIoResource, TRUE );
  1009. PagingIoAcquired = TRUE;
  1010. }
  1011. //
  1012. // Now check if the attribute has been deleted or is on a dismounted volume.
  1013. //
  1014. if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED | SCB_STATE_VOLUME_DISMOUNTED)) {
  1015. if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED )) {
  1016. NtfsRaiseStatus( IrpContext, STATUS_FILE_DELETED, NULL, NULL );
  1017. } else {
  1018. NtfsRaiseStatus( IrpContext, STATUS_VOLUME_DISMOUNTED, NULL, NULL );
  1019. }
  1020. }
  1021. //
  1022. // If this is async paging IO to a compressed file force it to be
  1023. // synchronous.
  1024. //
  1025. if (!Wait && (Scb->CompressionUnit != 0)) {
  1026. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  1027. Wait = TRUE;
  1028. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
  1029. }
  1030. }
  1031. //
  1032. // Note that the lazy writer must not be allowed to try and
  1033. // acquire the resource exclusive. This is not a problem since
  1034. // the lazy writer is paging IO and thus not allowed to extend
  1035. // file size, and is never the top level guy, thus not able to
  1036. // extend valid data length.
  1037. //
  1038. if (
  1039. #ifdef COMPRESS_ON_WIRE
  1040. (Scb->LazyWriteThread[0] == PsGetCurrentThread()) ||
  1041. (Scb->LazyWriteThread[1] == PsGetCurrentThread())
  1042. #else
  1043. (NtfsGetTopLevelContext()->SavedTopLevelIrp == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP)
  1044. #endif
  1045. ) {
  1046. DebugTrace( 0, Dbg, ("Lazy writer generated write\n") );
  1047. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_LAZY_WRITE );
  1048. //
  1049. // If the temporary bit is set in the Scb then set the temporary
  1050. // bit in the file object. In case the temporary bit has changed
  1051. // in the Scb, this is a good file object to fix it in!
  1052. //
  1053. if (FlagOn( Scb->ScbState, SCB_STATE_TEMPORARY )) {
  1054. SetFlag( FileObject->Flags, FO_TEMPORARY_FILE );
  1055. } else {
  1056. ClearFlag( FileObject->Flags, FO_TEMPORARY_FILE );
  1057. }
  1058. //
  1059. // Test if we are the result of a recursive flush in the write path. In
  1060. // that case we won't have to update valid data.
  1061. //
  1062. } else {
  1063. //
  1064. // Check if we are recursing into write from a write via the
  1065. // cache manager.
  1066. //
  1067. if (FlagOn( IrpContext->TopLevelIrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_SEEN )) {
  1068. RecursiveWriteThrough = TRUE;
  1069. //
  1070. // If the top level request is a write to the same file object
  1071. // then set the write-through flag in the current Scb. We
  1072. // know the current request is not top-level because some
  1073. // other write has already set the bit in the top IrpContext.
  1074. //
  1075. if ((IrpContext->TopLevelIrpContext->MajorFunction == IRP_MJ_WRITE) &&
  1076. (IrpContext->TopLevelIrpContext->OriginatingIrp != NULL) &&
  1077. (FileObject->FsContext ==
  1078. IoGetCurrentIrpStackLocation( IrpContext->TopLevelIrpContext->OriginatingIrp )->FileObject->FsContext)) {
  1079. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_WRITE_THROUGH );
  1080. }
  1081. //
  1082. // Otherwise set the flag in the top level IrpContext showing that
  1083. // we have entered write.
  1084. //
  1085. } else {
  1086. SetFlag(IrpContext->TopLevelIrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_SEEN);
  1087. SetWriteSeen = TRUE;
  1088. }
  1089. }
  1090. //
  1091. // This could be someone who extends valid data or valid data to disk,
  1092. // like the Mapped Page Writer or a flush or the lazy writer
  1093. // writing the last page contianing the VDL, so we have to
  1094. // duplicate code from above in the non paging case to serialize this guy with I/O
  1095. // at the end of the file. We do not extend valid data for
  1096. // metadata streams and need to eliminate them to avoid deadlocks
  1097. // later.
  1098. //
  1099. if (!RecursiveWriteThrough) {
  1100. if (!FlagOn(Scb->ScbState, SCB_STATE_MODIFIED_NO_WRITE)) {
  1101. ASSERT(!WriteToEof);
  1102. //
  1103. // Now synchronize with the FsRtl Header
  1104. //
  1105. NtfsAcquireFsrtlHeader( Scb );
  1106. //
  1107. // Now see if we will change FileSize. We have to do it now
  1108. // so that our reads are not nooped.
  1109. //
  1110. if (ByteRange > Header->ValidDataLength.QuadPart) {
  1111. //
  1112. // Our caller may already be synchronized with EOF.
  1113. // The FcbWithPaging field in the top level IrpContext
  1114. // will have either the current Fcb/Scb if so.
  1115. //
  1116. if ((IrpContext->TopLevelIrpContext->CleanupStructure == Fcb) ||
  1117. (IrpContext->TopLevelIrpContext->CleanupStructure == Scb)) {
  1118. DoingIoAtEof = TRUE;
  1119. OldFileSize = Header->FileSize.QuadPart;
  1120. } else {
  1121. //
  1122. // We can change FileSize and ValidDataLength if either, no one
  1123. // else is now, or we are still extending after waiting.
  1124. // We won't block the mapped page writer or deref seg thread on IoAtEof. // We also won't block on non-top level requests that are not recursing from the filesystem like the deref
  1125. // seg thread. Mm initiated flushes are originally not top level but the top level
  1126. // irp context is the current irp context. (as opposed to recursive file system writes
  1127. // which are not top level and top level irp context is different from the current one)
  1128. if (FlagOn( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE )) {
  1129. if (!OriginalTopLevel && NtfsIsTopLevelNtfs( IrpContext )) {
  1130. NtfsReleaseFsrtlHeader( Scb );
  1131. try_return( Status = STATUS_FILE_LOCK_CONFLICT );
  1132. }
  1133. DoingIoAtEof = NtfsWaitForIoAtEof( Header, (PLARGE_INTEGER)&StartingVbo, (ULONG)ByteCount );
  1134. } else {
  1135. DoingIoAtEof = TRUE;
  1136. }
  1137. //
  1138. // Set the Flag if we are changing FileSize or ValidDataLength,
  1139. // and save current values.
  1140. //
  1141. if (DoingIoAtEof) {
  1142. SetFlag( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE );
  1143. #if (DBG || defined( NTFS_FREE_ASSERTS ))
  1144. ((PSCB) Header)->IoAtEofThread = (PERESOURCE_THREAD) ExGetCurrentResourceThread();
  1145. #endif
  1146. //
  1147. // Store this in the IrpContext until commit or post
  1148. //
  1149. IrpContext->CleanupStructure = Scb;
  1150. OldFileSize = Header->FileSize.QuadPart;
  1151. #if (DBG || defined( NTFS_FREE_ASSERTS ))
  1152. } else {
  1153. ASSERT( ((PSCB) Header)->IoAtEofThread != (PERESOURCE_THREAD) ExGetCurrentResourceThread() );
  1154. #endif
  1155. }
  1156. }
  1157. }
  1158. NtfsReleaseFsrtlHeader( Scb );
  1159. }
  1160. //
  1161. // Now that we're synchronized with doing io at eof we can check
  1162. // the lazywrite's bounds
  1163. //
  1164. if (FlagOn( IrpContext->State, IRP_CONTEXT_STATE_LAZY_WRITE )) {
  1165. //
  1166. // The lazy writer should always be writing data ends on
  1167. // or before the page containing ValidDataLength.
  1168. // In some cases the lazy writer may be writing beyond this point.
  1169. //
  1170. // 1. The user may have truncated the size to zero through
  1171. // SetAllocation but the page was already queued to the lazy
  1172. // writer. In the typical case this write will be nooped
  1173. //
  1174. // 2. If there is a mapped section and the user actually modified
  1175. // the page in which VDL is contained but beyond VDL this page is written to disk
  1176. // and VDL is updated. Otherwise it may never get written since the mapped writer
  1177. // defers to the lazywriter
  1178. //
  1179. // 3. For all writes really beyond the page containing VDL when
  1180. // the file is mapped since ValidDataLength is notupdated here a
  1181. // subsequent write may zero this range and the data would be lost. So
  1182. // We will return FILE_LOCK_CONFLICT to lazy writer if there is a mapped section and wait
  1183. // for the mapped page writer to write this page (or any
  1184. // page beyond this point).
  1185. //
  1186. // Returning FILE_LOCK_CONFLICT should never cause us to lose
  1187. // the data so we can err on the conservative side here.
  1188. // There is nothing to worry about unless the file has been
  1189. // mapped.
  1190. //
  1191. if (FlagOn( Header->Flags, FSRTL_FLAG_USER_MAPPED_FILE )) {
  1192. //
  1193. // Fail if the start of this request is beyond valid data length.
  1194. // Don't worry if this is an unsafe test. MM and CC won't
  1195. // throw this page away if it is really dirty.
  1196. //
  1197. if ((ByteRange > Header->ValidDataLength.QuadPart) &&
  1198. (StartingVbo < Header->FileSize.QuadPart)) {
  1199. //
  1200. // It's OK if byte range is within the page containing valid data length.
  1201. //
  1202. if (ByteRange > ((Header->ValidDataLength.QuadPart + PAGE_SIZE - 1) & ~((LONGLONG) (PAGE_SIZE - 1)))) {
  1203. //
  1204. // Don't flush this now.
  1205. //
  1206. try_return( Status = STATUS_FILE_LOCK_CONFLICT );
  1207. }
  1208. }
  1209. //
  1210. // This is a stale callback by cc we can discard the data
  1211. // this usually indicates a failed purge at some point during a truncate
  1212. //
  1213. } else if (ByteRange >= Header->ValidDataLength.QuadPart) {
  1214. //
  1215. // Trim the write down
  1216. //
  1217. ByteRange = Header->ValidDataLength.QuadPart;
  1218. ByteCount = ByteRange - StartingVbo;
  1219. //
  1220. // If all of the write is beyond vdl just noop it
  1221. //
  1222. if (StartingVbo >= Header->ValidDataLength.QuadPart) {
  1223. DoingIoAtEof = FALSE;
  1224. Irp->IoStatus.Information = 0;
  1225. try_return( Status = STATUS_SUCCESS );
  1226. }
  1227. }
  1228. } // lazy writer
  1229. } // not recursive write through
  1230. //
  1231. // If are paging io, then we do not want
  1232. // to write beyond end of file. If the base is beyond Eof, we will just
  1233. // Noop the call. If the transfer starts before Eof, but extends
  1234. // beyond, we will truncate the transfer to the last sector
  1235. // boundary.
  1236. //
  1237. // Just in case this is paging io, limit write to file size.
  1238. // Otherwise, in case of write through, since Mm rounds up
  1239. // to a page, we might try to acquire the resource exclusive
  1240. // when our top level guy only acquired it shared. Thus, =><=.
  1241. //
  1242. NtfsAcquireFsrtlHeader( Scb );
  1243. if (ByteRange > Header->FileSize.QuadPart) {
  1244. if (StartingVbo >= Header->FileSize.QuadPart) {
  1245. DebugTrace( 0, Dbg, ("PagingIo started beyond EOF.\n") );
  1246. Irp->IoStatus.Information = 0;
  1247. //
  1248. // Make sure we do not advance ValidDataLength!
  1249. // We also haven't really written anything so set doingioateof back to
  1250. // false
  1251. //
  1252. ByteRange = Header->ValidDataLength.QuadPart;
  1253. DoingIoAtEof = FALSE;
  1254. NtfsReleaseFsrtlHeader( Scb );
  1255. try_return( Status = STATUS_SUCCESS );
  1256. } else {
  1257. DebugTrace( 0, Dbg, ("PagingIo extending beyond EOF.\n") );
  1258. #ifdef NTFS_RWC_DEBUG
  1259. if ((FileObject->SectionObjectPointer != &Scb->NonpagedScb->SegmentObject) &&
  1260. (StartingVbo < NtfsRWCHighThreshold) &&
  1261. (ByteRange > NtfsRWCLowThreshold)) {
  1262. PRWC_HISTORY_ENTRY NextBuffer;
  1263. NextBuffer = NtfsGetHistoryEntry( Scb );
  1264. NextBuffer->Operation = TrimCompressedWrite;
  1265. NextBuffer->Information = Scb->Header.FileSize.LowPart;
  1266. NextBuffer->FileOffset = (ULONG) StartingVbo;
  1267. NextBuffer->Length = (ULONG) ByteRange;
  1268. }
  1269. #endif
  1270. ByteCount = Header->FileSize.QuadPart - StartingVbo;
  1271. ByteRange = Header->FileSize.QuadPart;
  1272. }
  1273. }
  1274. NtfsReleaseFsrtlHeader( Scb );
  1275. //
  1276. // If there is a user-mapped file and a Usn Journal, then try to post a change.
  1277. // Checkpoint the transaction to reduce resource contention of the UsnJournal
  1278. // and Mft.
  1279. //
  1280. if (FlagOn(Header->Flags, FSRTL_FLAG_USER_MAPPED_FILE) &&
  1281. FlagOn( Vcb->VcbState, VCB_STATE_USN_JOURNAL_ACTIVE )) {
  1282. ASSERT( Vcb->UsnJournal != NULL );
  1283. NtfsPostUsnChange( IrpContext, Scb, USN_REASON_DATA_OVERWRITE );
  1284. if (IrpContext->TransactionId != 0) {
  1285. NtfsCheckpointCurrentTransaction( IrpContext );
  1286. }
  1287. }
  1288. }
  1289. ASSERT( PagingIo || FileObject->WriteAccess || RawEncryptedWrite );
  1290. ASSERT( !(PagingIo && RawEncryptedWrite) );
  1291. //
  1292. // If the Scb is uninitialized, we initialize it now.
  1293. // We skip this step for a $INDEX_ALLOCATION stream. We need to
  1294. // protect ourselves in the case where an $INDEX_ALLOCATION
  1295. // stream was created and deleted in an aborted transaction.
  1296. // In that case we may get a lazy-writer call which will
  1297. // naturally be nooped below since the valid data length
  1298. // in the Scb is 0.
  1299. //
  1300. if (!FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED )) {
  1301. if (Scb->AttributeTypeCode != $INDEX_ALLOCATION) {
  1302. DebugTrace( 0, Dbg, ("Initializing Scb -> %08lx\n", Scb) );
  1303. //
  1304. // Acquire and drop the Scb when doing this.
  1305. //
  1306. // Make sure we don't have any Mft records.
  1307. //
  1308. NtfsPurgeFileRecordCache( IrpContext );
  1309. NtfsAcquireResourceShared( IrpContext, Scb, TRUE );
  1310. ScbAcquired = TRUE;
  1311. NtfsUpdateScbFromAttribute( IrpContext, Scb, NULL );
  1312. NtfsReleaseResource( IrpContext, Scb );
  1313. ScbAcquired = FALSE;
  1314. } else {
  1315. ASSERT( Header->ValidDataLength.QuadPart == Li0.QuadPart );
  1316. }
  1317. }
  1318. //
  1319. // We assert that Paging Io writes will never WriteToEof.
  1320. //
  1321. ASSERT( !WriteToEof || !PagingIo );
  1322. //
  1323. // We assert that we never get a non-cached io call for a non-$DATA,
  1324. // resident attribute.
  1325. //
  1326. ASSERTMSG( "Non-cached I/O call on resident system attribute\n",
  1327. NtfsIsTypeCodeUserData( Scb->AttributeTypeCode ) ||
  1328. NtfsIsTypeCodeLoggedUtilityStream( Scb->AttributeTypeCode ) ||
  1329. !NonCachedIo ||
  1330. !FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT ));
  1331. //
  1332. // Here is the deal with ValidDataLength and FileSize:
  1333. //
  1334. // Rule 1: PagingIo is never allowed to extend file size.
  1335. //
  1336. // Rule 2: Only the top level requestor may extend Valid
  1337. // Data Length. This may be paging IO, as when a
  1338. // a user maps a file, but will never be as a result
  1339. // of cache lazy writer writes since they are not the
  1340. // top level request.
  1341. //
  1342. // Rule 3: If, using Rules 1 and 2, we decide we must extend
  1343. // file size or valid data, we take the Fcb exclusive.
  1344. //
  1345. //
  1346. // Now see if we are writing beyond valid data length, and thus
  1347. // maybe beyond the file size. If so, then we must
  1348. // release the Fcb and reacquire it exclusive. Note that it is
  1349. // important that when not writing beyond EOF that we check it
  1350. // while acquired shared and keep the FCB acquired, in case some
  1351. // turkey truncates the file. Note that for paging Io we will
  1352. // already have acquired the file correctly.
  1353. //
  1354. if (DoingIoAtEof) {
  1355. //
  1356. // If this was a non-cached asynchronous operation we will
  1357. // convert it to synchronous. This is to allow the valid
  1358. // data length change to go out to disk and to fix the
  1359. // problem of the Fcb being in the exclusive Fcb list.
  1360. //
  1361. if (!Wait && NonCachedIo) {
  1362. Wait = TRUE;
  1363. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
  1364. //
  1365. // If this is async Io to a compressed stream
  1366. // then we will make this look synchronous.
  1367. //
  1368. } else if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  1369. Wait = TRUE;
  1370. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
  1371. }
  1372. //
  1373. // If the Scb is uninitialized, we initialize it now.
  1374. //
  1375. if (!FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED )) {
  1376. DebugTrace( 0, Dbg, ("Initializing Scb -> %08lx\n", Scb) );
  1377. //
  1378. // Acquire and drop the Scb when doing this.
  1379. //
  1380. // Make sure we don't have any Mft records.
  1381. //
  1382. NtfsPurgeFileRecordCache( IrpContext );
  1383. NtfsAcquireResourceShared( IrpContext, Scb, TRUE );
  1384. ScbAcquired = TRUE;
  1385. NtfsUpdateScbFromAttribute( IrpContext, Scb, NULL );
  1386. NtfsReleaseResource( IrpContext, Scb );
  1387. ScbAcquired = FALSE;
  1388. }
  1389. }
  1390. //
  1391. // We check whether we can proceed based on the state of the file oplocks.
  1392. //
  1393. if (!PagingIo && (TypeOfOpen == UserFileOpen)) {
  1394. BOOLEAN Inline = FALSE;
  1395. PVOLUME_DEVICE_OBJECT Vdo = CONTAINING_RECORD( Vcb, VOLUME_DEVICE_OBJECT, Vcb );
  1396. //
  1397. // For non-fsp, blocking writes that are top level lets complete oplocks inline
  1398. // Also if an unsafe test shows we're at the overflow queue limit and we're toplevel
  1399. // also do it inline
  1400. //
  1401. if (((Vdo->OverflowQueueCount >= OVERFLOW_QUEUE_LIMIT) || CanFsdWait( Irp )) &&
  1402. !FlagOn( IrpContext->State, IRP_CONTEXT_STATE_IN_FSP ) &&
  1403. NtfsIsTopLevelRequest( IrpContext )) {
  1404. SetFlag( IrpContext->Union.NtfsIoContext->Flags, NTFS_IO_CONTEXT_INLINE_OPLOCK );
  1405. Inline = TRUE;
  1406. }
  1407. Status = FsRtlCheckOplock( &Scb->ScbType.Data.Oplock,
  1408. Irp,
  1409. IrpContext,
  1410. NtfsOplockComplete,
  1411. NtfsWriteOplockPrePostIrp );
  1412. if (Status != STATUS_SUCCESS) {
  1413. if ((Status == STATUS_PENDING) && Inline) {
  1414. Status = STATUS_WAIT_FOR_OPLOCK;
  1415. }
  1416. OplockPostIrp = TRUE;
  1417. PostIrp = TRUE;
  1418. try_return( NOTHING );
  1419. }
  1420. //
  1421. // This oplock call can affect whether fast IO is possible.
  1422. // We may have broken an oplock to no oplock held. If the
  1423. // current state of the file is FastIoIsNotPossible then
  1424. // recheck the fast IO state.
  1425. //
  1426. if (Header->IsFastIoPossible == FastIoIsNotPossible) {
  1427. NtfsAcquireFsrtlHeader( Scb );
  1428. Header->IsFastIoPossible = NtfsIsFastIoPossible( Scb );
  1429. NtfsReleaseFsrtlHeader( Scb );
  1430. }
  1431. //
  1432. // We have to check for write access according to the current
  1433. // state of the file locks, and set FileSize from the Fcb.
  1434. //
  1435. if ((Scb->ScbType.Data.FileLock != NULL) &&
  1436. !FsRtlCheckLockForWriteAccess( Scb->ScbType.Data.FileLock, Irp )) {
  1437. try_return( Status = STATUS_FILE_LOCK_CONFLICT );
  1438. }
  1439. }
  1440. // ASSERT( Header->ValidDataLength.QuadPart <= Header->FileSize.QuadPart);
  1441. //
  1442. // If we are extending a file size, we may have to extend the allocation.
  1443. // For a non-resident attribute, this is a call to the add allocation
  1444. // routine. For a resident attribute it depends on whether we
  1445. // can use the change attribute routine to automatically extend
  1446. // the attribute.
  1447. //
  1448. if (DoingIoAtEof && !FlagOn( IrpContext->State, IRP_CONTEXT_STATE_LAZY_WRITE )) {
  1449. //
  1450. // EXTENDING THE FILE
  1451. //
  1452. //
  1453. // If the write goes beyond the allocation size, add some
  1454. // file allocation.
  1455. //
  1456. if (ByteRange > Header->AllocationSize.QuadPart) {
  1457. BOOLEAN NonResidentPath;
  1458. NtfsAcquireExclusiveScb( IrpContext, Scb );
  1459. ScbAcquired = TRUE;
  1460. NtfsMungeScbSnapshot( IrpContext, Scb, OldFileSize );
  1461. //
  1462. // We have to deal with both the resident and non-resident
  1463. // case. For the resident case we do the work here
  1464. // only if the new size is too large for the change attribute
  1465. // value routine.
  1466. //
  1467. if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
  1468. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  1469. NonResidentPath = FALSE;
  1470. //
  1471. // Now call the attribute routine to change the value, remembering
  1472. // the values up to the current valid data length.
  1473. //
  1474. NtfsInitializeAttributeContext( &AttrContext );
  1475. CleanupAttributeContext = TRUE;
  1476. NtfsLookupAttributeForScb( IrpContext,
  1477. Scb,
  1478. NULL,
  1479. &AttrContext );
  1480. FileRecord = NtfsContainingFileRecord( &AttrContext );
  1481. Attribute = NtfsFoundAttribute( &AttrContext );
  1482. LlTemp1 = (LONGLONG) (Vcb->BytesPerFileRecordSegment
  1483. - FileRecord->FirstFreeByte
  1484. + QuadAlign( Attribute->Form.Resident.ValueLength ));
  1485. //
  1486. // If the new attribute size will not fit then we have to be
  1487. // prepared to go non-resident. If the byte range takes more
  1488. // more than 32 bits or this attribute is big enough to move
  1489. // then it will go non-resident. Otherwise we simply may
  1490. // end up moving another attribute or splitting the file
  1491. // record.
  1492. //
  1493. //
  1494. // Note, there is an infinitesimal chance that before the Lazy Writer
  1495. // writes the data for an attribute which is extending, but fits
  1496. // when we check it here, that some other attribute will grow,
  1497. // and this attribute no longer fits. If in addition, the disk
  1498. // is full, then the Lazy Writer will fail to allocate space
  1499. // for the data when it gets around to writing. This is
  1500. // incredibly unlikely, and not fatal; the Lazy Writer gets an
  1501. // error rather than the user. What we are trying to avoid is
  1502. // having to update the attribute every time on small writes
  1503. // (also see comments below in NONCACHED RESIDENT ATTRIBUTE case).
  1504. //
  1505. if (ByteRange > LlTemp1) {
  1506. //
  1507. // Go ahead and convert this attribute to non-resident.
  1508. // Then take the non-resident path below. There is a chance
  1509. // that there was a more suitable candidate to move non-resident
  1510. // but we don't want to change the file size until we copy
  1511. // the user's data into the cache in case the buffer is
  1512. // corrupt.
  1513. //
  1514. //
  1515. // We must have the paging Io resource exclusive to prevent a
  1516. // collided page wait while doing the convert to non-resident.
  1517. //
  1518. if (!PagingIo &&
  1519. !FlagOn( IrpContext->State, IRP_CONTEXT_STATE_ACQUIRE_EX ) &&
  1520. (Scb->Header.PagingIoResource != NULL)) {
  1521. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_ACQUIRE_EX );
  1522. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  1523. }
  1524. NtfsConvertToNonresident( IrpContext,
  1525. Fcb,
  1526. Attribute,
  1527. NonCachedIo,
  1528. &AttrContext );
  1529. NonResidentPath = TRUE;
  1530. //
  1531. // If there is room for the data, we will write a zero
  1532. // to the last byte to reserve the space since the
  1533. // Lazy Writer cannot grow the attribute with shared
  1534. // access.
  1535. //
  1536. } else {
  1537. //
  1538. // The attribute will stay resident because we
  1539. // have already checked that it will fit. It will
  1540. // not update the file size and valid data size in
  1541. // the Scb.
  1542. //
  1543. NtfsChangeAttributeValue( IrpContext,
  1544. Fcb,
  1545. (ULONG) ByteRange,
  1546. NULL,
  1547. 0,
  1548. TRUE,
  1549. FALSE,
  1550. FALSE,
  1551. FALSE,
  1552. &AttrContext );
  1553. Header->AllocationSize.LowPart = QuadAlign( (ULONG)ByteRange );
  1554. Scb->TotalAllocated = Header->AllocationSize.QuadPart;
  1555. }
  1556. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  1557. CleanupAttributeContext = FALSE;
  1558. } else {
  1559. NonResidentPath = TRUE;
  1560. }
  1561. //
  1562. // Note that we may have gotten all the space we need when
  1563. // we converted to nonresident above, so we have to check
  1564. // again if we are extending.
  1565. //
  1566. if (NonResidentPath &&
  1567. ByteRange > Scb->Header.AllocationSize.QuadPart) {
  1568. BOOLEAN AskForMore = TRUE;
  1569. //
  1570. // Assume we start allocating from the current allocation size unless we're
  1571. // sparse in which case we'll allocate from the starting compression unit if
  1572. // its beyond vdl
  1573. //
  1574. if (!FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_SPARSE ) ||
  1575. (BlockAlignTruncate( StartingVbo, (LONG)Scb->CompressionUnit) <= Scb->Header.ValidDataLength.QuadPart )) {
  1576. LlTemp1 = Scb->Header.AllocationSize.QuadPart;
  1577. } else {
  1578. LlTemp1 = BlockAlignTruncate( StartingVbo, (LONG)Scb->CompressionUnit );
  1579. }
  1580. //
  1581. // If we are not writing compressed then we may need to allocate precisely.
  1582. // This includes the uncompressed sparse file case
  1583. //
  1584. if (!FlagOn( Scb->ScbState, SCB_STATE_WRITE_COMPRESSED )) {
  1585. //
  1586. // If there is a compression unit then we could be in the process of
  1587. // decompressing. Allocate precisely in this case because we don't
  1588. // want to leave any holes. Specifically the user may have truncated
  1589. // the file and is now regenerating it yet the clear compression operation
  1590. // has already passed this point in the file (and dropped all resources).
  1591. // No one will go back to cleanup the allocation if we leave a hole now.
  1592. //
  1593. if (Scb->CompressionUnit != 0) {
  1594. LlTemp2 = ByteRange + Scb->CompressionUnit - 1;
  1595. ((PLARGE_INTEGER) &LlTemp2)->LowPart &= ~(Scb->CompressionUnit - 1);
  1596. LlTemp2 -= LlTemp1;
  1597. AskForMore = FALSE;
  1598. //
  1599. // Allocate through ByteRange.
  1600. //
  1601. } else {
  1602. LlTemp2 = ByteRange - LlTemp1;
  1603. }
  1604. //
  1605. // If the file is compressed, we want to limit how far we are
  1606. // willing to go beyond ValidDataLength, because we would just
  1607. // have to throw that space away anyway in NtfsZeroData. If
  1608. // we would have to zero more than two compression units (same
  1609. // limit as NtfsZeroData), then just allocate space where we
  1610. // need it.
  1611. //
  1612. } else {
  1613. if ((StartingVbo - Header->ValidDataLength.QuadPart) > (LONGLONG) (Scb->CompressionUnit * 2)) {
  1614. ASSERT( FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK ));
  1615. LlTemp1 = StartingVbo;
  1616. ((PLARGE_INTEGER) &LlTemp1)->LowPart &= ~(Scb->CompressionUnit - 1);
  1617. }
  1618. //
  1619. // Allocate to the end of ByteRange.
  1620. //
  1621. LlTemp2 = ByteRange - LlTemp1;
  1622. }
  1623. //
  1624. //
  1625. // This will add the allocation and modify the allocation
  1626. // size in the Scb.
  1627. //
  1628. NtfsAddAllocation( IrpContext,
  1629. FileObject,
  1630. Scb,
  1631. LlClustersFromBytesTruncate( Vcb, LlTemp1 ),
  1632. LlClustersFromBytes( Vcb, LlTemp2 ),
  1633. AskForMore,
  1634. Ccb );
  1635. //
  1636. // Assert that the allocation worked
  1637. //
  1638. ASSERT( Header->AllocationSize.QuadPart >= ByteRange ||
  1639. (Scb->CompressionUnit != 0));
  1640. SetFlag(Scb->ScbState, SCB_STATE_TRUNCATE_ON_CLOSE);
  1641. //
  1642. // If this is a sparse file lets pad the allocation by adding a
  1643. // hole at the end of the allocation. This will let us utilize
  1644. // the fast IO path.
  1645. //
  1646. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_SPARSE )) {
  1647. LlTemp2 = Int64ShllMod32( LlTemp2, 3 );
  1648. if (MAXFILESIZE - Header->AllocationSize.QuadPart > LlTemp2) {
  1649. NtfsAddSparseAllocation( IrpContext,
  1650. FileObject,
  1651. Scb,
  1652. Header->AllocationSize.QuadPart,
  1653. LlTemp2 );
  1654. }
  1655. }
  1656. }
  1657. //
  1658. // Now that we have grown the attribute, it is important to
  1659. // checkpoint the current transaction and free all main resources
  1660. // to avoid the tc type deadlocks. Note that the extend is ok
  1661. // to stand in its own right, and the stream will be truncated
  1662. // on close anyway.
  1663. //
  1664. NtfsCheckpointCurrentTransaction( IrpContext );
  1665. //
  1666. // Make sure we purge the file record cache as well. Otherwise
  1667. // a purge of the Mft may fail in a different thread which owns a resource
  1668. // this thread needs later.
  1669. //
  1670. NtfsPurgeFileRecordCache( IrpContext );
  1671. //
  1672. // Growing allocation can change file size (in ChangeAttributeValue).
  1673. // Make sure we know the correct value for file size to restore.
  1674. //
  1675. OldFileSize = Header->FileSize.QuadPart;
  1676. while (!IsListEmpty(&IrpContext->ExclusiveFcbList)) {
  1677. NtfsReleaseFcb( IrpContext,
  1678. (PFCB)CONTAINING_RECORD(IrpContext->ExclusiveFcbList.Flink,
  1679. FCB,
  1680. ExclusiveFcbLinks ));
  1681. }
  1682. ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_RELEASE_USN_JRNL |
  1683. IRP_CONTEXT_FLAG_RELEASE_MFT );
  1684. //
  1685. // Go through and free any Scb's in the queue of shared
  1686. // Scb's for transactions.
  1687. //
  1688. if (IrpContext->SharedScb != NULL) {
  1689. NtfsReleaseSharedResources( IrpContext );
  1690. }
  1691. ScbAcquired = FALSE;
  1692. }
  1693. //
  1694. // Now synchronize with the FsRtl Header and set FileSize
  1695. // now so that our reads will not get truncated.
  1696. //
  1697. NtfsAcquireFsrtlHeader( Scb );
  1698. if (ByteRange > Header->FileSize.QuadPart) {
  1699. ASSERT( ByteRange <= Header->AllocationSize.QuadPart );
  1700. Header->FileSize.QuadPart = ByteRange;
  1701. SetFlag( UserFileObject->Flags, FO_FILE_SIZE_CHANGED );
  1702. }
  1703. NtfsReleaseFsrtlHeader( Scb );
  1704. }
  1705. //
  1706. // HANDLE THE NONCACHED RESIDENT ATTRIBUTE CASE
  1707. //
  1708. // We let the cached case take the normal path for the following
  1709. // reasons:
  1710. //
  1711. // o To insure data coherency if a user maps the file
  1712. // o To get a page in the cache to keep the Fcb around
  1713. // o So the data can be accessed via the Fast I/O path
  1714. // o To reduce the number of calls to NtfsChangeAttributeValue,
  1715. // to infrequent calls from the Lazy Writer. Calls to CcCopyWrite
  1716. // are much cheaper. With any luck, if the attribute actually stays
  1717. // resident, we will only have to update it (and log it) once
  1718. // when the Lazy Writer gets around to the data.
  1719. //
  1720. // The disadvantage is the overhead to fault the data in the
  1721. // first time, but we may be able to do this with asynchronous
  1722. // read ahead.
  1723. //
  1724. if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT | SCB_STATE_CONVERT_UNDERWAY )
  1725. && NonCachedIo) {
  1726. //
  1727. // The attribute is already resident and we have already tested
  1728. // if we are going past the end of the file.
  1729. //
  1730. DebugTrace( 0, Dbg, ("Resident attribute write\n") );
  1731. //
  1732. // If this buffer is not in system space then we can't
  1733. // trust it. In that case we will allocate a temporary buffer
  1734. // and copy the user's data to it.
  1735. //
  1736. SystemBuffer = NtfsMapUserBuffer( Irp, NormalPagePriority );
  1737. if (!PagingIo && (Irp->RequestorMode != KernelMode)) {
  1738. SafeBuffer = NtfsAllocatePool( NonPagedPool,
  1739. (ULONG) ByteCount );
  1740. try {
  1741. RtlCopyMemory( SafeBuffer, SystemBuffer, (ULONG)ByteCount );
  1742. } except( EXCEPTION_EXECUTE_HANDLER ) {
  1743. try_return( Status = STATUS_INVALID_USER_BUFFER );
  1744. }
  1745. SystemBuffer = SafeBuffer;
  1746. }
  1747. //
  1748. // Make sure we don't have any Mft records.
  1749. //
  1750. NtfsPurgeFileRecordCache( IrpContext );
  1751. NtfsAcquireExclusiveScb( IrpContext, Scb );
  1752. ScbAcquired = TRUE;
  1753. //
  1754. // If the Scb is uninitialized, we initialize it now.
  1755. //
  1756. if (!FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED )) {
  1757. DebugTrace( 0, Dbg, ("Initializing Scb -> %08lx\n", Scb) );
  1758. //
  1759. // Unlike the other cases, we're already holding the Scb, so
  1760. // there's no need to acquire & drop it around the Update call.
  1761. //
  1762. NtfsUpdateScbFromAttribute( IrpContext, Scb, NULL );
  1763. //
  1764. // Make sure we purge the file record cache as well. Otherwise
  1765. // a purge of the Mft may fail in a different thread which owns a resource
  1766. // this thread needs later.
  1767. //
  1768. NtfsPurgeFileRecordCache( IrpContext );
  1769. }
  1770. //
  1771. // Now see if the file is still resident, and if not
  1772. // fall through below.
  1773. //
  1774. if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
  1775. //
  1776. // If this Scb is for an $EA attribute which is now resident then
  1777. // we don't want to write the data into the attribute. All resident
  1778. // EA's are modified directly.
  1779. //
  1780. if (Scb->AttributeTypeCode != $EA) {
  1781. NtfsInitializeAttributeContext( &AttrContext );
  1782. CleanupAttributeContext = TRUE;
  1783. NtfsLookupAttributeForScb( IrpContext,
  1784. Scb,
  1785. NULL,
  1786. &AttrContext );
  1787. Attribute = NtfsFoundAttribute( &AttrContext );
  1788. //
  1789. // The attribute should already be optionally extended,
  1790. // just write the data to it now.
  1791. //
  1792. NtfsChangeAttributeValue( IrpContext,
  1793. Fcb,
  1794. ((ULONG)StartingVbo),
  1795. SystemBuffer,
  1796. (ULONG)ByteCount,
  1797. (BOOLEAN)((((ULONG)StartingVbo) + (ULONG)ByteCount) >
  1798. Attribute->Form.Resident.ValueLength),
  1799. FALSE,
  1800. FALSE,
  1801. FALSE,
  1802. &AttrContext );
  1803. }
  1804. //
  1805. // Make sure the cache FileSizes are updated if this is not paging I/O.
  1806. //
  1807. if (!PagingIo && DoingIoAtEof) {
  1808. NtfsSetBothCacheSizes( FileObject,
  1809. (PCC_FILE_SIZES)&Header->AllocationSize,
  1810. Scb );
  1811. }
  1812. Irp->IoStatus.Information = (ULONG)ByteCount;
  1813. try_return( Status = STATUS_SUCCESS );
  1814. //
  1815. // Gee, someone else made the file nonresident, so we can just
  1816. // free the resource and get on with life.
  1817. //
  1818. } else {
  1819. NtfsReleaseScb( IrpContext, Scb );
  1820. ScbAcquired = FALSE;
  1821. }
  1822. }
  1823. //
  1824. // HANDLE THE NON-CACHED CASE
  1825. //
  1826. if (NonCachedIo) {
  1827. ULONG SectorSize;
  1828. ULONG BytesToWrite;
  1829. //
  1830. // Make sure the cache FileSizes are updated if this is not paging I/O.
  1831. //
  1832. if (!PagingIo && DoingIoAtEof) {
  1833. NtfsSetBothCacheSizes( FileObject,
  1834. (PCC_FILE_SIZES)&Header->AllocationSize,
  1835. Scb );
  1836. }
  1837. //
  1838. // Get the sector size
  1839. //
  1840. SectorSize = Vcb->BytesPerSector;
  1841. //
  1842. // Round up to a sector boundry
  1843. //
  1844. BytesToWrite = ((ULONG)ByteCount + (SectorSize - 1))
  1845. & ~(SectorSize - 1);
  1846. //
  1847. // All requests should be well formed and
  1848. // make sure we don't wipe out any data
  1849. //
  1850. if (!FlagOn( IrpContext->State, IRP_CONTEXT_STATE_LAZY_WRITE )) {
  1851. if ((((ULONG)StartingVbo) & (SectorSize - 1))
  1852. || ((BytesToWrite != (ULONG)ByteCount)
  1853. && ByteRange < Header->ValidDataLength.QuadPart )) {
  1854. //**** we only reach this path via fast I/O and by returning not implemented we
  1855. //**** force it to return to use via slow I/O
  1856. DebugTrace( 0, Dbg, ("NtfsCommonWrite -> STATUS_NOT_IMPLEMENTED\n") );
  1857. try_return( Status = STATUS_NOT_IMPLEMENTED );
  1858. }
  1859. }
  1860. //
  1861. // If this is a write to an encrypted file then make it synchronous. We
  1862. // need to do this so that the encryption driver has a thread to run in.
  1863. //
  1864. if ((Scb->EncryptionContext != NULL) &&
  1865. !FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT ) &&
  1866. (NtfsData.EncryptionCallBackTable.BeforeWriteProcess != NULL) &&
  1867. NtfsIsTypeCodeUserData( Scb->AttributeTypeCode )) {
  1868. Wait = TRUE;
  1869. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
  1870. }
  1871. //
  1872. // If this noncached transfer is at least one sector beyond
  1873. // the current ValidDataLength in the Scb, then we have to
  1874. // zero the sectors in between. This can happen if the user
  1875. // has opened the file noncached, or if the user has mapped
  1876. // the file and modified a page beyond ValidDataLength. It
  1877. // *cannot* happen if the user opened the file cached, because
  1878. // ValidDataLength in the Fcb is updated when he does the cached
  1879. // write (we also zero data in the cache at that time), and
  1880. // therefore, we will bypass this action when the data
  1881. // is ultimately written through (by the Lazy Writer).
  1882. //
  1883. // For the paging file we don't care about security (ie.
  1884. // stale data), do don't bother zeroing.
  1885. //
  1886. // We can actually get writes wholly beyond valid data length
  1887. // from the LazyWriter because of paging Io decoupling.
  1888. //
  1889. // We drop this zeroing on the floor in any case where this
  1890. // request is a recursive write caused by a flush from a higher level write.
  1891. //
  1892. if (Header->ValidDataLength.QuadPart > Scb->ValidDataToDisk) {
  1893. LlTemp1 = Header->ValidDataLength.QuadPart;
  1894. } else {
  1895. //
  1896. // This can only occur for compressed files
  1897. //
  1898. LlTemp1 = Scb->ValidDataToDisk;
  1899. }
  1900. if (!FlagOn( IrpContext->State, IRP_CONTEXT_STATE_LAZY_WRITE ) &&
  1901. !RecursiveWriteThrough &&
  1902. (StartingVbo > LlTemp1)) {
  1903. #ifdef SYSCACHE_DEBUG
  1904. if (ScbIsBeingLogged( Scb )) {
  1905. CalculateSyscacheFlags( IrpContext, Flags, SCE_FLAG_WRITE );
  1906. TempEntry = FsRtlLogSyscacheEvent( Scb, SCE_ZERO_NC, Flags, LlTemp1, StartingVbo - LlTemp1, 0);
  1907. }
  1908. #endif
  1909. if (!NtfsZeroData( IrpContext,
  1910. Scb,
  1911. FileObject,
  1912. LlTemp1,
  1913. StartingVbo - LlTemp1,
  1914. &OldFileSize )) {
  1915. #ifdef SYSCACHE_DEBUG
  1916. if (ScbIsBeingLogged( Scb )) {
  1917. FsRtlUpdateSyscacheEvent( Scb, TempEntry, Header->ValidDataLength.QuadPart, 0 );
  1918. }
  1919. #endif
  1920. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  1921. }
  1922. #ifdef SYSCACHE_DEBUG
  1923. if (ScbIsBeingLogged( Scb )) {
  1924. FsRtlUpdateSyscacheEvent( Scb, TempEntry, Header->ValidDataLength.QuadPart, 0 );
  1925. }
  1926. #endif
  1927. }
  1928. //
  1929. // If this Scb uses update sequence protection, we need to transform
  1930. // the blocks to a protected version. We first allocate an auxilary
  1931. // buffer and Mdl. Then we copy the data to this buffer and
  1932. // transform it. Finally we attach this Mdl to the Irp and use
  1933. // it to perform the Io.
  1934. //
  1935. if (FlagOn( Scb->ScbState, SCB_STATE_USA_PRESENT )) {
  1936. TempLength = BytesToWrite;
  1937. //
  1938. // Find the system buffer for this request and initialize the
  1939. // local state.
  1940. //
  1941. SystemBuffer = NtfsMapUserBuffer( Irp, HighPagePriority );
  1942. OriginalMdl = Irp->MdlAddress;
  1943. OriginalBuffer = Irp->UserBuffer;
  1944. NewBuffer = NULL;
  1945. //
  1946. // Protect this operation with a try-finally.
  1947. //
  1948. try {
  1949. //
  1950. // If this is the Mft Scb and the range of bytes falls into
  1951. // the range for the Mirror Mft, we generate a write to
  1952. // the mirror as well. Don't do this if we detected a problem
  1953. // with the Mft when analyzing the first file records. We
  1954. // can use the presence of the version number in the Vcb
  1955. // to tell us this.
  1956. //
  1957. if ((Scb == Vcb->MftScb) &&
  1958. (StartingVbo < Vcb->Mft2Scb->Header.FileSize.QuadPart) &&
  1959. (Vcb->MajorVersion != 0)) {
  1960. LlTemp1 = Vcb->Mft2Scb->Header.FileSize.QuadPart - StartingVbo;
  1961. if ((ULONG)LlTemp1 > BytesToWrite) {
  1962. (ULONG)LlTemp1 = BytesToWrite;
  1963. }
  1964. CcCopyWrite( Vcb->Mft2Scb->FileObject,
  1965. (PLARGE_INTEGER)&StartingVbo,
  1966. (ULONG)LlTemp1,
  1967. TRUE,
  1968. SystemBuffer );
  1969. //
  1970. // Now flush this to disk.
  1971. //
  1972. CcFlushCache( &Vcb->Mft2Scb->NonpagedScb->SegmentObject,
  1973. (PLARGE_INTEGER)&StartingVbo,
  1974. (ULONG)LlTemp1,
  1975. &Irp->IoStatus );
  1976. NtfsCleanupTransaction( IrpContext, Irp->IoStatus.Status, TRUE );
  1977. }
  1978. //
  1979. // Start by allocating buffer and Mdl.
  1980. //
  1981. NtfsCreateMdlAndBuffer( IrpContext,
  1982. Scb,
  1983. RESERVED_BUFFER_ONE_NEEDED,
  1984. &TempLength,
  1985. &NewMdl,
  1986. &NewBuffer );
  1987. //
  1988. // Now transform and write out the original stream.
  1989. //
  1990. RtlCopyMemory( NewBuffer, SystemBuffer, BytesToWrite );
  1991. //
  1992. // We copy our Mdl into the Irp and then perform the Io.
  1993. //
  1994. Irp->MdlAddress = NewMdl;
  1995. Irp->UserBuffer = NewBuffer;
  1996. //
  1997. // Now increment the sequence number in both the original
  1998. // and copied buffer, and transform the copied buffer.
  1999. // If this is the LogFile then adjust the range of the transform.
  2000. //
  2001. if ((PAGE_SIZE != LFS_DEFAULT_LOG_PAGE_SIZE) &&
  2002. (Scb == Vcb->LogFileScb)) {
  2003. LONGLONG LfsFileOffset;
  2004. ULONG LfsLength;
  2005. ULONG LfsBias;
  2006. LfsFileOffset = StartingVbo;
  2007. LfsLength = BytesToWrite;
  2008. LfsCheckWriteRange( &Vcb->LfsWriteData, &LfsFileOffset, &LfsLength );
  2009. LfsBias = (ULONG) (LfsFileOffset - StartingVbo);
  2010. NtfsTransformUsaBlock( Scb,
  2011. Add2Ptr( SystemBuffer, LfsBias ),
  2012. Add2Ptr( NewBuffer, LfsBias ),
  2013. LfsLength );
  2014. } else {
  2015. NtfsTransformUsaBlock( Scb,
  2016. SystemBuffer,
  2017. NewBuffer,
  2018. BytesToWrite );
  2019. }
  2020. ASSERT( Wait );
  2021. NtfsNonCachedIo( IrpContext,
  2022. Irp,
  2023. Scb,
  2024. StartingVbo,
  2025. BytesToWrite,
  2026. 0 );
  2027. } finally {
  2028. //
  2029. // In all cases we restore the user's Mdl and cleanup
  2030. // our Mdl and buffer.
  2031. //
  2032. if (NewBuffer != NULL) {
  2033. Irp->MdlAddress = OriginalMdl;
  2034. Irp->UserBuffer = OriginalBuffer;
  2035. NtfsDeleteMdlAndBuffer( NewMdl, NewBuffer );
  2036. }
  2037. }
  2038. //
  2039. // Otherwise we simply perform the Io.
  2040. //
  2041. } else {
  2042. ULONG StreamFlags = 0;
  2043. //
  2044. // Setup async info in the io context before doing non cached io
  2045. //
  2046. if (!Wait) {
  2047. if (!PagingIo) {
  2048. NtfsSetIoContextAsync( IrpContext, Scb->Header.PagingIoResource, IrpSp->Parameters.Write.Length );
  2049. } else {
  2050. NtfsSetIoContextAsync( IrpContext, NULL, IrpSp->Parameters.Write.Length );
  2051. }
  2052. }
  2053. //
  2054. // If the file has an UpdateLsn, then flush the log file before
  2055. // allowing the data to go out. The UpdateLsn is synchronized
  2056. // with the FcbLock. However, since we are in the process of
  2057. // doing a write, if we see a 0 in our unsafe test, it is ok
  2058. // to procede without an LfsFlush.
  2059. //
  2060. if (Fcb->UpdateLsn.QuadPart != 0) {
  2061. LSN UpdateLsn;
  2062. NtfsLockFcb( IrpContext, Fcb );
  2063. UpdateLsn = Fcb->UpdateLsn;
  2064. Fcb->UpdateLsn.QuadPart = 0;
  2065. NtfsUnlockFcb( IrpContext, Fcb );
  2066. LfsFlushToLsn( Vcb->LogHandle, UpdateLsn );
  2067. }
  2068. //
  2069. // Remember that from this point on we need to restore ValidDataToDisk.
  2070. // (Doing so earlier can get us into deadlocks if we hit the finally
  2071. // clause holding the Mft & UsnJournal.)
  2072. //
  2073. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  2074. RestoreValidDataToDisk = TRUE;
  2075. }
  2076. //
  2077. // Let's decide if there's anything special we need to tell NonCachedIo
  2078. // about this stream and how we're accessing it.
  2079. //
  2080. if (FileObject->SectionObjectPointer != &Scb->NonpagedScb->SegmentObject) {
  2081. SetFlag( StreamFlags, COMPRESSED_STREAM );
  2082. }
  2083. if (RawEncryptedWrite) {
  2084. SetFlag( StreamFlags, ENCRYPTED_STREAM );
  2085. }
  2086. #ifdef NTFS_RWC_DEBUG
  2087. if (FlagOn( StreamFlags, COMPRESSED_STREAM )) {
  2088. if ((StartingVbo < NtfsRWCHighThreshold) &&
  2089. (StartingVbo + BytesToWrite > NtfsRWCLowThreshold)) {
  2090. PRWC_HISTORY_ENTRY NextBuffer;
  2091. NextBuffer = NtfsGetHistoryEntry( Scb );
  2092. NextBuffer->Operation = WriteCompressed;
  2093. NextBuffer->Information = 0;
  2094. NextBuffer->FileOffset = (ULONG) StartingVbo;
  2095. NextBuffer->Length = (ULONG) BytesToWrite;
  2096. }
  2097. }
  2098. #endif
  2099. #ifdef SYSCACHE_DEBUG
  2100. if (ScbIsBeingLogged( Scb )) {
  2101. CalculateSyscacheFlags( IrpContext, Flags, SCE_FLAG_WRITE );
  2102. if (DoingIoAtEof && (StartingVbo + BytesToWrite > Scb->Header.FileSize.QuadPart)) {
  2103. SetFlag( Flags, SCE_FLAG_END_BUFFER );
  2104. }
  2105. TempEntry = FsRtlLogSyscacheEvent( Scb, SCE_WRITE, Flags, StartingVbo, BytesToWrite, Status );
  2106. SystemBuffer = NtfsMapUserBufferNoRaise( Irp, NormalPagePriority );
  2107. if (DoingIoAtEof && (StartingVbo + BytesToWrite > Scb->Header.FileSize.QuadPart)) {
  2108. Flags = *((UNALIGNED LONG *)Add2Ptr( SystemBuffer, BytesToWrite - sizeof(LONG) ));
  2109. } else {
  2110. Flags = *((UNALIGNED LONG *)SystemBuffer);
  2111. }
  2112. FsRtlUpdateSyscacheEvent( Scb, TempEntry, Flags, 0 );
  2113. }
  2114. #endif
  2115. #if defined( BENL_DBG ) || defined( SYSCACHE_DEBUG )
  2116. try {
  2117. #endif
  2118. Status = NtfsNonCachedIo( IrpContext,
  2119. Irp,
  2120. Scb,
  2121. StartingVbo,
  2122. BytesToWrite,
  2123. StreamFlags );
  2124. #if defined( BENL_DBG ) || defined( SYSCACHE_DEBUG )
  2125. } finally {
  2126. #endif
  2127. #ifdef SYSCACHE_DEBUG
  2128. if (AbnormalTermination()) {
  2129. if (ScbIsBeingLogged( Scb )) {
  2130. FsRtlUpdateSyscacheEvent( Scb, TempEntry, IrpContext->ExceptionStatus, 0 );
  2131. }
  2132. }
  2133. #endif
  2134. #if defined( BENL_DBG ) || defined( SYSCACHE_DEBUG )
  2135. }
  2136. #endif
  2137. #ifdef SYSCACHE_DEBUG
  2138. if (ScbIsBeingLogged( Scb ) && !NT_SUCCESS( Status ) && (Status != STATUS_PENDING)) {
  2139. FsRtlUpdateSyscacheEvent( Scb, TempEntry, Status, 0 );
  2140. }
  2141. #endif
  2142. if (Status == STATUS_PENDING) {
  2143. IrpContext->Union.NtfsIoContext = NULL;
  2144. PagingIoAcquired = FALSE;
  2145. Irp = NULL;
  2146. try_return( Status );
  2147. }
  2148. }
  2149. //
  2150. // Show that we want to immediately update the Mft.
  2151. //
  2152. UpdateMft = TRUE;
  2153. //
  2154. // If the call didn't succeed, raise the error status
  2155. //
  2156. if (!NT_SUCCESS( Status = Irp->IoStatus.Status )) {
  2157. NtfsNormalizeAndRaiseStatus( IrpContext, Status, STATUS_UNEXPECTED_IO_ERROR );
  2158. } else {
  2159. //
  2160. // Else set the context block to reflect the entire write
  2161. // Also assert we got how many bytes we asked for.
  2162. //
  2163. ASSERT( Irp->IoStatus.Information == BytesToWrite );
  2164. Irp->IoStatus.Information = (ULONG)ByteCount;
  2165. }
  2166. //
  2167. // The transfer is either complete, or the Iosb contains the
  2168. // appropriate status.
  2169. //
  2170. try_return( Status );
  2171. } // if No Intermediate Buffering
  2172. //
  2173. // HANDLE THE CACHED CASE
  2174. //
  2175. ASSERT( !PagingIo );
  2176. //
  2177. // Remember if we need to update the Mft.
  2178. //
  2179. if (!FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
  2180. UpdateMft = BooleanFlagOn( IrpContext->State, IRP_CONTEXT_STATE_WRITE_THROUGH );
  2181. }
  2182. //
  2183. // If this write is beyond (valid data length / valid data to disk), then we
  2184. // must zero the data in between. Only compressed files have a nonzero VDD
  2185. //
  2186. if (Header->ValidDataLength.QuadPart > Scb->ValidDataToDisk) {
  2187. ZeroStart = Header->ValidDataLength.QuadPart;
  2188. } else {
  2189. ZeroStart = Scb->ValidDataToDisk;
  2190. }
  2191. ZeroLength = StartingVbo - ZeroStart;
  2192. //
  2193. // We delay setting up the file cache until now, in case the
  2194. // caller never does any I/O to the file, and thus
  2195. // FileObject->PrivateCacheMap == NULL. Don't cache the normal
  2196. // stream unless we need to.
  2197. //
  2198. if ((FileObject->PrivateCacheMap == NULL)
  2199. &&
  2200. !FlagOn(IrpContext->MinorFunction, IRP_MN_COMPRESSED) || (ZeroLength > 0)) {
  2201. DebugTrace( 0, Dbg, ("Initialize cache mapping.\n") );
  2202. //
  2203. // Get the file allocation size, and if it is less than
  2204. // the file size, raise file corrupt error.
  2205. //
  2206. if (Header->FileSize.QuadPart > Header->AllocationSize.QuadPart) {
  2207. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  2208. }
  2209. //
  2210. // Now initialize the cache map. Notice that we may extending
  2211. // the ValidDataLength with this write call. At this point
  2212. // we haven't updated the ValidDataLength in the Scb header.
  2213. // This way we will get a call from the cache manager
  2214. // when the lazy writer writes out the data.
  2215. //
  2216. //
  2217. // Make sure we are serialized with the FileSizes, and
  2218. // will remove this condition if we abort.
  2219. //
  2220. if (!DoingIoAtEof) {
  2221. FsRtlLockFsRtlHeader( Header );
  2222. IrpContext->CleanupStructure = Scb;
  2223. }
  2224. CcInitializeCacheMap( FileObject,
  2225. (PCC_FILE_SIZES)&Header->AllocationSize,
  2226. FALSE,
  2227. &NtfsData.CacheManagerCallbacks,
  2228. Scb );
  2229. if (!DoingIoAtEof) {
  2230. FsRtlUnlockFsRtlHeader( Header );
  2231. IrpContext->CleanupStructure = NULL;
  2232. }
  2233. CcSetReadAheadGranularity( FileObject, READ_AHEAD_GRANULARITY );
  2234. }
  2235. //
  2236. // Make sure the cache FileSizes are updated.
  2237. //
  2238. if (DoingIoAtEof) {
  2239. NtfsSetBothCacheSizes( FileObject,
  2240. (PCC_FILE_SIZES)&Header->AllocationSize,
  2241. Scb );
  2242. }
  2243. if (ZeroLength > 0) {
  2244. //
  2245. // If the caller is writing zeros way beyond ValidDataLength,
  2246. // then noop it. We need to wrap the compare in a try-except
  2247. // to protect ourselves from an invalid user buffer.
  2248. //
  2249. if ((ZeroLength > PAGE_SIZE) &&
  2250. (ByteCount <= sizeof( LARGE_INTEGER ))) {
  2251. ULONG Zeroes;
  2252. try {
  2253. Zeroes = RtlEqualMemory( NtfsMapUserBuffer( Irp, NormalPagePriority ),
  2254. &Li0,
  2255. (ULONG)ByteCount );
  2256. } except( EXCEPTION_EXECUTE_HANDLER ) {
  2257. try_return( Status = STATUS_INVALID_USER_BUFFER );
  2258. }
  2259. if (Zeroes) {
  2260. ByteRange = Header->ValidDataLength.QuadPart;
  2261. Irp->IoStatus.Information = (ULONG)ByteCount;
  2262. try_return( Status = STATUS_SUCCESS );
  2263. }
  2264. }
  2265. //
  2266. // Call the Cache Manager to zero the data.
  2267. //
  2268. #ifdef SYSCACHE_DEBUG
  2269. if (ScbIsBeingLogged( Scb )) {
  2270. CalculateSyscacheFlags( IrpContext, Flags, SCE_FLAG_WRITE );
  2271. TempEntry = FsRtlLogSyscacheEvent( Scb, SCE_ZERO_C, Flags, ZeroStart, ZeroLength, StartingVbo );
  2272. }
  2273. #endif
  2274. if (!NtfsZeroData( IrpContext,
  2275. Scb,
  2276. FileObject,
  2277. ZeroStart,
  2278. ZeroLength,
  2279. &OldFileSize )) {
  2280. #ifdef SYSCACHE_DEBUG
  2281. if (ScbIsBeingLogged( Scb )) {
  2282. FsRtlUpdateSyscacheEvent( Scb, TempEntry, Header->ValidDataLength.QuadPart, SCE_FLAG_CANT_WAIT );
  2283. }
  2284. #endif
  2285. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  2286. }
  2287. }
  2288. //
  2289. // For a compressed stream, we must first reserve the space.
  2290. //
  2291. if ((Scb->CompressionUnit != 0) &&
  2292. !FlagOn(Scb->ScbState, SCB_STATE_REALLOCATE_ON_WRITE) &&
  2293. !NtfsReserveClusters(IrpContext, Scb, StartingVbo, (ULONG)ByteCount)) {
  2294. //
  2295. // If the file is only sparse and is fully allocated then there is no
  2296. // reason to reserve.
  2297. //
  2298. if (!FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK ) &&
  2299. !FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
  2300. VCN CurrentVcn;
  2301. LCN CurrentLcn;
  2302. ULONGLONG RemainingClusters;
  2303. ULONGLONG CurrentClusters;
  2304. CurrentVcn = LlClustersFromBytesTruncate( Vcb, StartingVbo );
  2305. RemainingClusters = LlClustersFromBytes( Vcb, StartingVbo + ByteCount );
  2306. while (NtfsLookupAllocation( IrpContext,
  2307. Scb,
  2308. CurrentVcn,
  2309. &CurrentLcn,
  2310. &CurrentClusters,
  2311. NULL,
  2312. NULL )) {
  2313. if (CurrentClusters >= RemainingClusters) {
  2314. RemainingClusters = 0;
  2315. break;
  2316. }
  2317. CurrentVcn += CurrentClusters;
  2318. RemainingClusters -= CurrentClusters;
  2319. }
  2320. if (RemainingClusters != 0) {
  2321. NtfsRaiseStatus( IrpContext, STATUS_DISK_FULL, NULL, NULL );
  2322. }
  2323. } else {
  2324. NtfsRaiseStatus( IrpContext, STATUS_DISK_FULL, NULL, NULL );
  2325. }
  2326. }
  2327. //
  2328. // We need to go through the cache for this
  2329. // file object. First handle the noncompressed calls.
  2330. //
  2331. if (!FlagOn(IrpContext->MinorFunction, IRP_MN_COMPRESSED)) {
  2332. //
  2333. // If there is a compressed section, we have to do cache coherency for
  2334. // that stream, and loop here to do a Cache Manager view at a time.
  2335. //
  2336. #ifdef COMPRESS_ON_WIRE
  2337. if (Scb->NonpagedScb->SegmentObjectC.DataSectionObject != NULL) {
  2338. LONGLONG LocalOffset = StartingVbo;
  2339. ULONG LocalLength;
  2340. ULONG LengthLeft = (ULONG)ByteCount;
  2341. //
  2342. // Create the compressed stream if not there.
  2343. //
  2344. if (Header->FileObjectC == NULL) {
  2345. NtfsCreateInternalCompressedStream( IrpContext, Scb, FALSE, NULL );
  2346. }
  2347. if (!FlagOn(IrpContext->MinorFunction, IRP_MN_MDL)) {
  2348. //
  2349. // Get hold of the user's buffer.
  2350. //
  2351. SystemBuffer = NtfsMapUserBuffer( Irp );
  2352. }
  2353. //
  2354. // We must loop to do a view at a time, because that is how much
  2355. // we synchronize at once below.
  2356. //
  2357. do {
  2358. //
  2359. // Calculate length left in view.
  2360. //
  2361. LocalLength = (ULONG)LengthLeft;
  2362. if (LocalLength > (ULONG)(VACB_MAPPING_GRANULARITY - (LocalOffset & (VACB_MAPPING_GRANULARITY - 1)))) {
  2363. LocalLength = (ULONG)(VACB_MAPPING_GRANULARITY - (LocalOffset & (VACB_MAPPING_GRANULARITY - 1)));
  2364. }
  2365. //
  2366. // Synchronize the current view.
  2367. //
  2368. Status = NtfsSynchronizeUncompressedIo( Scb,
  2369. &LocalOffset,
  2370. LocalLength,
  2371. TRUE,
  2372. &CompressionSync );
  2373. //
  2374. // If we successfully synchronized, then do a piece of the transfer.
  2375. //
  2376. if (NT_SUCCESS(Status)) {
  2377. if (!FlagOn(IrpContext->MinorFunction, IRP_MN_MDL)) {
  2378. DebugTrace( 0, Dbg, ("Cached write.\n") );
  2379. //
  2380. // Do the write, possibly writing through
  2381. //
  2382. // Make sure we don't have any Mft records.
  2383. //
  2384. NtfsPurgeFileRecordCache( IrpContext );
  2385. if (!CcCopyWrite( FileObject,
  2386. (PLARGE_INTEGER)&LocalOffset,
  2387. LocalLength,
  2388. (BOOLEAN) FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT ),
  2389. SystemBuffer )) {
  2390. DebugTrace( 0, Dbg, ("Cached Write could not wait\n") );
  2391. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  2392. } else if (!NT_SUCCESS( IrpContext->ExceptionStatus )) {
  2393. NtfsRaiseStatus( IrpContext, IrpContext->ExceptionStatus, NULL, NULL );
  2394. }
  2395. Irp->IoStatus.Status = STATUS_SUCCESS;
  2396. SystemBuffer = Add2Ptr( SystemBuffer, LocalLength );
  2397. } else {
  2398. //
  2399. // DO AN MDL WRITE
  2400. //
  2401. DebugTrace( 0, Dbg, ("MDL write.\n") );
  2402. ASSERT( FlagOn(IrpContext->State, IRP_CONTEXT_STATE_WAIT) );
  2403. //
  2404. // If we got this far and then hit a log file full the Mdl will
  2405. // already be present.
  2406. //
  2407. ASSERT((Irp->MdlAddress == NULL) || (LocalOffset != StartingVbo));
  2408. #ifdef NTFS_RWCMP_TRACE
  2409. if (NtfsCompressionTrace && IsSyscache(Header)) {
  2410. DbgPrint("CcMdlWrite: FO = %08lx, Len = %08lx\n", (ULONG)LocalOffset, LocalLength );
  2411. }
  2412. #endif
  2413. CcPrepareMdlWrite( FileObject,
  2414. (PLARGE_INTEGER)&LocalOffset,
  2415. LocalLength,
  2416. &Irp->MdlAddress,
  2417. &Irp->IoStatus );
  2418. }
  2419. Status = Irp->IoStatus.Status;
  2420. LocalOffset += LocalLength;
  2421. LengthLeft -= LocalLength;
  2422. }
  2423. } while ((LengthLeft != 0) && NT_SUCCESS(Status));
  2424. if (NT_SUCCESS(Status)) {
  2425. Irp->IoStatus.Information = (ULONG)ByteCount;
  2426. }
  2427. try_return( Status );
  2428. }
  2429. #endif
  2430. //
  2431. // DO A NORMAL CACHED WRITE, if the MDL bit is not set,
  2432. //
  2433. if (!FlagOn(IrpContext->MinorFunction, IRP_MN_MDL)) {
  2434. DebugTrace( 0, Dbg, ("Cached write.\n") );
  2435. //
  2436. // Get hold of the user's buffer.
  2437. //
  2438. SystemBuffer = NtfsMapUserBuffer( Irp, NormalPagePriority );
  2439. //
  2440. // Do the write, possibly writing through
  2441. //
  2442. // Make sure we don't have any Mft records.
  2443. //
  2444. NtfsPurgeFileRecordCache( IrpContext );
  2445. if (!CcCopyWrite( FileObject,
  2446. (PLARGE_INTEGER)&StartingVbo,
  2447. (ULONG)ByteCount,
  2448. (BOOLEAN) FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT ),
  2449. SystemBuffer )) {
  2450. DebugTrace( 0, Dbg, ("Cached Write could not wait\n") );
  2451. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  2452. } else if (!NT_SUCCESS( IrpContext->ExceptionStatus )) {
  2453. NtfsRaiseStatus( IrpContext, IrpContext->ExceptionStatus, NULL, NULL );
  2454. }
  2455. Irp->IoStatus.Status = STATUS_SUCCESS;
  2456. Irp->IoStatus.Information = (ULONG)ByteCount;
  2457. try_return( Status = STATUS_SUCCESS );
  2458. } else {
  2459. //
  2460. // DO AN MDL WRITE
  2461. //
  2462. DebugTrace( 0, Dbg, ("MDL write.\n") );
  2463. ASSERT( FlagOn(IrpContext->State, IRP_CONTEXT_STATE_WAIT) );
  2464. //
  2465. // If we got this far and then hit a log file full the Mdl will
  2466. // already be present.
  2467. //
  2468. ASSERT(Irp->MdlAddress == NULL);
  2469. #ifdef NTFS_RWCMP_TRACE
  2470. if (NtfsCompressionTrace && IsSyscache(Header)) {
  2471. DbgPrint("CcMdlWrite: FO = %08lx, Len = %08lx\n", (ULONG)StartingVbo, (ULONG)ByteCount );
  2472. }
  2473. #endif
  2474. CcPrepareMdlWrite( FileObject,
  2475. (PLARGE_INTEGER)&StartingVbo,
  2476. (ULONG)ByteCount,
  2477. &Irp->MdlAddress,
  2478. &Irp->IoStatus );
  2479. Status = Irp->IoStatus.Status;
  2480. ASSERT( NT_SUCCESS( Status ));
  2481. try_return( Status );
  2482. }
  2483. //
  2484. // Handle the compressed calls.
  2485. //
  2486. } else {
  2487. #ifdef COMPRESS_ON_WIRE
  2488. ASSERT((StartingVbo & (NTFS_CHUNK_SIZE - 1)) == 0);
  2489. //
  2490. // Get out if COW is not supported.
  2491. //
  2492. if (!NtfsEnableCompressedIO) {
  2493. NtfsRaiseStatus( IrpContext, STATUS_UNSUPPORTED_COMPRESSION, NULL, NULL );
  2494. }
  2495. if ((Scb->Header.FileObjectC == NULL) ||
  2496. (Scb->Header.FileObjectC->PrivateCacheMap == NULL)) {
  2497. //
  2498. // Don't do compressed IO on a stream which is changing its
  2499. // compression state.
  2500. //
  2501. if (FlagOn( Scb->ScbState, SCB_STATE_REALLOCATE_ON_WRITE )) {
  2502. NtfsRaiseStatus( IrpContext, STATUS_UNSUPPORTED_COMPRESSION, NULL, NULL );
  2503. }
  2504. //
  2505. // Make sure we are serialized with the FileSizes, and
  2506. // will remove this condition if we abort.
  2507. //
  2508. if (!DoingIoAtEof) {
  2509. FsRtlLockFsRtlHeader( Header );
  2510. IrpContext->CleanupStructure = Scb;
  2511. }
  2512. NtfsCreateInternalCompressedStream( IrpContext, Scb, FALSE, NULL );
  2513. if (!DoingIoAtEof) {
  2514. FsRtlUnlockFsRtlHeader( Header );
  2515. IrpContext->CleanupStructure = NULL;
  2516. }
  2517. }
  2518. //
  2519. // Make sure the cache FileSizes are updated.
  2520. //
  2521. if (DoingIoAtEof) {
  2522. NtfsSetBothCacheSizes( FileObject,
  2523. (PCC_FILE_SIZES)&Header->AllocationSize,
  2524. Scb );
  2525. }
  2526. //
  2527. // Assume success.
  2528. //
  2529. Irp->IoStatus.Status = Status = STATUS_SUCCESS;
  2530. Irp->IoStatus.Information = (ULONG)(ByteRange - StartingVbo);
  2531. //
  2532. // Based on the Mdl minor function, set up the appropriate
  2533. // parameters for the call below. (NewMdl is not exactly the
  2534. // right type, so it is cast...)
  2535. //
  2536. if (!FlagOn(IrpContext->MinorFunction, IRP_MN_MDL)) {
  2537. //
  2538. // Get hold of the user's buffer.
  2539. //
  2540. SystemBuffer = NtfsMapUserBuffer( Irp, NormalPagePriority );
  2541. NewMdl = NULL;
  2542. } else {
  2543. //
  2544. // We will deliver the Mdl directly to the Irp.
  2545. //
  2546. SystemBuffer = NULL;
  2547. NewMdl = (PMDL)&Irp->MdlAddress;
  2548. }
  2549. CompressedDataInfo = (PCOMPRESSED_DATA_INFO)IrpContext->Union.AuxiliaryBuffer->Buffer;
  2550. //
  2551. // Calculate the compression unit and chunk sizes.
  2552. //
  2553. CompressionUnitSize = Scb->CompressionUnit;
  2554. ChunkSize = 1 << CompressedDataInfo->ChunkShift;
  2555. //
  2556. // See if the engine matches, so we can pass that on to the
  2557. // compressed write routine.
  2558. //
  2559. EngineMatches =
  2560. ((CompressedDataInfo->CompressionFormatAndEngine == ((Scb->AttributeFlags & ATTRIBUTE_FLAG_COMPRESSION_MASK) + 1)) &&
  2561. (CompressedDataInfo->ChunkShift == NTFS_CHUNK_SHIFT));
  2562. //
  2563. // Do the compressed write in common code with the Fast Io path.
  2564. // We do it from a loop because we may need to create the other
  2565. // data stream.
  2566. //
  2567. while (TRUE) {
  2568. Status = NtfsCompressedCopyWrite( FileObject,
  2569. (PLARGE_INTEGER)&StartingVbo,
  2570. (ULONG)ByteCount,
  2571. SystemBuffer,
  2572. (PMDL *)NewMdl,
  2573. CompressedDataInfo,
  2574. IoGetRelatedDeviceObject(FileObject),
  2575. Header,
  2576. Scb->CompressionUnit,
  2577. NTFS_CHUNK_SIZE,
  2578. EngineMatches );
  2579. //
  2580. // On successful Mdl requests we hang on to the PagingIo resource.
  2581. //
  2582. if ((NewMdl != NULL) && NT_SUCCESS(Status) && (*((PMDL *) NewMdl) != NULL)) {
  2583. PagingIoAcquired = FALSE;
  2584. }
  2585. //
  2586. // Check for the status that says we need to create the normal
  2587. // data stream, else we are done.
  2588. //
  2589. if (Status != STATUS_NOT_MAPPED_DATA) {
  2590. break;
  2591. }
  2592. //
  2593. // Create the normal data stream and loop back to try again.
  2594. //
  2595. ASSERT(Scb->FileObject == NULL);
  2596. //
  2597. // Make sure we are serialized with the FileSizes, and
  2598. // will remove this condition if we abort.
  2599. //
  2600. if (!DoingIoAtEof) {
  2601. FsRtlLockFsRtlHeader( Header );
  2602. IrpContext->CleanupStructure = Scb;
  2603. }
  2604. NtfsCreateInternalAttributeStream( IrpContext, Scb, FALSE, NULL );
  2605. if (!DoingIoAtEof) {
  2606. FsRtlUnlockFsRtlHeader( Header );
  2607. IrpContext->CleanupStructure = NULL;
  2608. }
  2609. }
  2610. #endif
  2611. }
  2612. try_exit: NOTHING;
  2613. if (Irp) {
  2614. if (PostIrp) {
  2615. //
  2616. // If we acquired this Scb exclusive, we won't need to release
  2617. // the Scb. That is done in the oplock post request.
  2618. //
  2619. if (OplockPostIrp) {
  2620. ScbAcquired = FALSE;
  2621. }
  2622. //
  2623. // If we didn't post the Irp, we may have written some bytes to the
  2624. // file. We report the number of bytes written and update the
  2625. // file object for synchronous writes.
  2626. //
  2627. } else {
  2628. DebugTrace( 0, Dbg, ("Completing request with status = %08lx\n", Status) );
  2629. DebugTrace( 0, Dbg, (" Information = %08lx\n",
  2630. Irp->IoStatus.Information));
  2631. //
  2632. // Record the total number of bytes actually written
  2633. //
  2634. LlTemp1 = Irp->IoStatus.Information;
  2635. //
  2636. // If the file was opened for Synchronous IO, update the current
  2637. // file position.
  2638. //
  2639. if (SynchronousIo && !PagingIo) {
  2640. UserFileObject->CurrentByteOffset.QuadPart = StartingVbo + LlTemp1;
  2641. }
  2642. //
  2643. // The following are things we only do if we were successful
  2644. //
  2645. if (NT_SUCCESS( Status )) {
  2646. //
  2647. // Mark that the modify time needs to be updated on close.
  2648. // Note that only the top level User requests will generate
  2649. // correct
  2650. if (!PagingIo) {
  2651. //
  2652. // Set the flag in the file object to know we modified this file.
  2653. //
  2654. SetFlag( UserFileObject->Flags, FO_FILE_MODIFIED );
  2655. //
  2656. // On successful paging I/O to a compressed or sparse data stream
  2657. // which is not mapped, try to free any reserved space for the stream.
  2658. // Note: mapped compressed streams will generally not free reserved
  2659. // space
  2660. //
  2661. } else if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE )) {
  2662. NtfsFreeReservedClusters( Scb,
  2663. StartingVbo,
  2664. (ULONG) Irp->IoStatus.Information );
  2665. }
  2666. //
  2667. // If we extended the file size and we are meant to
  2668. // immediately update the dirent, do so. (This flag is
  2669. // set for either WriteThrough or noncached, because
  2670. // in either case the data and any necessary zeros are
  2671. // actually written to the file.) Note that a flush of
  2672. // a user-mapped file could cause VDL to get updated the
  2673. // first time because we never had a cached write, so we
  2674. // have to be sure to update VDL here in that case as well.
  2675. //
  2676. if (DoingIoAtEof) {
  2677. CC_FILE_SIZES CcFileSizes;
  2678. //
  2679. // If we know this has gone to disk we update the Mft.
  2680. // This variable should never be set for a resident
  2681. // attribute.
  2682. // The lazy writer uses callbacks to have the filesizes updated on disk
  2683. // so we don't do any of this here
  2684. //
  2685. if (!FlagOn( IrpContext->State, IRP_CONTEXT_STATE_LAZY_WRITE )) {
  2686. if (UpdateMft) {
  2687. //
  2688. // Get the Scb if we don't already have it.
  2689. //
  2690. if (!ScbAcquired) {
  2691. //
  2692. // Make sure we don't have any Mft records.
  2693. //
  2694. NtfsPurgeFileRecordCache( IrpContext );
  2695. NtfsAcquireExclusiveScb( IrpContext, Scb );
  2696. ScbAcquired = TRUE;
  2697. if (FlagOn( Scb->ScbState, SCB_STATE_RESTORE_UNDERWAY )) {
  2698. goto RestoreUnderway;
  2699. }
  2700. NtfsMungeScbSnapshot( IrpContext, Scb, OldFileSize );
  2701. } else if (FlagOn( Scb->ScbState, SCB_STATE_RESTORE_UNDERWAY )) {
  2702. goto RestoreUnderway;
  2703. }
  2704. //
  2705. // Start by capturing any file size changes.
  2706. //
  2707. NtfsUpdateScbFromFileObject( IrpContext, UserFileObject, Scb, FALSE );
  2708. //
  2709. // Write a log entry to update these sizes.
  2710. //
  2711. NtfsWriteFileSizes( IrpContext,
  2712. Scb,
  2713. &ByteRange,
  2714. TRUE,
  2715. TRUE,
  2716. TRUE );
  2717. //
  2718. // Clear the check attribute size flag.
  2719. //
  2720. NtfsAcquireFsrtlHeader( Scb );
  2721. ClearFlag( Scb->ScbState, SCB_STATE_CHECK_ATTRIBUTE_SIZE );
  2722. //
  2723. // Otherwise we set the flag indicating that we need to
  2724. // update the attribute size.
  2725. //
  2726. } else {
  2727. RestoreUnderway:
  2728. NtfsAcquireFsrtlHeader( Scb );
  2729. SetFlag( Scb->ScbState, SCB_STATE_CHECK_ATTRIBUTE_SIZE );
  2730. }
  2731. } else {
  2732. NtfsAcquireFsrtlHeader( Scb );
  2733. }
  2734. ASSERT( !FlagOn( IrpContext->State, IRP_CONTEXT_STATE_LAZY_WRITE ) ||
  2735. ByteRange <= ((Header->ValidDataLength.QuadPart + PAGE_SIZE - 1) & ~((LONGLONG) (PAGE_SIZE - 1))) );
  2736. //
  2737. // Now is the time to update valid data length.
  2738. // The Eof condition will be freed when we commit.
  2739. //
  2740. if (ByteRange > Header->ValidDataLength.QuadPart) {
  2741. Header->ValidDataLength.QuadPart = ByteRange;
  2742. #ifdef SYSCACHE_DEBUG
  2743. if (ScbIsBeingLogged( Scb )) {
  2744. CalculateSyscacheFlags( IrpContext, Flags, SCE_FLAG_WRITE );
  2745. FsRtlLogSyscacheEvent( Scb, SCE_VDL_CHANGE, Flags, StartingVbo, ByteCount, ByteRange );
  2746. }
  2747. #endif
  2748. }
  2749. CcFileSizes = *(PCC_FILE_SIZES)&Header->AllocationSize;
  2750. DoingIoAtEof = FALSE;
  2751. //
  2752. // Inform Cc that we changed the VDL for non cached toplevel
  2753. //
  2754. if (CcIsFileCached( FileObject ) && NonCachedIo) {
  2755. NtfsSetBothCacheSizes( FileObject, &CcFileSizes, Scb );
  2756. } else {
  2757. //
  2758. // If there is a compressed section, then update both file sizes to get
  2759. // the ValidDataLength update in the one we did not write.
  2760. //
  2761. #ifdef COMPRESS_ON_WIRE
  2762. if (Header->FileObjectC != NULL) {
  2763. if (FlagOn(IrpContext->MinorFunction, IRP_MN_COMPRESSED)) {
  2764. if (Scb->NonpagedScb->SegmentObject.SharedCacheMap != NULL) {
  2765. CcSetFileSizes( FileObject, &CcFileSizes );
  2766. }
  2767. } else {
  2768. CcSetFileSizes( Header->FileObjectC, &CcFileSizes );
  2769. }
  2770. }
  2771. #endif
  2772. }
  2773. NtfsReleaseFsrtlHeader( Scb );
  2774. }
  2775. }
  2776. //
  2777. // Abort transaction on error by raising. If this is the log file itself
  2778. // then just return normally.
  2779. //
  2780. NtfsPurgeFileRecordCache( IrpContext );
  2781. if (Scb != Scb->Vcb->LogFileScb) {
  2782. NtfsCleanupTransaction( IrpContext, Status, FALSE );
  2783. }
  2784. }
  2785. }
  2786. } finally {
  2787. DebugUnwind( NtfsCommonWrite );
  2788. //
  2789. // Clean up any Bcb from read/synchronize compressed.
  2790. //
  2791. #ifdef COMPRESS_ON_WIRE
  2792. if (CompressionSync != NULL) {
  2793. NtfsReleaseCompressionSync( CompressionSync );
  2794. }
  2795. #endif
  2796. if (CleanupAttributeContext) {
  2797. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  2798. }
  2799. if (SafeBuffer) {
  2800. NtfsFreePool( SafeBuffer );
  2801. }
  2802. //
  2803. // Now is the time to restore FileSize on errors.
  2804. // The Eof condition will be freed when we commit.
  2805. //
  2806. if (DoingIoAtEof && !PagingIo) {
  2807. //
  2808. // Acquire the main resource to knock valid data to disk back.
  2809. //
  2810. if (RestoreValidDataToDisk) {
  2811. //
  2812. // Make sure we purge the file record cache as well. Otherwise
  2813. // a purge of the Mft may fail in a different thread which owns a resource
  2814. // this thread needs.
  2815. //
  2816. NtfsPurgeFileRecordCache( IrpContext );
  2817. NtfsAcquireExclusiveScb( IrpContext, Scb );
  2818. if (Scb->ValidDataToDisk > OldFileSize) {
  2819. Scb->ValidDataToDisk = OldFileSize;
  2820. }
  2821. NtfsReleaseScb( IrpContext, Scb );
  2822. }
  2823. NtfsAcquireFsrtlHeader( Scb );
  2824. //
  2825. // Always force a recalc for write at eof unless we've commited the filesize
  2826. // forward. In that case we should write at the calculated offset unless the
  2827. // file shrinks in between. See test at beginning of common write
  2828. //
  2829. if (FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WRITING_AT_EOF ) &&
  2830. OldFileSize == IrpSp->Parameters.Write.ByteOffset.QuadPart) {
  2831. ClearFlag( IrpContext->State, IRP_CONTEXT_STATE_WRITING_AT_EOF );
  2832. IrpSp->Parameters.Write.ByteOffset.LowPart = FILE_WRITE_TO_END_OF_FILE;
  2833. IrpSp->Parameters.Write.ByteOffset.HighPart = -1;
  2834. }
  2835. Header->FileSize.QuadPart = OldFileSize;
  2836. ASSERT( Header->ValidDataLength.QuadPart <= Header->FileSize.QuadPart );
  2837. if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) {
  2838. CcGetFileSizePointer(FileObject)->QuadPart = OldFileSize;
  2839. }
  2840. #ifdef COMPRESS_ON_WIRE
  2841. if (Header->FileObjectC != NULL) {
  2842. CcGetFileSizePointer(Header->FileObjectC)->QuadPart = OldFileSize;
  2843. }
  2844. #endif
  2845. NtfsReleaseFsrtlHeader( Scb );
  2846. }
  2847. //
  2848. // If the Scb or PagingIo resource has been acquired, release it.
  2849. //
  2850. if (PagingIoAcquired) {
  2851. ExReleaseResourceLite( Header->PagingIoResource );
  2852. }
  2853. if (Irp) {
  2854. if (ScbAcquired) {
  2855. NtfsReleaseScb( IrpContext, Scb );
  2856. }
  2857. //
  2858. // Now remember to clear the WriteSeen flag if we set it. We only
  2859. // do this if there is still an Irp. It is possible for the current
  2860. // Irp to be posted or asynchronous. In that case this is a top
  2861. // level request and the cleanup happens elsewhere. For synchronous
  2862. // recursive cases the Irp will still be here.
  2863. //
  2864. if (SetWriteSeen) {
  2865. ClearFlag(IrpContext->TopLevelIrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_SEEN);
  2866. }
  2867. }
  2868. DebugTrace( -1, Dbg, ("NtfsCommonWrite -> %08lx\n", Status) );
  2869. }
  2870. //
  2871. // Complete the request if we didn't post it and no exception
  2872. //
  2873. // Note that NtfsCompleteRequest does the right thing if either
  2874. // IrpContext or Irp are NULL
  2875. //
  2876. if (!PostIrp) {
  2877. NtfsCompleteRequest( IrpContext, Irp, Status );
  2878. } else if (!OplockPostIrp) {
  2879. Status = NtfsPostRequest( IrpContext, Irp );
  2880. }
  2881. return Status;
  2882. }
  2883. //
  2884. // Local support routine
  2885. //
  2886. NTSTATUS NtfsGetIoAtEof (
  2887. IN PIRP_CONTEXT IrpContext,
  2888. IN PSCB Scb,
  2889. IN LONGLONG StartingVbo,
  2890. IN LONGLONG ByteCount,
  2891. IN BOOLEAN Wait,
  2892. OUT PBOOLEAN DoingIoAtEof,
  2893. OUT PLONGLONG OldFileSize
  2894. )
  2895. {
  2896. //
  2897. // Our caller may already be synchronized with EOF.
  2898. // The FcbWithPaging field in the top level IrpContext
  2899. // will have either the current Fcb/Scb if so.
  2900. //
  2901. if ((IrpContext->TopLevelIrpContext->CleanupStructure == Scb->Fcb) ||
  2902. (IrpContext->TopLevelIrpContext->CleanupStructure == Scb)) {
  2903. *DoingIoAtEof = TRUE;
  2904. *OldFileSize = Scb->Header.FileSize.QuadPart;
  2905. } else {
  2906. if (FlagOn( Scb->Header.Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE ) && !Wait) {
  2907. return STATUS_FILE_LOCK_CONFLICT;
  2908. }
  2909. *DoingIoAtEof = !FlagOn( Scb->Header.Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE ) ||
  2910. NtfsWaitForIoAtEof( &(Scb->Header), (PLARGE_INTEGER)&StartingVbo, (ULONG)ByteCount );
  2911. //
  2912. // Set the Flag if we are changing FileSize or ValidDataLength,
  2913. // and save current values.
  2914. //
  2915. if (*DoingIoAtEof) {
  2916. SetFlag( Scb->Header.Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE );
  2917. #if (DBG || defined( NTFS_FREE_ASSERTS ))
  2918. Scb->IoAtEofThread = (PERESOURCE_THREAD) ExGetCurrentResourceThread();
  2919. #endif
  2920. //
  2921. // Store this in the IrpContext until commit or post
  2922. //
  2923. IrpContext->CleanupStructure = Scb;
  2924. *OldFileSize = Scb->Header.FileSize.QuadPart;
  2925. #if (DBG || defined( NTFS_FREE_ASSERTS ))
  2926. } else {
  2927. ASSERT( Scb->IoAtEofThread != (PERESOURCE_THREAD) ExGetCurrentResourceThread() );
  2928. #endif
  2929. }
  2930. }
  2931. return STATUS_SUCCESS;
  2932. }