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