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