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