[FASTFAT][CDFS]
[reactos.git] / reactos / drivers / filesystems / fastfat / fat.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: drivers/fs/vfat/fat.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 /* GLOBALS ******************************************************************/
18
19 #define CACHEPAGESIZE(pDeviceExt) ((pDeviceExt)->FatInfo.BytesPerCluster > PAGE_SIZE ? \
20 (pDeviceExt)->FatInfo.BytesPerCluster : PAGE_SIZE)
21
22 /* FUNCTIONS ****************************************************************/
23
24 /*
25 * FUNCTION: Retrieve the next FAT32 cluster from the FAT table via a physical
26 * disk read
27 */
28 NTSTATUS
29 FAT32GetNextCluster(
30 PDEVICE_EXTENSION DeviceExt,
31 ULONG CurrentCluster,
32 PULONG NextCluster)
33 {
34 NTSTATUS Status = STATUS_SUCCESS;
35 PVOID BaseAddress;
36 ULONG FATOffset;
37 ULONG ChunkSize;
38 PVOID Context;
39 LARGE_INTEGER Offset;
40
41 ChunkSize = CACHEPAGESIZE(DeviceExt);
42 FATOffset = CurrentCluster * sizeof(ULONG);
43 Offset.QuadPart = ROUND_DOWN(FATOffset, ChunkSize);
44 _SEH2_TRY
45 {
46 CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, MAP_WAIT, &Context, &BaseAddress);
47 }
48 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
49 {
50 _SEH2_YIELD(return _SEH2_GetExceptionCode());
51 }
52 _SEH2_END;
53
54 CurrentCluster = (*(PULONG)((char*)BaseAddress + (FATOffset % ChunkSize))) & 0x0fffffff;
55 if (CurrentCluster >= 0xffffff8 && CurrentCluster <= 0xfffffff)
56 CurrentCluster = 0xffffffff;
57
58 if (CurrentCluster == 0)
59 {
60 DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
61 Status = STATUS_FILE_CORRUPT_ERROR;
62 if (VfatGlobalData->Flags & VFAT_BREAK_ON_CORRUPTION)
63 ASSERT(CurrentCluster != 0);
64 }
65 CcUnpinData(Context);
66 *NextCluster = CurrentCluster;
67 return Status;
68 }
69
70 /*
71 * FUNCTION: Retrieve the next FAT16 cluster from the FAT table
72 */
73 NTSTATUS
74 FAT16GetNextCluster(
75 PDEVICE_EXTENSION DeviceExt,
76 ULONG CurrentCluster,
77 PULONG NextCluster)
78 {
79 NTSTATUS Status = STATUS_SUCCESS;
80 PVOID BaseAddress;
81 ULONG FATOffset;
82 ULONG ChunkSize;
83 PVOID Context;
84 LARGE_INTEGER Offset;
85
86 ChunkSize = CACHEPAGESIZE(DeviceExt);
87 FATOffset = CurrentCluster * 2;
88 Offset.QuadPart = ROUND_DOWN(FATOffset, ChunkSize);
89 _SEH2_TRY
90 {
91 CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, MAP_WAIT, &Context, &BaseAddress);
92 }
93 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
94 {
95 _SEH2_YIELD(return _SEH2_GetExceptionCode());
96 }
97 _SEH2_END;
98
99 CurrentCluster = *((PUSHORT)((char*)BaseAddress + (FATOffset % ChunkSize)));
100 if (CurrentCluster >= 0xfff8 && CurrentCluster <= 0xffff)
101 CurrentCluster = 0xffffffff;
102
103 if (CurrentCluster == 0)
104 {
105 DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
106 Status = STATUS_FILE_CORRUPT_ERROR;
107 if (VfatGlobalData->Flags & VFAT_BREAK_ON_CORRUPTION)
108 ASSERT(CurrentCluster != 0);
109 }
110
111 CcUnpinData(Context);
112 *NextCluster = CurrentCluster;
113 return Status;
114 }
115
116 /*
117 * FUNCTION: Retrieve the next FAT12 cluster from the FAT table
118 */
119 NTSTATUS
120 FAT12GetNextCluster(
121 PDEVICE_EXTENSION DeviceExt,
122 ULONG CurrentCluster,
123 PULONG NextCluster)
124 {
125 PUSHORT CBlock;
126 ULONG Entry;
127 PVOID BaseAddress;
128 PVOID Context;
129 LARGE_INTEGER Offset;
130
131 *NextCluster = 0;
132
133 Offset.QuadPart = 0;
134 _SEH2_TRY
135 {
136 CcMapData(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, MAP_WAIT, &Context, &BaseAddress);
137 }
138 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
139 {
140 _SEH2_YIELD(return _SEH2_GetExceptionCode());
141 }
142 _SEH2_END;
143
144 CBlock = (PUSHORT)((char*)BaseAddress + (CurrentCluster * 12) / 8);
145 if ((CurrentCluster % 2) == 0)
146 {
147 Entry = *CBlock & 0x0fff;
148 }
149 else
150 {
151 Entry = *CBlock >> 4;
152 }
153
154 // DPRINT("Entry %x\n",Entry);
155 if (Entry >= 0xff8 && Entry <= 0xfff)
156 Entry = 0xffffffff;
157
158 // DPRINT("Returning %x\n",Entry);
159 ASSERT(Entry != 0);
160 *NextCluster = Entry;
161 CcUnpinData(Context);
162 // return Entry == 0xffffffff ? STATUS_END_OF_FILE : STATUS_SUCCESS;
163 return STATUS_SUCCESS;
164 }
165
166 /*
167 * FUNCTION: Finds the first available cluster in a FAT16 table
168 */
169 NTSTATUS
170 FAT16FindAndMarkAvailableCluster(
171 PDEVICE_EXTENSION DeviceExt,
172 PULONG Cluster)
173 {
174 ULONG FatLength;
175 ULONG StartCluster;
176 ULONG i, j;
177 PVOID BaseAddress;
178 ULONG ChunkSize;
179 PVOID Context = 0;
180 LARGE_INTEGER Offset;
181 PUSHORT Block;
182 PUSHORT BlockEnd;
183
184 ChunkSize = CACHEPAGESIZE(DeviceExt);
185 FatLength = (DeviceExt->FatInfo.NumberOfClusters + 2);
186 *Cluster = 0;
187 StartCluster = DeviceExt->LastAvailableCluster;
188
189 for (j = 0; j < 2; j++)
190 {
191 for (i = StartCluster; i < FatLength;)
192 {
193 Offset.QuadPart = ROUND_DOWN(i * 2, ChunkSize);
194 _SEH2_TRY
195 {
196 CcPinRead(DeviceExt->FATFileObject, &Offset, ChunkSize, PIN_WAIT, &Context, &BaseAddress);
197 }
198 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
199 {
200 DPRINT1("CcPinRead(Offset %x, Length %u) failed\n", (ULONG)Offset.QuadPart, ChunkSize);
201 _SEH2_YIELD(return _SEH2_GetExceptionCode());
202 }
203 _SEH2_END;
204
205 Block = (PUSHORT)((ULONG_PTR)BaseAddress + (i * 2) % ChunkSize);
206 BlockEnd = (PUSHORT)((ULONG_PTR)BaseAddress + ChunkSize);
207
208 /* Now process the whole block */
209 while (Block < BlockEnd && i < FatLength)
210 {
211 if (*Block == 0)
212 {
213 DPRINT("Found available cluster 0x%x\n", i);
214 DeviceExt->LastAvailableCluster = *Cluster = i;
215 *Block = 0xffff;
216 CcSetDirtyPinnedData(Context, NULL);
217 CcUnpinData(Context);
218 if (DeviceExt->AvailableClustersValid)
219 InterlockedDecrement((PLONG)&DeviceExt->AvailableClusters);
220 return STATUS_SUCCESS;
221 }
222
223 Block++;
224 i++;
225 }
226
227 CcUnpinData(Context);
228 }
229
230 FatLength = StartCluster;
231 StartCluster = 2;
232 }
233
234 return STATUS_DISK_FULL;
235 }
236
237 /*
238 * FUNCTION: Finds the first available cluster in a FAT12 table
239 */
240 NTSTATUS
241 FAT12FindAndMarkAvailableCluster(
242 PDEVICE_EXTENSION DeviceExt,
243 PULONG Cluster)
244 {
245 ULONG FatLength;
246 ULONG StartCluster;
247 ULONG Entry;
248 PUSHORT CBlock;
249 ULONG i, j;
250 PVOID BaseAddress;
251 PVOID Context;
252 LARGE_INTEGER Offset;
253
254 FatLength = DeviceExt->FatInfo.NumberOfClusters + 2;
255 *Cluster = 0;
256 StartCluster = DeviceExt->LastAvailableCluster;
257 Offset.QuadPart = 0;
258 _SEH2_TRY
259 {
260 CcPinRead(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, PIN_WAIT, &Context, &BaseAddress);
261 }
262 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
263 {
264 DPRINT1("CcPinRead(Offset %x, Length %u) failed\n", (ULONG)Offset.QuadPart, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector);
265 _SEH2_YIELD(return _SEH2_GetExceptionCode());
266 }
267 _SEH2_END;
268
269 for (j = 0; j < 2; j++)
270 {
271 for (i = StartCluster; i < FatLength; i++)
272 {
273 CBlock = (PUSHORT)((char*)BaseAddress + (i * 12) / 8);
274 if ((i % 2) == 0)
275 {
276 Entry = *CBlock & 0xfff;
277 }
278 else
279 {
280 Entry = *CBlock >> 4;
281 }
282
283 if (Entry == 0)
284 {
285 DPRINT("Found available cluster 0x%x\n", i);
286 DeviceExt->LastAvailableCluster = *Cluster = i;
287 if ((i % 2) == 0)
288 *CBlock = (*CBlock & 0xf000) | 0xfff;
289 else
290 *CBlock = (*CBlock & 0xf) | 0xfff0;
291 CcSetDirtyPinnedData(Context, NULL);
292 CcUnpinData(Context);
293 if (DeviceExt->AvailableClustersValid)
294 InterlockedDecrement((PLONG)&DeviceExt->AvailableClusters);
295 return STATUS_SUCCESS;
296 }
297 }
298 FatLength = StartCluster;
299 StartCluster = 2;
300 }
301 CcUnpinData(Context);
302 return STATUS_DISK_FULL;
303 }
304
305 /*
306 * FUNCTION: Finds the first available cluster in a FAT32 table
307 */
308 NTSTATUS
309 FAT32FindAndMarkAvailableCluster(
310 PDEVICE_EXTENSION DeviceExt,
311 PULONG Cluster)
312 {
313 ULONG FatLength;
314 ULONG StartCluster;
315 ULONG i, j;
316 PVOID BaseAddress;
317 ULONG ChunkSize;
318 PVOID Context;
319 LARGE_INTEGER Offset;
320 PULONG Block;
321 PULONG BlockEnd;
322
323 ChunkSize = CACHEPAGESIZE(DeviceExt);
324 FatLength = (DeviceExt->FatInfo.NumberOfClusters + 2);
325 *Cluster = 0;
326 StartCluster = DeviceExt->LastAvailableCluster;
327
328 for (j = 0; j < 2; j++)
329 {
330 for (i = StartCluster; i < FatLength;)
331 {
332 Offset.QuadPart = ROUND_DOWN(i * 4, ChunkSize);
333 _SEH2_TRY
334 {
335 CcPinRead(DeviceExt->FATFileObject, &Offset, ChunkSize, PIN_WAIT, &Context, &BaseAddress);
336 }
337 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
338 {
339 DPRINT1("CcPinRead(Offset %x, Length %u) failed\n", (ULONG)Offset.QuadPart, ChunkSize);
340 _SEH2_YIELD(return _SEH2_GetExceptionCode());
341 }
342 _SEH2_END;
343 Block = (PULONG)((ULONG_PTR)BaseAddress + (i * 4) % ChunkSize);
344 BlockEnd = (PULONG)((ULONG_PTR)BaseAddress + ChunkSize);
345
346 /* Now process the whole block */
347 while (Block < BlockEnd && i < FatLength)
348 {
349 if ((*Block & 0x0fffffff) == 0)
350 {
351 DPRINT("Found available cluster 0x%x\n", i);
352 DeviceExt->LastAvailableCluster = *Cluster = i;
353 *Block = 0x0fffffff;
354 CcSetDirtyPinnedData(Context, NULL);
355 CcUnpinData(Context);
356 if (DeviceExt->AvailableClustersValid)
357 InterlockedDecrement((PLONG)&DeviceExt->AvailableClusters);
358 return STATUS_SUCCESS;
359 }
360
361 Block++;
362 i++;
363 }
364
365 CcUnpinData(Context);
366 }
367 FatLength = StartCluster;
368 StartCluster = 2;
369 }
370 return STATUS_DISK_FULL;
371 }
372
373 /*
374 * FUNCTION: Counts free cluster in a FAT12 table
375 */
376 static
377 NTSTATUS
378 FAT12CountAvailableClusters(
379 PDEVICE_EXTENSION DeviceExt)
380 {
381 ULONG Entry;
382 PVOID BaseAddress;
383 ULONG ulCount = 0;
384 ULONG i;
385 ULONG numberofclusters;
386 LARGE_INTEGER Offset;
387 PVOID Context;
388 PUSHORT CBlock;
389
390 Offset.QuadPart = 0;
391 _SEH2_TRY
392 {
393 CcMapData(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, MAP_WAIT, &Context, &BaseAddress);
394 }
395 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
396 {
397 _SEH2_YIELD(return _SEH2_GetExceptionCode());
398 }
399 _SEH2_END;
400
401 numberofclusters = DeviceExt->FatInfo.NumberOfClusters + 2;
402
403 for (i = 2; i < numberofclusters; i++)
404 {
405 CBlock = (PUSHORT)((char*)BaseAddress + (i * 12) / 8);
406 if ((i % 2) == 0)
407 {
408 Entry = *CBlock & 0x0fff;
409 }
410 else
411 {
412 Entry = *CBlock >> 4;
413 }
414
415 if (Entry == 0)
416 ulCount++;
417 }
418
419 CcUnpinData(Context);
420 DeviceExt->AvailableClusters = ulCount;
421 DeviceExt->AvailableClustersValid = TRUE;
422
423 return STATUS_SUCCESS;
424 }
425
426
427 /*
428 * FUNCTION: Counts free clusters in a FAT16 table
429 */
430 static
431 NTSTATUS
432 FAT16CountAvailableClusters(
433 PDEVICE_EXTENSION DeviceExt)
434 {
435 PUSHORT Block;
436 PUSHORT BlockEnd;
437 PVOID BaseAddress = NULL;
438 ULONG ulCount = 0;
439 ULONG i;
440 ULONG ChunkSize;
441 PVOID Context = NULL;
442 LARGE_INTEGER Offset;
443 ULONG FatLength;
444
445 ChunkSize = CACHEPAGESIZE(DeviceExt);
446 FatLength = (DeviceExt->FatInfo.NumberOfClusters + 2);
447
448 for (i = 2; i < FatLength; )
449 {
450 Offset.QuadPart = ROUND_DOWN(i * 2, ChunkSize);
451 _SEH2_TRY
452 {
453 CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, MAP_WAIT, &Context, &BaseAddress);
454 }
455 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
456 {
457 _SEH2_YIELD(return _SEH2_GetExceptionCode());
458 }
459 _SEH2_END;
460 Block = (PUSHORT)((ULONG_PTR)BaseAddress + (i * 2) % ChunkSize);
461 BlockEnd = (PUSHORT)((ULONG_PTR)BaseAddress + ChunkSize);
462
463 /* Now process the whole block */
464 while (Block < BlockEnd && i < FatLength)
465 {
466 if (*Block == 0)
467 ulCount++;
468 Block++;
469 i++;
470 }
471
472 CcUnpinData(Context);
473 }
474
475 DeviceExt->AvailableClusters = ulCount;
476 DeviceExt->AvailableClustersValid = TRUE;
477
478 return STATUS_SUCCESS;
479 }
480
481
482 /*
483 * FUNCTION: Counts free clusters in a FAT32 table
484 */
485 static
486 NTSTATUS
487 FAT32CountAvailableClusters(
488 PDEVICE_EXTENSION DeviceExt)
489 {
490 PULONG Block;
491 PULONG BlockEnd;
492 PVOID BaseAddress = NULL;
493 ULONG ulCount = 0;
494 ULONG i;
495 ULONG ChunkSize;
496 PVOID Context = NULL;
497 LARGE_INTEGER Offset;
498 ULONG FatLength;
499
500 ChunkSize = CACHEPAGESIZE(DeviceExt);
501 FatLength = (DeviceExt->FatInfo.NumberOfClusters + 2);
502
503 for (i = 2; i < FatLength; )
504 {
505 Offset.QuadPart = ROUND_DOWN(i * 4, ChunkSize);
506 _SEH2_TRY
507 {
508 CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, MAP_WAIT, &Context, &BaseAddress);
509 }
510 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
511 {
512 DPRINT1("CcMapData(Offset %x, Length %u) failed\n", (ULONG)Offset.QuadPart, ChunkSize);
513 _SEH2_YIELD(return _SEH2_GetExceptionCode());
514 }
515 _SEH2_END;
516 Block = (PULONG)((ULONG_PTR)BaseAddress + (i * 4) % ChunkSize);
517 BlockEnd = (PULONG)((ULONG_PTR)BaseAddress + ChunkSize);
518
519 /* Now process the whole block */
520 while (Block < BlockEnd && i < FatLength)
521 {
522 if ((*Block & 0x0fffffff) == 0)
523 ulCount++;
524 Block++;
525 i++;
526 }
527
528 CcUnpinData(Context);
529 }
530
531 DeviceExt->AvailableClusters = ulCount;
532 DeviceExt->AvailableClustersValid = TRUE;
533
534 return STATUS_SUCCESS;
535 }
536
537 NTSTATUS
538 CountAvailableClusters(
539 PDEVICE_EXTENSION DeviceExt,
540 PLARGE_INTEGER Clusters)
541 {
542 NTSTATUS Status = STATUS_SUCCESS;
543 ExAcquireResourceExclusiveLite (&DeviceExt->FatResource, TRUE);
544 if (!DeviceExt->AvailableClustersValid)
545 {
546 if (DeviceExt->FatInfo.FatType == FAT12)
547 Status = FAT12CountAvailableClusters(DeviceExt);
548 else if (DeviceExt->FatInfo.FatType == FAT16 || DeviceExt->FatInfo.FatType == FATX16)
549 Status = FAT16CountAvailableClusters(DeviceExt);
550 else
551 Status = FAT32CountAvailableClusters(DeviceExt);
552 }
553 Clusters->QuadPart = DeviceExt->AvailableClusters;
554 ExReleaseResourceLite (&DeviceExt->FatResource);
555
556 return Status;
557 }
558
559
560 /*
561 * FUNCTION: Writes a cluster to the FAT12 physical and in-memory tables
562 */
563 NTSTATUS
564 FAT12WriteCluster(
565 PDEVICE_EXTENSION DeviceExt,
566 ULONG ClusterToWrite,
567 ULONG NewValue,
568 PULONG OldValue)
569 {
570 ULONG FATOffset;
571 PUCHAR CBlock;
572 PVOID BaseAddress;
573 PVOID Context;
574 LARGE_INTEGER Offset;
575
576 Offset.QuadPart = 0;
577 _SEH2_TRY
578 {
579 CcPinRead(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, PIN_WAIT, &Context, &BaseAddress);
580 }
581 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
582 {
583 _SEH2_YIELD(return _SEH2_GetExceptionCode());
584 }
585 _SEH2_END;
586 CBlock = (PUCHAR)BaseAddress;
587
588 FATOffset = (ClusterToWrite * 12) / 8;
589 DPRINT("Writing 0x%x for 0x%x at 0x%x\n",
590 NewValue, ClusterToWrite, FATOffset);
591 if ((ClusterToWrite % 2) == 0)
592 {
593 *OldValue = CBlock[FATOffset] + ((CBlock[FATOffset + 1] & 0x0f) << 8);
594 CBlock[FATOffset] = (UCHAR)NewValue;
595 CBlock[FATOffset + 1] &= 0xf0;
596 CBlock[FATOffset + 1] |= (NewValue & 0xf00) >> 8;
597 }
598 else
599 {
600 *OldValue = (CBlock[FATOffset] >> 4) + (CBlock[FATOffset + 1] << 4);
601 CBlock[FATOffset] &= 0x0f;
602 CBlock[FATOffset] |= (NewValue & 0xf) << 4;
603 CBlock[FATOffset + 1] = (UCHAR)(NewValue >> 4);
604 }
605 /* Write the changed FAT sector(s) to disk */
606 CcSetDirtyPinnedData(Context, NULL);
607 CcUnpinData(Context);
608 return STATUS_SUCCESS;
609 }
610
611 /*
612 * FUNCTION: Writes a cluster to the FAT16 physical and in-memory tables
613 */
614 NTSTATUS
615 FAT16WriteCluster(
616 PDEVICE_EXTENSION DeviceExt,
617 ULONG ClusterToWrite,
618 ULONG NewValue,
619 PULONG OldValue)
620 {
621 PVOID BaseAddress;
622 ULONG FATOffset;
623 ULONG ChunkSize;
624 PVOID Context;
625 LARGE_INTEGER Offset;
626 PUSHORT Cluster;
627
628 ChunkSize = CACHEPAGESIZE(DeviceExt);
629 FATOffset = ClusterToWrite * 2;
630 Offset.QuadPart = ROUND_DOWN(FATOffset, ChunkSize);
631 _SEH2_TRY
632 {
633 CcPinRead(DeviceExt->FATFileObject, &Offset, ChunkSize, PIN_WAIT, &Context, &BaseAddress);
634 }
635 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
636 {
637 _SEH2_YIELD(return _SEH2_GetExceptionCode());
638 }
639 _SEH2_END;
640
641 DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue, FATOffset,
642 ClusterToWrite);
643 Cluster = ((PUSHORT)((char*)BaseAddress + (FATOffset % ChunkSize)));
644 *OldValue = *Cluster;
645 *Cluster = (USHORT)NewValue;
646 CcSetDirtyPinnedData(Context, NULL);
647 CcUnpinData(Context);
648 return STATUS_SUCCESS;
649 }
650
651 /*
652 * FUNCTION: Writes a cluster to the FAT32 physical tables
653 */
654 NTSTATUS
655 FAT32WriteCluster(
656 PDEVICE_EXTENSION DeviceExt,
657 ULONG ClusterToWrite,
658 ULONG NewValue,
659 PULONG OldValue)
660 {
661 PVOID BaseAddress;
662 ULONG FATOffset;
663 ULONG ChunkSize;
664 PVOID Context;
665 LARGE_INTEGER Offset;
666 PULONG Cluster;
667
668 ChunkSize = CACHEPAGESIZE(DeviceExt);
669
670 FATOffset = (ClusterToWrite * 4);
671 Offset.QuadPart = ROUND_DOWN(FATOffset, ChunkSize);
672 _SEH2_TRY
673 {
674 CcPinRead(DeviceExt->FATFileObject, &Offset, ChunkSize, PIN_WAIT, &Context, &BaseAddress);
675 }
676 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
677 {
678 _SEH2_YIELD(return _SEH2_GetExceptionCode());
679 }
680 _SEH2_END;
681
682 DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue, FATOffset,
683 ClusterToWrite);
684 Cluster = ((PULONG)((char*)BaseAddress + (FATOffset % ChunkSize)));
685 *OldValue = *Cluster & 0x0fffffff;
686 *Cluster = (*Cluster & 0xf0000000) | (NewValue & 0x0fffffff);
687
688 CcSetDirtyPinnedData(Context, NULL);
689 CcUnpinData(Context);
690
691 return STATUS_SUCCESS;
692 }
693
694
695 /*
696 * FUNCTION: Write a changed FAT entry
697 */
698 NTSTATUS
699 WriteCluster(
700 PDEVICE_EXTENSION DeviceExt,
701 ULONG ClusterToWrite,
702 ULONG NewValue)
703 {
704 NTSTATUS Status;
705 ULONG OldValue;
706
707 ExAcquireResourceExclusiveLite (&DeviceExt->FatResource, TRUE);
708 Status = DeviceExt->WriteCluster(DeviceExt, ClusterToWrite, NewValue, &OldValue);
709 if (DeviceExt->AvailableClustersValid)
710 {
711 if (OldValue && NewValue == 0)
712 InterlockedIncrement((PLONG)&DeviceExt->AvailableClusters);
713 else if (OldValue == 0 && NewValue)
714 InterlockedDecrement((PLONG)&DeviceExt->AvailableClusters);
715 }
716 ExReleaseResourceLite(&DeviceExt->FatResource);
717 return Status;
718 }
719
720 /*
721 * FUNCTION: Converts the cluster number to a sector number for this physical
722 * device
723 */
724 ULONGLONG
725 ClusterToSector(
726 PDEVICE_EXTENSION DeviceExt,
727 ULONG Cluster)
728 {
729 return DeviceExt->FatInfo.dataStart +
730 ((ULONGLONG)(Cluster - 2) * DeviceExt->FatInfo.SectorsPerCluster);
731
732 }
733
734 /*
735 * FUNCTION: Retrieve the next cluster depending on the FAT type
736 */
737 NTSTATUS
738 GetNextCluster(
739 PDEVICE_EXTENSION DeviceExt,
740 ULONG CurrentCluster,
741 PULONG NextCluster)
742 {
743 NTSTATUS Status;
744
745 DPRINT("GetNextCluster(DeviceExt %p, CurrentCluster %x)\n",
746 DeviceExt, CurrentCluster);
747
748 if (CurrentCluster == 0)
749 {
750 DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
751 if (VfatGlobalData->Flags & VFAT_BREAK_ON_CORRUPTION)
752 ASSERT(CurrentCluster != 0);
753 return STATUS_FILE_CORRUPT_ERROR;
754 }
755
756 ExAcquireResourceSharedLite(&DeviceExt->FatResource, TRUE);
757 Status = DeviceExt->GetNextCluster(DeviceExt, CurrentCluster, NextCluster);
758 ExReleaseResourceLite(&DeviceExt->FatResource);
759
760 return Status;
761 }
762
763 /*
764 * FUNCTION: Retrieve the next cluster depending on the FAT type
765 */
766 NTSTATUS
767 GetNextClusterExtend(
768 PDEVICE_EXTENSION DeviceExt,
769 ULONG CurrentCluster,
770 PULONG NextCluster)
771 {
772 ULONG NewCluster;
773 NTSTATUS Status;
774
775 DPRINT("GetNextClusterExtend(DeviceExt %p, CurrentCluster %x)\n",
776 DeviceExt, CurrentCluster);
777
778 ExAcquireResourceExclusiveLite(&DeviceExt->FatResource, TRUE);
779 /*
780 * If the file hasn't any clusters allocated then we need special
781 * handling
782 */
783 if (CurrentCluster == 0)
784 {
785 Status = DeviceExt->FindAndMarkAvailableCluster(DeviceExt, &NewCluster);
786 if (!NT_SUCCESS(Status))
787 {
788 ExReleaseResourceLite(&DeviceExt->FatResource);
789 return Status;
790 }
791
792 *NextCluster = NewCluster;
793 ExReleaseResourceLite(&DeviceExt->FatResource);
794 return STATUS_SUCCESS;
795 }
796
797 Status = DeviceExt->GetNextCluster(DeviceExt, CurrentCluster, NextCluster);
798
799 if ((*NextCluster) == 0xFFFFFFFF)
800 {
801 /* We are after last existing cluster, we must add one to file */
802 /* Firstly, find the next available open allocation unit and
803 mark it as end of file */
804 Status = DeviceExt->FindAndMarkAvailableCluster(DeviceExt, &NewCluster);
805 if (!NT_SUCCESS(Status))
806 {
807 ExReleaseResourceLite(&DeviceExt->FatResource);
808 return Status;
809 }
810
811 /* Now, write the AU of the LastCluster with the value of the newly
812 found AU */
813 WriteCluster(DeviceExt, CurrentCluster, NewCluster);
814 *NextCluster = NewCluster;
815 }
816
817 ExReleaseResourceLite(&DeviceExt->FatResource);
818 return Status;
819 }
820
821 /* EOF */