[FASTFAT] Use the FastFAT mechanism for counting clusters already implemented
[reactos.git] / drivers / filesystems / fastfat / fat.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: drivers/filesystems/fastfat/fat.c
5 * PURPOSE: FastFAT Filesystem
6 * PROGRAMMER: Jason Filby (jasonfilby@yahoo.com)
7 * Pierre Schweitzer (pierre@reactos.org)
8 *
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include "vfat.h"
14
15 #define NDEBUG
16 #include <debug.h>
17
18 /* GLOBALS ******************************************************************/
19
20 #define CACHEPAGESIZE(pDeviceExt) ((pDeviceExt)->FatInfo.BytesPerCluster > PAGE_SIZE ? \
21 (pDeviceExt)->FatInfo.BytesPerCluster : PAGE_SIZE)
22
23 /* FIXME: because volume is not cached, we have to perform direct IOs
24 * The day this is fixed, just comment out that line, and check
25 * it still works (and delete old code ;-))
26 */
27 #define VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
28
29 /* FUNCTIONS ****************************************************************/
30
31 /*
32 * FUNCTION: Retrieve the next FAT32 cluster from the FAT table via a physical
33 * disk read
34 */
35 NTSTATUS
36 FAT32GetNextCluster(
37 PDEVICE_EXTENSION DeviceExt,
38 ULONG CurrentCluster,
39 PULONG NextCluster)
40 {
41 NTSTATUS Status = STATUS_SUCCESS;
42 PVOID BaseAddress;
43 ULONG FATOffset;
44 ULONG ChunkSize;
45 PVOID Context;
46 LARGE_INTEGER Offset;
47
48 ChunkSize = CACHEPAGESIZE(DeviceExt);
49 FATOffset = CurrentCluster * sizeof(ULONG);
50 Offset.QuadPart = ROUND_DOWN(FATOffset, ChunkSize);
51 _SEH2_TRY
52 {
53 CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, MAP_WAIT, &Context, &BaseAddress);
54 }
55 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
56 {
57 _SEH2_YIELD(return _SEH2_GetExceptionCode());
58 }
59 _SEH2_END;
60
61 CurrentCluster = (*(PULONG)((char*)BaseAddress + (FATOffset % ChunkSize))) & 0x0fffffff;
62 if (CurrentCluster >= 0xffffff8 && CurrentCluster <= 0xfffffff)
63 CurrentCluster = 0xffffffff;
64
65 if (CurrentCluster == 0)
66 {
67 DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
68 Status = STATUS_FILE_CORRUPT_ERROR;
69 if (VfatGlobalData->Flags & VFAT_BREAK_ON_CORRUPTION)
70 ASSERT(CurrentCluster != 0);
71 }
72 CcUnpinData(Context);
73 *NextCluster = CurrentCluster;
74 return Status;
75 }
76
77 /*
78 * FUNCTION: Retrieve the next FAT16 cluster from the FAT table
79 */
80 NTSTATUS
81 FAT16GetNextCluster(
82 PDEVICE_EXTENSION DeviceExt,
83 ULONG CurrentCluster,
84 PULONG NextCluster)
85 {
86 NTSTATUS Status = STATUS_SUCCESS;
87 PVOID BaseAddress;
88 ULONG FATOffset;
89 ULONG ChunkSize;
90 PVOID Context;
91 LARGE_INTEGER Offset;
92
93 ChunkSize = CACHEPAGESIZE(DeviceExt);
94 FATOffset = CurrentCluster * 2;
95 Offset.QuadPart = ROUND_DOWN(FATOffset, ChunkSize);
96 _SEH2_TRY
97 {
98 CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, MAP_WAIT, &Context, &BaseAddress);
99 }
100 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
101 {
102 _SEH2_YIELD(return _SEH2_GetExceptionCode());
103 }
104 _SEH2_END;
105
106 CurrentCluster = *((PUSHORT)((char*)BaseAddress + (FATOffset % ChunkSize)));
107 if (CurrentCluster >= 0xfff8 && CurrentCluster <= 0xffff)
108 CurrentCluster = 0xffffffff;
109
110 if (CurrentCluster == 0)
111 {
112 DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
113 Status = STATUS_FILE_CORRUPT_ERROR;
114 if (VfatGlobalData->Flags & VFAT_BREAK_ON_CORRUPTION)
115 ASSERT(CurrentCluster != 0);
116 }
117
118 CcUnpinData(Context);
119 *NextCluster = CurrentCluster;
120 return Status;
121 }
122
123 /*
124 * FUNCTION: Retrieve the next FAT12 cluster from the FAT table
125 */
126 NTSTATUS
127 FAT12GetNextCluster(
128 PDEVICE_EXTENSION DeviceExt,
129 ULONG CurrentCluster,
130 PULONG NextCluster)
131 {
132 PUSHORT CBlock;
133 ULONG Entry;
134 PVOID BaseAddress;
135 PVOID Context;
136 LARGE_INTEGER Offset;
137
138 *NextCluster = 0;
139
140 Offset.QuadPart = 0;
141 _SEH2_TRY
142 {
143 CcMapData(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, MAP_WAIT, &Context, &BaseAddress);
144 }
145 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
146 {
147 _SEH2_YIELD(return _SEH2_GetExceptionCode());
148 }
149 _SEH2_END;
150
151 CBlock = (PUSHORT)((char*)BaseAddress + (CurrentCluster * 12) / 8);
152 if ((CurrentCluster % 2) == 0)
153 {
154 Entry = *CBlock & 0x0fff;
155 }
156 else
157 {
158 Entry = *CBlock >> 4;
159 }
160
161 // DPRINT("Entry %x\n",Entry);
162 if (Entry >= 0xff8 && Entry <= 0xfff)
163 Entry = 0xffffffff;
164
165 // DPRINT("Returning %x\n",Entry);
166 ASSERT(Entry != 0);
167 *NextCluster = Entry;
168 CcUnpinData(Context);
169 // return Entry == 0xffffffff ? STATUS_END_OF_FILE : STATUS_SUCCESS;
170 return STATUS_SUCCESS;
171 }
172
173 /*
174 * FUNCTION: Finds the first available cluster in a FAT16 table
175 */
176 NTSTATUS
177 FAT16FindAndMarkAvailableCluster(
178 PDEVICE_EXTENSION DeviceExt,
179 PULONG Cluster)
180 {
181 ULONG FatLength;
182 ULONG StartCluster;
183 ULONG i, j;
184 PVOID BaseAddress;
185 ULONG ChunkSize;
186 PVOID Context = 0;
187 LARGE_INTEGER Offset;
188 PUSHORT Block;
189 PUSHORT BlockEnd;
190
191 ChunkSize = CACHEPAGESIZE(DeviceExt);
192 FatLength = (DeviceExt->FatInfo.NumberOfClusters + 2);
193 *Cluster = 0;
194 StartCluster = DeviceExt->LastAvailableCluster;
195
196 for (j = 0; j < 2; j++)
197 {
198 for (i = StartCluster; i < FatLength;)
199 {
200 Offset.QuadPart = ROUND_DOWN(i * 2, ChunkSize);
201 _SEH2_TRY
202 {
203 CcPinRead(DeviceExt->FATFileObject, &Offset, ChunkSize, PIN_WAIT, &Context, &BaseAddress);
204 }
205 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
206 {
207 DPRINT1("CcPinRead(Offset %x, Length %u) failed\n", (ULONG)Offset.QuadPart, ChunkSize);
208 _SEH2_YIELD(return _SEH2_GetExceptionCode());
209 }
210 _SEH2_END;
211
212 Block = (PUSHORT)((ULONG_PTR)BaseAddress + (i * 2) % ChunkSize);
213 BlockEnd = (PUSHORT)((ULONG_PTR)BaseAddress + ChunkSize);
214
215 /* Now process the whole block */
216 while (Block < BlockEnd && i < FatLength)
217 {
218 if (*Block == 0)
219 {
220 DPRINT("Found available cluster 0x%x\n", i);
221 DeviceExt->LastAvailableCluster = *Cluster = i;
222 *Block = 0xffff;
223 CcSetDirtyPinnedData(Context, NULL);
224 CcUnpinData(Context);
225 if (DeviceExt->AvailableClustersValid)
226 InterlockedDecrement((PLONG)&DeviceExt->AvailableClusters);
227 return STATUS_SUCCESS;
228 }
229
230 Block++;
231 i++;
232 }
233
234 CcUnpinData(Context);
235 }
236
237 FatLength = StartCluster;
238 StartCluster = 2;
239 }
240
241 return STATUS_DISK_FULL;
242 }
243
244 /*
245 * FUNCTION: Finds the first available cluster in a FAT12 table
246 */
247 NTSTATUS
248 FAT12FindAndMarkAvailableCluster(
249 PDEVICE_EXTENSION DeviceExt,
250 PULONG Cluster)
251 {
252 ULONG FatLength;
253 ULONG StartCluster;
254 ULONG Entry;
255 PUSHORT CBlock;
256 ULONG i, j;
257 PVOID BaseAddress;
258 PVOID Context;
259 LARGE_INTEGER Offset;
260
261 FatLength = DeviceExt->FatInfo.NumberOfClusters + 2;
262 *Cluster = 0;
263 StartCluster = DeviceExt->LastAvailableCluster;
264 Offset.QuadPart = 0;
265 _SEH2_TRY
266 {
267 CcPinRead(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, PIN_WAIT, &Context, &BaseAddress);
268 }
269 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
270 {
271 DPRINT1("CcPinRead(Offset %x, Length %u) failed\n", (ULONG)Offset.QuadPart, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector);
272 _SEH2_YIELD(return _SEH2_GetExceptionCode());
273 }
274 _SEH2_END;
275
276 for (j = 0; j < 2; j++)
277 {
278 for (i = StartCluster; i < FatLength; i++)
279 {
280 CBlock = (PUSHORT)((char*)BaseAddress + (i * 12) / 8);
281 if ((i % 2) == 0)
282 {
283 Entry = *CBlock & 0xfff;
284 }
285 else
286 {
287 Entry = *CBlock >> 4;
288 }
289
290 if (Entry == 0)
291 {
292 DPRINT("Found available cluster 0x%x\n", i);
293 DeviceExt->LastAvailableCluster = *Cluster = i;
294 if ((i % 2) == 0)
295 *CBlock = (*CBlock & 0xf000) | 0xfff;
296 else
297 *CBlock = (*CBlock & 0xf) | 0xfff0;
298 CcSetDirtyPinnedData(Context, NULL);
299 CcUnpinData(Context);
300 if (DeviceExt->AvailableClustersValid)
301 InterlockedDecrement((PLONG)&DeviceExt->AvailableClusters);
302 return STATUS_SUCCESS;
303 }
304 }
305 FatLength = StartCluster;
306 StartCluster = 2;
307 }
308 CcUnpinData(Context);
309 return STATUS_DISK_FULL;
310 }
311
312 /*
313 * FUNCTION: Finds the first available cluster in a FAT32 table
314 */
315 NTSTATUS
316 FAT32FindAndMarkAvailableCluster(
317 PDEVICE_EXTENSION DeviceExt,
318 PULONG Cluster)
319 {
320 ULONG FatLength;
321 ULONG StartCluster;
322 ULONG i, j;
323 PVOID BaseAddress;
324 ULONG ChunkSize;
325 PVOID Context;
326 LARGE_INTEGER Offset;
327 PULONG Block;
328 PULONG BlockEnd;
329
330 ChunkSize = CACHEPAGESIZE(DeviceExt);
331 FatLength = (DeviceExt->FatInfo.NumberOfClusters + 2);
332 *Cluster = 0;
333 StartCluster = DeviceExt->LastAvailableCluster;
334
335 for (j = 0; j < 2; j++)
336 {
337 for (i = StartCluster; i < FatLength;)
338 {
339 Offset.QuadPart = ROUND_DOWN(i * 4, ChunkSize);
340 _SEH2_TRY
341 {
342 CcPinRead(DeviceExt->FATFileObject, &Offset, ChunkSize, PIN_WAIT, &Context, &BaseAddress);
343 }
344 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
345 {
346 DPRINT1("CcPinRead(Offset %x, Length %u) failed\n", (ULONG)Offset.QuadPart, ChunkSize);
347 _SEH2_YIELD(return _SEH2_GetExceptionCode());
348 }
349 _SEH2_END;
350 Block = (PULONG)((ULONG_PTR)BaseAddress + (i * 4) % ChunkSize);
351 BlockEnd = (PULONG)((ULONG_PTR)BaseAddress + ChunkSize);
352
353 /* Now process the whole block */
354 while (Block < BlockEnd && i < FatLength)
355 {
356 if ((*Block & 0x0fffffff) == 0)
357 {
358 DPRINT("Found available cluster 0x%x\n", i);
359 DeviceExt->LastAvailableCluster = *Cluster = i;
360 *Block = 0x0fffffff;
361 CcSetDirtyPinnedData(Context, NULL);
362 CcUnpinData(Context);
363 if (DeviceExt->AvailableClustersValid)
364 InterlockedDecrement((PLONG)&DeviceExt->AvailableClusters);
365 return STATUS_SUCCESS;
366 }
367
368 Block++;
369 i++;
370 }
371
372 CcUnpinData(Context);
373 }
374 FatLength = StartCluster;
375 StartCluster = 2;
376 }
377 return STATUS_DISK_FULL;
378 }
379
380 /*
381 * FUNCTION: Counts free cluster in a FAT12 table
382 */
383 static
384 NTSTATUS
385 FAT12CountAvailableClusters(
386 PDEVICE_EXTENSION DeviceExt)
387 {
388 ULONG Entry;
389 PVOID BaseAddress;
390 ULONG ulCount = 0;
391 ULONG i;
392 ULONG numberofclusters;
393 LARGE_INTEGER Offset;
394 PVOID Context;
395 PUSHORT CBlock;
396
397 Offset.QuadPart = 0;
398 _SEH2_TRY
399 {
400 CcMapData(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, MAP_WAIT, &Context, &BaseAddress);
401 }
402 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
403 {
404 _SEH2_YIELD(return _SEH2_GetExceptionCode());
405 }
406 _SEH2_END;
407
408 numberofclusters = DeviceExt->FatInfo.NumberOfClusters + 2;
409
410 for (i = 2; i < numberofclusters; i++)
411 {
412 CBlock = (PUSHORT)((char*)BaseAddress + (i * 12) / 8);
413 if ((i % 2) == 0)
414 {
415 Entry = *CBlock & 0x0fff;
416 }
417 else
418 {
419 Entry = *CBlock >> 4;
420 }
421
422 if (Entry == 0)
423 ulCount++;
424 }
425
426 CcUnpinData(Context);
427 DeviceExt->AvailableClusters = ulCount;
428 DeviceExt->AvailableClustersValid = TRUE;
429
430 return STATUS_SUCCESS;
431 }
432
433
434 /*
435 * FUNCTION: Counts free clusters in a FAT16 table
436 */
437 static
438 NTSTATUS
439 FAT16CountAvailableClusters(
440 PDEVICE_EXTENSION DeviceExt)
441 {
442 PUSHORT Block;
443 PUSHORT BlockEnd;
444 PVOID BaseAddress = NULL;
445 ULONG ulCount = 0;
446 ULONG i;
447 ULONG ChunkSize;
448 PVOID Context = NULL;
449 LARGE_INTEGER Offset;
450 ULONG FatLength;
451
452 ChunkSize = CACHEPAGESIZE(DeviceExt);
453 FatLength = (DeviceExt->FatInfo.NumberOfClusters + 2);
454
455 for (i = 2; i < FatLength; )
456 {
457 Offset.QuadPart = ROUND_DOWN(i * 2, ChunkSize);
458 _SEH2_TRY
459 {
460 CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, MAP_WAIT, &Context, &BaseAddress);
461 }
462 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
463 {
464 _SEH2_YIELD(return _SEH2_GetExceptionCode());
465 }
466 _SEH2_END;
467 Block = (PUSHORT)((ULONG_PTR)BaseAddress + (i * 2) % ChunkSize);
468 BlockEnd = (PUSHORT)((ULONG_PTR)BaseAddress + ChunkSize);
469
470 /* Now process the whole block */
471 while (Block < BlockEnd && i < FatLength)
472 {
473 if (*Block == 0)
474 ulCount++;
475 Block++;
476 i++;
477 }
478
479 CcUnpinData(Context);
480 }
481
482 DeviceExt->AvailableClusters = ulCount;
483 DeviceExt->AvailableClustersValid = TRUE;
484
485 return STATUS_SUCCESS;
486 }
487
488
489 /*
490 * FUNCTION: Counts free clusters in a FAT32 table
491 */
492 static
493 NTSTATUS
494 FAT32CountAvailableClusters(
495 PDEVICE_EXTENSION DeviceExt)
496 {
497 PULONG Block;
498 PULONG BlockEnd;
499 PVOID BaseAddress = NULL;
500 ULONG ulCount = 0;
501 ULONG i;
502 ULONG ChunkSize;
503 PVOID Context = NULL;
504 LARGE_INTEGER Offset;
505 ULONG FatLength;
506
507 ChunkSize = CACHEPAGESIZE(DeviceExt);
508 FatLength = (DeviceExt->FatInfo.NumberOfClusters + 2);
509
510 for (i = 2; i < FatLength; )
511 {
512 Offset.QuadPart = ROUND_DOWN(i * 4, ChunkSize);
513 _SEH2_TRY
514 {
515 CcMapData(DeviceExt->FATFileObject, &Offset, ChunkSize, MAP_WAIT, &Context, &BaseAddress);
516 }
517 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
518 {
519 DPRINT1("CcMapData(Offset %x, Length %u) failed\n", (ULONG)Offset.QuadPart, ChunkSize);
520 _SEH2_YIELD(return _SEH2_GetExceptionCode());
521 }
522 _SEH2_END;
523 Block = (PULONG)((ULONG_PTR)BaseAddress + (i * 4) % ChunkSize);
524 BlockEnd = (PULONG)((ULONG_PTR)BaseAddress + ChunkSize);
525
526 /* Now process the whole block */
527 while (Block < BlockEnd && i < FatLength)
528 {
529 if ((*Block & 0x0fffffff) == 0)
530 ulCount++;
531 Block++;
532 i++;
533 }
534
535 CcUnpinData(Context);
536 }
537
538 DeviceExt->AvailableClusters = ulCount;
539 DeviceExt->AvailableClustersValid = TRUE;
540
541 return STATUS_SUCCESS;
542 }
543
544 NTSTATUS
545 CountAvailableClusters(
546 PDEVICE_EXTENSION DeviceExt,
547 PLARGE_INTEGER Clusters)
548 {
549 NTSTATUS Status = STATUS_SUCCESS;
550 ExAcquireResourceExclusiveLite (&DeviceExt->FatResource, TRUE);
551 if (!DeviceExt->AvailableClustersValid)
552 {
553 if (DeviceExt->FatInfo.FatType == FAT12)
554 Status = FAT12CountAvailableClusters(DeviceExt);
555 else if (DeviceExt->FatInfo.FatType == FAT16 || DeviceExt->FatInfo.FatType == FATX16)
556 Status = FAT16CountAvailableClusters(DeviceExt);
557 else
558 Status = FAT32CountAvailableClusters(DeviceExt);
559 }
560 if (Clusters != NULL)
561 {
562 Clusters->QuadPart = DeviceExt->AvailableClusters;
563 }
564 ExReleaseResourceLite (&DeviceExt->FatResource);
565
566 return Status;
567 }
568
569
570 /*
571 * FUNCTION: Writes a cluster to the FAT12 physical and in-memory tables
572 */
573 NTSTATUS
574 FAT12WriteCluster(
575 PDEVICE_EXTENSION DeviceExt,
576 ULONG ClusterToWrite,
577 ULONG NewValue,
578 PULONG OldValue)
579 {
580 ULONG FATOffset;
581 PUCHAR CBlock;
582 PVOID BaseAddress;
583 PVOID Context;
584 LARGE_INTEGER Offset;
585
586 Offset.QuadPart = 0;
587 _SEH2_TRY
588 {
589 CcPinRead(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, PIN_WAIT, &Context, &BaseAddress);
590 }
591 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
592 {
593 _SEH2_YIELD(return _SEH2_GetExceptionCode());
594 }
595 _SEH2_END;
596 CBlock = (PUCHAR)BaseAddress;
597
598 FATOffset = (ClusterToWrite * 12) / 8;
599 DPRINT("Writing 0x%x for 0x%x at 0x%x\n",
600 NewValue, ClusterToWrite, FATOffset);
601 if ((ClusterToWrite % 2) == 0)
602 {
603 *OldValue = CBlock[FATOffset] + ((CBlock[FATOffset + 1] & 0x0f) << 8);
604 CBlock[FATOffset] = (UCHAR)NewValue;
605 CBlock[FATOffset + 1] &= 0xf0;
606 CBlock[FATOffset + 1] |= (NewValue & 0xf00) >> 8;
607 }
608 else
609 {
610 *OldValue = (CBlock[FATOffset] >> 4) + (CBlock[FATOffset + 1] << 4);
611 CBlock[FATOffset] &= 0x0f;
612 CBlock[FATOffset] |= (NewValue & 0xf) << 4;
613 CBlock[FATOffset + 1] = (UCHAR)(NewValue >> 4);
614 }
615 /* Write the changed FAT sector(s) to disk */
616 CcSetDirtyPinnedData(Context, NULL);
617 CcUnpinData(Context);
618 return STATUS_SUCCESS;
619 }
620
621 /*
622 * FUNCTION: Writes a cluster to the FAT16 physical and in-memory tables
623 */
624 NTSTATUS
625 FAT16WriteCluster(
626 PDEVICE_EXTENSION DeviceExt,
627 ULONG ClusterToWrite,
628 ULONG NewValue,
629 PULONG OldValue)
630 {
631 PVOID BaseAddress;
632 ULONG FATOffset;
633 ULONG ChunkSize;
634 PVOID Context;
635 LARGE_INTEGER Offset;
636 PUSHORT Cluster;
637
638 ChunkSize = CACHEPAGESIZE(DeviceExt);
639 FATOffset = ClusterToWrite * 2;
640 Offset.QuadPart = ROUND_DOWN(FATOffset, ChunkSize);
641 _SEH2_TRY
642 {
643 CcPinRead(DeviceExt->FATFileObject, &Offset, ChunkSize, PIN_WAIT, &Context, &BaseAddress);
644 }
645 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
646 {
647 _SEH2_YIELD(return _SEH2_GetExceptionCode());
648 }
649 _SEH2_END;
650
651 DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue, FATOffset,
652 ClusterToWrite);
653 Cluster = ((PUSHORT)((char*)BaseAddress + (FATOffset % ChunkSize)));
654 *OldValue = *Cluster;
655 *Cluster = (USHORT)NewValue;
656 CcSetDirtyPinnedData(Context, NULL);
657 CcUnpinData(Context);
658 return STATUS_SUCCESS;
659 }
660
661 /*
662 * FUNCTION: Writes a cluster to the FAT32 physical tables
663 */
664 NTSTATUS
665 FAT32WriteCluster(
666 PDEVICE_EXTENSION DeviceExt,
667 ULONG ClusterToWrite,
668 ULONG NewValue,
669 PULONG OldValue)
670 {
671 PVOID BaseAddress;
672 ULONG FATOffset;
673 ULONG ChunkSize;
674 PVOID Context;
675 LARGE_INTEGER Offset;
676 PULONG Cluster;
677
678 ChunkSize = CACHEPAGESIZE(DeviceExt);
679
680 FATOffset = (ClusterToWrite * 4);
681 Offset.QuadPart = ROUND_DOWN(FATOffset, ChunkSize);
682 _SEH2_TRY
683 {
684 CcPinRead(DeviceExt->FATFileObject, &Offset, ChunkSize, PIN_WAIT, &Context, &BaseAddress);
685 }
686 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
687 {
688 _SEH2_YIELD(return _SEH2_GetExceptionCode());
689 }
690 _SEH2_END;
691
692 DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue, FATOffset,
693 ClusterToWrite);
694 Cluster = ((PULONG)((char*)BaseAddress + (FATOffset % ChunkSize)));
695 *OldValue = *Cluster & 0x0fffffff;
696 *Cluster = (*Cluster & 0xf0000000) | (NewValue & 0x0fffffff);
697
698 CcSetDirtyPinnedData(Context, NULL);
699 CcUnpinData(Context);
700
701 return STATUS_SUCCESS;
702 }
703
704
705 /*
706 * FUNCTION: Write a changed FAT entry
707 */
708 NTSTATUS
709 WriteCluster(
710 PDEVICE_EXTENSION DeviceExt,
711 ULONG ClusterToWrite,
712 ULONG NewValue)
713 {
714 NTSTATUS Status;
715 ULONG OldValue;
716
717 ExAcquireResourceExclusiveLite (&DeviceExt->FatResource, TRUE);
718 Status = DeviceExt->WriteCluster(DeviceExt, ClusterToWrite, NewValue, &OldValue);
719 if (DeviceExt->AvailableClustersValid)
720 {
721 if (OldValue && NewValue == 0)
722 InterlockedIncrement((PLONG)&DeviceExt->AvailableClusters);
723 else if (OldValue == 0 && NewValue)
724 InterlockedDecrement((PLONG)&DeviceExt->AvailableClusters);
725 }
726 ExReleaseResourceLite(&DeviceExt->FatResource);
727 return Status;
728 }
729
730 /*
731 * FUNCTION: Converts the cluster number to a sector number for this physical
732 * device
733 */
734 ULONGLONG
735 ClusterToSector(
736 PDEVICE_EXTENSION DeviceExt,
737 ULONG Cluster)
738 {
739 return DeviceExt->FatInfo.dataStart +
740 ((ULONGLONG)(Cluster - 2) * DeviceExt->FatInfo.SectorsPerCluster);
741
742 }
743
744 /*
745 * FUNCTION: Retrieve the next cluster depending on the FAT type
746 */
747 NTSTATUS
748 GetNextCluster(
749 PDEVICE_EXTENSION DeviceExt,
750 ULONG CurrentCluster,
751 PULONG NextCluster)
752 {
753 NTSTATUS Status;
754
755 DPRINT("GetNextCluster(DeviceExt %p, CurrentCluster %x)\n",
756 DeviceExt, CurrentCluster);
757
758 if (CurrentCluster == 0)
759 {
760 DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
761 if (VfatGlobalData->Flags & VFAT_BREAK_ON_CORRUPTION)
762 ASSERT(CurrentCluster != 0);
763 return STATUS_FILE_CORRUPT_ERROR;
764 }
765
766 ExAcquireResourceSharedLite(&DeviceExt->FatResource, TRUE);
767 Status = DeviceExt->GetNextCluster(DeviceExt, CurrentCluster, NextCluster);
768 ExReleaseResourceLite(&DeviceExt->FatResource);
769
770 return Status;
771 }
772
773 /*
774 * FUNCTION: Retrieve the next cluster depending on the FAT type
775 */
776 NTSTATUS
777 GetNextClusterExtend(
778 PDEVICE_EXTENSION DeviceExt,
779 ULONG CurrentCluster,
780 PULONG NextCluster)
781 {
782 ULONG NewCluster;
783 NTSTATUS Status;
784
785 DPRINT("GetNextClusterExtend(DeviceExt %p, CurrentCluster %x)\n",
786 DeviceExt, CurrentCluster);
787
788 ExAcquireResourceExclusiveLite(&DeviceExt->FatResource, TRUE);
789 /*
790 * If the file hasn't any clusters allocated then we need special
791 * handling
792 */
793 if (CurrentCluster == 0)
794 {
795 Status = DeviceExt->FindAndMarkAvailableCluster(DeviceExt, &NewCluster);
796 if (!NT_SUCCESS(Status))
797 {
798 ExReleaseResourceLite(&DeviceExt->FatResource);
799 return Status;
800 }
801
802 *NextCluster = NewCluster;
803 ExReleaseResourceLite(&DeviceExt->FatResource);
804 return STATUS_SUCCESS;
805 }
806
807 Status = DeviceExt->GetNextCluster(DeviceExt, CurrentCluster, NextCluster);
808
809 if ((*NextCluster) == 0xFFFFFFFF)
810 {
811 /* We are after last existing cluster, we must add one to file */
812 /* Firstly, find the next available open allocation unit and
813 mark it as end of file */
814 Status = DeviceExt->FindAndMarkAvailableCluster(DeviceExt, &NewCluster);
815 if (!NT_SUCCESS(Status))
816 {
817 ExReleaseResourceLite(&DeviceExt->FatResource);
818 return Status;
819 }
820
821 /* Now, write the AU of the LastCluster with the value of the newly
822 found AU */
823 WriteCluster(DeviceExt, CurrentCluster, NewCluster);
824 *NextCluster = NewCluster;
825 }
826
827 ExReleaseResourceLite(&DeviceExt->FatResource);
828 return Status;
829 }
830
831 /*
832 * FUNCTION: Retrieve the dirty status
833 */
834 NTSTATUS
835 GetDirtyStatus(
836 PDEVICE_EXTENSION DeviceExt,
837 PBOOLEAN DirtyStatus)
838 {
839 NTSTATUS Status;
840
841 DPRINT("GetDirtyStatus(DeviceExt %p)\n", DeviceExt);
842
843 /* FAT12 has no dirty bit */
844 if (DeviceExt->FatInfo.FatType == FAT12)
845 {
846 *DirtyStatus = FALSE;
847 return STATUS_SUCCESS;
848 }
849
850 /* Not really in the FAT, but share the lock because
851 * we're really low-level and shouldn't happent that often
852 * And call the appropriate function
853 */
854 ExAcquireResourceSharedLite(&DeviceExt->FatResource, TRUE);
855 Status = DeviceExt->GetDirtyStatus(DeviceExt, DirtyStatus);
856 ExReleaseResourceLite(&DeviceExt->FatResource);
857
858 return Status;
859 }
860
861 NTSTATUS
862 FAT16GetDirtyStatus(
863 PDEVICE_EXTENSION DeviceExt,
864 PBOOLEAN DirtyStatus)
865 {
866 LARGE_INTEGER Offset;
867 ULONG Length;
868 #ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
869 NTSTATUS Status;
870 #else
871 PVOID Context;
872 #endif
873 struct _BootSector * Sector;
874
875 /* We'll read the bootsector at 0 */
876 Offset.QuadPart = 0;
877 Length = DeviceExt->FatInfo.BytesPerSector;
878 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
879 /* Go through Cc for this */
880 _SEH2_TRY
881 {
882 CcPinRead(DeviceExt->VolumeFcb->FileObject, &Offset, Length, PIN_WAIT, &Context, (PVOID *)&Sector);
883 }
884 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
885 {
886 _SEH2_YIELD(return _SEH2_GetExceptionCode());
887 }
888 _SEH2_END;
889 #else
890 /* No Cc, do it the old way:
891 * - Allocate a big enough buffer
892 * - And read the disk
893 */
894 Sector = ExAllocatePoolWithTag(NonPagedPool, Length, TAG_VFAT);
895 if (Sector == NULL)
896 {
897 *DirtyStatus = TRUE;
898 return STATUS_INSUFFICIENT_RESOURCES;
899 }
900
901 Status = VfatReadDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
902 if (!NT_SUCCESS(Status))
903 {
904 *DirtyStatus = TRUE;
905 ExFreePoolWithTag(Sector, TAG_VFAT);
906 return Status;
907 }
908 #endif
909
910 /* Make sure we have a boot sector...
911 * FIXME: This check is a bit lame and should be improved
912 */
913 if (Sector->Signatur1 != 0xaa55)
914 {
915 /* Set we are dirty so that we don't attempt anything */
916 *DirtyStatus = TRUE;
917 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
918 CcUnpinData(Context);
919 #else
920 ExFreePoolWithTag(Sector, TAG_VFAT);
921 #endif
922 return STATUS_DISK_CORRUPT_ERROR;
923 }
924
925 /* Return the status of the dirty bit */
926 if (Sector->Res1 & FAT_DIRTY_BIT)
927 *DirtyStatus = TRUE;
928 else
929 *DirtyStatus = FALSE;
930
931 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
932 CcUnpinData(Context);
933 #else
934 ExFreePoolWithTag(Sector, TAG_VFAT);
935 #endif
936 return STATUS_SUCCESS;
937 }
938
939 NTSTATUS
940 FAT32GetDirtyStatus(
941 PDEVICE_EXTENSION DeviceExt,
942 PBOOLEAN DirtyStatus)
943 {
944 LARGE_INTEGER Offset;
945 ULONG Length;
946 #ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
947 NTSTATUS Status;
948 #else
949 PVOID Context;
950 #endif
951 struct _BootSector32 * Sector;
952
953 /* We'll read the bootsector at 0 */
954 Offset.QuadPart = 0;
955 Length = DeviceExt->FatInfo.BytesPerSector;
956 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
957 /* Go through Cc for this */
958 _SEH2_TRY
959 {
960 CcPinRead(DeviceExt->VolumeFcb->FileObject, &Offset, Length, PIN_WAIT, &Context, (PVOID *)&Sector);
961 }
962 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
963 {
964 _SEH2_YIELD(return _SEH2_GetExceptionCode());
965 }
966 _SEH2_END;
967 #else
968 /* No Cc, do it the old way:
969 * - Allocate a big enough buffer
970 * - And read the disk
971 */
972 Sector = ExAllocatePoolWithTag(NonPagedPool, Length, TAG_VFAT);
973 if (Sector == NULL)
974 {
975 *DirtyStatus = TRUE;
976 return STATUS_INSUFFICIENT_RESOURCES;
977 }
978
979 Status = VfatReadDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
980 if (!NT_SUCCESS(Status))
981 {
982 *DirtyStatus = TRUE;
983 ExFreePoolWithTag(Sector, TAG_VFAT);
984 return Status;
985 }
986 #endif
987
988 /* Make sure we have a boot sector...
989 * FIXME: This check is a bit lame and should be improved
990 */
991 if (Sector->Signature1 != 0xaa55)
992 {
993 /* Set we are dirty so that we don't attempt anything */
994 *DirtyStatus = TRUE;
995 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
996 CcUnpinData(Context);
997 #else
998 ExFreePoolWithTag(Sector, TAG_VFAT);
999 #endif
1000 return STATUS_DISK_CORRUPT_ERROR;
1001 }
1002
1003 /* Return the status of the dirty bit */
1004 if (Sector->Res4 & FAT_DIRTY_BIT)
1005 *DirtyStatus = TRUE;
1006 else
1007 *DirtyStatus = FALSE;
1008
1009 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1010 CcUnpinData(Context);
1011 #else
1012 ExFreePoolWithTag(Sector, TAG_VFAT);
1013 #endif
1014 return STATUS_SUCCESS;
1015 }
1016
1017 /*
1018 * FUNCTION: Set the dirty status
1019 */
1020 NTSTATUS
1021 SetDirtyStatus(
1022 PDEVICE_EXTENSION DeviceExt,
1023 BOOLEAN DirtyStatus)
1024 {
1025 NTSTATUS Status;
1026
1027 DPRINT("SetDirtyStatus(DeviceExt %p, DirtyStatus %d)\n", DeviceExt, DirtyStatus);
1028
1029 /* FAT12 has no dirty bit */
1030 if (DeviceExt->FatInfo.FatType == FAT12)
1031 {
1032 return STATUS_SUCCESS;
1033 }
1034
1035 /* Not really in the FAT, but share the lock because
1036 * we're really low-level and shouldn't happent that often
1037 * And call the appropriate function
1038 * Acquire exclusive because we will modify ondisk value
1039 */
1040 ExAcquireResourceExclusiveLite(&DeviceExt->FatResource, TRUE);
1041 Status = DeviceExt->SetDirtyStatus(DeviceExt, DirtyStatus);
1042 ExReleaseResourceLite(&DeviceExt->FatResource);
1043
1044 return Status;
1045 }
1046
1047 NTSTATUS
1048 FAT16SetDirtyStatus(
1049 PDEVICE_EXTENSION DeviceExt,
1050 BOOLEAN DirtyStatus)
1051 {
1052 LARGE_INTEGER Offset;
1053 ULONG Length;
1054 #ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1055 NTSTATUS Status;
1056 #else
1057 PVOID Context;
1058 #endif
1059 struct _BootSector * Sector;
1060
1061 /* We'll read (and then write) the bootsector at 0 */
1062 Offset.QuadPart = 0;
1063 Length = DeviceExt->FatInfo.BytesPerSector;
1064 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1065 /* Go through Cc for this */
1066 _SEH2_TRY
1067 {
1068 CcPinRead(DeviceExt->VolumeFcb->FileObject, &Offset, Length, PIN_WAIT, &Context, (PVOID *)&Sector);
1069 }
1070 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1071 {
1072 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1073 }
1074 _SEH2_END;
1075 #else
1076 /* No Cc, do it the old way:
1077 * - Allocate a big enough buffer
1078 * - And read the disk
1079 */
1080 Sector = ExAllocatePoolWithTag(NonPagedPool, Length, TAG_VFAT);
1081 if (Sector == NULL)
1082 {
1083 return STATUS_INSUFFICIENT_RESOURCES;
1084 }
1085
1086 Status = VfatReadDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
1087 if (!NT_SUCCESS(Status))
1088 {
1089 ExFreePoolWithTag(Sector, TAG_VFAT);
1090 return Status;
1091 }
1092 #endif
1093
1094 /* Make sure we have a boot sector...
1095 * FIXME: This check is a bit lame and should be improved
1096 */
1097 if (Sector->Signatur1 != 0xaa55)
1098 {
1099 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1100 CcUnpinData(Context);
1101 #else
1102 ExFreePoolWithTag(Sector, TAG_VFAT);
1103 #endif
1104 return STATUS_DISK_CORRUPT_ERROR;
1105 }
1106
1107 /* Modify the dirty bit status according
1108 * to caller needs
1109 */
1110 if (!DirtyStatus)
1111 {
1112 Sector->Res1 &= ~FAT_DIRTY_BIT;
1113 }
1114 else
1115 {
1116 Sector->Res1 |= FAT_DIRTY_BIT;
1117 }
1118
1119 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1120 /* Mark boot sector dirty so that it gets written to the disk */
1121 CcSetDirtyPinnedData(Context, NULL);
1122 CcUnpinData(Context);
1123 return STATUS_SUCCESS;
1124 #else
1125 /* Write back the boot sector to the disk */
1126 Status = VfatWriteDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
1127 ExFreePoolWithTag(Sector, TAG_VFAT);
1128 return Status;
1129 #endif
1130 }
1131
1132 NTSTATUS
1133 FAT32SetDirtyStatus(
1134 PDEVICE_EXTENSION DeviceExt,
1135 BOOLEAN DirtyStatus)
1136 {
1137 LARGE_INTEGER Offset;
1138 ULONG Length;
1139 #ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1140 NTSTATUS Status;
1141 #else
1142 PVOID Context;
1143 #endif
1144 struct _BootSector32 * Sector;
1145
1146 /* We'll read (and then write) the bootsector at 0 */
1147 Offset.QuadPart = 0;
1148 Length = DeviceExt->FatInfo.BytesPerSector;
1149 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1150 /* Go through Cc for this */
1151 _SEH2_TRY
1152 {
1153 CcPinRead(DeviceExt->VolumeFcb->FileObject, &Offset, Length, PIN_WAIT, &Context, (PVOID *)&Sector);
1154 }
1155 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1156 {
1157 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1158 }
1159 _SEH2_END;
1160 #else
1161 /* No Cc, do it the old way:
1162 * - Allocate a big enough buffer
1163 * - And read the disk
1164 */
1165 Sector = ExAllocatePoolWithTag(NonPagedPool, Length, TAG_VFAT);
1166 if (Sector == NULL)
1167 {
1168 return STATUS_INSUFFICIENT_RESOURCES;
1169 }
1170
1171 Status = VfatReadDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
1172 if (!NT_SUCCESS(Status))
1173 {
1174 ExFreePoolWithTag(Sector, TAG_VFAT);
1175 return Status;
1176 }
1177 #endif
1178
1179 /* Make sure we have a boot sector...
1180 * FIXME: This check is a bit lame and should be improved
1181 */
1182 if (Sector->Signature1 != 0xaa55)
1183 {
1184 ASSERT(FALSE);
1185 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1186 CcUnpinData(Context);
1187 #else
1188 ExFreePoolWithTag(Sector, TAG_VFAT);
1189 #endif
1190 return STATUS_DISK_CORRUPT_ERROR;
1191 }
1192
1193 /* Modify the dirty bit status according
1194 * to caller needs
1195 */
1196 if (!DirtyStatus)
1197 {
1198 Sector->Res4 &= ~FAT_DIRTY_BIT;
1199 }
1200 else
1201 {
1202 Sector->Res4 |= FAT_DIRTY_BIT;
1203 }
1204
1205 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1206 /* Mark boot sector dirty so that it gets written to the disk */
1207 CcSetDirtyPinnedData(Context, NULL);
1208 CcUnpinData(Context);
1209 return STATUS_SUCCESS;
1210 #else
1211 /* Write back the boot sector to the disk */
1212 Status = VfatWriteDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
1213 ExFreePoolWithTag(Sector, TAG_VFAT);
1214 return Status;
1215 #endif
1216 }
1217
1218 NTSTATUS
1219 FAT32UpdateFreeClustersCount(
1220 PDEVICE_EXTENSION DeviceExt)
1221 {
1222 LARGE_INTEGER Offset;
1223 ULONG Length;
1224 #ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1225 NTSTATUS Status;
1226 #else
1227 PVOID Context;
1228 #endif
1229 struct _FsInfoSector * Sector;
1230
1231 if (!DeviceExt->AvailableClustersValid)
1232 {
1233 return STATUS_INVALID_PARAMETER;
1234 }
1235
1236 /* We'll read (and then write) the fsinfo sector */
1237 Offset.QuadPart = DeviceExt->FatInfo.FSInfoSector * DeviceExt->FatInfo.BytesPerSector;
1238 Length = DeviceExt->FatInfo.BytesPerSector;
1239 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1240 /* Go through Cc for this */
1241 _SEH2_TRY
1242 {
1243 CcPinRead(DeviceExt->VolumeFcb->FileObject, &Offset, Length, PIN_WAIT, &Context, (PVOID *)&Sector);
1244 }
1245 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1246 {
1247 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1248 }
1249 _SEH2_END;
1250 #else
1251 /* No Cc, do it the old way:
1252 * - Allocate a big enough buffer
1253 * - And read the disk
1254 */
1255 Sector = ExAllocatePoolWithTag(NonPagedPool, Length, TAG_VFAT);
1256 if (Sector == NULL)
1257 {
1258 return STATUS_INSUFFICIENT_RESOURCES;
1259 }
1260
1261 Status = VfatReadDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
1262 if (!NT_SUCCESS(Status))
1263 {
1264 ExFreePoolWithTag(Sector, TAG_VFAT);
1265 return Status;
1266 }
1267 #endif
1268
1269 /* Make sure we have a FSINFO sector */
1270 if (Sector->ExtBootSignature2 != 0x41615252 ||
1271 Sector->FSINFOSignature != 0x61417272 ||
1272 Sector->Signatur2 != 0xaa550000)
1273 {
1274 ASSERT(FALSE);
1275 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1276 CcUnpinData(Context);
1277 #else
1278 ExFreePoolWithTag(Sector, TAG_VFAT);
1279 #endif
1280 return STATUS_DISK_CORRUPT_ERROR;
1281 }
1282
1283 /* Update the free clusters count */
1284 Sector->FreeCluster = InterlockedCompareExchange((PLONG)&DeviceExt->AvailableClusters, 0, 0);
1285
1286 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1287 /* Mark FSINFO sector dirty so that it gets written to the disk */
1288 CcSetDirtyPinnedData(Context, NULL);
1289 CcUnpinData(Context);
1290 return STATUS_SUCCESS;
1291 #else
1292 /* Write back the FSINFO sector to the disk */
1293 Status = VfatWriteDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
1294 ExFreePoolWithTag(Sector, TAG_VFAT);
1295 return Status;
1296 #endif
1297 }
1298
1299 /* EOF */