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