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