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