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