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