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