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

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