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