[FASTFAT] Use the FastFAT mechanism for counting clusters already implemented
[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
543 return Status;
544 }
545
546 NTSTATUS
547 VfatCommonRead(
548 PVFAT_IRP_CONTEXT IrpContext)
549 {
550 PVFATFCB Fcb;
551 PVOID Buffer;
552 NTSTATUS Status;
553 ULONG Length = 0;
554 ULONG BytesPerSector;
555 LARGE_INTEGER ByteOffset;
556 ULONG ReturnedLength = 0;
557 BOOLEAN PagingIo, CanWait, IsVolume, NoCache;
558
559 PagingIo = BooleanFlagOn(IrpContext->Irp->Flags, IRP_PAGING_IO);
560 CanWait = BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT);
561 NoCache = BooleanFlagOn(IrpContext->Irp->Flags, IRP_NOCACHE);
562 Fcb = IrpContext->FileObject->FsContext;
563 IsVolume = BooleanFlagOn(Fcb->Flags, FCB_IS_VOLUME);
564
565 ByteOffset = IrpContext->Stack->Parameters.Read.ByteOffset;
566 Length = IrpContext->Stack->Parameters.Read.Length;
567 BytesPerSector = IrpContext->DeviceExt->FatInfo.BytesPerSector;
568
569 if (!PagingIo &&
570 FsRtlAreThereCurrentFileLocks(&Fcb->FileLock))
571 {
572 if (!FsRtlCheckLockForReadAccess(&Fcb->FileLock, IrpContext->Irp))
573 {
574 return STATUS_FILE_LOCK_CONFLICT;
575 }
576 }
577
578 Buffer = VfatGetUserBuffer(IrpContext->Irp, PagingIo);
579
580 if (!PagingIo && !NoCache && !IsVolume)
581 {
582 // cached read
583 Status = STATUS_SUCCESS;
584 if (ByteOffset.u.LowPart + Length > Fcb->RFCB.FileSize.u.LowPart)
585 {
586 Length = Fcb->RFCB.FileSize.u.LowPart - ByteOffset.u.LowPart;
587 Status = /*STATUS_END_OF_FILE*/STATUS_SUCCESS;
588 }
589
590 vfatAddToStat(IrpContext->DeviceExt, Base.UserFileReads, 1);
591 vfatAddToStat(IrpContext->DeviceExt, Base.UserFileReadBytes, Length);
592
593 _SEH2_TRY
594 {
595 if (IrpContext->FileObject->PrivateCacheMap == NULL)
596 {
597 CcInitializeCacheMap(IrpContext->FileObject,
598 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
599 FALSE,
600 &(VfatGlobalData->CacheMgrCallbacks),
601 Fcb);
602 }
603
604 if (!CcCopyRead(IrpContext->FileObject,
605 &ByteOffset,
606 Length,
607 CanWait,
608 Buffer,
609 &IrpContext->Irp->IoStatus))
610 {
611 ASSERT(!CanWait);
612 Status = STATUS_PENDING;
613 goto ByeBye;
614 }
615 }
616 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
617 {
618 Status = _SEH2_GetExceptionCode();
619 goto ByeBye;
620 }
621 _SEH2_END;
622
623 if (!NT_SUCCESS(IrpContext->Irp->IoStatus.Status))
624 {
625 Status = IrpContext->Irp->IoStatus.Status;
626 }
627 }
628 else
629 {
630 // non cached read
631 Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoWriteAccess);
632 if (!NT_SUCCESS(Status))
633 {
634 goto ByeBye;
635 }
636
637 if (ByteOffset.QuadPart + Length > ROUND_UP_64(Fcb->RFCB.FileSize.QuadPart, BytesPerSector))
638 {
639 Length = (ULONG)(ROUND_UP_64(Fcb->RFCB.FileSize.QuadPart, BytesPerSector) - ByteOffset.QuadPart);
640 }
641
642 if (!IsVolume)
643 {
644 vfatAddToStat(IrpContext->DeviceExt, Fat.NonCachedReads, 1);
645 vfatAddToStat(IrpContext->DeviceExt, Fat.NonCachedReadBytes, Length);
646 }
647 else
648 {
649 vfatAddToStat(IrpContext->DeviceExt, Base.MetaDataReads, 1);
650 vfatAddToStat(IrpContext->DeviceExt, Base.MetaDataReadBytes, Length);
651 }
652
653 Status = VfatReadFileData(IrpContext, Length, ByteOffset, &ReturnedLength);
654 if (NT_SUCCESS(Status))
655 {
656 IrpContext->Irp->IoStatus.Information = ReturnedLength;
657 }
658 }
659
660 ByeBye:
661 return Status;
662 }
663
664 VOID
665 NTAPI
666 VfatStackOverflowRead(
667 PVOID Context,
668 IN PKEVENT Event)
669 {
670 PVFAT_IRP_CONTEXT IrpContext;
671
672 IrpContext = Context;
673 /* In a separate thread, we can wait and resources got locked */
674 SetFlag(IrpContext->Flags, IRPCONTEXT_CANWAIT);
675
676 /* Perform the read operation */
677 DPRINT1("Performing posted read\n");
678 VfatCommonRead(IrpContext);
679
680 KeSetEvent(Event, 0, FALSE);
681 }
682
683 VOID
684 VfatPostRead(
685 PVFAT_IRP_CONTEXT IrpContext,
686 PERESOURCE Lock,
687 BOOLEAN PagingIo)
688 {
689 KEVENT Event;
690
691 KeInitializeEvent(&Event, NotificationEvent, FALSE);
692
693 ExAcquireResourceSharedLite(Lock, TRUE);
694
695 /* If paging IO, call the non failing but blocking routine */
696 if (PagingIo)
697 {
698 FsRtlPostPagingFileStackOverflow(IrpContext, &Event, VfatStackOverflowRead);
699 }
700 else
701 {
702 FsRtlPostStackOverflow(IrpContext, &Event, VfatStackOverflowRead);
703 }
704
705 /* Wait till it's done */
706 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
707 }
708
709 NTSTATUS
710 VfatRead(
711 PVFAT_IRP_CONTEXT IrpContext)
712 {
713 NTSTATUS Status;
714 PVFATFCB Fcb;
715 ULONG Length = 0;
716 PERESOURCE Resource = NULL;
717 LARGE_INTEGER ByteOffset;
718 ULONG BytesPerSector;
719 BOOLEAN PagingIo, CanWait, IsVolume, NoCache;
720
721 ASSERT(IrpContext);
722
723 DPRINT("VfatRead(IrpContext %p)\n", IrpContext);
724
725 ASSERT(IrpContext->DeviceObject);
726
727 PagingIo = BooleanFlagOn(IrpContext->Irp->Flags, IRP_PAGING_IO);
728 CanWait = BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT);
729 NoCache = BooleanFlagOn(IrpContext->Irp->Flags, IRP_NOCACHE);
730
731 // This request is not allowed on the main device object
732 if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
733 {
734 DPRINT("VfatRead is called with the main device object.\n");
735 Status = STATUS_INVALID_DEVICE_REQUEST;
736 goto ByeBye;
737 }
738
739 ASSERT(IrpContext->DeviceExt);
740 ASSERT(IrpContext->FileObject);
741 Fcb = IrpContext->FileObject->FsContext;
742 ASSERT(Fcb);
743
744 IsVolume = BooleanFlagOn(Fcb->Flags, FCB_IS_VOLUME);
745
746 if (BooleanFlagOn(Fcb->Flags, FCB_IS_PAGE_FILE))
747 {
748 PFATINFO FatInfo = &IrpContext->DeviceExt->FatInfo;
749 IrpContext->Stack->Parameters.Read.ByteOffset.QuadPart += FatInfo->dataStart * FatInfo->BytesPerSector;
750 IoSkipCurrentIrpStackLocation(IrpContext->Irp);
751 IrpContext->Flags &= ~IRPCONTEXT_COMPLETE;
752 DPRINT("Read from page file, disk offset %I64x\n", IrpContext->Stack->Parameters.Read.ByteOffset.QuadPart);
753 Status = IoCallDriver(IrpContext->DeviceExt->StorageDevice, IrpContext->Irp);
754 return Status;
755 }
756
757 DPRINT("<%wZ>\n", &Fcb->PathNameU);
758
759 ByteOffset = IrpContext->Stack->Parameters.Read.ByteOffset;
760 Length = IrpContext->Stack->Parameters.Read.Length;
761 BytesPerSector = IrpContext->DeviceExt->FatInfo.BytesPerSector;
762
763 /* fail if file is a directory and no paged read */
764 if (vfatFCBIsDirectory(Fcb) && !PagingIo)
765 {
766 Status = STATUS_INVALID_PARAMETER;
767 goto ByeBye;
768 }
769
770 DPRINT("'%wZ', Offset: %u, Length %u\n", &Fcb->PathNameU, ByteOffset.u.LowPart, Length);
771
772 if (ByteOffset.u.HighPart && !IsVolume)
773 {
774 Status = STATUS_INVALID_PARAMETER;
775 goto ByeBye;
776 }
777
778 if (Length == 0)
779 {
780 IrpContext->Irp->IoStatus.Information = 0;
781 Status = STATUS_SUCCESS;
782 goto ByeBye;
783 }
784
785 if (ByteOffset.QuadPart >= Fcb->RFCB.FileSize.QuadPart)
786 {
787 IrpContext->Irp->IoStatus.Information = 0;
788 Status = STATUS_END_OF_FILE;
789 goto ByeBye;
790 }
791
792 if (NoCache || PagingIo || IsVolume)
793 {
794 if (ByteOffset.u.LowPart % BytesPerSector != 0 || Length % BytesPerSector != 0)
795 {
796 DPRINT("%u %u\n", ByteOffset.u.LowPart, Length);
797 // non cached read must be sector aligned
798 Status = STATUS_INVALID_PARAMETER;
799 goto ByeBye;
800 }
801 }
802
803 if (IsVolume)
804 {
805 Resource = &IrpContext->DeviceExt->DirResource;
806 }
807 else if (PagingIo)
808 {
809 Resource = &Fcb->PagingIoResource;
810 }
811 else
812 {
813 Resource = &Fcb->MainResource;
814 }
815
816 /* Are we out of stack for the rest of the operation? */
817 if (IoGetRemainingStackSize() < OVERFLOW_READ_THRESHHOLD)
818 {
819 /* Lock the buffer */
820 Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoWriteAccess);
821 if (!NT_SUCCESS(Status))
822 {
823 return Status;
824 }
825
826 /* And post the read to the overflow thread */
827 VfatPostRead(IrpContext, Resource, PagingIo);
828
829 /* Return the appropriate status */
830 return IrpContext->Irp->IoStatus.Status;
831 }
832
833 if (!ExAcquireResourceSharedLite(Resource, CanWait))
834 {
835 Resource = NULL;
836 Status = STATUS_PENDING;
837 goto ByeBye;
838 }
839
840 Status = VfatCommonRead(IrpContext);
841
842 ByeBye:
843 if (Resource)
844 {
845 ExReleaseResourceLite(Resource);
846 }
847
848 if (Status == STATUS_PENDING)
849 {
850 Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoWriteAccess);
851 if (NT_SUCCESS(Status))
852 {
853 Status = VfatMarkIrpContextForQueue(IrpContext);
854 }
855 }
856 else
857 {
858 IrpContext->Irp->IoStatus.Status = Status;
859 if (BooleanFlagOn(IrpContext->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
860 !PagingIo &&
861 (NT_SUCCESS(Status) || Status == STATUS_END_OF_FILE))
862 {
863 IrpContext->FileObject->CurrentByteOffset.QuadPart =
864 ByteOffset.QuadPart + IrpContext->Irp->IoStatus.Information;
865 }
866
867 if (NT_SUCCESS(Status))
868 IrpContext->PriorityBoost = IO_DISK_INCREMENT;
869 }
870 DPRINT("%x\n", Status);
871 return Status;
872 }
873
874 NTSTATUS
875 VfatWrite(
876 PVFAT_IRP_CONTEXT IrpContext)
877 {
878 PVFATFCB Fcb;
879 PERESOURCE Resource = NULL;
880 LARGE_INTEGER ByteOffset;
881 LARGE_INTEGER OldFileSize;
882 NTSTATUS Status = STATUS_SUCCESS;
883 ULONG Length = 0;
884 PVOID Buffer;
885 ULONG BytesPerSector;
886 BOOLEAN PagingIo, CanWait, IsVolume, IsFAT, NoCache;
887
888 ASSERT(IrpContext);
889
890 DPRINT("VfatWrite(IrpContext %p)\n", IrpContext);
891
892 ASSERT(IrpContext->DeviceObject);
893
894 PagingIo = BooleanFlagOn(IrpContext->Irp->Flags, IRP_PAGING_IO);
895 CanWait = BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT);
896 NoCache = BooleanFlagOn(IrpContext->Irp->Flags, IRP_NOCACHE);
897
898 // This request is not allowed on the main device object
899 if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
900 {
901 DPRINT("VfatWrite is called with the main device object.\n");
902 Status = STATUS_INVALID_DEVICE_REQUEST;
903 goto ByeBye;
904 }
905
906 ASSERT(IrpContext->DeviceExt);
907 ASSERT(IrpContext->FileObject);
908 Fcb = IrpContext->FileObject->FsContext;
909 ASSERT(Fcb);
910
911 IsVolume = BooleanFlagOn(Fcb->Flags, FCB_IS_VOLUME);
912 IsFAT = BooleanFlagOn(Fcb->Flags, FCB_IS_FAT);
913
914 if (BooleanFlagOn(Fcb->Flags, FCB_IS_PAGE_FILE))
915 {
916 PFATINFO FatInfo = &IrpContext->DeviceExt->FatInfo;
917 IrpContext->Stack->Parameters.Write.ByteOffset.QuadPart += FatInfo->dataStart * FatInfo->BytesPerSector;
918 IoSkipCurrentIrpStackLocation(IrpContext->Irp);
919 IrpContext->Flags &= ~IRPCONTEXT_COMPLETE;
920 DPRINT("Write to page file, disk offset %I64x\n", IrpContext->Stack->Parameters.Write.ByteOffset.QuadPart);
921 Status = IoCallDriver(IrpContext->DeviceExt->StorageDevice, IrpContext->Irp);
922 return Status;
923 }
924
925 DPRINT("<%wZ>\n", &Fcb->PathNameU);
926
927 /* fail if file is a directory and no paged read */
928 if (vfatFCBIsDirectory(Fcb) && !PagingIo)
929 {
930 Status = STATUS_INVALID_PARAMETER;
931 goto ByeBye;
932 }
933
934 ByteOffset = IrpContext->Stack->Parameters.Write.ByteOffset;
935 if (ByteOffset.u.LowPart == FILE_WRITE_TO_END_OF_FILE &&
936 ByteOffset.u.HighPart == -1)
937 {
938 ByteOffset.QuadPart = Fcb->RFCB.FileSize.QuadPart;
939 }
940 Length = IrpContext->Stack->Parameters.Write.Length;
941 BytesPerSector = IrpContext->DeviceExt->FatInfo.BytesPerSector;
942
943 if (ByteOffset.u.HighPart && !IsVolume)
944 {
945 Status = STATUS_INVALID_PARAMETER;
946 goto ByeBye;
947 }
948
949 if (IsFAT || IsVolume ||
950 vfatDirEntryGetFirstCluster(IrpContext->DeviceExt, &Fcb->entry) == 1)
951 {
952 if (ByteOffset.QuadPart + Length > Fcb->RFCB.FileSize.QuadPart)
953 {
954 // we can't extend the FAT, the volume or the root on FAT12/FAT16
955 Status = STATUS_END_OF_FILE;
956 goto ByeBye;
957 }
958 }
959
960 if (PagingIo || NoCache || IsVolume)
961 {
962 if (ByteOffset.u.LowPart % BytesPerSector != 0 || Length % BytesPerSector != 0)
963 {
964 // non cached write must be sector aligned
965 Status = STATUS_INVALID_PARAMETER;
966 goto ByeBye;
967 }
968 }
969
970 OldFileSize = Fcb->RFCB.FileSize;
971
972 if (Length == 0)
973 {
974 /* Update last write time */
975 IrpContext->Irp->IoStatus.Information = 0;
976 Status = STATUS_SUCCESS;
977 goto Metadata;
978 }
979
980 if (PagingIo)
981 {
982 if (ByteOffset.u.LowPart + Length > Fcb->RFCB.AllocationSize.u.LowPart)
983 {
984 Status = STATUS_INVALID_PARAMETER;
985 goto ByeBye;
986 }
987
988 if (ByteOffset.u.LowPart + Length > ROUND_UP(Fcb->RFCB.AllocationSize.u.LowPart, BytesPerSector))
989 {
990 Length = ROUND_UP(Fcb->RFCB.FileSize.u.LowPart, BytesPerSector) - ByteOffset.u.LowPart;
991 }
992 }
993
994 if (!NoCache && !CcCanIWrite(IrpContext->FileObject, Length, CanWait,
995 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_DEFERRED_WRITE)))
996 {
997 BOOLEAN Retrying;
998
999 Retrying = BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_DEFERRED_WRITE);
1000 SetFlag(IrpContext->Flags, IRPCONTEXT_DEFERRED_WRITE);
1001
1002 Status = STATUS_PENDING;
1003 CcDeferWrite(IrpContext->FileObject, VfatHandleDeferredWrite,
1004 IrpContext, NULL, Length, Retrying);
1005
1006 DPRINT1("Dererring write!\n");
1007
1008 goto ByeBye;
1009 }
1010
1011 if (IsVolume)
1012 {
1013 Resource = &IrpContext->DeviceExt->DirResource;
1014 }
1015 else if (PagingIo)
1016 {
1017 Resource = &Fcb->PagingIoResource;
1018 }
1019 else
1020 {
1021 Resource = &Fcb->MainResource;
1022 }
1023
1024 if (PagingIo)
1025 {
1026 if (!ExAcquireResourceSharedLite(Resource, CanWait))
1027 {
1028 Resource = NULL;
1029 Status = STATUS_PENDING;
1030 goto ByeBye;
1031 }
1032 }
1033 else
1034 {
1035 if (!ExAcquireResourceExclusiveLite(Resource, CanWait))
1036 {
1037 Resource = NULL;
1038 Status = STATUS_PENDING;
1039 goto ByeBye;
1040 }
1041 }
1042
1043 if (!PagingIo &&
1044 FsRtlAreThereCurrentFileLocks(&Fcb->FileLock))
1045 {
1046 if (!FsRtlCheckLockForWriteAccess(&Fcb->FileLock, IrpContext->Irp))
1047 {
1048 Status = STATUS_FILE_LOCK_CONFLICT;
1049 goto ByeBye;
1050 }
1051 }
1052
1053 if (!CanWait && !IsVolume)
1054 {
1055 if (ByteOffset.u.LowPart + Length > Fcb->RFCB.AllocationSize.u.LowPart)
1056 {
1057 Status = STATUS_PENDING;
1058 goto ByeBye;
1059 }
1060 }
1061
1062 Buffer = VfatGetUserBuffer(IrpContext->Irp, PagingIo);
1063
1064 if (!IsFAT && !IsVolume && !PagingIo &&
1065 ByteOffset.u.LowPart + Length > Fcb->RFCB.FileSize.u.LowPart)
1066 {
1067 LARGE_INTEGER AllocationSize;
1068
1069 if (!ExAcquireResourceExclusiveLite(&IrpContext->DeviceExt->DirResource, CanWait))
1070 {
1071 Status = STATUS_PENDING;
1072 goto ByeBye;
1073 }
1074
1075 AllocationSize.QuadPart = ByteOffset.u.LowPart + Length;
1076 Status = VfatSetAllocationSizeInformation(IrpContext->FileObject, Fcb,
1077 IrpContext->DeviceExt, &AllocationSize);
1078
1079 ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
1080
1081 if (!NT_SUCCESS (Status))
1082 {
1083 goto ByeBye;
1084 }
1085 }
1086
1087 if (!NoCache && !PagingIo && !IsVolume)
1088 {
1089 // cached write
1090
1091 vfatAddToStat(IrpContext->DeviceExt, Base.UserFileWrites, 1);
1092 vfatAddToStat(IrpContext->DeviceExt, Base.UserFileWriteBytes, Length);
1093
1094 _SEH2_TRY
1095 {
1096 if (IrpContext->FileObject->PrivateCacheMap == NULL)
1097 {
1098 CcInitializeCacheMap(IrpContext->FileObject,
1099 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
1100 FALSE,
1101 &VfatGlobalData->CacheMgrCallbacks,
1102 Fcb);
1103 }
1104
1105 if (ByteOffset.QuadPart > OldFileSize.QuadPart)
1106 {
1107 CcZeroData(IrpContext->FileObject, &OldFileSize, &ByteOffset, TRUE);
1108 }
1109
1110 if (CcCopyWrite(IrpContext->FileObject,
1111 &ByteOffset,
1112 Length,
1113 TRUE /*CanWait*/,
1114 Buffer))
1115 {
1116 IrpContext->Irp->IoStatus.Information = Length;
1117 Status = STATUS_SUCCESS;
1118 }
1119 else
1120 {
1121 ASSERT(FALSE /*!CanWait*/);
1122 Status = STATUS_UNSUCCESSFUL;
1123 }
1124 }
1125 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1126 {
1127 Status = _SEH2_GetExceptionCode();
1128 }
1129 _SEH2_END;
1130 }
1131 else
1132 {
1133 // non cached write
1134 Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoReadAccess);
1135 if (!NT_SUCCESS(Status))
1136 {
1137 Status = STATUS_INVALID_USER_BUFFER;
1138 goto ByeBye;
1139 }
1140
1141 if (ByteOffset.QuadPart > OldFileSize.QuadPart)
1142 {
1143 CcZeroData(IrpContext->FileObject, &OldFileSize, &ByteOffset, TRUE);
1144 }
1145
1146 if (!IsVolume)
1147 {
1148 vfatAddToStat(IrpContext->DeviceExt, Fat.NonCachedWrites, 1);
1149 vfatAddToStat(IrpContext->DeviceExt, Fat.NonCachedWriteBytes, Length);
1150 }
1151 else
1152 {
1153 vfatAddToStat(IrpContext->DeviceExt, Base.MetaDataWrites, 1);
1154 vfatAddToStat(IrpContext->DeviceExt, Base.MetaDataWriteBytes, Length);
1155 }
1156
1157 Status = VfatWriteFileData(IrpContext, Length, ByteOffset);
1158 if (NT_SUCCESS(Status))
1159 {
1160 IrpContext->Irp->IoStatus.Information = Length;
1161 }
1162 }
1163
1164 Metadata:
1165 if (!PagingIo && !IsFAT && !IsVolume)
1166 {
1167 if(!vfatFCBIsDirectory(Fcb))
1168 {
1169 LARGE_INTEGER SystemTime;
1170 ULONG Filter;
1171
1172 // set dates and times
1173 KeQuerySystemTime (&SystemTime);
1174 if (vfatVolumeIsFatX(IrpContext->DeviceExt))
1175 {
1176 FsdSystemTimeToDosDateTime(IrpContext->DeviceExt,
1177 &SystemTime, &Fcb->entry.FatX.UpdateDate,
1178 &Fcb->entry.FatX.UpdateTime);
1179 Fcb->entry.FatX.AccessDate = Fcb->entry.FatX.UpdateDate;
1180 Fcb->entry.FatX.AccessTime = Fcb->entry.FatX.UpdateTime;
1181 }
1182 else
1183 {
1184 FsdSystemTimeToDosDateTime(IrpContext->DeviceExt,
1185 &SystemTime, &Fcb->entry.Fat.UpdateDate,
1186 &Fcb->entry.Fat.UpdateTime);
1187 Fcb->entry.Fat.AccessDate = Fcb->entry.Fat.UpdateDate;
1188 }
1189 /* set date and times to dirty */
1190 Fcb->Flags |= FCB_IS_DIRTY;
1191
1192 /* Time to notify the OS */
1193 Filter = FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
1194 if (ByteOffset.QuadPart != OldFileSize.QuadPart) Filter |= FILE_NOTIFY_CHANGE_SIZE;
1195
1196 vfatReportChange(IrpContext->DeviceExt, Fcb, Filter, FILE_ACTION_MODIFIED);
1197 }
1198 }
1199
1200 ByeBye:
1201 if (Resource)
1202 {
1203 ExReleaseResourceLite(Resource);
1204 }
1205
1206 if (Status == STATUS_PENDING)
1207 {
1208 Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoReadAccess);
1209 if (NT_SUCCESS(Status))
1210 {
1211 Status = VfatMarkIrpContextForQueue(IrpContext);
1212 }
1213 }
1214 else
1215 {
1216 IrpContext->Irp->IoStatus.Status = Status;
1217 if (BooleanFlagOn(IrpContext->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
1218 !PagingIo && NT_SUCCESS(Status))
1219 {
1220 IrpContext->FileObject->CurrentByteOffset.QuadPart =
1221 ByteOffset.QuadPart + IrpContext->Irp->IoStatus.Information;
1222 }
1223
1224 if (NT_SUCCESS(Status))
1225 IrpContext->PriorityBoost = IO_DISK_INCREMENT;
1226 }
1227 DPRINT("%x\n", Status);
1228 return Status;
1229 }