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