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.

2831 lines
92 KiB

  1. /*++
  2. Copyright (c) 1989-2000 Microsoft Corporation
  3. Module Name:
  4. Write.c
  5. Abstract:
  6. This module implements the File Write routine for Write called by the
  7. dispatch driver.
  8. // @@BEGIN_DDKSPLIT
  9. Author:
  10. DavidGoebel [DavidGoe] 11-Apr-1990
  11. Revision History:
  12. // @@END_DDKSPLIT
  13. --*/
  14. #include "FatProcs.h"
  15. //
  16. // The Bug check file id for this module
  17. //
  18. #define BugCheckFileId (FAT_BUG_CHECK_WRITE)
  19. //
  20. // The local debug trace level
  21. //
  22. #define Dbg (DEBUG_TRACE_WRITE)
  23. //
  24. // Macros to increment the appropriate performance counters.
  25. //
  26. #define CollectWriteStats(VCB,OPEN_TYPE,BYTE_COUNT) { \
  27. PFILESYSTEM_STATISTICS Stats = &(VCB)->Statistics[KeGetCurrentProcessorNumber()].Common; \
  28. if (((OPEN_TYPE) == UserFileOpen)) { \
  29. Stats->UserFileWrites += 1; \
  30. Stats->UserFileWriteBytes += (ULONG)(BYTE_COUNT); \
  31. } else if (((OPEN_TYPE) == VirtualVolumeFile || ((OPEN_TYPE) == DirectoryFile))) { \
  32. Stats->MetaDataWrites += 1; \
  33. Stats->MetaDataWriteBytes += (ULONG)(BYTE_COUNT); \
  34. } \
  35. }
  36. BOOLEAN FatNoAsync = FALSE;
  37. //
  38. // Local support routines
  39. //
  40. VOID
  41. FatDeferredFlushDpc (
  42. IN PKDPC Dpc,
  43. IN PVOID DeferredContext,
  44. IN PVOID SystemArgument1,
  45. IN PVOID SystemArgument2
  46. );
  47. VOID
  48. FatDeferredFlush (
  49. PVOID Parameter
  50. );
  51. #ifdef ALLOC_PRAGMA
  52. #pragma alloc_text(PAGE, FatDeferredFlush)
  53. #pragma alloc_text(PAGE, FatCommonWrite)
  54. #endif
  55. NTSTATUS
  56. FatFsdWrite (
  57. IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
  58. IN PIRP Irp
  59. )
  60. /*++
  61. Routine Description:
  62. This routine implements the FSD part of the NtWriteFile API call
  63. Arguments:
  64. VolumeDeviceObject - Supplies the volume device object where the
  65. file being Write exists
  66. Irp - Supplies the Irp being processed
  67. Return Value:
  68. NTSTATUS - The FSD status for the IRP
  69. --*/
  70. {
  71. PFCB Fcb;
  72. NTSTATUS Status;
  73. PIRP_CONTEXT IrpContext = NULL;
  74. BOOLEAN ModWriter = FALSE;
  75. BOOLEAN TopLevel;
  76. DebugTrace(+1, Dbg, "FatFsdWrite\n", 0);
  77. //
  78. // Call the common Write routine, with blocking allowed if synchronous
  79. //
  80. FsRtlEnterFileSystem();
  81. //
  82. // We are first going to do a quick check for paging file IO. Since this
  83. // is a fast path, we must replicate the check for the fsdo.
  84. //
  85. if (!FatDeviceIsFatFsdo( IoGetCurrentIrpStackLocation(Irp)->DeviceObject)) {
  86. Fcb = (PFCB)(IoGetCurrentIrpStackLocation(Irp)->FileObject->FsContext);
  87. if ((NodeType(Fcb) == FAT_NTC_FCB) &&
  88. FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE)) {
  89. //
  90. // Do the usual STATUS_PENDING things.
  91. //
  92. IoMarkIrpPending( Irp );
  93. //
  94. // Perform the actual IO, it will be completed when the io finishes.
  95. //
  96. FatPagingFileIo( Irp, Fcb );
  97. FsRtlExitFileSystem();
  98. return STATUS_PENDING;
  99. }
  100. }
  101. try {
  102. TopLevel = FatIsIrpTopLevel( Irp );
  103. IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );
  104. //
  105. // This is a kludge for the mod writer case. The correct state
  106. // of recursion is set in IrpContext, however, we much with the
  107. // actual top level Irp field to get the correct WriteThrough
  108. // behaviour.
  109. //
  110. if (IoGetTopLevelIrp() == (PIRP)FSRTL_MOD_WRITE_TOP_LEVEL_IRP) {
  111. ModWriter = TRUE;
  112. IoSetTopLevelIrp( Irp );
  113. }
  114. //
  115. // If this is an Mdl complete request, don't go through
  116. // common write.
  117. //
  118. if (FlagOn( IrpContext->MinorFunction, IRP_MN_COMPLETE )) {
  119. DebugTrace(0, Dbg, "Calling FatCompleteMdl\n", 0 );
  120. Status = FatCompleteMdl( IrpContext, Irp );
  121. } else {
  122. Status = FatCommonWrite( IrpContext, Irp );
  123. }
  124. } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {
  125. //
  126. // We had some trouble trying to perform the requested
  127. // operation, so we'll abort the I/O request with
  128. // the error status that we get back from the
  129. // execption code
  130. //
  131. Status = FatProcessException( IrpContext, Irp, GetExceptionCode() );
  132. }
  133. // ASSERT( !(ModWriter && (Status == STATUS_CANT_WAIT)) );
  134. ASSERT( !(ModWriter && TopLevel) );
  135. if (ModWriter) { IoSetTopLevelIrp((PIRP)FSRTL_MOD_WRITE_TOP_LEVEL_IRP); }
  136. if (TopLevel) { IoSetTopLevelIrp( NULL ); }
  137. FsRtlExitFileSystem();
  138. //
  139. // And return to our caller
  140. //
  141. DebugTrace(-1, Dbg, "FatFsdWrite -> %08lx\n", Status);
  142. UNREFERENCED_PARAMETER( VolumeDeviceObject );
  143. return Status;
  144. }
  145. NTSTATUS
  146. FatCommonWrite (
  147. IN PIRP_CONTEXT IrpContext,
  148. IN PIRP Irp
  149. )
  150. /*++
  151. Routine Description:
  152. This is the common write routine for NtWriteFile, called from both
  153. the Fsd, or from the Fsp if a request could not be completed without
  154. blocking in the Fsd. This routine's actions are
  155. conditionalized by the Wait input parameter, which determines whether
  156. it is allowed to block or not. If a blocking condition is encountered
  157. with Wait == FALSE, however, the request is posted to the Fsp, who
  158. always calls with WAIT == TRUE.
  159. Arguments:
  160. Irp - Supplies the Irp to process
  161. Return Value:
  162. NTSTATUS - The return status for the operation
  163. --*/
  164. {
  165. PVCB Vcb;
  166. PFCB FcbOrDcb;
  167. PCCB Ccb;
  168. VBO StartingVbo;
  169. ULONG ByteCount;
  170. ULONG FileSize;
  171. ULONG InitialFileSize;
  172. ULONG InitialValidDataLength;
  173. PIO_STACK_LOCATION IrpSp;
  174. PFILE_OBJECT FileObject;
  175. TYPE_OF_OPEN TypeOfOpen;
  176. BOOLEAN PostIrp = FALSE;
  177. BOOLEAN OplockPostIrp = FALSE;
  178. BOOLEAN ExtendingFile = FALSE;
  179. BOOLEAN FcbOrDcbAcquired = FALSE;
  180. BOOLEAN SwitchBackToAsync = FALSE;
  181. BOOLEAN CalledByLazyWriter = FALSE;
  182. BOOLEAN ExtendingValidData = FALSE;
  183. BOOLEAN FcbAcquiredExclusive = FALSE;
  184. BOOLEAN FcbCanDemoteToShared = FALSE;
  185. BOOLEAN WriteFileSizeToDirent = FALSE;
  186. BOOLEAN RecursiveWriteThrough = FALSE;
  187. BOOLEAN UnwindOutstandingAsync = FALSE;
  188. BOOLEAN PagingIoResourceAcquired = FALSE;
  189. BOOLEAN SynchronousIo;
  190. BOOLEAN WriteToEof;
  191. BOOLEAN PagingIo;
  192. BOOLEAN NonCachedIo;
  193. BOOLEAN Wait;
  194. NTSTATUS Status;
  195. FAT_IO_CONTEXT StackFatIoContext;
  196. //
  197. // A system buffer is only used if we have to access the buffer directly
  198. // from the Fsp to clear a portion or to do a synchronous I/O, or a
  199. // cached transfer. It is possible that our caller may have already
  200. // mapped a system buffer, in which case we must remember this so
  201. // we do not unmap it on the way out.
  202. //
  203. PVOID SystemBuffer = (PVOID) NULL;
  204. LARGE_INTEGER StartingByte;
  205. //
  206. // Get current Irp stack location and file object
  207. //
  208. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  209. FileObject = IrpSp->FileObject;
  210. DebugTrace(+1, Dbg, "FatCommonWrite\n", 0);
  211. DebugTrace( 0, Dbg, "Irp = %8lx\n", Irp);
  212. DebugTrace( 0, Dbg, "ByteCount = %8lx\n", IrpSp->Parameters.Write.Length);
  213. DebugTrace( 0, Dbg, "ByteOffset.LowPart = %8lx\n", IrpSp->Parameters.Write.ByteOffset.LowPart);
  214. DebugTrace( 0, Dbg, "ByteOffset.HighPart = %8lx\n", IrpSp->Parameters.Write.ByteOffset.HighPart);
  215. //
  216. // Initialize the appropriate local variables.
  217. //
  218. Wait = BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
  219. PagingIo = BooleanFlagOn(Irp->Flags, IRP_PAGING_IO);
  220. NonCachedIo = BooleanFlagOn(Irp->Flags,IRP_NOCACHE);
  221. SynchronousIo = BooleanFlagOn(FileObject->Flags, FO_SYNCHRONOUS_IO);
  222. //ASSERT( PagingIo || FileObject->WriteAccess );
  223. //
  224. // Extract the bytecount and do our noop/throttle checking.
  225. //
  226. ByteCount = IrpSp->Parameters.Write.Length;
  227. //
  228. // If there is nothing to write, return immediately.
  229. //
  230. if (ByteCount == 0) {
  231. Irp->IoStatus.Information = 0;
  232. FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
  233. return STATUS_SUCCESS;
  234. }
  235. //
  236. // See if we have to defer the write.
  237. //
  238. if (!NonCachedIo &&
  239. !CcCanIWrite(FileObject,
  240. ByteCount,
  241. (BOOLEAN)(Wait && !BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_IN_FSP)),
  242. BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_DEFERRED_WRITE))) {
  243. BOOLEAN Retrying = BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_DEFERRED_WRITE);
  244. FatPrePostIrp( IrpContext, Irp );
  245. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DEFERRED_WRITE );
  246. CcDeferWrite( FileObject,
  247. (PCC_POST_DEFERRED_WRITE)FatAddToWorkque,
  248. IrpContext,
  249. Irp,
  250. ByteCount,
  251. Retrying );
  252. return STATUS_PENDING;
  253. }
  254. //
  255. // Determine our starting position and type. If we are writing
  256. // at EOF, then we will need additional synchronization before
  257. // the IO is issued to determine where the data will go.
  258. //
  259. StartingByte = IrpSp->Parameters.Write.ByteOffset;
  260. StartingVbo = StartingByte.LowPart;
  261. WriteToEof = ( (StartingByte.LowPart == FILE_WRITE_TO_END_OF_FILE) &&
  262. (StartingByte.HighPart == -1) );
  263. //
  264. // Extract the nature of the write from the file object, and case on it
  265. //
  266. TypeOfOpen = FatDecodeFileObject(FileObject, &Vcb, &FcbOrDcb, &Ccb);
  267. ASSERT( Vcb != NULL );
  268. //
  269. // Save callers who try to do cached IO to the raw volume from themselves.
  270. //
  271. if (TypeOfOpen == UserVolumeOpen) {
  272. NonCachedIo = TRUE;
  273. }
  274. ASSERT(!(NonCachedIo == FALSE && TypeOfOpen == VirtualVolumeFile));
  275. //
  276. // Collect interesting statistics. The FLAG_USER_IO bit will indicate
  277. // what type of io we're doing in the FatNonCachedIo function.
  278. //
  279. if (PagingIo) {
  280. CollectWriteStats(Vcb, TypeOfOpen, ByteCount);
  281. if (TypeOfOpen == UserFileOpen) {
  282. SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_USER_IO);
  283. } else {
  284. ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_USER_IO);
  285. }
  286. }
  287. //
  288. // We must disallow writes to regular objects that would require us
  289. // to maintain an AllocationSize of greater than 32 significant bits.
  290. //
  291. // If this is paging IO, this is simply a case where we need to trim.
  292. // This will occur in due course.
  293. //
  294. if (!PagingIo && !WriteToEof && (TypeOfOpen != UserVolumeOpen)) {
  295. if (!FatIsIoRangeValid( Vcb, StartingByte, ByteCount )) {
  296. Irp->IoStatus.Information = 0;
  297. FatCompleteRequest( IrpContext, Irp, STATUS_DISK_FULL );
  298. return STATUS_DISK_FULL;
  299. }
  300. }
  301. //
  302. // Allocate if necessary and initialize a FAT_IO_CONTEXT block for
  303. // all non cached Io. For synchronous Io
  304. // we use stack storage, otherwise we allocate pool.
  305. //
  306. if (NonCachedIo) {
  307. if (IrpContext->FatIoContext == NULL) {
  308. if (!Wait) {
  309. IrpContext->FatIoContext =
  310. FsRtlAllocatePoolWithTag( NonPagedPool,
  311. sizeof(FAT_IO_CONTEXT),
  312. TAG_FAT_IO_CONTEXT );
  313. } else {
  314. IrpContext->FatIoContext = &StackFatIoContext;
  315. SetFlag( IrpContext->Flags, IRP_CONTEXT_STACK_IO_CONTEXT );
  316. }
  317. }
  318. RtlZeroMemory( IrpContext->FatIoContext, sizeof(FAT_IO_CONTEXT) );
  319. if (Wait) {
  320. KeInitializeEvent( &IrpContext->FatIoContext->Wait.SyncEvent,
  321. NotificationEvent,
  322. FALSE );
  323. } else {
  324. IrpContext->FatIoContext->Wait.Async.ResourceThreadId =
  325. ExGetCurrentResourceThread();
  326. IrpContext->FatIoContext->Wait.Async.RequestedByteCount =
  327. ByteCount;
  328. IrpContext->FatIoContext->Wait.Async.FileObject = FileObject;
  329. }
  330. }
  331. //
  332. // Check if this volume has already been shut down. If it has, fail
  333. // this write request.
  334. //
  335. if ( FlagOn(Vcb->VcbState, VCB_STATE_FLAG_SHUTDOWN) ) {
  336. Irp->IoStatus.Information = 0;
  337. FatCompleteRequest( IrpContext, Irp, STATUS_TOO_LATE );
  338. return STATUS_TOO_LATE;
  339. }
  340. //
  341. // This case corresponds to a write of the volume file (only the first
  342. // fat allowed, the other fats are written automatically in parallel).
  343. //
  344. // We use an Mcb keep track of dirty sectors. Actual entries are Vbos
  345. // and Lbos (ie. bytes), though they are all added in sector chunks.
  346. // Since Vbo == Lbo for the volume file, the Mcb entries
  347. // alternate between runs of Vbo == Lbo, and holes (Lbo == 0). We use
  348. // the prior to represent runs of dirty fat sectors, and the latter
  349. // for runs of clean fat. Note that since the first part of the volume
  350. // file (boot sector) is always clean (a hole), and an Mcb never ends in
  351. // a hole, there must always be an even number of runs(entries) in the Mcb.
  352. //
  353. // The strategy is to find the first and last dirty run in the desired
  354. // write range (which will always be a set of pages), and write from the
  355. // former to the later. The may result in writing some clean data, but
  356. // will generally be more efficient than writing each runs seperately.
  357. //
  358. if (TypeOfOpen == VirtualVolumeFile) {
  359. LBO DirtyLbo;
  360. LBO CleanLbo;
  361. VBO DirtyVbo;
  362. VBO StartingDirtyVbo;
  363. ULONG DirtyByteCount;
  364. ULONG CleanByteCount;
  365. ULONG WriteLength;
  366. BOOLEAN MoreDirtyRuns = TRUE;
  367. IO_STATUS_BLOCK RaiseIosb;
  368. DebugTrace(0, Dbg, "Type of write is Virtual Volume File\n", 0);
  369. //
  370. // If we can't wait we have to post this.
  371. //
  372. if (!Wait) {
  373. DebugTrace( 0, Dbg, "Passing request to Fsp\n", 0 );
  374. Status = FatFsdPostRequest(IrpContext, Irp);
  375. return Status;
  376. }
  377. //
  378. // If we weren't called by the Lazy Writer, then this write
  379. // must be the result of a write-through or flush operation.
  380. // Setting the IrpContext flag, will cause DevIoSup.c to
  381. // write-through the data to the disk.
  382. //
  383. if (!FlagOn((ULONG_PTR)IoGetTopLevelIrp(), FSRTL_CACHE_TOP_LEVEL_IRP)) {
  384. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH );
  385. }
  386. //
  387. // Assert an even number of entries in the Mcb, an odd number would
  388. // mean that the Mcb is corrupt.
  389. //
  390. ASSERT( (FsRtlNumberOfRunsInLargeMcb( &Vcb->DirtyFatMcb ) & 1) == 0);
  391. //
  392. // We need to skip over any clean sectors at the start of the write.
  393. //
  394. // Also check the two cases where there are no dirty fats in the
  395. // desired write range, and complete them with success.
  396. //
  397. // 1) There is no Mcb entry corresponding to StartingVbo, meaning
  398. // we are beyond the end of the Mcb, and thus dirty fats.
  399. //
  400. // 2) The run at StartingVbo is clean and continues beyond the
  401. // desired write range.
  402. //
  403. if (!FatLookupMcbEntry( Vcb, &Vcb->DirtyFatMcb,
  404. StartingVbo,
  405. &DirtyLbo,
  406. &DirtyByteCount,
  407. NULL )
  408. || ( (DirtyLbo == 0) && (DirtyByteCount >= ByteCount) ) ) {
  409. DebugTrace(0, DEBUG_TRACE_DEBUG_HOOKS,
  410. "No dirty fat sectors in the write range.\n", 0);
  411. FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
  412. return STATUS_SUCCESS;
  413. }
  414. DirtyVbo = (VBO)DirtyLbo;
  415. //
  416. // If the last run was a hole (clean), up DirtyVbo to the next
  417. // run, which must be dirty.
  418. //
  419. if (DirtyVbo == 0) {
  420. DirtyVbo = StartingVbo + DirtyByteCount;
  421. }
  422. //
  423. // This is where the write will start.
  424. //
  425. StartingDirtyVbo = DirtyVbo;
  426. //
  427. //
  428. // Now start enumerating the dirty fat sectors spanning the desired
  429. // write range, this first one of which is now DirtyVbo.
  430. //
  431. while ( MoreDirtyRuns ) {
  432. //
  433. // Find the next dirty run, if it is not there, the Mcb ended
  434. // in a hole, or there is some other corruption of the Mcb.
  435. //
  436. if (!FatLookupMcbEntry( Vcb, &Vcb->DirtyFatMcb,
  437. DirtyVbo,
  438. &DirtyLbo,
  439. &DirtyByteCount,
  440. NULL )) {
  441. DirtyVbo = (VBO)DirtyLbo;
  442. DebugTrace(0, Dbg, "Last dirty fat Mcb entry was a hole: corrupt.\n", 0);
  443. FatBugCheck( 0, 0, 0 );
  444. } else {
  445. DirtyVbo = (VBO)DirtyLbo;
  446. //
  447. // This has to correspond to a dirty run, and must start
  448. // within the write range since we check it at entry to,
  449. // and at the bottom of this loop.
  450. //
  451. ASSERT((DirtyVbo != 0) && (DirtyVbo < StartingVbo + ByteCount));
  452. //
  453. // There are three ways we can know that this was the
  454. // last dirty run we want to write.
  455. //
  456. // 1) The current dirty run extends beyond or to the
  457. // desired write range.
  458. //
  459. // 2) On trying to find the following clean run, we
  460. // discover that this is the last run in the Mcb.
  461. //
  462. // 3) The following clean run extend beyond the
  463. // desired write range.
  464. //
  465. // In any of these cases we set MoreDirtyRuns = FALSE.
  466. //
  467. //
  468. // If the run is larger than we are writing, we also
  469. // must truncate the WriteLength. This is benign in
  470. // the equals case.
  471. //
  472. if (DirtyVbo + DirtyByteCount >= StartingVbo + ByteCount) {
  473. DirtyByteCount = StartingVbo + ByteCount - DirtyVbo;
  474. MoreDirtyRuns = FALSE;
  475. } else {
  476. //
  477. // Scan the clean hole after this dirty run. If this
  478. // run was the last, prepare to exit the loop
  479. //
  480. if (!FatLookupMcbEntry( Vcb, &Vcb->DirtyFatMcb,
  481. DirtyVbo + DirtyByteCount,
  482. &CleanLbo,
  483. &CleanByteCount,
  484. NULL )) {
  485. MoreDirtyRuns = FALSE;
  486. } else {
  487. //
  488. // Assert that we actually found a clean run.
  489. // and compute the start of the next dirty run.
  490. //
  491. ASSERT (CleanLbo == 0);
  492. //
  493. // If the next dirty run starts beyond the desired
  494. // write, we have found all the runs we need, so
  495. // prepare to exit.
  496. //
  497. if (DirtyVbo + DirtyByteCount + CleanByteCount >=
  498. StartingVbo + ByteCount) {
  499. MoreDirtyRuns = FALSE;
  500. } else {
  501. //
  502. // Compute the start of the next dirty run.
  503. //
  504. DirtyVbo += DirtyByteCount + CleanByteCount;
  505. }
  506. }
  507. }
  508. }
  509. } // while ( MoreDirtyRuns )
  510. //
  511. // At this point DirtyVbo and DirtyByteCount correctly reflect the
  512. // final dirty run, constrained to the desired write range.
  513. //
  514. // Now compute the length we finally must write.
  515. //
  516. WriteLength = (DirtyVbo + DirtyByteCount) - StartingDirtyVbo;
  517. //
  518. // We must now assume that the write will complete with success,
  519. // and initialize our expected status in RaiseIosb. It will be
  520. // modified below if an error occurs.
  521. //
  522. RaiseIosb.Status = STATUS_SUCCESS;
  523. RaiseIosb.Information = ByteCount;
  524. //
  525. // Loop through all the fats, setting up a multiple async to
  526. // write them all. If there are more than FAT_MAX_PARALLEL_IOS
  527. // then we do several muilple asyncs.
  528. //
  529. {
  530. ULONG Fat;
  531. ULONG BytesPerFat;
  532. IO_RUN StackIoRuns[2];
  533. PIO_RUN IoRuns;
  534. BytesPerFat = FatBytesPerFat( &Vcb->Bpb );
  535. if ((ULONG)Vcb->Bpb.Fats > 2) {
  536. IoRuns = FsRtlAllocatePoolWithTag( PagedPool,
  537. (ULONG)Vcb->Bpb.Fats,
  538. TAG_IO_RUNS );
  539. } else {
  540. IoRuns = StackIoRuns;
  541. }
  542. for (Fat = 0; Fat < (ULONG)Vcb->Bpb.Fats; Fat++) {
  543. IoRuns[Fat].Vbo = StartingDirtyVbo;
  544. IoRuns[Fat].Lbo = Fat * BytesPerFat + StartingDirtyVbo;
  545. IoRuns[Fat].Offset = StartingDirtyVbo - StartingVbo;
  546. IoRuns[Fat].ByteCount = WriteLength;
  547. }
  548. //
  549. // Keep track of meta-data disk ios.
  550. //
  551. Vcb->Statistics[KeGetCurrentProcessorNumber()].Common.MetaDataDiskWrites += Vcb->Bpb.Fats;
  552. try {
  553. FatMultipleAsync( IrpContext,
  554. Vcb,
  555. Irp,
  556. (ULONG)Vcb->Bpb.Fats,
  557. IoRuns );
  558. } finally {
  559. if (IoRuns != StackIoRuns) {
  560. ExFreePool( IoRuns );
  561. }
  562. }
  563. //
  564. // Wait for all the writes to finish
  565. //
  566. FatWaitSync( IrpContext );
  567. //
  568. // If we got an error, or verify required, remember it.
  569. //
  570. if (!NT_SUCCESS( Irp->IoStatus.Status )) {
  571. DebugTrace( 0,
  572. Dbg,
  573. "Error %X while writing volume file.\n",
  574. Irp->IoStatus.Status );
  575. RaiseIosb = Irp->IoStatus;
  576. }
  577. }
  578. //
  579. // If the writes were a success, set the sectors clean, else
  580. // raise the error status and mark the volume as needing
  581. // verification. This will automatically reset the volume
  582. // structures.
  583. //
  584. // If not, then mark this volume as needing verification to
  585. // automatically cause everything to get cleaned up.
  586. //
  587. Irp->IoStatus = RaiseIosb;
  588. if ( NT_SUCCESS( Status = Irp->IoStatus.Status )) {
  589. FatRemoveMcbEntry( Vcb, &Vcb->DirtyFatMcb,
  590. StartingDirtyVbo,
  591. WriteLength );
  592. } else {
  593. FatNormalizeAndRaiseStatus( IrpContext, Status );
  594. }
  595. DebugTrace(-1, Dbg, "CommonRead -> %08lx\n", Status );
  596. FatCompleteRequest( IrpContext, Irp, Status );
  597. return Status;
  598. }
  599. //
  600. // This case corresponds to a general opened volume (DASD), ie.
  601. // open ("a:").
  602. //
  603. if (TypeOfOpen == UserVolumeOpen) {
  604. LBO StartingLbo;
  605. LBO VolumeSize;
  606. //
  607. // Precalculate the volume size since we're nearly always going
  608. // to be wanting to use it.
  609. //
  610. VolumeSize = (LBO) Int32x32To64( Vcb->Bpb.BytesPerSector,
  611. (Vcb->Bpb.Sectors != 0 ? Vcb->Bpb.Sectors :
  612. Vcb->Bpb.LargeSectors));
  613. StartingLbo = StartingByte.QuadPart;
  614. DebugTrace(0, Dbg, "Type of write is User Volume.\n", 0);
  615. //
  616. // Verify that the volume for this handle is still valid, permitting
  617. // operations to proceed on dismounted volumes via the handle which
  618. // performed the dismount.
  619. //
  620. if (!FlagOn( Ccb->Flags, CCB_FLAG_COMPLETE_DISMOUNT )) {
  621. FatQuickVerifyVcb( IrpContext, Vcb );
  622. }
  623. if (!FlagOn( Ccb->Flags, CCB_FLAG_DASD_PURGE_DONE )) {
  624. BOOLEAN PreviousWait = BooleanFlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
  625. //
  626. // Grab the entire volume so that even the normally unsafe action
  627. // of writing to an unlocked volume won't open us to a race between
  628. // the flush and purge of the FAT below.
  629. //
  630. // I really don't think this is particularly important to worry about,
  631. // but a repro case for another bug happens to dance into this race
  632. // condition pretty easily. Eh.
  633. //
  634. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
  635. FatAcquireExclusiveVolume( IrpContext, Vcb );
  636. try {
  637. //
  638. // If the volume isn't locked, flush and purge it.
  639. //
  640. if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_LOCKED)) {
  641. FatFlushFat( IrpContext, Vcb );
  642. CcPurgeCacheSection( &Vcb->SectionObjectPointers,
  643. NULL,
  644. 0,
  645. FALSE );
  646. FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, Flush );
  647. }
  648. } finally {
  649. FatReleaseVolume( IrpContext, Vcb );
  650. if (!PreviousWait) {
  651. ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
  652. }
  653. }
  654. SetFlag( Ccb->Flags, CCB_FLAG_DASD_PURGE_DONE |
  655. CCB_FLAG_DASD_FLUSH_DONE );
  656. }
  657. if (!FlagOn( Ccb->Flags, CCB_FLAG_ALLOW_EXTENDED_DASD_IO )) {
  658. //
  659. // Make sure we don't try to write past end of volume,
  660. // reducing the requested byte count if necessary.
  661. //
  662. if (WriteToEof || StartingLbo >= VolumeSize) {
  663. FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
  664. return STATUS_SUCCESS;
  665. }
  666. if (ByteCount > VolumeSize - StartingLbo) {
  667. ByteCount = (ULONG) (VolumeSize - StartingLbo);
  668. //
  669. // For async writes we had set the byte count in the FatIoContext
  670. // above, so fix that here.
  671. //
  672. if (!Wait) {
  673. IrpContext->FatIoContext->Wait.Async.RequestedByteCount =
  674. ByteCount;
  675. }
  676. }
  677. } else {
  678. //
  679. // This has a peculiar interpretation, but just adjust the starting
  680. // byte to the end of the visible volume.
  681. //
  682. if (WriteToEof) {
  683. StartingLbo = VolumeSize;
  684. }
  685. }
  686. //
  687. // For DASD we have to probe and lock the user's buffer
  688. //
  689. FatLockUserBuffer( IrpContext, Irp, IoReadAccess, ByteCount );
  690. //
  691. // Set the FO_MODIFIED flag here to trigger a verify when this
  692. // handle is closed. Note that we can err on the conservative
  693. // side with no problem, i.e. if we accidently do an extra
  694. // verify there is no problem.
  695. //
  696. SetFlag( FileObject->Flags, FO_FILE_MODIFIED );
  697. //
  698. // Write the data and wait for the results
  699. //
  700. FatSingleAsync( IrpContext,
  701. Vcb,
  702. StartingLbo,
  703. ByteCount,
  704. Irp );
  705. if (!Wait) {
  706. //
  707. // We, nor anybody else, need the IrpContext any more.
  708. //
  709. IrpContext->FatIoContext = NULL;
  710. FatDeleteIrpContext( IrpContext );
  711. DebugTrace(-1, Dbg, "FatNonCachedIo -> STATUS_PENDING\n", 0);
  712. return STATUS_PENDING;
  713. }
  714. FatWaitSync( IrpContext );
  715. //
  716. // If the call didn't succeed, raise the error status
  717. //
  718. // Also mark this volume as needing verification to automatically
  719. // cause everything to get cleaned up.
  720. //
  721. if (!NT_SUCCESS( Status = Irp->IoStatus.Status )) {
  722. FatNormalizeAndRaiseStatus( IrpContext, Status );
  723. }
  724. //
  725. // Update the current file position. We assume that
  726. // open/create zeros out the CurrentByteOffset field.
  727. //
  728. if (SynchronousIo && !PagingIo) {
  729. FileObject->CurrentByteOffset.QuadPart =
  730. StartingLbo + Irp->IoStatus.Information;
  731. }
  732. DebugTrace(-1, Dbg, "FatCommonWrite -> %08lx\n", Status );
  733. FatCompleteRequest( IrpContext, Irp, Status );
  734. return Status;
  735. }
  736. //
  737. // At this point we know there is an Fcb/Dcb.
  738. //
  739. ASSERT( FcbOrDcb != NULL );
  740. //
  741. // Use a try-finally to free Fcb/Dcb and buffers on the way out.
  742. //
  743. try {
  744. //
  745. // This case corresponds to a normal user write file.
  746. //
  747. if ( TypeOfOpen == UserFileOpen ) {
  748. ULONG ValidDataLength;
  749. ULONG ValidDataToDisk;
  750. ULONG ValidDataToCheck;
  751. DebugTrace(0, Dbg, "Type of write is user file open\n", 0);
  752. //
  753. // If this is a noncached transfer and is not a paging I/O, and
  754. // the file has been opened cached, then we will do a flush here
  755. // to avoid stale data problems. Note that we must flush before
  756. // acquiring the Fcb shared since the write may try to acquire
  757. // it exclusive.
  758. //
  759. // The Purge following the flush will garentee cache coherency.
  760. //
  761. if (NonCachedIo && !PagingIo &&
  762. (FileObject->SectionObjectPointer->DataSectionObject != NULL)) {
  763. //
  764. // We need the Fcb exclsuive to do the CcPurgeCache
  765. //
  766. if (!FatAcquireExclusiveFcb( IrpContext, FcbOrDcb )) {
  767. DebugTrace( 0, Dbg, "Cannot acquire FcbOrDcb = %08lx shared without waiting\n", FcbOrDcb );
  768. try_return( PostIrp = TRUE );
  769. }
  770. FcbOrDcbAcquired = TRUE;
  771. FcbAcquiredExclusive = TRUE;
  772. //
  773. // Preacquire pagingio for the flush.
  774. //
  775. ExAcquireSharedStarveExclusive( FcbOrDcb->Header.PagingIoResource, TRUE );
  776. CcFlushCache( FileObject->SectionObjectPointer,
  777. WriteToEof ? &FcbOrDcb->Header.FileSize : &StartingByte,
  778. ByteCount,
  779. &Irp->IoStatus );
  780. ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource );
  781. if (!NT_SUCCESS( Irp->IoStatus.Status)) {
  782. try_return( Irp->IoStatus.Status );
  783. }
  784. //
  785. // Now pick up and hold pagingIO exclusive. This serializes us with the
  786. // completion of a coincedent lazy writer doing its part of the write of
  787. // this range.
  788. //
  789. // We hold so that we will prevent a pagefault from occuring and seeing
  790. // soon-to-be stale data from the disk. We used to believe this was
  791. // something to be left to the app to synchronize; we now realize that
  792. // noncached IO on a fileserver is doomed without the filesystem forcing
  793. // the coherency issue. By only penalizing noncached coherency when
  794. // needed, this is about the best we can do.
  795. //
  796. ExAcquireResourceExclusiveLite( FcbOrDcb->Header.PagingIoResource, TRUE);
  797. PagingIoResourceAcquired = TRUE;
  798. CcPurgeCacheSection( FileObject->SectionObjectPointer,
  799. WriteToEof ? &FcbOrDcb->Header.FileSize : &StartingByte,
  800. ByteCount,
  801. FALSE );
  802. //
  803. // Indicate we're OK with the fcb being demoted to shared access
  804. // if that turns out to be possible later on after VDL extension
  805. // is checked for.
  806. //
  807. // PagingIo must be held all the way through.
  808. //
  809. FcbCanDemoteToShared = TRUE;
  810. }
  811. //
  812. // We assert that Paging Io writes will never WriteToEof.
  813. //
  814. ASSERT( WriteToEof ? !PagingIo : TRUE );
  815. //
  816. // First let's acquire the Fcb shared. Shared is enough if we
  817. // are not writing beyond EOF.
  818. //
  819. if ( PagingIo ) {
  820. (VOID)ExAcquireResourceSharedLite( FcbOrDcb->Header.PagingIoResource, TRUE );
  821. PagingIoResourceAcquired = TRUE;
  822. if (!Wait) {
  823. IrpContext->FatIoContext->Wait.Async.Resource =
  824. FcbOrDcb->Header.PagingIoResource;
  825. }
  826. //
  827. // Check to see if we colided with a MoveFile call, and if
  828. // so block until it completes.
  829. //
  830. if (FcbOrDcb->MoveFileEvent) {
  831. (VOID)KeWaitForSingleObject( FcbOrDcb->MoveFileEvent,
  832. Executive,
  833. KernelMode,
  834. FALSE,
  835. NULL );
  836. }
  837. } else {
  838. //
  839. // We may already have the Fcb due to noncached coherency
  840. // work done just above; however, we may still have to extend
  841. // valid data length. We can't demote this to shared, matching
  842. // what occured before, until we figure that out a bit later.
  843. //
  844. // We kept ahold of it since our lockorder is main->paging,
  845. // and paging must now held across the noncached write from
  846. // the purge on.
  847. //
  848. //
  849. // If this is async I/O, we will wait if there is an exclusive
  850. // waiter.
  851. //
  852. if (!Wait && NonCachedIo) {
  853. if (!FcbOrDcbAcquired &&
  854. !FatAcquireSharedFcbWaitForEx( IrpContext, FcbOrDcb )) {
  855. DebugTrace( 0, Dbg, "Cannot acquire FcbOrDcb = %08lx shared without waiting\n", FcbOrDcb );
  856. try_return( PostIrp = TRUE );
  857. }
  858. //
  859. // Note we will have to release this resource elsewhere. If we came
  860. // out of the noncached coherency path, we will also have to drop
  861. // the paging io resource.
  862. //
  863. IrpContext->FatIoContext->Wait.Async.Resource = FcbOrDcb->Header.Resource;
  864. if (FcbCanDemoteToShared) {
  865. IrpContext->FatIoContext->Wait.Async.Resource2 = FcbOrDcb->Header.PagingIoResource;
  866. }
  867. } else {
  868. if (!FcbOrDcbAcquired &&
  869. !FatAcquireSharedFcb( IrpContext, FcbOrDcb )) {
  870. DebugTrace( 0, Dbg, "Cannot acquire FcbOrDcb = %08lx shared without waiting\n", FcbOrDcb );
  871. try_return( PostIrp = TRUE );
  872. }
  873. }
  874. FcbOrDcbAcquired = TRUE;
  875. }
  876. //
  877. // Get a first tentative file size and valid data length.
  878. // We must get ValidDataLength first since it is always
  879. // increased second (in case we are unprotected) and
  880. // we don't want to capture ValidDataLength > FileSize.
  881. //
  882. ValidDataToDisk = FcbOrDcb->ValidDataToDisk;
  883. ValidDataLength = FcbOrDcb->Header.ValidDataLength.LowPart;
  884. FileSize = FcbOrDcb->Header.FileSize.LowPart;
  885. ASSERT( ValidDataLength <= FileSize );
  886. //
  887. // If are paging io, then we do not want
  888. // to write beyond end of file. If the base is beyond Eof, we will just
  889. // Noop the call. If the transfer starts before Eof, but extends
  890. // beyond, we will truncate the transfer to the last sector
  891. // boundary.
  892. //
  893. //
  894. // Just in case this is paging io, limit write to file size.
  895. // Otherwise, in case of write through, since Mm rounds up
  896. // to a page, we might try to acquire the resource exclusive
  897. // when our top level guy only acquired it shared. Thus, =><=.
  898. //
  899. if ( PagingIo ) {
  900. if (StartingVbo >= FileSize) {
  901. DebugTrace( 0, Dbg, "PagingIo started beyond EOF.\n", 0 );
  902. Irp->IoStatus.Information = 0;
  903. try_return( Status = STATUS_SUCCESS );
  904. }
  905. if (ByteCount > FileSize - StartingVbo) {
  906. DebugTrace( 0, Dbg, "PagingIo extending beyond EOF.\n", 0 );
  907. ByteCount = FileSize - StartingVbo;
  908. }
  909. }
  910. //
  911. // Determine if we were called by the lazywriter.
  912. // (see resrcsup.c)
  913. //
  914. if (FcbOrDcb->Specific.Fcb.LazyWriteThread == PsGetCurrentThread()) {
  915. CalledByLazyWriter = TRUE;
  916. if (FlagOn( FcbOrDcb->Header.Flags, FSRTL_FLAG_USER_MAPPED_FILE )) {
  917. //
  918. // Fail if the start of this request is beyond valid data length.
  919. // Don't worry if this is an unsafe test. MM and CC won't
  920. // throw this page away if it is really dirty.
  921. //
  922. if ((StartingVbo + ByteCount > ValidDataLength) &&
  923. (StartingVbo < FileSize)) {
  924. //
  925. // It's OK if byte range is within the page containing valid data length,
  926. // since we will use ValidDataToDisk as the start point.
  927. //
  928. if (StartingVbo + ByteCount > ((ValidDataLength + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) {
  929. //
  930. // Don't flush this now.
  931. //
  932. try_return( Status = STATUS_FILE_LOCK_CONFLICT );
  933. }
  934. }
  935. }
  936. }
  937. //
  938. // This code detects if we are a recursive synchronous page write
  939. // on a write through file object.
  940. //
  941. if (FlagOn(Irp->Flags, IRP_SYNCHRONOUS_PAGING_IO) &&
  942. FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL)) {
  943. PIRP TopIrp;
  944. TopIrp = IoGetTopLevelIrp();
  945. //
  946. // This clause determines if the top level request was
  947. // in the FastIo path. Gack. Since we don't have a
  948. // real sharing protocol for the top level IRP field ...
  949. // yet ... if someone put things other than a pure IRP in
  950. // there we best be careful.
  951. //
  952. if ((ULONG_PTR)TopIrp > FSRTL_MAX_TOP_LEVEL_IRP_FLAG &&
  953. NodeType(TopIrp) == IO_TYPE_IRP) {
  954. PIO_STACK_LOCATION IrpStack;
  955. IrpStack = IoGetCurrentIrpStackLocation(TopIrp);
  956. //
  957. // Finally this routine detects if the Top irp was a
  958. // write to this file and thus we are the writethrough.
  959. //
  960. if ((IrpStack->MajorFunction == IRP_MJ_WRITE) &&
  961. (IrpStack->FileObject->FsContext == FileObject->FsContext)) {
  962. RecursiveWriteThrough = TRUE;
  963. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH );
  964. }
  965. }
  966. }
  967. //
  968. // Here is the deal with ValidDataLength and FileSize:
  969. //
  970. // Rule 1: PagingIo is never allowed to extend file size.
  971. //
  972. // Rule 2: Only the top level requestor may extend Valid
  973. // Data Length. This may be paging IO, as when a
  974. // a user maps a file, but will never be as a result
  975. // of cache lazy writer writes since they are not the
  976. // top level request.
  977. //
  978. // Rule 3: If, using Rules 1 and 2, we decide we must extend
  979. // file size or valid data, we take the Fcb exclusive.
  980. //
  981. //
  982. // Now see if we are writing beyond valid data length, and thus
  983. // maybe beyond the file size. If so, then we must
  984. // release the Fcb and reacquire it exclusive. Note that it is
  985. // important that when not writing beyond EOF that we check it
  986. // while acquired shared and keep the FCB acquired, in case some
  987. // turkey truncates the file.
  988. //
  989. //
  990. // Note that the lazy writer must not be allowed to try and
  991. // acquire the resource exclusive. This is not a problem since
  992. // the lazy writer is paging IO and thus not allowed to extend
  993. // file size, and is never the top level guy, thus not able to
  994. // extend valid data length.
  995. //
  996. if ( !CalledByLazyWriter &&
  997. !RecursiveWriteThrough &&
  998. (WriteToEof ||
  999. StartingVbo + ByteCount > ValidDataLength)) {
  1000. //
  1001. // If this was an asynchronous write, we are going to make
  1002. // the request synchronous at this point, but only kinda.
  1003. // At the last moment, before sending the write off to the
  1004. // driver, we may shift back to async.
  1005. //
  1006. // The modified page writer already has the resources
  1007. // he requires, so this will complete in small finite
  1008. // time.
  1009. //
  1010. if (!Wait) {
  1011. Wait = TRUE;
  1012. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
  1013. if (NonCachedIo) {
  1014. ASSERT( TypeOfOpen == UserFileOpen );
  1015. SwitchBackToAsync = TRUE;
  1016. }
  1017. }
  1018. //
  1019. // We need Exclusive access to the Fcb/Dcb since we will
  1020. // probably have to extend valid data and/or file.
  1021. //
  1022. //
  1023. // Y'know, the PagingIo case is a mapped page writer, and
  1024. // MmFlushSection or the mapped page writer itself already
  1025. // snatched up the main exclusive for us via the AcquireForCcFlush
  1026. // or AcquireForModWrite logic (the default logic parallels FAT's
  1027. // requirements since this order/model came first). Should ASSERT
  1028. // this since it'll just go 1->2, and a few more unnecesary DPC
  1029. // transitions.
  1030. //
  1031. // The preacquire is done to avoid inversion over the collided flush
  1032. // meta-resource in Mm. The one time this is not true is at final
  1033. // system shutdown time, when Mm goes off and flushes all the dirty
  1034. // pages. Since the callback is defined as Wait == FALSE he can't
  1035. // guarantee acquisition (though with clean process shutdown being
  1036. // enforced, it really should be now). Permit this to float.
  1037. //
  1038. // Note that since we're going to fall back on the acquisition aleady
  1039. // done for us, don't confuse things by thinking we did the work
  1040. // for it.
  1041. //
  1042. if ( PagingIo ) {
  1043. ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource );
  1044. PagingIoResourceAcquired = FALSE;
  1045. } else {
  1046. //
  1047. // The Fcb may already be acquired exclusive due to coherency
  1048. // work performed earlier. If so, obviously no work to do.
  1049. //
  1050. if (!FcbAcquiredExclusive) {
  1051. FatReleaseFcb( IrpContext, FcbOrDcb );
  1052. FcbOrDcbAcquired = FALSE;
  1053. if (!FatAcquireExclusiveFcb( IrpContext, FcbOrDcb )) {
  1054. DebugTrace( 0, Dbg, "Cannot acquire FcbOrDcb = %08lx shared without waiting\n", FcbOrDcb );
  1055. try_return( PostIrp = TRUE );
  1056. }
  1057. FcbOrDcbAcquired = TRUE;
  1058. FcbAcquiredExclusive = TRUE;
  1059. }
  1060. }
  1061. //
  1062. // Now that we have the Fcb exclusive, see if this write
  1063. // qualifies for being made async again. The key point
  1064. // here is that we are going to update ValidDataLength in
  1065. // the Fcb before returning. We must make sure this will
  1066. // not cause a problem. One thing we must do is keep out
  1067. // the FastIo path.
  1068. //
  1069. if (SwitchBackToAsync) {
  1070. if ((FcbOrDcb->NonPaged->SectionObjectPointers.DataSectionObject != NULL) ||
  1071. (StartingVbo + ByteCount > FcbOrDcb->Header.ValidDataLength.LowPart) ||
  1072. FatNoAsync) {
  1073. RtlZeroMemory( IrpContext->FatIoContext, sizeof(FAT_IO_CONTEXT) );
  1074. KeInitializeEvent( &IrpContext->FatIoContext->Wait.SyncEvent,
  1075. NotificationEvent,
  1076. FALSE );
  1077. SwitchBackToAsync = FALSE;
  1078. } else {
  1079. if (!FcbOrDcb->NonPaged->OutstandingAsyncEvent) {
  1080. FcbOrDcb->NonPaged->OutstandingAsyncEvent =
  1081. FsRtlAllocatePoolWithTag( NonPagedPool,
  1082. sizeof(KEVENT),
  1083. TAG_EVENT );
  1084. KeInitializeEvent( FcbOrDcb->NonPaged->OutstandingAsyncEvent,
  1085. NotificationEvent,
  1086. FALSE );
  1087. }
  1088. //
  1089. // If we are transitioning from 0 to 1, reset the event.
  1090. //
  1091. if (ExInterlockedAddUlong( &FcbOrDcb->NonPaged->OutstandingAsyncWrites,
  1092. 1,
  1093. &FatData.GeneralSpinLock ) == 0) {
  1094. KeClearEvent( FcbOrDcb->NonPaged->OutstandingAsyncEvent );
  1095. }
  1096. UnwindOutstandingAsync = TRUE;
  1097. IrpContext->FatIoContext->Wait.Async.NonPagedFcb = FcbOrDcb->NonPaged;
  1098. }
  1099. }
  1100. //
  1101. // Now that we have the Fcb exclusive, get a new batch of
  1102. // filesize and ValidDataLength.
  1103. //
  1104. ValidDataToDisk = FcbOrDcb->ValidDataToDisk;
  1105. ValidDataLength = FcbOrDcb->Header.ValidDataLength.LowPart;
  1106. FileSize = FcbOrDcb->Header.FileSize.LowPart;
  1107. //
  1108. // If this is PagingIo check again if any pruning is
  1109. // required. It is important to start from basic
  1110. // princples in case the file was *grown* ...
  1111. //
  1112. if ( PagingIo ) {
  1113. if (StartingVbo >= FileSize) {
  1114. Irp->IoStatus.Information = 0;
  1115. try_return( Status = STATUS_SUCCESS );
  1116. }
  1117. ByteCount = IrpSp->Parameters.Write.Length;
  1118. if (ByteCount > FileSize - StartingVbo) {
  1119. ByteCount = FileSize - StartingVbo;
  1120. }
  1121. }
  1122. }
  1123. //
  1124. // Remember the final requested byte count
  1125. //
  1126. if (NonCachedIo && !Wait) {
  1127. IrpContext->FatIoContext->Wait.Async.RequestedByteCount =
  1128. ByteCount;
  1129. }
  1130. //
  1131. // Remember the initial file size and valid data length,
  1132. // just in case .....
  1133. //
  1134. InitialFileSize = FileSize;
  1135. InitialValidDataLength = ValidDataLength;
  1136. //
  1137. // Make sure the FcbOrDcb is still good
  1138. //
  1139. FatVerifyFcb( IrpContext, FcbOrDcb );
  1140. //
  1141. // Check for writing to end of File. If we are, then we have to
  1142. // recalculate a number of fields.
  1143. //
  1144. if ( WriteToEof ) {
  1145. StartingVbo = FileSize;
  1146. StartingByte = FcbOrDcb->Header.FileSize;
  1147. //
  1148. // Since we couldn't know this information until now, perform the
  1149. // necessary bounds checking that we ommited at the top because
  1150. // this is a WriteToEof operation.
  1151. //
  1152. if (!FatIsIoRangeValid( Vcb, StartingByte, ByteCount )) {
  1153. Irp->IoStatus.Information = 0;
  1154. try_return( Status = STATUS_DISK_FULL );
  1155. }
  1156. }
  1157. //
  1158. // If this is a non paging write to a data stream object we have to
  1159. // check for access according to the current state op/filelocks.
  1160. //
  1161. // Note that after this point, operations will be performed on the file.
  1162. // No modifying activity can occur prior to this point in the write
  1163. // path.
  1164. //
  1165. if (!PagingIo && TypeOfOpen == UserFileOpen) {
  1166. Status = FsRtlCheckOplock( &FcbOrDcb->Specific.Fcb.Oplock,
  1167. Irp,
  1168. IrpContext,
  1169. FatOplockComplete,
  1170. FatPrePostIrp );
  1171. if (Status != STATUS_SUCCESS) {
  1172. OplockPostIrp = TRUE;
  1173. PostIrp = TRUE;
  1174. try_return( NOTHING );
  1175. }
  1176. //
  1177. // This oplock call can affect whether fast IO is possible.
  1178. // We may have broken an oplock to no oplock held. If the
  1179. // current state of the file is FastIoIsNotPossible then
  1180. // recheck the fast IO state.
  1181. //
  1182. if (FcbOrDcb->Header.IsFastIoPossible == FastIoIsNotPossible) {
  1183. FcbOrDcb->Header.IsFastIoPossible = FatIsFastIoPossible( FcbOrDcb );
  1184. }
  1185. //
  1186. // And finally check the regular file locks.
  1187. //
  1188. if (!FsRtlCheckLockForWriteAccess( &FcbOrDcb->Specific.Fcb.FileLock, Irp )) {
  1189. try_return( Status = STATUS_FILE_LOCK_CONFLICT );
  1190. }
  1191. }
  1192. //
  1193. // Determine if we will deal with extending the file. Note that
  1194. // this implies extending valid data, and so we already have all
  1195. // of the required synchronization done.
  1196. //
  1197. if (!PagingIo && (StartingVbo + ByteCount > FileSize)) {
  1198. ExtendingFile = TRUE;
  1199. }
  1200. if ( ExtendingFile ) {
  1201. //
  1202. // EXTENDING THE FILE
  1203. //
  1204. // Update our local copy of FileSize
  1205. //
  1206. FileSize = StartingVbo + ByteCount;
  1207. if (FcbOrDcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {
  1208. FatLookupFileAllocationSize( IrpContext, FcbOrDcb );
  1209. }
  1210. //
  1211. // If the write goes beyond the allocation size, add some
  1212. // file allocation.
  1213. //
  1214. if ( FileSize > FcbOrDcb->Header.AllocationSize.LowPart ) {
  1215. BOOLEAN AllocateMinimumSize = TRUE;
  1216. //
  1217. // Only do allocation chuncking on writes if this is
  1218. // not the first allocation added to the file.
  1219. //
  1220. if (FcbOrDcb->Header.AllocationSize.LowPart != 0 ) {
  1221. ULONG ApproximateClusterCount;
  1222. ULONG TargetAllocation;
  1223. ULONG Multiplier;
  1224. ULONG BytesPerCluster;
  1225. ULONG ClusterAlignedFileSize;
  1226. //
  1227. // We are going to try and allocate a bigger chunk than
  1228. // we actually need in order to maximize FastIo usage.
  1229. //
  1230. // The multiplier is computed as follows:
  1231. //
  1232. //
  1233. // (FreeDiskSpace )
  1234. // Mult = ( (-------------------------) / 32 ) + 1
  1235. // (FileSize - AllocationSize)
  1236. //
  1237. // and max out at 32.
  1238. //
  1239. // With this formula we start winding down chunking
  1240. // as we get near the disk space wall.
  1241. //
  1242. // For instance on an empty 1 MEG floppy doing an 8K
  1243. // write, the multiplier is 6, or 48K to allocate.
  1244. // When this disk is half full, the multipler is 3,
  1245. // and when it is 3/4 full, the mupltiplier is only 1.
  1246. //
  1247. // On a larger disk, the multiplier for a 8K read will
  1248. // reach its maximum of 32 when there is at least ~8 Megs
  1249. // available.
  1250. //
  1251. //
  1252. // Small write performance note, use cluster aligned
  1253. // file size in above equation.
  1254. //
  1255. //
  1256. // We need to carefully consider what happens when we approach
  1257. // a 2^32 byte filesize. Overflows will cause problems.
  1258. //
  1259. BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster;
  1260. //
  1261. // This can overflow if the target filesize is in the last cluster.
  1262. // In this case, we can obviously skip over all of this fancy
  1263. // logic and just max out the file right now.
  1264. //
  1265. ClusterAlignedFileSize = (FileSize + (BytesPerCluster - 1)) &
  1266. ~(BytesPerCluster - 1);
  1267. if (ClusterAlignedFileSize != 0) {
  1268. //
  1269. // This actually has a chance but the possibility of overflowing
  1270. // the numerator is pretty unlikely, made more unlikely by moving
  1271. // the divide by 32 up to scale the BytesPerCluster. However, even if it does the
  1272. // effect is completely benign.
  1273. //
  1274. // FAT32 with a 64k cluster and over 2^21 clusters would do it (and
  1275. // so forth - 2^(16 - 5 + 21) == 2^32). Since this implies a partition
  1276. // of 32gb and a number of clusters (and cluster size) we plan to
  1277. // disallow in format for FAT32, the odds of this happening are pretty
  1278. // low anyway.
  1279. //
  1280. Multiplier = ((Vcb->AllocationSupport.NumberOfFreeClusters *
  1281. (BytesPerCluster >> 5)) /
  1282. (ClusterAlignedFileSize -
  1283. FcbOrDcb->Header.AllocationSize.LowPart)) + 1;
  1284. if (Multiplier > 32) { Multiplier = 32; }
  1285. Multiplier *= (ClusterAlignedFileSize - FcbOrDcb->Header.AllocationSize.LowPart);
  1286. TargetAllocation = FcbOrDcb->Header.AllocationSize.LowPart + Multiplier;
  1287. //
  1288. // We know that TargetAllocation is in whole clusters, so simply
  1289. // checking if it wrapped is correct. If it did, we fall back
  1290. // to allocating up to the maximum legal size.
  1291. //
  1292. if (TargetAllocation < FcbOrDcb->Header.AllocationSize.LowPart) {
  1293. TargetAllocation = ~BytesPerCluster + 1;
  1294. Multiplier = TargetAllocation - FcbOrDcb->Header.AllocationSize.LowPart;
  1295. }
  1296. //
  1297. // Now do an unsafe check here to see if we should even
  1298. // try to allocate this much. If not, just allocate
  1299. // the minimum size we need, if so so try it, but if it
  1300. // fails, just allocate the minimum size we need.
  1301. //
  1302. ApproximateClusterCount = (Multiplier / BytesPerCluster);
  1303. if (ApproximateClusterCount <= Vcb->AllocationSupport.NumberOfFreeClusters) {
  1304. try {
  1305. FatAddFileAllocation( IrpContext,
  1306. FcbOrDcb,
  1307. FileObject,
  1308. TargetAllocation );
  1309. AllocateMinimumSize = FALSE;
  1310. SetFlag( FcbOrDcb->FcbState, FCB_STATE_TRUNCATE_ON_CLOSE );
  1311. } except( GetExceptionCode() == STATUS_DISK_FULL ?
  1312. EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
  1313. FatResetExceptionState( IrpContext );
  1314. }
  1315. }
  1316. }
  1317. }
  1318. if ( AllocateMinimumSize ) {
  1319. FatAddFileAllocation( IrpContext,
  1320. FcbOrDcb,
  1321. FileObject,
  1322. FileSize );
  1323. }
  1324. //
  1325. // Assert that the allocation worked
  1326. //
  1327. ASSERT( FcbOrDcb->Header.AllocationSize.LowPart >= FileSize );
  1328. }
  1329. //
  1330. // Set the new file size in the Fcb
  1331. //
  1332. ASSERT( FileSize <= FcbOrDcb->Header.AllocationSize.LowPart );
  1333. FcbOrDcb->Header.FileSize.LowPart = FileSize;
  1334. //
  1335. // Extend the cache map, letting mm knows the new file size.
  1336. // We only have to do this if the file is cached.
  1337. //
  1338. if (CcIsFileCached(FileObject)) {
  1339. CcSetFileSizes( FileObject, (PCC_FILE_SIZES)&FcbOrDcb->Header.AllocationSize );
  1340. }
  1341. }
  1342. //
  1343. // Determine if we will deal with extending valid data.
  1344. //
  1345. if ( !CalledByLazyWriter &&
  1346. !RecursiveWriteThrough &&
  1347. (StartingVbo + ByteCount > ValidDataLength) ) {
  1348. ExtendingValidData = TRUE;
  1349. } else {
  1350. //
  1351. // If not extending valid data, and we otherwise believe we
  1352. // could demote from exclusive to shared, do so. This will
  1353. // occur when we synchronize tight for noncached coherency
  1354. // but must defer the demotion until after we decide about
  1355. // valid data length, which requires it exclusive. Since we
  1356. // can't drop/re-pick the resources without letting a pagefault
  1357. // squirt through, the resource decision was kept up in the air
  1358. // until now.
  1359. //
  1360. // Note that we've still got PagingIo exclusive in these cases.
  1361. //
  1362. if (FcbCanDemoteToShared) {
  1363. ASSERT( FcbAcquiredExclusive && ExIsResourceAcquiredExclusiveLite( FcbOrDcb->Header.Resource ));
  1364. ExConvertExclusiveToSharedLite( FcbOrDcb->Header.Resource );
  1365. FcbAcquiredExclusive = FALSE;
  1366. }
  1367. }
  1368. if (ValidDataToDisk > ValidDataLength) {
  1369. ValidDataToCheck = ValidDataToDisk;
  1370. } else {
  1371. ValidDataToCheck = ValidDataLength;
  1372. }
  1373. //
  1374. // HANDLE THE NON-CACHED CASE
  1375. //
  1376. if ( NonCachedIo ) {
  1377. //
  1378. // Declare some local variables for enumeration through the
  1379. // runs of the file, and an array to store parameters for
  1380. // parallel I/Os
  1381. //
  1382. ULONG SectorSize;
  1383. ULONG BytesToWrite;
  1384. DebugTrace(0, Dbg, "Non cached write.\n", 0);
  1385. //
  1386. // Round up to sector boundry. The end of the write interval
  1387. // must, however, be beyond EOF.
  1388. //
  1389. SectorSize = (ULONG)Vcb->Bpb.BytesPerSector;
  1390. BytesToWrite = (ByteCount + (SectorSize - 1))
  1391. & ~(SectorSize - 1);
  1392. //
  1393. // All requests should be well formed and
  1394. // make sure we don't wipe out any data
  1395. //
  1396. if (((StartingVbo & (SectorSize - 1)) != 0) ||
  1397. ((BytesToWrite != ByteCount) &&
  1398. (StartingVbo + ByteCount < ValidDataLength))) {
  1399. ASSERT( FALSE );
  1400. DebugTrace( 0, Dbg, "FatCommonWrite -> STATUS_NOT_IMPLEMENTED\n", 0);
  1401. try_return( Status = STATUS_NOT_IMPLEMENTED );
  1402. }
  1403. //
  1404. // If this noncached transfer is at least one sector beyond
  1405. // the current ValidDataLength in the Fcb, then we have to
  1406. // zero the sectors in between. This can happen if the user
  1407. // has opened the file noncached, or if the user has mapped
  1408. // the file and modified a page beyond ValidDataLength. It
  1409. // *cannot* happen if the user opened the file cached, because
  1410. // ValidDataLength in the Fcb is updated when he does the cached
  1411. // write (we also zero data in the cache at that time), and
  1412. // therefore, we will bypass this test when the data
  1413. // is ultimately written through (by the Lazy Writer).
  1414. //
  1415. // For the paging file we don't care about security (ie.
  1416. // stale data), do don't bother zeroing.
  1417. //
  1418. // We can actually get writes wholly beyond valid data length
  1419. // from the LazyWriter because of paging Io decoupling.
  1420. //
  1421. if (!CalledByLazyWriter &&
  1422. !RecursiveWriteThrough &&
  1423. (StartingVbo > ValidDataToCheck)) {
  1424. FatZeroData( IrpContext,
  1425. Vcb,
  1426. FileObject,
  1427. ValidDataToCheck,
  1428. StartingVbo - ValidDataToCheck );
  1429. }
  1430. //
  1431. // Make sure we write FileSize to the dirent if we
  1432. // are extending it and we are successful. (This may or
  1433. // may not occur Write Through, but that is fine.)
  1434. //
  1435. WriteFileSizeToDirent = TRUE;
  1436. //
  1437. // Perform the actual IO
  1438. //
  1439. if (SwitchBackToAsync) {
  1440. Wait = FALSE;
  1441. ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
  1442. }
  1443. #ifdef SYSCACHE_COMPILE
  1444. #define MY_SIZE 0x1000000
  1445. #define LONGMAP_COUNTER
  1446. #ifdef BITMAP
  1447. //
  1448. // Maintain a bitmap of IO started on this file.
  1449. //
  1450. {
  1451. PULONG WriteMask = FcbOrDcb->WriteMask;
  1452. if (NULL == WriteMask) {
  1453. WriteMask = FsRtlAllocatePoolWithTag( NonPagedPool,
  1454. (MY_SIZE/PAGE_SIZE) / 8,
  1455. 'wtaF' );
  1456. FcbOrDcb->WriteMask = WriteMask;
  1457. RtlZeroMemory(WriteMask, (MY_SIZE/PAGE_SIZE) / 8);
  1458. }
  1459. if (StartingVbo < MY_SIZE) {
  1460. ULONG Off = StartingVbo;
  1461. ULONG Len = BytesToWrite;
  1462. if (Off + Len > MY_SIZE) {
  1463. Len = MY_SIZE - Off;
  1464. }
  1465. while (Len != 0) {
  1466. WriteMask[(Off/PAGE_SIZE) / 32] |=
  1467. 1 << (Off/PAGE_SIZE) % 32;
  1468. Off += PAGE_SIZE;
  1469. if (Len <= PAGE_SIZE) {
  1470. break;
  1471. }
  1472. Len -= PAGE_SIZE;
  1473. }
  1474. }
  1475. }
  1476. #endif
  1477. #ifdef LONGMAP_COUNTER
  1478. //
  1479. // Maintain a longmap of IO started on this file, each ulong containing
  1480. // the value of an ascending counter per write (gives us order information).
  1481. //
  1482. // Unlike the old bitmask stuff, this is mostly well synchronized.
  1483. //
  1484. {
  1485. PULONG WriteMask = (PULONG)FcbOrDcb->WriteMask;
  1486. if (NULL == WriteMask) {
  1487. WriteMask = FsRtlAllocatePoolWithTag( NonPagedPool,
  1488. (MY_SIZE/PAGE_SIZE) * sizeof(ULONG),
  1489. 'wtaF' );
  1490. FcbOrDcb->WriteMask = WriteMask;
  1491. RtlZeroMemory(WriteMask, (MY_SIZE/PAGE_SIZE) * sizeof(ULONG));
  1492. }
  1493. if (StartingVbo < MY_SIZE) {
  1494. ULONG Off = StartingVbo;
  1495. ULONG Len = BytesToWrite;
  1496. ULONG Tick = InterlockedIncrement( &FcbOrDcb->WriteMaskData );
  1497. if (Off + Len > MY_SIZE) {
  1498. Len = MY_SIZE - Off;
  1499. }
  1500. while (Len != 0) {
  1501. InterlockedExchange( WriteMask + Off/PAGE_SIZE, Tick );
  1502. Off += PAGE_SIZE;
  1503. if (Len <= PAGE_SIZE) {
  1504. break;
  1505. }
  1506. Len -= PAGE_SIZE;
  1507. }
  1508. }
  1509. }
  1510. #endif
  1511. #endif
  1512. if (FatNonCachedIo( IrpContext,
  1513. Irp,
  1514. FcbOrDcb,
  1515. StartingVbo,
  1516. BytesToWrite,
  1517. BytesToWrite ) == STATUS_PENDING) {
  1518. UnwindOutstandingAsync = FALSE;
  1519. Wait = TRUE;
  1520. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
  1521. IrpContext->FatIoContext = NULL;
  1522. Irp = NULL;
  1523. //
  1524. // As a matter of fact, if we hit this we are in deep trouble
  1525. // if VDL is being extended. We are no longer attached to the
  1526. // IRP, and have thus lost synchronization. Note that we should
  1527. // not hit this case anymore since we will not re-async vdl extension.
  1528. //
  1529. ASSERT( !ExtendingValidData );
  1530. try_return( Status = STATUS_PENDING );
  1531. }
  1532. //
  1533. // If the call didn't succeed, raise the error status
  1534. //
  1535. if (!NT_SUCCESS( Status = Irp->IoStatus.Status )) {
  1536. FatNormalizeAndRaiseStatus( IrpContext, Status );
  1537. } else {
  1538. ULONG Temp;
  1539. //
  1540. // Else set the context block to reflect the entire write
  1541. // Also assert we got how many bytes we asked for.
  1542. //
  1543. ASSERT( Irp->IoStatus.Information == BytesToWrite );
  1544. Irp->IoStatus.Information = ByteCount;
  1545. //
  1546. // Take this opportunity to update ValidDataToDisk.
  1547. //
  1548. Temp = StartingVbo + BytesToWrite;
  1549. if (FcbOrDcb->ValidDataToDisk < Temp) {
  1550. FcbOrDcb->ValidDataToDisk = Temp;
  1551. }
  1552. }
  1553. //
  1554. // The transfer is either complete, or the Iosb contains the
  1555. // appropriate status.
  1556. //
  1557. try_return( Status );
  1558. } // if No Intermediate Buffering
  1559. //
  1560. // HANDLE CACHED CASE
  1561. //
  1562. else {
  1563. ASSERT( !PagingIo );
  1564. //
  1565. // We delay setting up the file cache until now, in case the
  1566. // caller never does any I/O to the file, and thus
  1567. // FileObject->PrivateCacheMap == NULL.
  1568. //
  1569. if ( FileObject->PrivateCacheMap == NULL ) {
  1570. DebugTrace(0, Dbg, "Initialize cache mapping.\n", 0);
  1571. //
  1572. // Get the file allocation size, and if it is less than
  1573. // the file size, raise file corrupt error.
  1574. //
  1575. if (FcbOrDcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {
  1576. FatLookupFileAllocationSize( IrpContext, FcbOrDcb );
  1577. }
  1578. if ( FileSize > FcbOrDcb->Header.AllocationSize.LowPart ) {
  1579. FatPopUpFileCorrupt( IrpContext, FcbOrDcb );
  1580. FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
  1581. }
  1582. //
  1583. // Now initialize the cache map.
  1584. //
  1585. CcInitializeCacheMap( FileObject,
  1586. (PCC_FILE_SIZES)&FcbOrDcb->Header.AllocationSize,
  1587. FALSE,
  1588. &FatData.CacheManagerCallbacks,
  1589. FcbOrDcb );
  1590. CcSetReadAheadGranularity( FileObject, READ_AHEAD_GRANULARITY );
  1591. //
  1592. // Special case large floppy tranfers, and make the file
  1593. // object write through. For small floppy transfers,
  1594. // set a timer to go off in a second and flush the file.
  1595. //
  1596. //
  1597. if (!FlagOn( FileObject->Flags, FO_WRITE_THROUGH ) &&
  1598. FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH)) {
  1599. if (((StartingByte.LowPart & (PAGE_SIZE-1)) == 0) &&
  1600. (ByteCount >= PAGE_SIZE)) {
  1601. SetFlag( FileObject->Flags, FO_WRITE_THROUGH );
  1602. } else {
  1603. LARGE_INTEGER OneSecondFromNow;
  1604. PDEFERRED_FLUSH_CONTEXT FlushContext;
  1605. //
  1606. // Get pool and initialize the timer and DPC
  1607. //
  1608. FlushContext = FsRtlAllocatePoolWithTag( NonPagedPool,
  1609. sizeof(DEFERRED_FLUSH_CONTEXT),
  1610. TAG_DEFERRED_FLUSH_CONTEXT );
  1611. KeInitializeTimer( &FlushContext->Timer );
  1612. KeInitializeDpc( &FlushContext->Dpc,
  1613. FatDeferredFlushDpc,
  1614. FlushContext );
  1615. //
  1616. // We have to reference the file object here.
  1617. //
  1618. ObReferenceObject( FileObject );
  1619. FlushContext->File = FileObject;
  1620. //
  1621. // Let'er rip!
  1622. //
  1623. OneSecondFromNow.QuadPart = (LONG)-1*1000*1000*10;
  1624. KeSetTimer( &FlushContext->Timer,
  1625. OneSecondFromNow,
  1626. &FlushContext->Dpc );
  1627. }
  1628. }
  1629. }
  1630. //
  1631. // If this write is beyond valid data length, then we
  1632. // must zero the data in between.
  1633. //
  1634. if ( StartingVbo > ValidDataToCheck ) {
  1635. //
  1636. // Call the Cache Manager to zero the data.
  1637. //
  1638. if (!FatZeroData( IrpContext,
  1639. Vcb,
  1640. FileObject,
  1641. ValidDataToCheck,
  1642. StartingVbo - ValidDataToCheck )) {
  1643. DebugTrace( 0, Dbg, "Cached Write could not wait to zero\n", 0 );
  1644. try_return( PostIrp = TRUE );
  1645. }
  1646. }
  1647. WriteFileSizeToDirent = BooleanFlagOn(IrpContext->Flags,
  1648. IRP_CONTEXT_FLAG_WRITE_THROUGH);
  1649. //
  1650. // DO A NORMAL CACHED WRITE, if the MDL bit is not set,
  1651. //
  1652. if (!FlagOn(IrpContext->MinorFunction, IRP_MN_MDL)) {
  1653. DebugTrace(0, Dbg, "Cached write.\n", 0);
  1654. //
  1655. // Get hold of the user's buffer.
  1656. //
  1657. SystemBuffer = FatMapUserBuffer( IrpContext, Irp );
  1658. //
  1659. // Do the write, possibly writing through
  1660. //
  1661. if (!CcCopyWrite( FileObject,
  1662. &StartingByte,
  1663. ByteCount,
  1664. Wait,
  1665. SystemBuffer )) {
  1666. DebugTrace( 0, Dbg, "Cached Write could not wait\n", 0 );
  1667. try_return( PostIrp = TRUE );
  1668. }
  1669. Irp->IoStatus.Status = STATUS_SUCCESS;
  1670. Irp->IoStatus.Information = ByteCount;
  1671. try_return( Status = STATUS_SUCCESS );
  1672. } else {
  1673. //
  1674. // DO AN MDL WRITE
  1675. //
  1676. DebugTrace(0, Dbg, "MDL write.\n", 0);
  1677. ASSERT( Wait );
  1678. CcPrepareMdlWrite( FileObject,
  1679. &StartingByte,
  1680. ByteCount,
  1681. &Irp->MdlAddress,
  1682. &Irp->IoStatus );
  1683. Status = Irp->IoStatus.Status;
  1684. try_return( Status );
  1685. }
  1686. }
  1687. }
  1688. //
  1689. // These two cases correspond to a system write directory file and
  1690. // ea file.
  1691. //
  1692. if (( TypeOfOpen == DirectoryFile ) || ( TypeOfOpen == EaFile)) {
  1693. ULONG SectorSize;
  1694. DebugTrace(0, Dbg, "Write Directory or Ea file.\n", 0);
  1695. //
  1696. // Make sure the FcbOrDcb is still good
  1697. //
  1698. FatVerifyFcb( IrpContext, FcbOrDcb );
  1699. //
  1700. // Synchronize here with people deleting directories and
  1701. // mucking with the internals of the EA file.
  1702. //
  1703. if (!ExAcquireSharedStarveExclusive( FcbOrDcb->Header.PagingIoResource,
  1704. Wait )) {
  1705. DebugTrace( 0, Dbg, "Cannot acquire FcbOrDcb = %08lx shared without waiting\n", FcbOrDcb );
  1706. try_return( PostIrp = TRUE );
  1707. }
  1708. PagingIoResourceAcquired = TRUE;
  1709. if (!Wait) {
  1710. IrpContext->FatIoContext->Wait.Async.Resource =
  1711. FcbOrDcb->Header.PagingIoResource;
  1712. }
  1713. //
  1714. // Check to see if we colided with a MoveFile call, and if
  1715. // so block until it completes.
  1716. //
  1717. if (FcbOrDcb->MoveFileEvent) {
  1718. (VOID)KeWaitForSingleObject( FcbOrDcb->MoveFileEvent,
  1719. Executive,
  1720. KernelMode,
  1721. FALSE,
  1722. NULL );
  1723. }
  1724. //
  1725. // If we weren't called by the Lazy Writer, then this write
  1726. // must be the result of a write-through or flush operation.
  1727. // Setting the IrpContext flag, will cause DevIoSup.c to
  1728. // write-through the data to the disk.
  1729. //
  1730. if (!FlagOn((ULONG_PTR)IoGetTopLevelIrp(), FSRTL_CACHE_TOP_LEVEL_IRP)) {
  1731. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH );
  1732. }
  1733. //
  1734. // For the noncached case, assert that everything is sector
  1735. // alligned.
  1736. //
  1737. SectorSize = (ULONG)Vcb->Bpb.BytesPerSector;
  1738. //
  1739. // We make several assumptions about these two types of files.
  1740. // Make sure all of them are true.
  1741. //
  1742. ASSERT( NonCachedIo && PagingIo );
  1743. ASSERT( ((StartingVbo | ByteCount) & (SectorSize - 1)) == 0 );
  1744. //
  1745. // These calls must always be within the allocation size, which is
  1746. // convienently the same as filesize, which conveniently doesn't
  1747. // get reset to a hint value when we verify the volume.
  1748. //
  1749. if (StartingVbo >= FcbOrDcb->Header.FileSize.LowPart) {
  1750. DebugTrace( 0, Dbg, "PagingIo dirent started beyond EOF.\n", 0 );
  1751. Irp->IoStatus.Information = 0;
  1752. try_return( Status = STATUS_SUCCESS );
  1753. }
  1754. if ( StartingVbo + ByteCount > FcbOrDcb->Header.FileSize.LowPart ) {
  1755. DebugTrace( 0, Dbg, "PagingIo dirent extending beyond EOF.\n", 0 );
  1756. ByteCount = FcbOrDcb->Header.FileSize.LowPart - StartingVbo;
  1757. }
  1758. //
  1759. // Perform the actual IO
  1760. //
  1761. if (FatNonCachedIo( IrpContext,
  1762. Irp,
  1763. FcbOrDcb,
  1764. StartingVbo,
  1765. ByteCount,
  1766. ByteCount ) == STATUS_PENDING) {
  1767. IrpContext->FatIoContext = NULL;
  1768. Irp = NULL;
  1769. try_return( Status = STATUS_PENDING );
  1770. }
  1771. //
  1772. // The transfer is either complete, or the Iosb contains the
  1773. // appropriate status.
  1774. //
  1775. // Also, mark the volume as needing verification to automatically
  1776. // clean up stuff.
  1777. //
  1778. if (!NT_SUCCESS( Status = Irp->IoStatus.Status )) {
  1779. FatNormalizeAndRaiseStatus( IrpContext, Status );
  1780. }
  1781. try_return( Status );
  1782. }
  1783. //
  1784. // This is the case of a user who openned a directory. No writing is
  1785. // allowed.
  1786. //
  1787. if ( TypeOfOpen == UserDirectoryOpen ) {
  1788. DebugTrace( 0, Dbg, "FatCommonWrite -> STATUS_INVALID_PARAMETER\n", 0);
  1789. try_return( Status = STATUS_INVALID_PARAMETER );
  1790. }
  1791. //
  1792. // If we get this far, something really serious is wrong.
  1793. //
  1794. DebugDump("Illegal TypeOfOpen\n", 0, FcbOrDcb );
  1795. FatBugCheck( TypeOfOpen, (ULONG_PTR) FcbOrDcb, 0 );
  1796. try_exit: NOTHING;
  1797. //
  1798. // If the request was not posted and there is still an Irp,
  1799. // deal with it.
  1800. //
  1801. if (Irp) {
  1802. if ( !PostIrp ) {
  1803. ULONG ActualBytesWrote;
  1804. DebugTrace( 0, Dbg, "Completing request with status = %08lx\n",
  1805. Status);
  1806. DebugTrace( 0, Dbg, " Information = %08lx\n",
  1807. Irp->IoStatus.Information);
  1808. //
  1809. // Record the total number of bytes actually written
  1810. //
  1811. ActualBytesWrote = (ULONG)Irp->IoStatus.Information;
  1812. //
  1813. // If the file was opened for Synchronous IO, update the current
  1814. // file position.
  1815. //
  1816. if (SynchronousIo && !PagingIo) {
  1817. FileObject->CurrentByteOffset.LowPart =
  1818. StartingVbo + ActualBytesWrote;
  1819. }
  1820. //
  1821. // The following are things we only do if we were successful
  1822. //
  1823. if ( NT_SUCCESS( Status ) ) {
  1824. //
  1825. // If this was not PagingIo, mark that the modify
  1826. // time on the dirent needs to be updated on close.
  1827. //
  1828. if ( !PagingIo ) {
  1829. SetFlag( FileObject->Flags, FO_FILE_MODIFIED );
  1830. }
  1831. //
  1832. // If we extended the file size and we are meant to
  1833. // immediately update the dirent, do so. (This flag is
  1834. // set for either Write Through or noncached, because
  1835. // in either case the data and any necessary zeros are
  1836. // actually written to the file.)
  1837. //
  1838. if ( ExtendingFile && WriteFileSizeToDirent ) {
  1839. ASSERT( FileObject->DeleteAccess || FileObject->WriteAccess );
  1840. FatSetFileSizeInDirent( IrpContext, FcbOrDcb, NULL );
  1841. //
  1842. // Report that a file size has changed.
  1843. //
  1844. FatNotifyReportChange( IrpContext,
  1845. Vcb,
  1846. FcbOrDcb,
  1847. FILE_NOTIFY_CHANGE_SIZE,
  1848. FILE_ACTION_MODIFIED );
  1849. }
  1850. if ( ExtendingFile && !WriteFileSizeToDirent ) {
  1851. SetFlag( FileObject->Flags, FO_FILE_SIZE_CHANGED );
  1852. }
  1853. if ( ExtendingValidData ) {
  1854. ULONG EndingVboWritten = StartingVbo + ActualBytesWrote;
  1855. //
  1856. // Never set a ValidDataLength greater than FileSize.
  1857. //
  1858. if ( FileSize < EndingVboWritten ) {
  1859. FcbOrDcb->Header.ValidDataLength.LowPart = FileSize;
  1860. } else {
  1861. FcbOrDcb->Header.ValidDataLength.LowPart = EndingVboWritten;
  1862. }
  1863. //
  1864. // Now, if we are noncached and the file is cached, we must
  1865. // tell the cache manager about the VDL extension so that
  1866. // async cached IO will not be optimized into zero-page faults
  1867. // beyond where it believes VDL is.
  1868. //
  1869. // In the cached case, since Cc did the work, it has updated
  1870. // itself already.
  1871. //
  1872. if (NonCachedIo && CcIsFileCached(FileObject)) {
  1873. CcSetFileSizes( FileObject, (PCC_FILE_SIZES)&FcbOrDcb->Header.AllocationSize );
  1874. }
  1875. }
  1876. }
  1877. //
  1878. // Note that we have to unpin repinned Bcbs here after the above
  1879. // work, but if we are going to post the request, we must do this
  1880. // before the post (below).
  1881. //
  1882. FatUnpinRepinnedBcbs( IrpContext );
  1883. } else {
  1884. //
  1885. // Take action if the Oplock package is not going to post the Irp.
  1886. //
  1887. if (!OplockPostIrp) {
  1888. FatUnpinRepinnedBcbs( IrpContext );
  1889. if ( ExtendingFile ) {
  1890. //
  1891. // We need the PagingIo resource exclusive whenever we
  1892. // pull back either file size or valid data length.
  1893. //
  1894. if ( FcbOrDcb->Header.PagingIoResource != NULL ) {
  1895. (VOID)ExAcquireResourceExclusiveLite(FcbOrDcb->Header.PagingIoResource, TRUE);
  1896. }
  1897. FcbOrDcb->Header.FileSize.LowPart = InitialFileSize;
  1898. ASSERT( FcbOrDcb->Header.FileSize.LowPart <= FcbOrDcb->Header.AllocationSize.LowPart );
  1899. //
  1900. // Pull back the cache map as well
  1901. //
  1902. if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) {
  1903. *CcGetFileSizePointer(FileObject) = FcbOrDcb->Header.FileSize;
  1904. }
  1905. if ( FcbOrDcb->Header.PagingIoResource != NULL ) {
  1906. ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource );
  1907. }
  1908. }
  1909. DebugTrace( 0, Dbg, "Passing request to Fsp\n", 0 );
  1910. Status = FatFsdPostRequest(IrpContext, Irp);
  1911. }
  1912. }
  1913. }
  1914. } finally {
  1915. DebugUnwind( FatCommonWrite );
  1916. if (AbnormalTermination()) {
  1917. PERESOURCE PagingIoResource = NULL;
  1918. //
  1919. // Restore initial file size and valid data length
  1920. //
  1921. if (ExtendingFile || ExtendingValidData) {
  1922. //
  1923. // We got an error, pull back the file size if we extended it.
  1924. //
  1925. // We need the PagingIo resource exclusive whenever we
  1926. // pull back either file size or valid data length.
  1927. //
  1928. FcbOrDcb->Header.FileSize.LowPart = InitialFileSize;
  1929. FcbOrDcb->Header.ValidDataLength.LowPart = InitialValidDataLength;
  1930. ASSERT( FcbOrDcb->Header.FileSize.LowPart <= FcbOrDcb->Header.AllocationSize.LowPart );
  1931. //
  1932. // Pull back the cache map as well
  1933. //
  1934. if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) {
  1935. *CcGetFileSizePointer(FileObject) = FcbOrDcb->Header.FileSize;
  1936. }
  1937. }
  1938. }
  1939. //
  1940. // Check if this needs to be backed out.
  1941. //
  1942. if (UnwindOutstandingAsync) {
  1943. ExInterlockedAddUlong( &FcbOrDcb->NonPaged->OutstandingAsyncWrites,
  1944. 0xffffffff,
  1945. &FatData.GeneralSpinLock );
  1946. }
  1947. //
  1948. // If the FcbOrDcb has been acquired, release it.
  1949. //
  1950. if (FcbOrDcbAcquired && Irp) {
  1951. FatReleaseFcb( NULL, FcbOrDcb );
  1952. }
  1953. if (PagingIoResourceAcquired && Irp) {
  1954. ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource );
  1955. }
  1956. //
  1957. // Complete the request if we didn't post it and no exception
  1958. //
  1959. // Note that FatCompleteRequest does the right thing if either
  1960. // IrpContext or Irp are NULL
  1961. //
  1962. if ( !PostIrp && !AbnormalTermination() ) {
  1963. FatCompleteRequest( IrpContext, Irp, Status );
  1964. }
  1965. DebugTrace(-1, Dbg, "FatCommonWrite -> %08lx\n", Status );
  1966. }
  1967. return Status;
  1968. }
  1969. //
  1970. // Local support routine
  1971. //
  1972. VOID
  1973. FatDeferredFlushDpc (
  1974. IN PKDPC Dpc,
  1975. IN PVOID DeferredContext,
  1976. IN PVOID SystemArgument1,
  1977. IN PVOID SystemArgument2
  1978. )
  1979. /*++
  1980. Routine Description:
  1981. This routine is dispatched 1 second after a small write to a deferred
  1982. write device that initialized the cache map. It exqueues an executive
  1983. worker thread to perform the actual task of flushing the file.
  1984. Arguments:
  1985. DeferredContext - Contains the deferred flush context.
  1986. Return Value:
  1987. None.
  1988. --*/
  1989. {
  1990. PDEFERRED_FLUSH_CONTEXT FlushContext;
  1991. FlushContext = (PDEFERRED_FLUSH_CONTEXT)DeferredContext;
  1992. //
  1993. // Send it off
  1994. //
  1995. ExInitializeWorkItem( &FlushContext->Item,
  1996. FatDeferredFlush,
  1997. FlushContext );
  1998. ExQueueWorkItem( &FlushContext->Item, CriticalWorkQueue );
  1999. }
  2000. //
  2001. // Local support routine
  2002. //
  2003. VOID
  2004. FatDeferredFlush (
  2005. PVOID Parameter
  2006. )
  2007. /*++
  2008. Routine Description:
  2009. This routine performs the actual task of flushing the file.
  2010. Arguments:
  2011. DeferredContext - Contains the deferred flush context.
  2012. Return Value:
  2013. None.
  2014. --*/
  2015. {
  2016. PFILE_OBJECT File;
  2017. PVCB Vcb;
  2018. PFCB FcbOrDcb;
  2019. PCCB Ccb;
  2020. File = ((PDEFERRED_FLUSH_CONTEXT)Parameter)->File;
  2021. FatDecodeFileObject(File, &Vcb, &FcbOrDcb, &Ccb);
  2022. ASSERT( FcbOrDcb != NULL );
  2023. //
  2024. // Make us appear as a top level FSP request so that we will
  2025. // receive any errors from the flush.
  2026. //
  2027. IoSetTopLevelIrp( (PIRP)FSRTL_FSP_TOP_LEVEL_IRP );
  2028. ExAcquireResourceSharedLite( FcbOrDcb->Header.Resource, TRUE );
  2029. ExAcquireResourceSharedLite( FcbOrDcb->Header.PagingIoResource, TRUE );
  2030. CcFlushCache( File->SectionObjectPointer, NULL, 0, NULL );
  2031. ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource );
  2032. ExReleaseResourceLite( FcbOrDcb->Header.Resource );
  2033. IoSetTopLevelIrp( NULL );
  2034. ObDereferenceObject( File );
  2035. ExFreePool( Parameter );
  2036. }