Enabled caching for FAT32 partitions.
[reactos.git] / reactos / drivers / fs / vfat / rw.c
1
2 /* $Id: rw.c,v 1.25 2001/07/20 08:00:20 ekohl Exp $
3 *
4 * COPYRIGHT: See COPYING in the top level directory
5 * PROJECT: ReactOS kernel
6 * FILE: services/fs/vfat/rw.c
7 * PURPOSE: VFAT Filesystem
8 * PROGRAMMER: Jason Filby (jasonfilby@yahoo.com)
9 *
10 */
11
12 /* INCLUDES *****************************************************************/
13
14 #include <ddk/ntddk.h>
15 #include <wchar.h>
16 #include <ntos/minmax.h>
17
18 #define NDEBUG
19 #include <debug.h>
20
21 #include "vfat.h"
22
23 /* GLOBALS *******************************************************************/
24
25 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
26 #define ROUND_DOWN(N, S) ((N) - ((N) % (S)))
27
28 /* FUNCTIONS *****************************************************************/
29
30 NTSTATUS
31 NextCluster(PDEVICE_EXTENSION DeviceExt,
32 ULONG FirstCluster,
33 PULONG CurrentCluster,
34 BOOLEAN Extend)
35 /*
36 * Return the next cluster in a FAT chain, possibly extending the chain if
37 * necessary
38 */
39 {
40 if (FirstCluster == 1)
41 {
42 (*CurrentCluster) += DeviceExt->Boot->SectorsPerCluster;
43 return(STATUS_SUCCESS);
44 }
45 else
46 /* CN: FIXME: Real bug here or in dirwr, where CurrentCluster isn't initialized when 0*/
47 if (FirstCluster == 0)
48 {
49 NTSTATUS Status;
50
51 Status = GetNextCluster(DeviceExt, 0, CurrentCluster,
52 Extend);
53 return(Status);
54 }
55 else
56 {
57 NTSTATUS Status;
58
59 Status = GetNextCluster(DeviceExt, (*CurrentCluster), CurrentCluster,
60 Extend);
61 return(Status);
62 }
63 }
64
65 NTSTATUS
66 OffsetToCluster(PDEVICE_EXTENSION DeviceExt,
67 ULONG FirstCluster,
68 ULONG FileOffset,
69 PULONG Cluster,
70 BOOLEAN Extend)
71 /*
72 * Return the cluster corresponding to an offset within a file,
73 * possibly extending the file if necessary
74 */
75 {
76 ULONG CurrentCluster;
77 ULONG i;
78 NTSTATUS Status;
79
80 if (FirstCluster == 1)
81 {
82 /* root of FAT16 or FAT12 */
83 *Cluster = DeviceExt->rootStart + FileOffset
84 / (DeviceExt->BytesPerCluster) * DeviceExt->Boot->SectorsPerCluster;
85 return(STATUS_SUCCESS);
86 }
87 else
88 {
89 CurrentCluster = FirstCluster;
90 for (i = 0; i < FileOffset / DeviceExt->BytesPerCluster; i++)
91 {
92 Status = GetNextCluster (DeviceExt, CurrentCluster, &CurrentCluster,
93 Extend);
94 if (!NT_SUCCESS(Status))
95 {
96 return(Status);
97 }
98 }
99 *Cluster = CurrentCluster;
100 return(STATUS_SUCCESS);
101 }
102 }
103
104 NTSTATUS
105 VfatReadBigCluster(PDEVICE_EXTENSION DeviceExt,
106 PVFATFCB Fcb,
107 ULONG StartOffset,
108 ULONG FirstCluster,
109 PULONG CurrentCluster,
110 PVOID Destination,
111 ULONG NoCache,
112 ULONG InternalOffset,
113 ULONG InternalLength)
114 {
115 BOOLEAN Valid;
116 PVOID BaseAddress = NULL;
117 PCACHE_SEGMENT CacheSeg = NULL;
118 NTSTATUS Status;
119
120 /*
121 * In this case the size of a cache segment is the same as a cluster
122 */
123
124 if (!NoCache)
125 {
126 Status = CcRosRequestCacheSegment(Fcb->RFCB.Bcb,
127 StartOffset,
128 &BaseAddress,
129 &Valid,
130 &CacheSeg);
131 if (!NT_SUCCESS(Status))
132 {
133 return(Status);
134 }
135 }
136 else
137 {
138 Valid = FALSE;
139 if (InternalOffset == 0)
140 {
141 BaseAddress = Destination;
142 }
143 else
144 {
145 BaseAddress = ExAllocatePool(NonPagedPool,
146 DeviceExt->BytesPerCluster);
147 if (BaseAddress == NULL)
148 {
149 return(STATUS_NO_MEMORY);
150 }
151 }
152 }
153
154 if (!Valid)
155 {
156 /*
157 * If necessary read the cluster from the disk
158 */
159 Status = VfatRawReadCluster(DeviceExt, FirstCluster, BaseAddress,
160 *CurrentCluster);
161 if (!NT_SUCCESS(Status))
162 {
163 if (!NoCache)
164 {
165 CcRosReleaseCacheSegment(Fcb->RFCB.Bcb, CacheSeg, FALSE);
166 }
167 else if (InternalOffset != 0)
168 {
169 ExFreePool(BaseAddress);
170 }
171 return(Status);
172 }
173 }
174 /*
175 * Copy the data from the cache to the caller
176 */
177 if (InternalOffset != 0 || !NoCache)
178 {
179 memcpy(Destination, BaseAddress + InternalOffset, InternalLength);
180 }
181 if (!NoCache)
182 {
183 CcRosReleaseCacheSegment(Fcb->RFCB.Bcb, CacheSeg, TRUE);
184 }
185 else if (InternalOffset != 0)
186 {
187 ExFreePool(BaseAddress);
188 }
189
190 Status = NextCluster(DeviceExt, FirstCluster, CurrentCluster, FALSE);
191 if (!NT_SUCCESS(Status))
192 {
193 return(Status);
194 }
195 return(STATUS_SUCCESS);
196 }
197
198 NTSTATUS
199 VfatReadSmallCluster(PDEVICE_EXTENSION DeviceExt,
200 PVFATFCB Fcb,
201 ULONG StartOffset,
202 ULONG FirstCluster,
203 PULONG CurrentCluster,
204 PVOID Destination,
205 ULONG NoCache,
206 ULONG InternalOffset,
207 ULONG InternalLength)
208 {
209 BOOLEAN Valid;
210 PVOID BaseAddress = NULL;
211 PCACHE_SEGMENT CacheSeg = NULL;
212 NTSTATUS Status;
213 ULONG i;
214
215 /*
216 * Otherwise we read a page of clusters together
217 */
218 if (!NoCache)
219 {
220 Status = CcRosRequestCacheSegment(Fcb->RFCB.Bcb,
221 StartOffset,
222 &BaseAddress,
223 &Valid,
224 &CacheSeg);
225 if (!NT_SUCCESS(Status))
226 {
227 return(Status);
228 }
229 }
230 else
231 {
232 Valid = FALSE;
233 if (InternalOffset == 0)
234 {
235 BaseAddress = Destination;
236 }
237 else
238 {
239 BaseAddress = ExAllocatePool(NonPagedPool, PAGESIZE);
240 if (BaseAddress == NULL)
241 {
242 return(STATUS_NO_MEMORY);
243 }
244 }
245 }
246
247 /*
248 * If necessary read all the data for the page, unfortunately the
249 * file length may not be page aligned in which case the page will
250 * only be partially filled.
251 * FIXME: So zero fill the rest?
252 */
253 if (!Valid)
254 {
255 for (i = 0; i < (PAGESIZE / DeviceExt->BytesPerCluster); i++)
256 {
257 Status = VfatRawReadCluster(DeviceExt,
258 FirstCluster,
259 BaseAddress +
260 (i * DeviceExt->BytesPerCluster),
261 *CurrentCluster);
262 if (!NT_SUCCESS(Status))
263 {
264 if (!NoCache)
265 {
266 CcRosReleaseCacheSegment(Fcb->RFCB.Bcb, CacheSeg, FALSE);
267 }
268 else if (InternalOffset != 0)
269 {
270 ExFreePool(BaseAddress);
271 }
272 return(Status);
273 }
274 Status = NextCluster(DeviceExt, FirstCluster, CurrentCluster, FALSE);
275 if ((*CurrentCluster) == 0xFFFFFFFF)
276 {
277 break;
278 }
279 }
280 }
281 else
282 {
283 /*
284 * Otherwise just move onto the next cluster
285 */
286 for (i = 0; i < (PAGESIZE / DeviceExt->BytesPerCluster); i++)
287 {
288 NextCluster(DeviceExt, FirstCluster, CurrentCluster, FALSE);
289 if ((*CurrentCluster) == 0xFFFFFFFF)
290 {
291 break;
292 }
293 }
294 }
295 /*
296 * Copy the data from the cache to the caller
297 */
298 if (InternalOffset != 0 || !NoCache)
299 {
300 memcpy(Destination, BaseAddress + InternalOffset, InternalLength);
301 }
302 if (!NoCache)
303 {
304 CcRosReleaseCacheSegment(Fcb->RFCB.Bcb, CacheSeg, TRUE);
305 }
306 else if (InternalOffset != 0)
307 {
308 ExFreePool(BaseAddress);
309 }
310 return(STATUS_SUCCESS);
311 }
312
313 NTSTATUS
314 VfatReadCluster(PDEVICE_EXTENSION DeviceExt,
315 PVFATFCB Fcb,
316 ULONG StartOffset,
317 ULONG FirstCluster,
318 PULONG CurrentCluster,
319 PVOID Destination,
320 ULONG NoCache,
321 ULONG InternalOffset,
322 ULONG InternalLength)
323 {
324 if (DeviceExt->BytesPerCluster >= PAGESIZE)
325 {
326 return(VfatReadBigCluster(DeviceExt,
327 Fcb,
328 StartOffset,
329 FirstCluster,
330 CurrentCluster,
331 Destination,
332 NoCache,
333 InternalOffset,
334 InternalLength));
335 }
336 else
337 {
338 return(VfatReadSmallCluster(DeviceExt,
339 Fcb,
340 StartOffset,
341 FirstCluster,
342 CurrentCluster,
343 Destination,
344 NoCache,
345 InternalOffset,
346 InternalLength));
347 }
348 return(STATUS_SUCCESS);
349 }
350
351 NTSTATUS
352 VfatReadFile (PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
353 PVOID Buffer, ULONG Length, ULONG ReadOffset,
354 PULONG LengthRead, ULONG NoCache)
355 /*
356 * FUNCTION: Reads data from a file
357 */
358 {
359 ULONG CurrentCluster;
360 ULONG FirstCluster;
361 PVFATFCB Fcb;
362 ULONG ChunkSize;
363 NTSTATUS Status;
364 ULONG TempLength;
365
366 /* PRECONDITION */
367 assert (DeviceExt != NULL);
368 assert (DeviceExt->BytesPerCluster != 0);
369 assert (FileObject != NULL);
370 assert (FileObject->FsContext != NULL);
371
372 DPRINT("VfatReadFile(DeviceExt %x, FileObject %x, Buffer %x, "
373 "Length %d, ReadOffset 0x%x)\n", DeviceExt, FileObject, Buffer,
374 Length, ReadOffset);
375
376 *LengthRead = 0;
377
378 Fcb = ((PVFATCCB)FileObject->FsContext2)->pFcb;
379
380 /*
381 * Find the first cluster
382 */
383 if (DeviceExt->FatType == FAT32)
384 CurrentCluster = Fcb->entry.FirstCluster
385 + Fcb->entry.FirstClusterHigh * 65536;
386 else
387 CurrentCluster = Fcb->entry.FirstCluster;
388 FirstCluster = CurrentCluster;
389 DPRINT( "FirstCluster = %x\n", FirstCluster );
390 /*
391 * Truncate the read if necessary
392 */
393 if (!(Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY))
394 {
395 if (ReadOffset >= Fcb->entry.FileSize)
396 {
397 return (STATUS_END_OF_FILE);
398 }
399 if ((ReadOffset + Length) > Fcb->entry.FileSize)
400 {
401 Length = Fcb->entry.FileSize - ReadOffset;
402 }
403 }
404
405 /*
406 * Select an appropiate size for reads
407 */
408 if (DeviceExt->BytesPerCluster >= PAGESIZE)
409 {
410 ChunkSize = DeviceExt->BytesPerCluster;
411 }
412 else
413 {
414 ChunkSize = PAGESIZE;
415 }
416
417 /*
418 * Find the cluster to start the read from
419 * FIXME: Optimize by remembering the last cluster read and using if
420 * possible.
421 */
422 Status = OffsetToCluster(DeviceExt,
423 FirstCluster,
424 ROUND_DOWN(ReadOffset, ChunkSize),
425 &CurrentCluster,
426 FALSE);
427 if (!NT_SUCCESS(Status))
428 {
429 return(Status);
430 }
431
432 /*
433 * If the read doesn't begin on a chunk boundary then we need special
434 * handling
435 */
436 if ((ReadOffset % ChunkSize) != 0 )
437 {
438 TempLength = min (Length, ChunkSize - (ReadOffset % ChunkSize));
439 VfatReadCluster(DeviceExt, Fcb, ROUND_DOWN(ReadOffset, ChunkSize),
440 FirstCluster, &CurrentCluster, Buffer, NoCache,
441 ReadOffset % ChunkSize, TempLength);
442
443 (*LengthRead) = (*LengthRead) + TempLength;
444 Length = Length - TempLength;
445 Buffer = Buffer + TempLength;
446 ReadOffset = ReadOffset + TempLength;
447 }
448
449 while (Length >= ChunkSize && CurrentCluster)
450 {
451 VfatReadCluster(DeviceExt, Fcb, ReadOffset,
452 FirstCluster, &CurrentCluster, Buffer, NoCache, 0,
453 ChunkSize);
454
455 (*LengthRead) = (*LengthRead) + ChunkSize;
456 Buffer = Buffer + ChunkSize;
457 Length = Length - ChunkSize;
458 ReadOffset = ReadOffset + ChunkSize;
459 }
460 if (Length > 0 && CurrentCluster)
461 {
462 VfatReadCluster(DeviceExt, Fcb, ReadOffset,
463 FirstCluster, &CurrentCluster, Buffer, NoCache, 0,
464 Length);
465 (*LengthRead) = (*LengthRead) + Length;
466 }
467 return (STATUS_SUCCESS);
468 }
469
470 NTSTATUS
471 VfatWriteBigCluster(PDEVICE_EXTENSION DeviceExt,
472 PVFATFCB Fcb,
473 ULONG StartOffset,
474 ULONG FirstCluster,
475 PULONG CurrentCluster,
476 PVOID Source,
477 ULONG NoCache,
478 ULONG InternalOffset,
479 ULONG InternalLength,
480 BOOLEAN Extend)
481 {
482 BOOLEAN Valid;
483 PVOID BaseAddress;
484 PCACHE_SEGMENT CacheSeg;
485 NTSTATUS Status;
486
487 /*
488 * In this case the size of a cache segment is the same as a cluster
489 */
490 if (!NoCache)
491 {
492 Status = CcRosRequestCacheSegment(Fcb->RFCB.Bcb,
493 StartOffset,
494 &BaseAddress,
495 &Valid,
496 &CacheSeg);
497 if (!NT_SUCCESS(Status))
498 {
499 return(Status);
500 }
501 }
502 else
503 {
504 Valid = FALSE;
505 /*
506 * If we are bypassing the cache and not writing starting on
507 * cluster boundary then allocate a temporary buffer
508 */
509 if (InternalOffset != 0)
510 {
511 BaseAddress = ExAllocatePool(NonPagedPool,
512 DeviceExt->BytesPerCluster);
513 if (BaseAddress == NULL)
514 {
515 return(STATUS_NO_MEMORY);
516 }
517 }
518 }
519 if (!Valid && InternalLength != DeviceExt->BytesPerCluster)
520 {
521 /*
522 * If the data in the cache isn't valid or we are bypassing the
523 * cache and not writing a cluster aligned, cluster sized region
524 * then read data in to base address
525 */
526 Status = VfatRawReadCluster(DeviceExt, FirstCluster, BaseAddress,
527 *CurrentCluster);
528 if (!NT_SUCCESS(Status))
529 {
530 if (!NoCache)
531 {
532 CcRosReleaseCacheSegment(Fcb->RFCB.Bcb, CacheSeg, FALSE);
533 }
534 else if (InternalOffset != 0)
535 {
536 ExFreePool(BaseAddress);
537 }
538 return(Status);
539 }
540 }
541 if (!NoCache || InternalLength != DeviceExt->BytesPerCluster)
542 {
543 /*
544 * If we are writing into the cache or we are writing from a
545 * temporary buffer then copy the data over
546 */
547 DPRINT("InternalOffset 0x%x InternalLength 0x%x BA %x\n",
548 InternalOffset, InternalLength, BaseAddress);
549 memcpy(BaseAddress + InternalOffset, Source, InternalLength);
550 }
551 /*
552 * Write the data back to disk
553 */
554 DPRINT("Writing 0x%x\n", *CurrentCluster);
555 Status = VfatRawWriteCluster(DeviceExt, FirstCluster, BaseAddress,
556 *CurrentCluster);
557 if (!NoCache)
558 {
559 CcRosReleaseCacheSegment(Fcb->RFCB.Bcb, CacheSeg, TRUE);
560 }
561 else if (InternalOffset != 0)
562 {
563 ExFreePool(BaseAddress);
564 }
565
566 Status = NextCluster(DeviceExt, FirstCluster, CurrentCluster, Extend);
567 if (!NT_SUCCESS(Status))
568 {
569 return(Status);
570 }
571 return(STATUS_SUCCESS);
572 }
573
574 NTSTATUS
575 VfatWriteSmallCluster(PDEVICE_EXTENSION DeviceExt,
576 PVFATFCB Fcb,
577 ULONG StartOffset,
578 ULONG FirstCluster,
579 PULONG CurrentCluster,
580 PVOID Source,
581 ULONG NoCache,
582 ULONG InternalOffset,
583 ULONG InternalLength,
584 BOOLEAN Extend)
585 {
586 BOOLEAN Valid;
587 PVOID BaseAddress;
588 PCACHE_SEGMENT CacheSeg;
589 NTSTATUS Status;
590 ULONG NCluster;
591 ULONG i;
592
593 /*
594 * Otherwise we read a page of clusters together
595 */
596
597 if (!NoCache)
598 {
599 Status = CcRosRequestCacheSegment(Fcb->RFCB.Bcb,
600 StartOffset,
601 &BaseAddress,
602 &Valid,
603 &CacheSeg);
604 if (!NT_SUCCESS(Status))
605 {
606 return(Status);
607 }
608 }
609 else
610 {
611 Valid = FALSE;
612 if (InternalOffset != 0)
613 {
614 BaseAddress = ExAllocatePool(NonPagedPool, PAGESIZE);
615 if (BaseAddress == NULL)
616 {
617 return(STATUS_NO_MEMORY);
618 }
619 }
620 else
621 {
622 BaseAddress = Source;
623 }
624 }
625
626 /*
627 * If necessary read all the data for the page, unfortunately the
628 * file length may not be page aligned in which case the page will
629 * only be partially filled.
630 * FIXME: So zero fill the rest?
631 */
632 if (!Valid || InternalLength != PAGESIZE)
633 {
634 NCluster = *CurrentCluster;
635
636 for (i = 0; i < (PAGESIZE / DeviceExt->BytesPerCluster); i++)
637 {
638 Status = VfatRawReadCluster(DeviceExt,
639 FirstCluster,
640 BaseAddress +
641 (i * DeviceExt->BytesPerCluster),
642 NCluster);
643 if (!NT_SUCCESS(Status))
644 {
645 if (!NoCache)
646 {
647 CcRosReleaseCacheSegment(Fcb->RFCB.Bcb, CacheSeg, FALSE);
648 }
649 else if (InternalOffset != 0)
650 {
651 ExFreePool(BaseAddress);
652 }
653 return(Status);
654 }
655 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, Extend);
656 if (NCluster == 0xFFFFFFFF)
657 {
658 break;
659 }
660 }
661 }
662 else
663 {
664 /*
665 * Otherwise just move onto the next cluster
666 */
667 for (i = 0; i < (PAGESIZE / DeviceExt->BytesPerCluster); i++)
668 {
669 NextCluster(DeviceExt, FirstCluster, &NCluster, Extend);
670 if (NCluster == 0xFFFFFFFF)
671 {
672 break;
673 }
674 }
675 }
676
677 if (!NoCache || InternalLength != PAGESIZE)
678 {
679 /*
680 * Copy the caller's data if we are using the cache or writing
681 * from temporary buffer
682 */
683 memcpy(BaseAddress + InternalOffset, Source, InternalLength);
684 }
685
686 /*
687 * Write the data to the disk
688 */
689 NCluster = *CurrentCluster;
690
691 for (i = 0; i < (PAGESIZE / DeviceExt->BytesPerCluster); i++)
692 {
693 Status = VfatRawWriteCluster(DeviceExt,
694 FirstCluster,
695 BaseAddress +
696 (i * DeviceExt->BytesPerCluster),
697 NCluster);
698 if (!NT_SUCCESS(Status))
699 {
700 if (!NoCache)
701 {
702 CcRosReleaseCacheSegment(Fcb->RFCB.Bcb, CacheSeg, FALSE);
703 }
704 else if (InternalOffset != 0)
705 {
706 ExFreePool(BaseAddress);
707 }
708 return(Status);
709 }
710 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, Extend);
711 if (NCluster == 0xFFFFFFFF)
712 {
713 break;
714 }
715 }
716 *CurrentCluster = NCluster;
717
718 if (!NoCache)
719 {
720 CcRosReleaseCacheSegment(Fcb->RFCB.Bcb, CacheSeg, TRUE);
721 }
722 else if (InternalOffset != 0)
723 {
724 ExFreePool(BaseAddress);
725 }
726 return(STATUS_SUCCESS);
727 }
728
729 NTSTATUS
730 VfatWriteCluster(PDEVICE_EXTENSION DeviceExt,
731 PVFATFCB Fcb,
732 ULONG StartOffset,
733 ULONG FirstCluster,
734 PULONG CurrentCluster,
735 PVOID Source,
736 ULONG NoCache,
737 ULONG InternalOffset,
738 ULONG InternalLength,
739 BOOLEAN Extend)
740 {
741
742
743 if (DeviceExt->BytesPerCluster >= PAGESIZE)
744 {
745 return(VfatWriteBigCluster(DeviceExt,
746 Fcb,
747 StartOffset,
748 FirstCluster,
749 CurrentCluster,
750 Source,
751 NoCache,
752 InternalOffset,
753 InternalLength,
754 Extend));
755 }
756 else
757 {
758 return(VfatWriteSmallCluster(DeviceExt,
759 Fcb,
760 StartOffset,
761 FirstCluster,
762 CurrentCluster,
763 Source,
764 NoCache,
765 InternalOffset,
766 InternalLength,
767 Extend));
768 }
769 }
770
771 NTSTATUS
772 VfatWriteFile (PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
773 PVOID Buffer, ULONG Length, ULONG WriteOffset,
774 ULONG NoCache)
775 /*
776 * FUNCTION: Writes data to file
777 */
778 {
779 ULONG CurrentCluster;
780 ULONG FirstCluster;
781 PVFATFCB Fcb;
782 PVFATCCB pCcb;
783 ULONG TempLength;
784 LARGE_INTEGER SystemTime, LocalTime;
785 ULONG ChunkSize;
786 NTSTATUS Status;
787 BOOLEAN Extend;
788
789 DPRINT ("VfatWriteFile(FileObject %x, Buffer %x, Length %x, "
790 "WriteOffset %x\n", FileObject, Buffer, Length, WriteOffset);
791
792 /* Locate the first cluster of the file */
793 assert (FileObject);
794 pCcb = (PVFATCCB) (FileObject->FsContext2);
795 assert (pCcb);
796 Fcb = pCcb->pFcb;
797 assert (Fcb);
798
799 if (DeviceExt->BytesPerCluster >= PAGESIZE)
800 {
801 ChunkSize = DeviceExt->BytesPerCluster;
802 }
803 else
804 {
805 ChunkSize = PAGESIZE;
806 }
807
808 if (DeviceExt->FatType == FAT32)
809 {
810 CurrentCluster =
811 Fcb->entry.FirstCluster + Fcb->entry.FirstClusterHigh * 65536;
812 }
813 else
814 {
815 CurrentCluster = Fcb->entry.FirstCluster;
816 }
817 FirstCluster = CurrentCluster;
818
819 /* Find the cluster according to the offset in the file */
820 if (CurrentCluster == 0)
821 {
822 /*
823 * File of size zero
824 */
825 Status = NextCluster (DeviceExt, FirstCluster, &CurrentCluster,
826 TRUE);
827 if (DeviceExt->FatType == FAT32)
828 {
829 Fcb->entry.FirstClusterHigh = CurrentCluster >> 16;
830 Fcb->entry.FirstCluster = CurrentCluster;
831 }
832 else
833 {
834 Fcb->entry.FirstCluster = CurrentCluster;
835 }
836 FirstCluster = CurrentCluster;
837 }
838 Status = OffsetToCluster(DeviceExt, FirstCluster, WriteOffset,
839 &CurrentCluster, TRUE);
840
841 if (WriteOffset + Length > Fcb->entry.FileSize &&
842 !(Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY))
843 {
844 Fcb->entry.FileSize = WriteOffset + Length;
845 }
846
847 /*
848 * If the offset in the cluster doesn't fall on the cluster boundary
849 * then we have to write only from the specified offset
850 */
851
852 if ((WriteOffset % ChunkSize) != 0)
853 {
854 TempLength = min (Length, ChunkSize - (WriteOffset % ChunkSize));
855 if ((Length - TempLength) > 0)
856 {
857 Extend = TRUE;
858 }
859 else
860 {
861 Extend = FALSE;
862 }
863 Status = VfatWriteCluster(DeviceExt,
864 Fcb,
865 ROUND_DOWN(WriteOffset, ChunkSize),
866 FirstCluster,
867 &CurrentCluster,
868 Buffer,
869 NoCache,
870 WriteOffset % ChunkSize,
871 TempLength,
872 Extend);
873 Buffer = Buffer + TempLength;
874 Length = Length - TempLength;
875 WriteOffset = WriteOffset + TempLength;
876 }
877
878 while (Length >= ChunkSize)
879 {
880 if ((Length - ChunkSize) > 0)
881 {
882 Extend = TRUE;
883 }
884 else
885 {
886 Extend = FALSE;
887 }
888 Status = VfatWriteCluster(DeviceExt,
889 Fcb,
890 ROUND_DOWN(WriteOffset, ChunkSize),
891 FirstCluster,
892 &CurrentCluster,
893 Buffer,
894 NoCache,
895 0,
896 ChunkSize,
897 Extend);
898 Buffer = Buffer + ChunkSize;
899 Length = Length - ChunkSize;
900 WriteOffset = WriteOffset + ChunkSize;
901 }
902
903 /* Write the remainder */
904 if (Length > 0)
905 {
906 Status = VfatWriteCluster(DeviceExt,
907 Fcb,
908 ROUND_DOWN(WriteOffset, ChunkSize),
909 FirstCluster,
910 &CurrentCluster,
911 Buffer,
912 NoCache,
913 0,
914 Length,
915 FALSE);
916 }
917
918
919 /* set dates and times */
920 if (!(Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY))
921 {
922 KeQuerySystemTime (&SystemTime);
923 ExSystemTimeToLocalTime (&SystemTime, &LocalTime);
924 FsdFileTimeToDosDateTime ((TIME*)&LocalTime,
925 &Fcb->entry.UpdateDate,
926 &Fcb->entry.UpdateTime);
927 Fcb->entry.AccessDate = Fcb->entry.UpdateDate;
928 updEntry (DeviceExt, FileObject);
929 }
930
931 return (STATUS_SUCCESS);
932 }
933
934 NTSTATUS STDCALL
935 VfatWrite (PDEVICE_OBJECT DeviceObject, PIRP Irp)
936 /*
937 * FUNCTION: Write to a file
938 */
939 {
940 ULONG Length;
941 PVOID Buffer;
942 ULONG Offset;
943 PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation (Irp);
944 PFILE_OBJECT FileObject = Stack->FileObject;
945 PDEVICE_EXTENSION DeviceExt = DeviceObject->DeviceExtension;
946 NTSTATUS Status;
947 ULONG NoCache;
948
949 DPRINT ("VfatWrite(DeviceObject %x Irp %x)\n", DeviceObject, Irp);
950
951 Length = Stack->Parameters.Write.Length;
952 Buffer = MmGetSystemAddressForMdl (Irp->MdlAddress);
953 Offset = Stack->Parameters.Write.ByteOffset.u.LowPart;
954
955 if (Irp->Flags & IRP_PAGING_IO ||
956 FileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING)
957 {
958 NoCache = TRUE;
959 }
960 else
961 {
962 NoCache = FALSE;
963 }
964
965 Status = VfatWriteFile (DeviceExt, FileObject, Buffer, Length, Offset,
966 NoCache);
967
968 Irp->IoStatus.Status = Status;
969 Irp->IoStatus.Information = Length;
970 IoCompleteRequest (Irp, IO_NO_INCREMENT);
971
972 return (Status);
973 }
974
975 NTSTATUS STDCALL
976 VfatRead (PDEVICE_OBJECT DeviceObject, PIRP Irp)
977 /*
978 * FUNCTION: Read from a file
979 */
980 {
981 ULONG Length;
982 PVOID Buffer;
983 ULONG Offset;
984 PIO_STACK_LOCATION Stack;
985 PFILE_OBJECT FileObject;
986 PDEVICE_EXTENSION DeviceExt;
987 NTSTATUS Status;
988 ULONG LengthRead;
989 PVFATFCB Fcb;
990 ULONG NoCache;
991
992 DPRINT ("VfatRead(DeviceObject %x, Irp %x)\n", DeviceObject, Irp);
993
994 /* Precondition / Initialization */
995 assert (Irp != NULL);
996 Stack = IoGetCurrentIrpStackLocation (Irp);
997 assert (Stack != NULL);
998 FileObject = Stack->FileObject;
999 assert (FileObject != NULL);
1000 DeviceExt = DeviceObject->DeviceExtension;
1001 assert (DeviceExt != NULL);
1002
1003 Length = Stack->Parameters.Read.Length;
1004 Buffer = MmGetSystemAddressForMdl (Irp->MdlAddress);
1005 Offset = Stack->Parameters.Read.ByteOffset.u.LowPart;
1006
1007 if (Irp->Flags & IRP_PAGING_IO ||
1008 FileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING)
1009 {
1010 NoCache = TRUE;
1011 }
1012 else
1013 {
1014 NoCache = FALSE;
1015 }
1016
1017 /* fail if file is a directory */
1018 Fcb = ((PVFATCCB) (FileObject->FsContext2))->pFcb;
1019 if (Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY)
1020 {
1021 Status = STATUS_FILE_IS_A_DIRECTORY;
1022 }
1023 else
1024 {
1025 Status = VfatReadFile (DeviceExt,
1026 FileObject, Buffer, Length, Offset, &LengthRead,
1027 NoCache);
1028 }
1029
1030 Irp->IoStatus.Status = Status;
1031 Irp->IoStatus.Information = LengthRead;
1032 IoCompleteRequest (Irp, IO_NO_INCREMENT);
1033
1034 return (Status);
1035 }