[FASTFAT] When extending a file, also lock the DirResource.
[reactos.git] / drivers / filesystems / fastfat / rw.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: drivers/filesystems/fastfat/rw.c
5 * PURPOSE: VFAT Filesystem
6 * PROGRAMMER: Jason Filby (jasonfilby@yahoo.com)
7 * Pierre Schweitzer (pierre@reactos.org)
8 *
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include "vfat.h"
14
15 #define NDEBUG
16 #include <debug.h>
17
18 /*
19 * Uncomment to enable strict verification of cluster/offset pair
20 * caching. If this option is enabled you lose all the benefits of
21 * the caching and the read/write operations will actually be
22 * slower. It's meant only for debugging!!!
23 * - Filip Navara, 26/07/2004
24 */
25 /* #define DEBUG_VERIFY_OFFSET_CACHING */
26
27 /* Arbitrary, taken from MS FastFAT, should be
28 * refined given what we experience in common
29 * out of stack operations
30 */
31 #define OVERFLOW_READ_THRESHHOLD 0xE00
32
33 /* FUNCTIONS *****************************************************************/
34
35 /*
36 * Return the next cluster in a FAT chain, possibly extending the chain if
37 * necessary
38 */
39 NTSTATUS
40 NextCluster(
41 PDEVICE_EXTENSION DeviceExt,
42 ULONG FirstCluster,
43 PULONG CurrentCluster,
44 BOOLEAN Extend)
45 {
46 if (FirstCluster == 1)
47 {
48 (*CurrentCluster) += DeviceExt->FatInfo.SectorsPerCluster;
49 return STATUS_SUCCESS;
50 }
51 else
52 {
53 if (Extend)
54 return GetNextClusterExtend(DeviceExt, (*CurrentCluster), CurrentCluster);
55 else
56 return GetNextCluster(DeviceExt, (*CurrentCluster), CurrentCluster);
57 }
58 }
59
60 NTSTATUS
61 OffsetToCluster(
62 PDEVICE_EXTENSION DeviceExt,
63 ULONG FirstCluster,
64 ULONG FileOffset,
65 PULONG Cluster,
66 BOOLEAN Extend)
67 {
68 ULONG CurrentCluster;
69 ULONG i;
70 NTSTATUS Status;
71 /*
72 DPRINT("OffsetToCluster(DeviceExt %x, Fcb %x, FirstCluster %x,"
73 " FileOffset %x, Cluster %x, Extend %d)\n", DeviceExt,
74 Fcb, FirstCluster, FileOffset, Cluster, Extend);
75 */
76 if (FirstCluster == 0)
77 {
78 DbgPrint("OffsetToCluster is called with FirstCluster = 0!\n");
79 ASSERT(FALSE);
80 }
81
82 if (FirstCluster == 1)
83 {
84 /* root of FAT16 or FAT12 */
85 *Cluster = DeviceExt->FatInfo.rootStart + FileOffset
86 / (DeviceExt->FatInfo.BytesPerCluster) * DeviceExt->FatInfo.SectorsPerCluster;
87 return STATUS_SUCCESS;
88 }
89 else
90 {
91 CurrentCluster = FirstCluster;
92 if (Extend)
93 {
94 for (i = 0; i < FileOffset / DeviceExt->FatInfo.BytesPerCluster; i++)
95 {
96 Status = GetNextClusterExtend (DeviceExt, CurrentCluster, &CurrentCluster);
97 if (!NT_SUCCESS(Status))
98 return Status;
99 }
100 *Cluster = CurrentCluster;
101 }
102 else
103 {
104 for (i = 0; i < FileOffset / DeviceExt->FatInfo.BytesPerCluster; i++)
105 {
106 Status = GetNextCluster (DeviceExt, CurrentCluster, &CurrentCluster);
107 if (!NT_SUCCESS(Status))
108 return Status;
109 }
110 *Cluster = CurrentCluster;
111 }
112 return STATUS_SUCCESS;
113 }
114 }
115
116 /*
117 * FUNCTION: Reads data from a file
118 */
119 static
120 NTSTATUS
121 VfatReadFileData(
122 PVFAT_IRP_CONTEXT IrpContext,
123 ULONG Length,
124 LARGE_INTEGER ReadOffset,
125 PULONG LengthRead)
126 {
127 ULONG CurrentCluster;
128 ULONG FirstCluster;
129 ULONG StartCluster;
130 ULONG ClusterCount;
131 LARGE_INTEGER StartOffset;
132 PDEVICE_EXTENSION DeviceExt;
133 BOOLEAN First = TRUE;
134 PVFATFCB Fcb;
135 NTSTATUS Status;
136 ULONG BytesDone;
137 ULONG BytesPerSector;
138 ULONG BytesPerCluster;
139 ULONG LastCluster;
140 ULONG LastOffset;
141
142 /* PRECONDITION */
143 ASSERT(IrpContext);
144 DeviceExt = IrpContext->DeviceExt;
145 ASSERT(DeviceExt);
146 ASSERT(DeviceExt->FatInfo.BytesPerCluster);
147 ASSERT(IrpContext->FileObject);
148 ASSERT(IrpContext->FileObject->FsContext2 != NULL);
149
150 DPRINT("VfatReadFileData(DeviceExt %p, FileObject %p, "
151 "Length %u, ReadOffset 0x%I64x)\n", DeviceExt,
152 IrpContext->FileObject, Length, ReadOffset.QuadPart);
153
154 *LengthRead = 0;
155
156 Fcb = IrpContext->FileObject->FsContext;
157 BytesPerSector = DeviceExt->FatInfo.BytesPerSector;
158 BytesPerCluster = DeviceExt->FatInfo.BytesPerCluster;
159
160 ASSERT(ReadOffset.QuadPart + Length <= ROUND_UP_64(Fcb->RFCB.FileSize.QuadPart, BytesPerSector));
161 ASSERT(ReadOffset.u.LowPart % BytesPerSector == 0);
162 ASSERT(Length % BytesPerSector == 0);
163
164 /* Is this a read of the FAT? */
165 if (BooleanFlagOn(Fcb->Flags, FCB_IS_FAT))
166 {
167 ReadOffset.QuadPart += DeviceExt->FatInfo.FATStart * BytesPerSector;
168 Status = VfatReadDiskPartial(IrpContext, &ReadOffset, Length, 0, TRUE);
169
170 if (NT_SUCCESS(Status))
171 {
172 *LengthRead = Length;
173 }
174 else
175 {
176 DPRINT1("FAT reading failed, Status %x\n", Status);
177 }
178 return Status;
179 }
180
181 /* Is this a read of the Volume ? */
182 if (BooleanFlagOn(Fcb->Flags, FCB_IS_VOLUME))
183 {
184 Status = VfatReadDiskPartial(IrpContext, &ReadOffset, Length, 0, TRUE);
185 if (NT_SUCCESS(Status))
186 {
187 *LengthRead = Length;
188 }
189 else
190 {
191 DPRINT1("Volume reading failed, Status %x\n", Status);
192 }
193 return Status;
194 }
195
196 /* Find the first cluster */
197 FirstCluster =
198 CurrentCluster = vfatDirEntryGetFirstCluster (DeviceExt, &Fcb->entry);
199
200 if (FirstCluster == 1)
201 {
202 /* Directory of FAT12/16 needs a special handling */
203 if (ReadOffset.u.LowPart + Length > DeviceExt->FatInfo.rootDirectorySectors * BytesPerSector)
204 {
205 Length = DeviceExt->FatInfo.rootDirectorySectors * BytesPerSector - ReadOffset.u.LowPart;
206 }
207 ReadOffset.u.LowPart += DeviceExt->FatInfo.rootStart * BytesPerSector;
208
209 /* Fire up the read command */
210 Status = VfatReadDiskPartial (IrpContext, &ReadOffset, Length, 0, TRUE);
211 if (NT_SUCCESS(Status))
212 {
213 *LengthRead = Length;
214 }
215 return Status;
216 }
217
218 ExAcquireFastMutex(&Fcb->LastMutex);
219 LastCluster = Fcb->LastCluster;
220 LastOffset = Fcb->LastOffset;
221 ExReleaseFastMutex(&Fcb->LastMutex);
222
223 /* Find the cluster to start the read from */
224 if (LastCluster > 0 && ReadOffset.u.LowPart >= LastOffset)
225 {
226 Status = OffsetToCluster(DeviceExt, LastCluster,
227 ROUND_DOWN(ReadOffset.u.LowPart, BytesPerCluster) -
228 LastOffset,
229 &CurrentCluster, FALSE);
230 #ifdef DEBUG_VERIFY_OFFSET_CACHING
231 /* DEBUG VERIFICATION */
232 {
233 ULONG CorrectCluster;
234 OffsetToCluster(DeviceExt, FirstCluster,
235 ROUND_DOWN(ReadOffset.u.LowPart, BytesPerCluster),
236 &CorrectCluster, FALSE);
237 if (CorrectCluster != CurrentCluster)
238 KeBugCheck(FAT_FILE_SYSTEM);
239 }
240 #endif
241 }
242 else
243 {
244 Status = OffsetToCluster(DeviceExt, FirstCluster,
245 ROUND_DOWN(ReadOffset.u.LowPart, BytesPerCluster),
246 &CurrentCluster, FALSE);
247 }
248
249 if (!NT_SUCCESS(Status))
250 {
251 return Status;
252 }
253
254 ExAcquireFastMutex(&Fcb->LastMutex);
255 Fcb->LastCluster = CurrentCluster;
256 Fcb->LastOffset = ROUND_DOWN (ReadOffset.u.LowPart, BytesPerCluster);
257 ExReleaseFastMutex(&Fcb->LastMutex);
258
259 KeInitializeEvent(&IrpContext->Event, NotificationEvent, FALSE);
260 IrpContext->RefCount = 1;
261
262 while (Length > 0 && CurrentCluster != 0xffffffff)
263 {
264 StartCluster = CurrentCluster;
265 StartOffset.QuadPart = ClusterToSector(DeviceExt, StartCluster) * BytesPerSector;
266 BytesDone = 0;
267 ClusterCount = 0;
268
269 do
270 {
271 ClusterCount++;
272 if (First)
273 {
274 BytesDone = min (Length, BytesPerCluster - (ReadOffset.u.LowPart % BytesPerCluster));
275 StartOffset.QuadPart += ReadOffset.u.LowPart % BytesPerCluster;
276 First = FALSE;
277 }
278 else
279 {
280 if (Length - BytesDone > BytesPerCluster)
281 {
282 BytesDone += BytesPerCluster;
283 }
284 else
285 {
286 BytesDone = Length;
287 }
288 }
289 Status = NextCluster(DeviceExt, FirstCluster, &CurrentCluster, FALSE);
290 }
291 while (StartCluster + ClusterCount == CurrentCluster && NT_SUCCESS(Status) && Length > BytesDone);
292 DPRINT("start %08x, next %08x, count %u\n",
293 StartCluster, CurrentCluster, ClusterCount);
294
295 ExAcquireFastMutex(&Fcb->LastMutex);
296 Fcb->LastCluster = StartCluster + (ClusterCount - 1);
297 Fcb->LastOffset = ROUND_DOWN(ReadOffset.u.LowPart, BytesPerCluster) + (ClusterCount - 1) * BytesPerCluster;
298 ExReleaseFastMutex(&Fcb->LastMutex);
299
300 /* Fire up the read command */
301 Status = VfatReadDiskPartial (IrpContext, &StartOffset, BytesDone, *LengthRead, FALSE);
302 if (!NT_SUCCESS(Status) && Status != STATUS_PENDING)
303 {
304 break;
305 }
306 *LengthRead += BytesDone;
307 Length -= BytesDone;
308 ReadOffset.u.LowPart += BytesDone;
309 }
310
311 if (InterlockedDecrement((PLONG)&IrpContext->RefCount) != 0)
312 {
313 KeWaitForSingleObject(&IrpContext->Event, Executive, KernelMode, FALSE, NULL);
314 }
315
316 if (NT_SUCCESS(Status) || Status == STATUS_PENDING)
317 {
318 if (Length > 0)
319 {
320 Status = STATUS_UNSUCCESSFUL;
321 }
322 else
323 {
324 Status = IrpContext->Irp->IoStatus.Status;
325 }
326 }
327
328 return Status;
329 }
330
331 static
332 NTSTATUS
333 VfatWriteFileData(
334 PVFAT_IRP_CONTEXT IrpContext,
335 ULONG Length,
336 LARGE_INTEGER WriteOffset)
337 {
338 PDEVICE_EXTENSION DeviceExt;
339 PVFATFCB Fcb;
340 ULONG Count;
341 ULONG FirstCluster;
342 ULONG CurrentCluster;
343 ULONG BytesDone;
344 ULONG StartCluster;
345 ULONG ClusterCount;
346 NTSTATUS Status = STATUS_SUCCESS;
347 BOOLEAN First = TRUE;
348 ULONG BytesPerSector;
349 ULONG BytesPerCluster;
350 LARGE_INTEGER StartOffset;
351 ULONG BufferOffset;
352 ULONG LastCluster;
353 ULONG LastOffset;
354
355 /* PRECONDITION */
356 ASSERT(IrpContext);
357 DeviceExt = IrpContext->DeviceExt;
358 ASSERT(DeviceExt);
359 ASSERT(DeviceExt->FatInfo.BytesPerCluster);
360 ASSERT(IrpContext->FileObject);
361 ASSERT(IrpContext->FileObject->FsContext2 != NULL);
362
363 Fcb = IrpContext->FileObject->FsContext;
364 BytesPerCluster = DeviceExt->FatInfo.BytesPerCluster;
365 BytesPerSector = DeviceExt->FatInfo.BytesPerSector;
366
367 DPRINT("VfatWriteFileData(DeviceExt %p, FileObject %p, "
368 "Length %u, WriteOffset 0x%I64x), '%wZ'\n", DeviceExt,
369 IrpContext->FileObject, Length, WriteOffset.QuadPart,
370 &Fcb->PathNameU);
371
372 ASSERT(WriteOffset.QuadPart + Length <= Fcb->RFCB.AllocationSize.QuadPart);
373 ASSERT(WriteOffset.u.LowPart % BytesPerSector == 0);
374 ASSERT(Length % BytesPerSector == 0);
375
376 /* Is this a write of the volume? */
377 if (BooleanFlagOn(Fcb->Flags, FCB_IS_VOLUME))
378 {
379 Status = VfatWriteDiskPartial(IrpContext, &WriteOffset, Length, 0, TRUE);
380 if (!NT_SUCCESS(Status))
381 {
382 DPRINT1("Volume writing failed, Status %x\n", Status);
383 }
384 return Status;
385 }
386
387 /* Is this a write to the FAT? */
388 if (BooleanFlagOn(Fcb->Flags, FCB_IS_FAT))
389 {
390 WriteOffset.u.LowPart += DeviceExt->FatInfo.FATStart * BytesPerSector;
391 IrpContext->RefCount = 1;
392 for (Count = 0; Count < DeviceExt->FatInfo.FATCount; Count++)
393 {
394 Status = VfatWriteDiskPartial(IrpContext, &WriteOffset, Length, 0, FALSE);
395 if (!NT_SUCCESS(Status) && Status != STATUS_PENDING)
396 {
397 DPRINT1("FAT writing failed, Status %x\n", Status);
398 break;
399 }
400 WriteOffset.u.LowPart += Fcb->RFCB.FileSize.u.LowPart;
401 }
402
403 if (InterlockedDecrement((PLONG)&IrpContext->RefCount) != 0)
404 {
405 KeWaitForSingleObject(&IrpContext->Event, Executive, KernelMode, FALSE, NULL);
406 }
407
408 if (NT_SUCCESS(Status) || Status == STATUS_PENDING)
409 {
410 Status = IrpContext->Irp->IoStatus.Status;
411 }
412 return Status;
413 }
414
415 /*
416 * Find the first cluster
417 */
418 FirstCluster =
419 CurrentCluster = vfatDirEntryGetFirstCluster (DeviceExt, &Fcb->entry);
420
421 if (FirstCluster == 1)
422 {
423 ASSERT(WriteOffset.u.LowPart + Length <= DeviceExt->FatInfo.rootDirectorySectors * BytesPerSector);
424 // Directory of FAT12/16 needs a special handling
425 WriteOffset.u.LowPart += DeviceExt->FatInfo.rootStart * BytesPerSector;
426 // Fire up the write command
427 Status = VfatWriteDiskPartial (IrpContext, &WriteOffset, Length, 0, TRUE);
428 return Status;
429 }
430
431 ExAcquireFastMutex(&Fcb->LastMutex);
432 LastCluster = Fcb->LastCluster;
433 LastOffset = Fcb->LastOffset;
434 ExReleaseFastMutex(&Fcb->LastMutex);
435
436 /*
437 * Find the cluster to start the write from
438 */
439 if (LastCluster > 0 && WriteOffset.u.LowPart >= LastOffset)
440 {
441 Status = OffsetToCluster(DeviceExt, LastCluster,
442 ROUND_DOWN(WriteOffset.u.LowPart, BytesPerCluster) -
443 LastOffset,
444 &CurrentCluster, FALSE);
445 #ifdef DEBUG_VERIFY_OFFSET_CACHING
446 /* DEBUG VERIFICATION */
447 {
448 ULONG CorrectCluster;
449 OffsetToCluster(DeviceExt, FirstCluster,
450 ROUND_DOWN(WriteOffset.u.LowPart, BytesPerCluster),
451 &CorrectCluster, FALSE);
452 if (CorrectCluster != CurrentCluster)
453 KeBugCheck(FAT_FILE_SYSTEM);
454 }
455 #endif
456 }
457 else
458 {
459 Status = OffsetToCluster(DeviceExt, FirstCluster,
460 ROUND_DOWN(WriteOffset.u.LowPart, BytesPerCluster),
461 &CurrentCluster, FALSE);
462 }
463
464 if (!NT_SUCCESS(Status))
465 {
466 return Status;
467 }
468
469 ExAcquireFastMutex(&Fcb->LastMutex);
470 Fcb->LastCluster = CurrentCluster;
471 Fcb->LastOffset = ROUND_DOWN (WriteOffset.u.LowPart, BytesPerCluster);
472 ExReleaseFastMutex(&Fcb->LastMutex);
473
474 IrpContext->RefCount = 1;
475 BufferOffset = 0;
476
477 while (Length > 0 && CurrentCluster != 0xffffffff)
478 {
479 StartCluster = CurrentCluster;
480 StartOffset.QuadPart = ClusterToSector(DeviceExt, StartCluster) * BytesPerSector;
481 BytesDone = 0;
482 ClusterCount = 0;
483
484 do
485 {
486 ClusterCount++;
487 if (First)
488 {
489 BytesDone = min (Length, BytesPerCluster - (WriteOffset.u.LowPart % BytesPerCluster));
490 StartOffset.QuadPart += WriteOffset.u.LowPart % BytesPerCluster;
491 First = FALSE;
492 }
493 else
494 {
495 if (Length - BytesDone > BytesPerCluster)
496 {
497 BytesDone += BytesPerCluster;
498 }
499 else
500 {
501 BytesDone = Length;
502 }
503 }
504 Status = NextCluster(DeviceExt, FirstCluster, &CurrentCluster, FALSE);
505 }
506 while (StartCluster + ClusterCount == CurrentCluster && NT_SUCCESS(Status) && Length > BytesDone);
507 DPRINT("start %08x, next %08x, count %u\n",
508 StartCluster, CurrentCluster, ClusterCount);
509
510 ExAcquireFastMutex(&Fcb->LastMutex);
511 Fcb->LastCluster = StartCluster + (ClusterCount - 1);
512 Fcb->LastOffset = ROUND_DOWN(WriteOffset.u.LowPart, BytesPerCluster) + (ClusterCount - 1) * BytesPerCluster;
513 ExReleaseFastMutex(&Fcb->LastMutex);
514
515 // Fire up the write command
516 Status = VfatWriteDiskPartial (IrpContext, &StartOffset, BytesDone, BufferOffset, FALSE);
517 if (!NT_SUCCESS(Status) && Status != STATUS_PENDING)
518 {
519 break;
520 }
521 BufferOffset += BytesDone;
522 Length -= BytesDone;
523 WriteOffset.u.LowPart += BytesDone;
524 }
525
526 if (InterlockedDecrement((PLONG)&IrpContext->RefCount) != 0)
527 {
528 KeWaitForSingleObject(&IrpContext->Event, Executive, KernelMode, FALSE, NULL);
529 }
530
531 if (NT_SUCCESS(Status) || Status == STATUS_PENDING)
532 {
533 if (Length > 0)
534 {
535 Status = STATUS_UNSUCCESSFUL;
536 }
537 else
538 {
539 Status = IrpContext->Irp->IoStatus.Status;
540 }
541 }
542 return Status;
543 }
544
545 NTSTATUS
546 VfatCommonRead(
547 PVFAT_IRP_CONTEXT IrpContext)
548 {
549 PVFATFCB Fcb;
550 PVOID Buffer;
551 NTSTATUS Status;
552 ULONG Length = 0;
553 ULONG BytesPerSector;
554 LARGE_INTEGER ByteOffset;
555 ULONG ReturnedLength = 0;
556 BOOLEAN PagingIo, CanWait, IsVolume, NoCache;
557
558 PagingIo = BooleanFlagOn(IrpContext->Irp->Flags, IRP_PAGING_IO);
559 CanWait = BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT);
560 NoCache = BooleanFlagOn(IrpContext->Irp->Flags, IRP_NOCACHE);
561 Fcb = IrpContext->FileObject->FsContext;
562 IsVolume = BooleanFlagOn(Fcb->Flags, FCB_IS_VOLUME);
563
564 ByteOffset = IrpContext->Stack->Parameters.Read.ByteOffset;
565 Length = IrpContext->Stack->Parameters.Read.Length;
566 BytesPerSector = IrpContext->DeviceExt->FatInfo.BytesPerSector;
567
568 if (!PagingIo &&
569 FsRtlAreThereCurrentFileLocks(&Fcb->FileLock))
570 {
571 if (!FsRtlCheckLockForReadAccess(&Fcb->FileLock, IrpContext->Irp))
572 {
573 return STATUS_FILE_LOCK_CONFLICT;
574 }
575 }
576
577 Buffer = VfatGetUserBuffer(IrpContext->Irp, PagingIo);
578
579 if (!PagingIo && !NoCache && !IsVolume)
580 {
581 // cached read
582 Status = STATUS_SUCCESS;
583 if (ByteOffset.u.LowPart + Length > Fcb->RFCB.FileSize.u.LowPart)
584 {
585 Length = Fcb->RFCB.FileSize.u.LowPart - ByteOffset.u.LowPart;
586 Status = /*STATUS_END_OF_FILE*/STATUS_SUCCESS;
587 }
588
589 vfatAddToStat(IrpContext->DeviceExt, Base.UserFileReads, 1);
590 vfatAddToStat(IrpContext->DeviceExt, Base.UserFileReadBytes, Length);
591
592 _SEH2_TRY
593 {
594 if (IrpContext->FileObject->PrivateCacheMap == NULL)
595 {
596 CcInitializeCacheMap(IrpContext->FileObject,
597 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
598 FALSE,
599 &(VfatGlobalData->CacheMgrCallbacks),
600 Fcb);
601 }
602
603 if (!CcCopyRead(IrpContext->FileObject,
604 &ByteOffset,
605 Length,
606 CanWait,
607 Buffer,
608 &IrpContext->Irp->IoStatus))
609 {
610 ASSERT(!CanWait);
611 Status = STATUS_PENDING;
612 goto ByeBye;
613 }
614 }
615 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
616 {
617 Status = _SEH2_GetExceptionCode();
618 goto ByeBye;
619 }
620 _SEH2_END;
621
622 if (!NT_SUCCESS(IrpContext->Irp->IoStatus.Status))
623 {
624 Status = IrpContext->Irp->IoStatus.Status;
625 }
626 }
627 else
628 {
629 // non cached read
630 Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoWriteAccess);
631 if (!NT_SUCCESS(Status))
632 {
633 goto ByeBye;
634 }
635
636 if (ByteOffset.QuadPart + Length > ROUND_UP_64(Fcb->RFCB.FileSize.QuadPart, BytesPerSector))
637 {
638 Length = (ULONG)(ROUND_UP_64(Fcb->RFCB.FileSize.QuadPart, BytesPerSector) - ByteOffset.QuadPart);
639 }
640
641 if (!IsVolume)
642 {
643 vfatAddToStat(IrpContext->DeviceExt, Fat.NonCachedReads, 1);
644 vfatAddToStat(IrpContext->DeviceExt, Fat.NonCachedReadBytes, Length);
645 }
646 else
647 {
648 vfatAddToStat(IrpContext->DeviceExt, Base.MetaDataReads, 1);
649 vfatAddToStat(IrpContext->DeviceExt, Base.MetaDataReadBytes, Length);
650 }
651
652 Status = VfatReadFileData(IrpContext, Length, ByteOffset, &ReturnedLength);
653 if (NT_SUCCESS(Status))
654 {
655 IrpContext->Irp->IoStatus.Information = ReturnedLength;
656 }
657 }
658
659 ByeBye:
660 return Status;
661 }
662
663 VOID
664 NTAPI
665 VfatStackOverflowRead(
666 PVOID Context,
667 IN PKEVENT Event)
668 {
669 PVFAT_IRP_CONTEXT IrpContext;
670
671 IrpContext = Context;
672 /* In a separate thread, we can wait and resources got locked */
673 SetFlag(IrpContext->Flags, IRPCONTEXT_CANWAIT);
674
675 /* Perform the read operation */
676 DPRINT1("Performing posted read\n");
677 VfatCommonRead(IrpContext);
678
679 KeSetEvent(Event, 0, FALSE);
680 }
681
682 VOID
683 VfatPostRead(
684 PVFAT_IRP_CONTEXT IrpContext,
685 PERESOURCE Lock,
686 BOOLEAN PagingIo)
687 {
688 KEVENT Event;
689
690 KeInitializeEvent(&Event, NotificationEvent, FALSE);
691
692 ExAcquireResourceSharedLite(Lock, TRUE);
693
694 /* If paging IO, call the non failing but blocking routine */
695 if (PagingIo)
696 {
697 FsRtlPostPagingFileStackOverflow(IrpContext, &Event, VfatStackOverflowRead);
698 }
699 else
700 {
701 FsRtlPostStackOverflow(IrpContext, &Event, VfatStackOverflowRead);
702 }
703
704 /* Wait till it's done */
705 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
706 }
707
708 NTSTATUS
709 VfatRead(
710 PVFAT_IRP_CONTEXT IrpContext)
711 {
712 NTSTATUS Status;
713 PVFATFCB Fcb;
714 ULONG Length = 0;
715 PERESOURCE Resource = NULL;
716 LARGE_INTEGER ByteOffset;
717 ULONG BytesPerSector;
718 BOOLEAN PagingIo, CanWait, IsVolume, NoCache;
719
720 ASSERT(IrpContext);
721
722 DPRINT("VfatRead(IrpContext %p)\n", IrpContext);
723
724 ASSERT(IrpContext->DeviceObject);
725
726 PagingIo = BooleanFlagOn(IrpContext->Irp->Flags, IRP_PAGING_IO);
727 CanWait = BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT);
728 NoCache = BooleanFlagOn(IrpContext->Irp->Flags, IRP_NOCACHE);
729
730 // This request is not allowed on the main device object
731 if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
732 {
733 DPRINT("VfatRead is called with the main device object.\n");
734 Status = STATUS_INVALID_DEVICE_REQUEST;
735 goto ByeBye;
736 }
737
738 ASSERT(IrpContext->DeviceExt);
739 ASSERT(IrpContext->FileObject);
740 Fcb = IrpContext->FileObject->FsContext;
741 ASSERT(Fcb);
742
743 IsVolume = BooleanFlagOn(Fcb->Flags, FCB_IS_VOLUME);
744
745 if (BooleanFlagOn(Fcb->Flags, FCB_IS_PAGE_FILE))
746 {
747 PFATINFO FatInfo = &IrpContext->DeviceExt->FatInfo;
748 IrpContext->Stack->Parameters.Read.ByteOffset.QuadPart += FatInfo->dataStart * FatInfo->BytesPerSector;
749 IoSkipCurrentIrpStackLocation(IrpContext->Irp);
750 IrpContext->Flags &= ~IRPCONTEXT_COMPLETE;
751 DPRINT("Read from page file, disk offset %I64x\n", IrpContext->Stack->Parameters.Read.ByteOffset.QuadPart);
752 Status = IoCallDriver(IrpContext->DeviceExt->StorageDevice, IrpContext->Irp);
753 return Status;
754 }
755
756 DPRINT("<%wZ>\n", &Fcb->PathNameU);
757
758 ByteOffset = IrpContext->Stack->Parameters.Read.ByteOffset;
759 Length = IrpContext->Stack->Parameters.Read.Length;
760 BytesPerSector = IrpContext->DeviceExt->FatInfo.BytesPerSector;
761
762 /* fail if file is a directory and no paged read */
763 if (vfatFCBIsDirectory(Fcb) && !PagingIo)
764 {
765 Status = STATUS_INVALID_PARAMETER;
766 goto ByeBye;
767 }
768
769 DPRINT("'%wZ', Offset: %u, Length %u\n", &Fcb->PathNameU, ByteOffset.u.LowPart, Length);
770
771 if (ByteOffset.u.HighPart && !IsVolume)
772 {
773 Status = STATUS_INVALID_PARAMETER;
774 goto ByeBye;
775 }
776
777 if (Length == 0)
778 {
779 IrpContext->Irp->IoStatus.Information = 0;
780 Status = STATUS_SUCCESS;
781 goto ByeBye;
782 }
783
784 if (ByteOffset.QuadPart >= Fcb->RFCB.FileSize.QuadPart)
785 {
786 IrpContext->Irp->IoStatus.Information = 0;
787 Status = STATUS_END_OF_FILE;
788 goto ByeBye;
789 }
790
791 if (NoCache || PagingIo || IsVolume)
792 {
793 if (ByteOffset.u.LowPart % BytesPerSector != 0 || Length % BytesPerSector != 0)
794 {
795 DPRINT("%u %u\n", ByteOffset.u.LowPart, Length);
796 // non cached read must be sector aligned
797 Status = STATUS_INVALID_PARAMETER;
798 goto ByeBye;
799 }
800 }
801
802 if (IsVolume)
803 {
804 Resource = &IrpContext->DeviceExt->DirResource;
805 }
806 else if (PagingIo)
807 {
808 Resource = &Fcb->PagingIoResource;
809 }
810 else
811 {
812 Resource = &Fcb->MainResource;
813 }
814
815 /* Are we out of stack for the rest of the operation? */
816 if (IoGetRemainingStackSize() < OVERFLOW_READ_THRESHHOLD)
817 {
818 /* Lock the buffer */
819 Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoWriteAccess);
820 if (!NT_SUCCESS(Status))
821 {
822 return Status;
823 }
824
825 /* And post the read to the overflow thread */
826 VfatPostRead(IrpContext, Resource, PagingIo);
827
828 /* Return the appropriate status */
829 return IrpContext->Irp->IoStatus.Status;
830 }
831
832 if (!ExAcquireResourceSharedLite(Resource, CanWait))
833 {
834 Resource = NULL;
835 Status = STATUS_PENDING;
836 goto ByeBye;
837 }
838
839 Status = VfatCommonRead(IrpContext);
840
841 ByeBye:
842 if (Resource)
843 {
844 ExReleaseResourceLite(Resource);
845 }
846
847 if (Status == STATUS_PENDING)
848 {
849 Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoWriteAccess);
850 if (NT_SUCCESS(Status))
851 {
852 Status = VfatMarkIrpContextForQueue(IrpContext);
853 }
854 }
855 else
856 {
857 IrpContext->Irp->IoStatus.Status = Status;
858 if (BooleanFlagOn(IrpContext->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
859 !PagingIo &&
860 (NT_SUCCESS(Status) || Status == STATUS_END_OF_FILE))
861 {
862 IrpContext->FileObject->CurrentByteOffset.QuadPart =
863 ByteOffset.QuadPart + IrpContext->Irp->IoStatus.Information;
864 }
865
866 if (NT_SUCCESS(Status))
867 IrpContext->PriorityBoost = IO_DISK_INCREMENT;
868 }
869 DPRINT("%x\n", Status);
870 return Status;
871 }
872
873 NTSTATUS
874 VfatWrite(
875 PVFAT_IRP_CONTEXT IrpContext)
876 {
877 PVFATFCB Fcb;
878 PERESOURCE Resource = NULL;
879 LARGE_INTEGER ByteOffset;
880 LARGE_INTEGER OldFileSize;
881 NTSTATUS Status = STATUS_SUCCESS;
882 ULONG Length = 0;
883 PVOID Buffer;
884 ULONG BytesPerSector;
885 BOOLEAN PagingIo, CanWait, IsVolume, IsFAT, NoCache;
886
887 ASSERT(IrpContext);
888
889 DPRINT("VfatWrite(IrpContext %p)\n", IrpContext);
890
891 ASSERT(IrpContext->DeviceObject);
892
893 PagingIo = BooleanFlagOn(IrpContext->Irp->Flags, IRP_PAGING_IO);
894 CanWait = BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT);
895 NoCache = BooleanFlagOn(IrpContext->Irp->Flags, IRP_NOCACHE);
896
897 // This request is not allowed on the main device object
898 if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
899 {
900 DPRINT("VfatWrite is called with the main device object.\n");
901 Status = STATUS_INVALID_DEVICE_REQUEST;
902 goto ByeBye;
903 }
904
905 ASSERT(IrpContext->DeviceExt);
906 ASSERT(IrpContext->FileObject);
907 Fcb = IrpContext->FileObject->FsContext;
908 ASSERT(Fcb);
909
910 IsVolume = BooleanFlagOn(Fcb->Flags, FCB_IS_VOLUME);
911 IsFAT = BooleanFlagOn(Fcb->Flags, FCB_IS_FAT);
912
913 if (BooleanFlagOn(Fcb->Flags, FCB_IS_PAGE_FILE))
914 {
915 PFATINFO FatInfo = &IrpContext->DeviceExt->FatInfo;
916 IrpContext->Stack->Parameters.Write.ByteOffset.QuadPart += FatInfo->dataStart * FatInfo->BytesPerSector;
917 IoSkipCurrentIrpStackLocation(IrpContext->Irp);
918 IrpContext->Flags &= ~IRPCONTEXT_COMPLETE;
919 DPRINT("Write to page file, disk offset %I64x\n", IrpContext->Stack->Parameters.Write.ByteOffset.QuadPart);
920 Status = IoCallDriver(IrpContext->DeviceExt->StorageDevice, IrpContext->Irp);
921 return Status;
922 }
923
924 DPRINT("<%wZ>\n", &Fcb->PathNameU);
925
926 /* fail if file is a directory and no paged read */
927 if (vfatFCBIsDirectory(Fcb) && !PagingIo)
928 {
929 Status = STATUS_INVALID_PARAMETER;
930 goto ByeBye;
931 }
932
933 ByteOffset = IrpContext->Stack->Parameters.Write.ByteOffset;
934 if (ByteOffset.u.LowPart == FILE_WRITE_TO_END_OF_FILE &&
935 ByteOffset.u.HighPart == -1)
936 {
937 ByteOffset.QuadPart = Fcb->RFCB.FileSize.QuadPart;
938 }
939 Length = IrpContext->Stack->Parameters.Write.Length;
940 BytesPerSector = IrpContext->DeviceExt->FatInfo.BytesPerSector;
941
942 if (ByteOffset.u.HighPart && !IsVolume)
943 {
944 Status = STATUS_INVALID_PARAMETER;
945 goto ByeBye;
946 }
947
948 if (IsFAT || IsVolume ||
949 vfatDirEntryGetFirstCluster(IrpContext->DeviceExt, &Fcb->entry) == 1)
950 {
951 if (ByteOffset.QuadPart + Length > Fcb->RFCB.FileSize.QuadPart)
952 {
953 // we can't extend the FAT, the volume or the root on FAT12/FAT16
954 Status = STATUS_END_OF_FILE;
955 goto ByeBye;
956 }
957 }
958
959 if (PagingIo || NoCache || IsVolume)
960 {
961 if (ByteOffset.u.LowPart % BytesPerSector != 0 || Length % BytesPerSector != 0)
962 {
963 // non cached write must be sector aligned
964 Status = STATUS_INVALID_PARAMETER;
965 goto ByeBye;
966 }
967 }
968
969 OldFileSize = Fcb->RFCB.FileSize;
970
971 if (Length == 0)
972 {
973 /* Update last write time */
974 IrpContext->Irp->IoStatus.Information = 0;
975 Status = STATUS_SUCCESS;
976 goto Metadata;
977 }
978
979 if (PagingIo)
980 {
981 if (ByteOffset.u.LowPart + Length > Fcb->RFCB.AllocationSize.u.LowPart)
982 {
983 Status = STATUS_INVALID_PARAMETER;
984 goto ByeBye;
985 }
986
987 if (ByteOffset.u.LowPart + Length > ROUND_UP(Fcb->RFCB.AllocationSize.u.LowPart, BytesPerSector))
988 {
989 Length = ROUND_UP(Fcb->RFCB.FileSize.u.LowPart, BytesPerSector) - ByteOffset.u.LowPart;
990 }
991 }
992
993 if (!NoCache && !CcCanIWrite(IrpContext->FileObject, Length, CanWait,
994 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_DEFERRED_WRITE)))
995 {
996 BOOLEAN Retrying;
997
998 Retrying = BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_DEFERRED_WRITE);
999 SetFlag(IrpContext->Flags, IRPCONTEXT_DEFERRED_WRITE);
1000
1001 Status = STATUS_PENDING;
1002 CcDeferWrite(IrpContext->FileObject, VfatHandleDeferredWrite,
1003 IrpContext, NULL, Length, Retrying);
1004
1005 DPRINT1("Dererring write!\n");
1006
1007 goto ByeBye;
1008 }
1009
1010 if (IsVolume)
1011 {
1012 Resource = &IrpContext->DeviceExt->DirResource;
1013 }
1014 else if (PagingIo)
1015 {
1016 Resource = &Fcb->PagingIoResource;
1017 }
1018 else
1019 {
1020 Resource = &Fcb->MainResource;
1021 }
1022
1023 if (PagingIo)
1024 {
1025 if (!ExAcquireResourceSharedLite(Resource, CanWait))
1026 {
1027 Resource = NULL;
1028 Status = STATUS_PENDING;
1029 goto ByeBye;
1030 }
1031 }
1032 else
1033 {
1034 if (!ExAcquireResourceExclusiveLite(Resource, CanWait))
1035 {
1036 Resource = NULL;
1037 Status = STATUS_PENDING;
1038 goto ByeBye;
1039 }
1040 }
1041
1042 if (!PagingIo &&
1043 FsRtlAreThereCurrentFileLocks(&Fcb->FileLock))
1044 {
1045 if (!FsRtlCheckLockForWriteAccess(&Fcb->FileLock, IrpContext->Irp))
1046 {
1047 Status = STATUS_FILE_LOCK_CONFLICT;
1048 goto ByeBye;
1049 }
1050 }
1051
1052 if (!CanWait && !IsVolume)
1053 {
1054 if (ByteOffset.u.LowPart + Length > Fcb->RFCB.AllocationSize.u.LowPart)
1055 {
1056 Status = STATUS_PENDING;
1057 goto ByeBye;
1058 }
1059 }
1060
1061 Buffer = VfatGetUserBuffer(IrpContext->Irp, PagingIo);
1062
1063 if (!IsFAT && !IsVolume && !PagingIo &&
1064 ByteOffset.u.LowPart + Length > Fcb->RFCB.FileSize.u.LowPart)
1065 {
1066 LARGE_INTEGER AllocationSize;
1067
1068 if (!ExAcquireResourceExclusiveLite(&IrpContext->DeviceExt->DirResource, CanWait))
1069 {
1070 goto ByeBye;
1071 }
1072
1073 AllocationSize.QuadPart = ByteOffset.u.LowPart + Length;
1074 Status = VfatSetAllocationSizeInformation(IrpContext->FileObject, Fcb,
1075 IrpContext->DeviceExt, &AllocationSize);
1076
1077 ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
1078
1079 if (!NT_SUCCESS (Status))
1080 {
1081 goto ByeBye;
1082 }
1083 }
1084
1085 if (!NoCache && !PagingIo && !IsVolume)
1086 {
1087 // cached write
1088
1089 vfatAddToStat(IrpContext->DeviceExt, Base.UserFileWrites, 1);
1090 vfatAddToStat(IrpContext->DeviceExt, Base.UserFileWriteBytes, Length);
1091
1092 _SEH2_TRY
1093 {
1094 if (IrpContext->FileObject->PrivateCacheMap == NULL)
1095 {
1096 CcInitializeCacheMap(IrpContext->FileObject,
1097 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
1098 FALSE,
1099 &VfatGlobalData->CacheMgrCallbacks,
1100 Fcb);
1101 }
1102
1103 if (ByteOffset.QuadPart > OldFileSize.QuadPart)
1104 {
1105 CcZeroData(IrpContext->FileObject, &OldFileSize, &ByteOffset, TRUE);
1106 }
1107
1108 if (CcCopyWrite(IrpContext->FileObject,
1109 &ByteOffset,
1110 Length,
1111 TRUE /*CanWait*/,
1112 Buffer))
1113 {
1114 IrpContext->Irp->IoStatus.Information = Length;
1115 Status = STATUS_SUCCESS;
1116 }
1117 else
1118 {
1119 ASSERT(FALSE /*!CanWait*/);
1120 Status = STATUS_UNSUCCESSFUL;
1121 }
1122 }
1123 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1124 {
1125 Status = _SEH2_GetExceptionCode();
1126 }
1127 _SEH2_END;
1128 }
1129 else
1130 {
1131 // non cached write
1132 Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoReadAccess);
1133 if (!NT_SUCCESS(Status))
1134 {
1135 Status = STATUS_INVALID_USER_BUFFER;
1136 goto ByeBye;
1137 }
1138
1139 if (ByteOffset.QuadPart > OldFileSize.QuadPart)
1140 {
1141 CcZeroData(IrpContext->FileObject, &OldFileSize, &ByteOffset, TRUE);
1142 }
1143
1144 if (!IsVolume)
1145 {
1146 vfatAddToStat(IrpContext->DeviceExt, Fat.NonCachedWrites, 1);
1147 vfatAddToStat(IrpContext->DeviceExt, Fat.NonCachedWriteBytes, Length);
1148 }
1149 else
1150 {
1151 vfatAddToStat(IrpContext->DeviceExt, Base.MetaDataWrites, 1);
1152 vfatAddToStat(IrpContext->DeviceExt, Base.MetaDataWriteBytes, Length);
1153 }
1154
1155 Status = VfatWriteFileData(IrpContext, Length, ByteOffset);
1156 if (NT_SUCCESS(Status))
1157 {
1158 IrpContext->Irp->IoStatus.Information = Length;
1159 }
1160 }
1161
1162 Metadata:
1163 if (!PagingIo && !IsFAT && !IsVolume)
1164 {
1165 if(!vfatFCBIsDirectory(Fcb))
1166 {
1167 LARGE_INTEGER SystemTime;
1168 ULONG Filter;
1169
1170 // set dates and times
1171 KeQuerySystemTime (&SystemTime);
1172 if (vfatVolumeIsFatX(IrpContext->DeviceExt))
1173 {
1174 FsdSystemTimeToDosDateTime(IrpContext->DeviceExt,
1175 &SystemTime, &Fcb->entry.FatX.UpdateDate,
1176 &Fcb->entry.FatX.UpdateTime);
1177 Fcb->entry.FatX.AccessDate = Fcb->entry.FatX.UpdateDate;
1178 Fcb->entry.FatX.AccessTime = Fcb->entry.FatX.UpdateTime;
1179 }
1180 else
1181 {
1182 FsdSystemTimeToDosDateTime(IrpContext->DeviceExt,
1183 &SystemTime, &Fcb->entry.Fat.UpdateDate,
1184 &Fcb->entry.Fat.UpdateTime);
1185 Fcb->entry.Fat.AccessDate = Fcb->entry.Fat.UpdateDate;
1186 }
1187 /* set date and times to dirty */
1188 Fcb->Flags |= FCB_IS_DIRTY;
1189
1190 /* Time to notify the OS */
1191 Filter = FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
1192 if (ByteOffset.QuadPart != OldFileSize.QuadPart) Filter |= FILE_NOTIFY_CHANGE_SIZE;
1193
1194 vfatReportChange(IrpContext->DeviceExt, Fcb, Filter, FILE_ACTION_MODIFIED);
1195 }
1196 }
1197
1198 ByeBye:
1199 if (Resource)
1200 {
1201 ExReleaseResourceLite(Resource);
1202 }
1203
1204 if (Status == STATUS_PENDING)
1205 {
1206 Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoReadAccess);
1207 if (NT_SUCCESS(Status))
1208 {
1209 Status = VfatMarkIrpContextForQueue(IrpContext);
1210 }
1211 }
1212 else
1213 {
1214 IrpContext->Irp->IoStatus.Status = Status;
1215 if (BooleanFlagOn(IrpContext->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
1216 !PagingIo && NT_SUCCESS(Status))
1217 {
1218 IrpContext->FileObject->CurrentByteOffset.QuadPart =
1219 ByteOffset.QuadPart + IrpContext->Irp->IoStatus.Information;
1220 }
1221
1222 if (NT_SUCCESS(Status))
1223 IrpContext->PriorityBoost = IO_DISK_INCREMENT;
1224 }
1225 DPRINT("%x\n", Status);
1226 return Status;
1227 }