b291ff2843d80bebb336d3158c01050100f25c71
[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 Clusters->QuadPart = DeviceExt->AvailableClusters;
561 ExReleaseResourceLite (&DeviceExt->FatResource);
562
563 return Status;
564 }
565
566
567 /*
568 * FUNCTION: Writes a cluster to the FAT12 physical and in-memory tables
569 */
570 NTSTATUS
571 FAT12WriteCluster(
572 PDEVICE_EXTENSION DeviceExt,
573 ULONG ClusterToWrite,
574 ULONG NewValue,
575 PULONG OldValue)
576 {
577 ULONG FATOffset;
578 PUCHAR CBlock;
579 PVOID BaseAddress;
580 PVOID Context;
581 LARGE_INTEGER Offset;
582
583 Offset.QuadPart = 0;
584 _SEH2_TRY
585 {
586 CcPinRead(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, PIN_WAIT, &Context, &BaseAddress);
587 }
588 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
589 {
590 _SEH2_YIELD(return _SEH2_GetExceptionCode());
591 }
592 _SEH2_END;
593 CBlock = (PUCHAR)BaseAddress;
594
595 FATOffset = (ClusterToWrite * 12) / 8;
596 DPRINT("Writing 0x%x for 0x%x at 0x%x\n",
597 NewValue, ClusterToWrite, FATOffset);
598 if ((ClusterToWrite % 2) == 0)
599 {
600 *OldValue = CBlock[FATOffset] + ((CBlock[FATOffset + 1] & 0x0f) << 8);
601 CBlock[FATOffset] = (UCHAR)NewValue;
602 CBlock[FATOffset + 1] &= 0xf0;
603 CBlock[FATOffset + 1] |= (NewValue & 0xf00) >> 8;
604 }
605 else
606 {
607 *OldValue = (CBlock[FATOffset] >> 4) + (CBlock[FATOffset + 1] << 4);
608 CBlock[FATOffset] &= 0x0f;
609 CBlock[FATOffset] |= (NewValue & 0xf) << 4;
610 CBlock[FATOffset + 1] = (UCHAR)(NewValue >> 4);
611 }
612 /* Write the changed FAT sector(s) to disk */
613 CcSetDirtyPinnedData(Context, NULL);
614 CcUnpinData(Context);
615 return STATUS_SUCCESS;
616 }
617
618 /*
619 * FUNCTION: Writes a cluster to the FAT16 physical and in-memory tables
620 */
621 NTSTATUS
622 FAT16WriteCluster(
623 PDEVICE_EXTENSION DeviceExt,
624 ULONG ClusterToWrite,
625 ULONG NewValue,
626 PULONG OldValue)
627 {
628 PVOID BaseAddress;
629 ULONG FATOffset;
630 ULONG ChunkSize;
631 PVOID Context;
632 LARGE_INTEGER Offset;
633 PUSHORT Cluster;
634
635 ChunkSize = CACHEPAGESIZE(DeviceExt);
636 FATOffset = ClusterToWrite * 2;
637 Offset.QuadPart = ROUND_DOWN(FATOffset, ChunkSize);
638 _SEH2_TRY
639 {
640 CcPinRead(DeviceExt->FATFileObject, &Offset, ChunkSize, PIN_WAIT, &Context, &BaseAddress);
641 }
642 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
643 {
644 _SEH2_YIELD(return _SEH2_GetExceptionCode());
645 }
646 _SEH2_END;
647
648 DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue, FATOffset,
649 ClusterToWrite);
650 Cluster = ((PUSHORT)((char*)BaseAddress + (FATOffset % ChunkSize)));
651 *OldValue = *Cluster;
652 *Cluster = (USHORT)NewValue;
653 CcSetDirtyPinnedData(Context, NULL);
654 CcUnpinData(Context);
655 return STATUS_SUCCESS;
656 }
657
658 /*
659 * FUNCTION: Writes a cluster to the FAT32 physical tables
660 */
661 NTSTATUS
662 FAT32WriteCluster(
663 PDEVICE_EXTENSION DeviceExt,
664 ULONG ClusterToWrite,
665 ULONG NewValue,
666 PULONG OldValue)
667 {
668 PVOID BaseAddress;
669 ULONG FATOffset;
670 ULONG ChunkSize;
671 PVOID Context;
672 LARGE_INTEGER Offset;
673 PULONG Cluster;
674
675 ChunkSize = CACHEPAGESIZE(DeviceExt);
676
677 FATOffset = (ClusterToWrite * 4);
678 Offset.QuadPart = ROUND_DOWN(FATOffset, ChunkSize);
679 _SEH2_TRY
680 {
681 CcPinRead(DeviceExt->FATFileObject, &Offset, ChunkSize, PIN_WAIT, &Context, &BaseAddress);
682 }
683 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
684 {
685 _SEH2_YIELD(return _SEH2_GetExceptionCode());
686 }
687 _SEH2_END;
688
689 DPRINT("Writing 0x%x for offset 0x%x 0x%x\n", NewValue, FATOffset,
690 ClusterToWrite);
691 Cluster = ((PULONG)((char*)BaseAddress + (FATOffset % ChunkSize)));
692 *OldValue = *Cluster & 0x0fffffff;
693 *Cluster = (*Cluster & 0xf0000000) | (NewValue & 0x0fffffff);
694
695 CcSetDirtyPinnedData(Context, NULL);
696 CcUnpinData(Context);
697
698 return STATUS_SUCCESS;
699 }
700
701
702 /*
703 * FUNCTION: Write a changed FAT entry
704 */
705 NTSTATUS
706 WriteCluster(
707 PDEVICE_EXTENSION DeviceExt,
708 ULONG ClusterToWrite,
709 ULONG NewValue)
710 {
711 NTSTATUS Status;
712 ULONG OldValue;
713
714 ExAcquireResourceExclusiveLite (&DeviceExt->FatResource, TRUE);
715 Status = DeviceExt->WriteCluster(DeviceExt, ClusterToWrite, NewValue, &OldValue);
716 if (DeviceExt->AvailableClustersValid)
717 {
718 if (OldValue && NewValue == 0)
719 InterlockedIncrement((PLONG)&DeviceExt->AvailableClusters);
720 else if (OldValue == 0 && NewValue)
721 InterlockedDecrement((PLONG)&DeviceExt->AvailableClusters);
722 }
723 ExReleaseResourceLite(&DeviceExt->FatResource);
724 return Status;
725 }
726
727 /*
728 * FUNCTION: Converts the cluster number to a sector number for this physical
729 * device
730 */
731 ULONGLONG
732 ClusterToSector(
733 PDEVICE_EXTENSION DeviceExt,
734 ULONG Cluster)
735 {
736 return DeviceExt->FatInfo.dataStart +
737 ((ULONGLONG)(Cluster - 2) * DeviceExt->FatInfo.SectorsPerCluster);
738
739 }
740
741 /*
742 * FUNCTION: Retrieve the next cluster depending on the FAT type
743 */
744 NTSTATUS
745 GetNextCluster(
746 PDEVICE_EXTENSION DeviceExt,
747 ULONG CurrentCluster,
748 PULONG NextCluster)
749 {
750 NTSTATUS Status;
751
752 DPRINT("GetNextCluster(DeviceExt %p, CurrentCluster %x)\n",
753 DeviceExt, CurrentCluster);
754
755 if (CurrentCluster == 0)
756 {
757 DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
758 if (VfatGlobalData->Flags & VFAT_BREAK_ON_CORRUPTION)
759 ASSERT(CurrentCluster != 0);
760 return STATUS_FILE_CORRUPT_ERROR;
761 }
762
763 ExAcquireResourceSharedLite(&DeviceExt->FatResource, TRUE);
764 Status = DeviceExt->GetNextCluster(DeviceExt, CurrentCluster, NextCluster);
765 ExReleaseResourceLite(&DeviceExt->FatResource);
766
767 return Status;
768 }
769
770 /*
771 * FUNCTION: Retrieve the next cluster depending on the FAT type
772 */
773 NTSTATUS
774 GetNextClusterExtend(
775 PDEVICE_EXTENSION DeviceExt,
776 ULONG CurrentCluster,
777 PULONG NextCluster)
778 {
779 ULONG NewCluster;
780 NTSTATUS Status;
781
782 DPRINT("GetNextClusterExtend(DeviceExt %p, CurrentCluster %x)\n",
783 DeviceExt, CurrentCluster);
784
785 ExAcquireResourceExclusiveLite(&DeviceExt->FatResource, TRUE);
786 /*
787 * If the file hasn't any clusters allocated then we need special
788 * handling
789 */
790 if (CurrentCluster == 0)
791 {
792 Status = DeviceExt->FindAndMarkAvailableCluster(DeviceExt, &NewCluster);
793 if (!NT_SUCCESS(Status))
794 {
795 ExReleaseResourceLite(&DeviceExt->FatResource);
796 return Status;
797 }
798
799 *NextCluster = NewCluster;
800 ExReleaseResourceLite(&DeviceExt->FatResource);
801 return STATUS_SUCCESS;
802 }
803
804 Status = DeviceExt->GetNextCluster(DeviceExt, CurrentCluster, NextCluster);
805
806 if ((*NextCluster) == 0xFFFFFFFF)
807 {
808 /* We are after last existing cluster, we must add one to file */
809 /* Firstly, find the next available open allocation unit and
810 mark it as end of file */
811 Status = DeviceExt->FindAndMarkAvailableCluster(DeviceExt, &NewCluster);
812 if (!NT_SUCCESS(Status))
813 {
814 ExReleaseResourceLite(&DeviceExt->FatResource);
815 return Status;
816 }
817
818 /* Now, write the AU of the LastCluster with the value of the newly
819 found AU */
820 WriteCluster(DeviceExt, CurrentCluster, NewCluster);
821 *NextCluster = NewCluster;
822 }
823
824 ExReleaseResourceLite(&DeviceExt->FatResource);
825 return Status;
826 }
827
828 /*
829 * FUNCTION: Retrieve the dirty status
830 */
831 NTSTATUS
832 GetDirtyStatus(
833 PDEVICE_EXTENSION DeviceExt,
834 PBOOLEAN DirtyStatus)
835 {
836 NTSTATUS Status;
837
838 DPRINT("GetDirtyStatus(DeviceExt %p)\n", DeviceExt);
839
840 /* FAT12 has no dirty bit */
841 if (DeviceExt->FatInfo.FatType == FAT12)
842 {
843 *DirtyStatus = FALSE;
844 return STATUS_SUCCESS;
845 }
846
847 /* Not really in the FAT, but share the lock because
848 * we're really low-level and shouldn't happent that often
849 * And call the appropriate function
850 */
851 ExAcquireResourceSharedLite(&DeviceExt->FatResource, TRUE);
852 Status = DeviceExt->GetDirtyStatus(DeviceExt, DirtyStatus);
853 ExReleaseResourceLite(&DeviceExt->FatResource);
854
855 return Status;
856 }
857
858 NTSTATUS
859 FAT16GetDirtyStatus(
860 PDEVICE_EXTENSION DeviceExt,
861 PBOOLEAN DirtyStatus)
862 {
863 LARGE_INTEGER Offset;
864 ULONG Length;
865 #ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
866 NTSTATUS Status;
867 #else
868 PVOID Context;
869 #endif
870 struct _BootSector * Sector;
871
872 /* We'll read the bootsector at 0 */
873 Offset.QuadPart = 0;
874 Length = DeviceExt->FatInfo.BytesPerSector;
875 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
876 /* Go through Cc for this */
877 _SEH2_TRY
878 {
879 CcPinRead(DeviceExt->VolumeFcb->FileObject, &Offset, Length, PIN_WAIT, &Context, (PVOID *)&Sector);
880 }
881 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
882 {
883 _SEH2_YIELD(return _SEH2_GetExceptionCode());
884 }
885 _SEH2_END;
886 #else
887 /* No Cc, do it the old way:
888 * - Allocate a big enough buffer
889 * - And read the disk
890 */
891 Sector = ExAllocatePoolWithTag(NonPagedPool, Length, TAG_VFAT);
892 if (Sector == NULL)
893 {
894 *DirtyStatus = TRUE;
895 return STATUS_INSUFFICIENT_RESOURCES;
896 }
897
898 Status = VfatReadDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
899 if (!NT_SUCCESS(Status))
900 {
901 *DirtyStatus = TRUE;
902 ExFreePoolWithTag(Sector, TAG_VFAT);
903 return Status;
904 }
905 #endif
906
907 /* Make sure we have a boot sector...
908 * FIXME: This check is a bit lame and should be improved
909 */
910 if (Sector->Signatur1 != 0xaa55)
911 {
912 /* Set we are dirty so that we don't attempt anything */
913 *DirtyStatus = TRUE;
914 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
915 CcUnpinData(Context);
916 #else
917 ExFreePoolWithTag(Sector, TAG_VFAT);
918 #endif
919 return STATUS_DISK_CORRUPT_ERROR;
920 }
921
922 /* Return the status of the dirty bit */
923 if (Sector->Res1 & FAT_DIRTY_BIT)
924 *DirtyStatus = TRUE;
925 else
926 *DirtyStatus = FALSE;
927
928 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
929 CcUnpinData(Context);
930 #else
931 ExFreePoolWithTag(Sector, TAG_VFAT);
932 #endif
933 return STATUS_SUCCESS;
934 }
935
936 NTSTATUS
937 FAT32GetDirtyStatus(
938 PDEVICE_EXTENSION DeviceExt,
939 PBOOLEAN DirtyStatus)
940 {
941 LARGE_INTEGER Offset;
942 ULONG Length;
943 #ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
944 NTSTATUS Status;
945 #else
946 PVOID Context;
947 #endif
948 struct _BootSector32 * Sector;
949
950 /* We'll read the bootsector at 0 */
951 Offset.QuadPart = 0;
952 Length = DeviceExt->FatInfo.BytesPerSector;
953 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
954 /* Go through Cc for this */
955 _SEH2_TRY
956 {
957 CcPinRead(DeviceExt->VolumeFcb->FileObject, &Offset, Length, PIN_WAIT, &Context, (PVOID *)&Sector);
958 }
959 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
960 {
961 _SEH2_YIELD(return _SEH2_GetExceptionCode());
962 }
963 _SEH2_END;
964 #else
965 /* No Cc, do it the old way:
966 * - Allocate a big enough buffer
967 * - And read the disk
968 */
969 Sector = ExAllocatePoolWithTag(NonPagedPool, Length, TAG_VFAT);
970 if (Sector == NULL)
971 {
972 *DirtyStatus = TRUE;
973 return STATUS_INSUFFICIENT_RESOURCES;
974 }
975
976 Status = VfatReadDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
977 if (!NT_SUCCESS(Status))
978 {
979 *DirtyStatus = TRUE;
980 ExFreePoolWithTag(Sector, TAG_VFAT);
981 return Status;
982 }
983 #endif
984
985 /* Make sure we have a boot sector...
986 * FIXME: This check is a bit lame and should be improved
987 */
988 if (Sector->Signature1 != 0xaa55)
989 {
990 /* Set we are dirty so that we don't attempt anything */
991 *DirtyStatus = TRUE;
992 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
993 CcUnpinData(Context);
994 #else
995 ExFreePoolWithTag(Sector, TAG_VFAT);
996 #endif
997 return STATUS_DISK_CORRUPT_ERROR;
998 }
999
1000 /* Return the status of the dirty bit */
1001 if (Sector->Res4 & FAT_DIRTY_BIT)
1002 *DirtyStatus = TRUE;
1003 else
1004 *DirtyStatus = FALSE;
1005
1006 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1007 CcUnpinData(Context);
1008 #else
1009 ExFreePoolWithTag(Sector, TAG_VFAT);
1010 #endif
1011 return STATUS_SUCCESS;
1012 }
1013
1014 /*
1015 * FUNCTION: Set the dirty status
1016 */
1017 NTSTATUS
1018 SetDirtyStatus(
1019 PDEVICE_EXTENSION DeviceExt,
1020 BOOLEAN DirtyStatus)
1021 {
1022 NTSTATUS Status;
1023
1024 DPRINT("SetDirtyStatus(DeviceExt %p, DirtyStatus %d)\n", DeviceExt, DirtyStatus);
1025
1026 /* FAT12 has no dirty bit */
1027 if (DeviceExt->FatInfo.FatType == FAT12)
1028 {
1029 return STATUS_SUCCESS;
1030 }
1031
1032 /* Not really in the FAT, but share the lock because
1033 * we're really low-level and shouldn't happent that often
1034 * And call the appropriate function
1035 * Acquire exclusive because we will modify ondisk value
1036 */
1037 ExAcquireResourceExclusiveLite(&DeviceExt->FatResource, TRUE);
1038 Status = DeviceExt->SetDirtyStatus(DeviceExt, DirtyStatus);
1039 ExReleaseResourceLite(&DeviceExt->FatResource);
1040
1041 return Status;
1042 }
1043
1044 NTSTATUS
1045 FAT16SetDirtyStatus(
1046 PDEVICE_EXTENSION DeviceExt,
1047 BOOLEAN DirtyStatus)
1048 {
1049 LARGE_INTEGER Offset;
1050 ULONG Length;
1051 #ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1052 NTSTATUS Status;
1053 #else
1054 PVOID Context;
1055 #endif
1056 struct _BootSector * Sector;
1057
1058 /* We'll read (and then write) the bootsector at 0 */
1059 Offset.QuadPart = 0;
1060 Length = DeviceExt->FatInfo.BytesPerSector;
1061 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1062 /* Go through Cc for this */
1063 _SEH2_TRY
1064 {
1065 CcPinRead(DeviceExt->VolumeFcb->FileObject, &Offset, Length, PIN_WAIT, &Context, (PVOID *)&Sector);
1066 }
1067 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1068 {
1069 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1070 }
1071 _SEH2_END;
1072 #else
1073 /* No Cc, do it the old way:
1074 * - Allocate a big enough buffer
1075 * - And read the disk
1076 */
1077 Sector = ExAllocatePoolWithTag(NonPagedPool, Length, TAG_VFAT);
1078 if (Sector == NULL)
1079 {
1080 return STATUS_INSUFFICIENT_RESOURCES;
1081 }
1082
1083 Status = VfatReadDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
1084 if (!NT_SUCCESS(Status))
1085 {
1086 ExFreePoolWithTag(Sector, TAG_VFAT);
1087 return Status;
1088 }
1089 #endif
1090
1091 /* Make sure we have a boot sector...
1092 * FIXME: This check is a bit lame and should be improved
1093 */
1094 if (Sector->Signatur1 != 0xaa55)
1095 {
1096 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1097 CcUnpinData(Context);
1098 #else
1099 ExFreePoolWithTag(Sector, TAG_VFAT);
1100 #endif
1101 return STATUS_DISK_CORRUPT_ERROR;
1102 }
1103
1104 /* Modify the dirty bit status according
1105 * to caller needs
1106 */
1107 if (!DirtyStatus)
1108 {
1109 Sector->Res1 &= ~FAT_DIRTY_BIT;
1110 }
1111 else
1112 {
1113 Sector->Res1 |= FAT_DIRTY_BIT;
1114 }
1115
1116 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1117 /* Mark boot sector dirty so that it gets written to the disk */
1118 CcSetDirtyPinnedData(Context, NULL);
1119 CcUnpinData(Context);
1120 return STATUS_SUCCESS;
1121 #else
1122 /* Write back the boot sector to the disk */
1123 Status = VfatWriteDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
1124 ExFreePoolWithTag(Sector, TAG_VFAT);
1125 return Status;
1126 #endif
1127 }
1128
1129 NTSTATUS
1130 FAT32SetDirtyStatus(
1131 PDEVICE_EXTENSION DeviceExt,
1132 BOOLEAN DirtyStatus)
1133 {
1134 LARGE_INTEGER Offset;
1135 ULONG Length;
1136 #ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1137 NTSTATUS Status;
1138 #else
1139 PVOID Context;
1140 #endif
1141 struct _BootSector32 * Sector;
1142
1143 /* We'll read (and then write) the bootsector at 0 */
1144 Offset.QuadPart = 0;
1145 Length = DeviceExt->FatInfo.BytesPerSector;
1146 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1147 /* Go through Cc for this */
1148 _SEH2_TRY
1149 {
1150 CcPinRead(DeviceExt->VolumeFcb->FileObject, &Offset, Length, PIN_WAIT, &Context, (PVOID *)&Sector);
1151 }
1152 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1153 {
1154 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1155 }
1156 _SEH2_END;
1157 #else
1158 /* No Cc, do it the old way:
1159 * - Allocate a big enough buffer
1160 * - And read the disk
1161 */
1162 Sector = ExAllocatePoolWithTag(NonPagedPool, Length, TAG_VFAT);
1163 if (Sector == NULL)
1164 {
1165 return STATUS_INSUFFICIENT_RESOURCES;
1166 }
1167
1168 Status = VfatReadDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
1169 if (!NT_SUCCESS(Status))
1170 {
1171 ExFreePoolWithTag(Sector, TAG_VFAT);
1172 return Status;
1173 }
1174 #endif
1175
1176 /* Make sure we have a boot sector...
1177 * FIXME: This check is a bit lame and should be improved
1178 */
1179 if (Sector->Signature1 != 0xaa55)
1180 {
1181 ASSERT(FALSE);
1182 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1183 CcUnpinData(Context);
1184 #else
1185 ExFreePoolWithTag(Sector, TAG_VFAT);
1186 #endif
1187 return STATUS_DISK_CORRUPT_ERROR;
1188 }
1189
1190 /* Modify the dirty bit status according
1191 * to caller needs
1192 */
1193 if (!DirtyStatus)
1194 {
1195 Sector->Res4 &= ~FAT_DIRTY_BIT;
1196 }
1197 else
1198 {
1199 Sector->Res4 |= FAT_DIRTY_BIT;
1200 }
1201
1202 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1203 /* Mark boot sector dirty so that it gets written to the disk */
1204 CcSetDirtyPinnedData(Context, NULL);
1205 CcUnpinData(Context);
1206 return STATUS_SUCCESS;
1207 #else
1208 /* Write back the boot sector to the disk */
1209 Status = VfatWriteDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
1210 ExFreePoolWithTag(Sector, TAG_VFAT);
1211 return Status;
1212 #endif
1213 }
1214
1215 NTSTATUS
1216 FAT32UpdateFreeClustersCount(
1217 PDEVICE_EXTENSION DeviceExt,
1218 ULONG Count,
1219 BOOLEAN Freed)
1220 {
1221 LARGE_INTEGER Offset;
1222 ULONG Length;
1223 #ifdef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1224 NTSTATUS Status;
1225 #else
1226 PVOID Context;
1227 #endif
1228 struct _FsInfoSector * Sector;
1229
1230 /* We'll read (and then write) the fsinfo sector */
1231 Offset.QuadPart = DeviceExt->FatInfo.FSInfoSector * DeviceExt->FatInfo.BytesPerSector;
1232 Length = DeviceExt->FatInfo.BytesPerSector;
1233 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1234 /* Go through Cc for this */
1235 _SEH2_TRY
1236 {
1237 CcPinRead(DeviceExt->VolumeFcb->FileObject, &Offset, Length, PIN_WAIT, &Context, (PVOID *)&Sector);
1238 }
1239 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1240 {
1241 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1242 }
1243 _SEH2_END;
1244 #else
1245 /* No Cc, do it the old way:
1246 * - Allocate a big enough buffer
1247 * - And read the disk
1248 */
1249 Sector = ExAllocatePoolWithTag(NonPagedPool, Length, TAG_VFAT);
1250 if (Sector == NULL)
1251 {
1252 return STATUS_INSUFFICIENT_RESOURCES;
1253 }
1254
1255 Status = VfatReadDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
1256 if (!NT_SUCCESS(Status))
1257 {
1258 ExFreePoolWithTag(Sector, TAG_VFAT);
1259 return Status;
1260 }
1261 #endif
1262
1263 /* Make sure we have a FSINFO sector */
1264 if (Sector->ExtBootSignature2 != 0x41615252 ||
1265 Sector->FSINFOSignature != 0x61417272 ||
1266 Sector->Signatur2 != 0xaa550000)
1267 {
1268 ASSERT(FALSE);
1269 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1270 CcUnpinData(Context);
1271 #else
1272 ExFreePoolWithTag(Sector, TAG_VFAT);
1273 #endif
1274 return STATUS_DISK_CORRUPT_ERROR;
1275 }
1276
1277 /* Update the free clusters count */
1278 if (Freed)
1279 {
1280 Sector->FreeCluster += Count;
1281 }
1282 else
1283 {
1284 Sector->FreeCluster -= Count;
1285 }
1286
1287 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
1288 /* Mark FSINFO sector dirty so that it gets written to the disk */
1289 CcSetDirtyPinnedData(Context, NULL);
1290 CcUnpinData(Context);
1291 return STATUS_SUCCESS;
1292 #else
1293 /* Write back the FSINFO sector to the disk */
1294 Status = VfatWriteDisk(DeviceExt->StorageDevice, &Offset, Length, (PUCHAR)Sector, FALSE);
1295 ExFreePoolWithTag(Sector, TAG_VFAT);
1296 return Status;
1297 #endif
1298 }
1299
1300 /* EOF */