3 * Copyright (C) 2002 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 * COPYRIGHT: See COPYING in the top level directory
21 * PROJECT: ReactOS kernel
22 * FILE: drivers/fs/vfat/fsctl.c
23 * PURPOSE: VFAT Filesystem
26 /* INCLUDES *****************************************************************/
33 /* FUNCTIONS ****************************************************************/
35 #define CACHEPAGESIZE(pDeviceExt) ((pDeviceExt)->FatInfo.BytesPerCluster > PAGE_SIZE ? \
36 (pDeviceExt)->FatInfo.BytesPerCluster : PAGE_SIZE)
41 PDEVICE_OBJECT DeviceToMount
,
42 PBOOLEAN RecognizedFS
,
47 PARTITION_INFORMATION PartitionInfo
;
48 DISK_GEOMETRY DiskGeometry
;
53 struct _BootSector
* Boot
;
54 struct _BootSectorFatX
* BootFatX
;
55 BOOLEAN PartitionInfoIsValid
= FALSE
;
57 DPRINT("VfatHasFileSystem\n");
59 *RecognizedFS
= FALSE
;
61 Size
= sizeof(DISK_GEOMETRY
);
62 Status
= VfatBlockDeviceIoControl(DeviceToMount
,
63 IOCTL_DISK_GET_DRIVE_GEOMETRY
,
69 if (!NT_SUCCESS(Status
))
71 DPRINT("VfatBlockDeviceIoControl failed (%x)\n", Status
);
75 FatInfo
.FixedMedia
= DiskGeometry
.MediaType
== FixedMedia
? TRUE
: FALSE
;
76 if (DiskGeometry
.MediaType
== FixedMedia
|| DiskGeometry
.MediaType
== RemovableMedia
)
78 // We have found a hard disk
79 Size
= sizeof(PARTITION_INFORMATION
);
80 Status
= VfatBlockDeviceIoControl(DeviceToMount
,
81 IOCTL_DISK_GET_PARTITION_INFO
,
87 if (!NT_SUCCESS(Status
))
89 DPRINT("VfatBlockDeviceIoControl failed (%x)\n", Status
);
93 DPRINT("Partition Information:\n");
94 DPRINT("StartingOffset %I64x\n", PartitionInfo
.StartingOffset
.QuadPart
/ 512);
95 DPRINT("PartitionLength %I64x\n", PartitionInfo
.PartitionLength
.QuadPart
/ 512);
96 DPRINT("HiddenSectors %u\n", PartitionInfo
.HiddenSectors
);
97 DPRINT("PartitionNumber %u\n", PartitionInfo
.PartitionNumber
);
98 DPRINT("PartitionType %u\n", PartitionInfo
.PartitionType
);
99 DPRINT("BootIndicator %u\n", PartitionInfo
.BootIndicator
);
100 DPRINT("RecognizedPartition %u\n", PartitionInfo
.RecognizedPartition
);
101 DPRINT("RewritePartition %u\n", PartitionInfo
.RewritePartition
);
102 if (PartitionInfo
.PartitionType
)
104 if (PartitionInfo
.PartitionType
== PARTITION_FAT_12
||
105 PartitionInfo
.PartitionType
== PARTITION_FAT_16
||
106 PartitionInfo
.PartitionType
== PARTITION_HUGE
||
107 PartitionInfo
.PartitionType
== PARTITION_FAT32
||
108 PartitionInfo
.PartitionType
== PARTITION_FAT32_XINT13
||
109 PartitionInfo
.PartitionType
== PARTITION_XINT13
)
111 PartitionInfoIsValid
= TRUE
;
112 *RecognizedFS
= TRUE
;
115 else if (DiskGeometry
.MediaType
== RemovableMedia
&&
116 PartitionInfo
.PartitionNumber
> 0 &&
117 PartitionInfo
.StartingOffset
.QuadPart
== 0 &&
118 PartitionInfo
.PartitionLength
.QuadPart
> 0)
120 /* This is possible a removable media formated as super floppy */
121 PartitionInfoIsValid
= TRUE
;
122 *RecognizedFS
= TRUE
;
127 *RecognizedFS
= TRUE
;
132 Boot
= ExAllocatePoolWithTag(NonPagedPool
, DiskGeometry
.BytesPerSector
, TAG_VFAT
);
135 return STATUS_INSUFFICIENT_RESOURCES
;
140 /* Try to recognize FAT12/FAT16/FAT32 partitions */
141 Status
= VfatReadDisk(DeviceToMount
, &Offset
, DiskGeometry
.BytesPerSector
, (PUCHAR
) Boot
, Override
);
142 if (NT_SUCCESS(Status
))
144 if (Boot
->Signatur1
!= 0xaa55)
146 *RecognizedFS
= FALSE
;
150 Boot
->BytesPerSector
!= 512 &&
151 Boot
->BytesPerSector
!= 1024 &&
152 Boot
->BytesPerSector
!= 2048 &&
153 Boot
->BytesPerSector
!= 4096)
155 DPRINT1("BytesPerSector %u\n", Boot
->BytesPerSector
);
156 *RecognizedFS
= FALSE
;
160 Boot
->FATCount
!= 1 &&
163 DPRINT1("FATCount %u\n", Boot
->FATCount
);
164 *RecognizedFS
= FALSE
;
168 Boot
->Media
!= 0xf0 &&
169 Boot
->Media
!= 0xf8 &&
170 Boot
->Media
!= 0xf9 &&
171 Boot
->Media
!= 0xfa &&
172 Boot
->Media
!= 0xfb &&
173 Boot
->Media
!= 0xfc &&
174 Boot
->Media
!= 0xfd &&
175 Boot
->Media
!= 0xfe &&
178 DPRINT1("Media %02x\n", Boot
->Media
);
179 *RecognizedFS
= FALSE
;
183 Boot
->SectorsPerCluster
!= 1 &&
184 Boot
->SectorsPerCluster
!= 2 &&
185 Boot
->SectorsPerCluster
!= 4 &&
186 Boot
->SectorsPerCluster
!= 8 &&
187 Boot
->SectorsPerCluster
!= 16 &&
188 Boot
->SectorsPerCluster
!= 32 &&
189 Boot
->SectorsPerCluster
!= 64 &&
190 Boot
->SectorsPerCluster
!= 128)
192 DPRINT1("SectorsPerCluster %02x\n", Boot
->SectorsPerCluster
);
193 *RecognizedFS
= FALSE
;
197 Boot
->BytesPerSector
* Boot
->SectorsPerCluster
> 32 * 1024)
199 DPRINT1("ClusterSize %dx\n", Boot
->BytesPerSector
* Boot
->SectorsPerCluster
);
200 *RecognizedFS
= FALSE
;
205 FatInfo
.VolumeID
= Boot
->VolumeID
;
206 FatInfo
.FATStart
= Boot
->ReservedSectors
;
207 FatInfo
.FATCount
= Boot
->FATCount
;
208 FatInfo
.FATSectors
= Boot
->FATSectors
? Boot
->FATSectors
: ((struct _BootSector32
*) Boot
)->FATSectors32
;
209 FatInfo
.BytesPerSector
= Boot
->BytesPerSector
;
210 FatInfo
.SectorsPerCluster
= Boot
->SectorsPerCluster
;
211 FatInfo
.BytesPerCluster
= FatInfo
.BytesPerSector
* FatInfo
.SectorsPerCluster
;
212 FatInfo
.rootDirectorySectors
= ((Boot
->RootEntries
* 32) + Boot
->BytesPerSector
- 1) / Boot
->BytesPerSector
;
213 FatInfo
.rootStart
= FatInfo
.FATStart
+ FatInfo
.FATCount
* FatInfo
.FATSectors
;
214 FatInfo
.dataStart
= FatInfo
.rootStart
+ FatInfo
.rootDirectorySectors
;
215 FatInfo
.Sectors
= Sectors
= Boot
->Sectors
? Boot
->Sectors
: Boot
->SectorsHuge
;
216 Sectors
-= Boot
->ReservedSectors
+ FatInfo
.FATCount
* FatInfo
.FATSectors
+ FatInfo
.rootDirectorySectors
;
217 FatInfo
.NumberOfClusters
= Sectors
/ Boot
->SectorsPerCluster
;
218 if (FatInfo
.NumberOfClusters
< 4085)
221 FatInfo
.FatType
= FAT12
;
222 FatInfo
.RootCluster
= (FatInfo
.rootStart
- 1) / FatInfo
.SectorsPerCluster
;
223 RtlCopyMemory(&FatInfo
.VolumeLabel
, &Boot
->VolumeLabel
, sizeof(FatInfo
.VolumeLabel
));
225 else if (FatInfo
.NumberOfClusters
>= 65525)
228 FatInfo
.FatType
= FAT32
;
229 FatInfo
.RootCluster
= ((struct _BootSector32
*) Boot
)->RootCluster
;
230 FatInfo
.rootStart
= FatInfo
.dataStart
+ ((FatInfo
.RootCluster
- 2) * FatInfo
.SectorsPerCluster
);
231 FatInfo
.VolumeID
= ((struct _BootSector32
*) Boot
)->VolumeID
;
232 RtlCopyMemory(&FatInfo
.VolumeLabel
, &((struct _BootSector32
*)Boot
)->VolumeLabel
, sizeof(FatInfo
.VolumeLabel
));
237 FatInfo
.FatType
= FAT16
;
238 FatInfo
.RootCluster
= FatInfo
.rootStart
/ FatInfo
.SectorsPerCluster
;
239 RtlCopyMemory(&FatInfo
.VolumeLabel
, &Boot
->VolumeLabel
, sizeof(FatInfo
.VolumeLabel
));
242 if (PartitionInfoIsValid
&&
243 FatInfo
.Sectors
> PartitionInfo
.PartitionLength
.QuadPart
/ FatInfo
.BytesPerSector
)
245 *RecognizedFS
= FALSE
;
248 if (pFatInfo
&& *RecognizedFS
)
258 if (!*RecognizedFS
&& PartitionInfoIsValid
)
260 BootFatX
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(struct _BootSectorFatX
), TAG_VFAT
);
261 if (BootFatX
== NULL
)
264 return STATUS_INSUFFICIENT_RESOURCES
;
269 /* Try to recognize FATX16/FATX32 partitions (Xbox) */
270 Status
= VfatReadDisk(DeviceToMount
, &Offset
, sizeof(struct _BootSectorFatX
), (PUCHAR
) BootFatX
, Override
);
271 if (NT_SUCCESS(Status
))
273 *RecognizedFS
= TRUE
;
274 if (BootFatX
->SysType
[0] != 'F' ||
275 BootFatX
->SysType
[1] != 'A' ||
276 BootFatX
->SysType
[2] != 'T' ||
277 BootFatX
->SysType
[3] != 'X')
279 DPRINT1("SysType %c%c%c%c\n", BootFatX
->SysType
[0], BootFatX
->SysType
[1], BootFatX
->SysType
[2], BootFatX
->SysType
[3]);
284 BootFatX
->SectorsPerCluster
!= 1 &&
285 BootFatX
->SectorsPerCluster
!= 2 &&
286 BootFatX
->SectorsPerCluster
!= 4 &&
287 BootFatX
->SectorsPerCluster
!= 8 &&
288 BootFatX
->SectorsPerCluster
!= 16 &&
289 BootFatX
->SectorsPerCluster
!= 32 &&
290 BootFatX
->SectorsPerCluster
!= 64 &&
291 BootFatX
->SectorsPerCluster
!= 128)
293 DPRINT1("SectorsPerCluster %lu\n", BootFatX
->SectorsPerCluster
);
299 FatInfo
.BytesPerSector
= DiskGeometry
.BytesPerSector
;
300 FatInfo
.SectorsPerCluster
= BootFatX
->SectorsPerCluster
;
301 FatInfo
.rootDirectorySectors
= BootFatX
->SectorsPerCluster
;
302 FatInfo
.BytesPerCluster
= BootFatX
->SectorsPerCluster
* DiskGeometry
.BytesPerSector
;
303 FatInfo
.Sectors
= (ULONG
)(PartitionInfo
.PartitionLength
.QuadPart
/ DiskGeometry
.BytesPerSector
);
304 if (FatInfo
.Sectors
/ FatInfo
.SectorsPerCluster
< 65525)
307 FatInfo
.FatType
= FATX16
;
312 FatInfo
.FatType
= FATX32
;
314 FatInfo
.VolumeID
= BootFatX
->VolumeID
;
315 FatInfo
.FATStart
= sizeof(struct _BootSectorFatX
) / DiskGeometry
.BytesPerSector
;
316 FatInfo
.FATCount
= BootFatX
->FATCount
;
318 ROUND_UP(FatInfo
.Sectors
/ FatInfo
.SectorsPerCluster
* (FatInfo
.FatType
== FATX16
? 2 : 4), 4096) /
319 FatInfo
.BytesPerSector
;
320 FatInfo
.rootStart
= FatInfo
.FATStart
+ FatInfo
.FATCount
* FatInfo
.FATSectors
;
321 FatInfo
.RootCluster
= (FatInfo
.rootStart
- 1) / FatInfo
.SectorsPerCluster
;
322 FatInfo
.dataStart
= FatInfo
.rootStart
+ FatInfo
.rootDirectorySectors
;
323 FatInfo
.NumberOfClusters
= (FatInfo
.Sectors
- FatInfo
.dataStart
) / FatInfo
.SectorsPerCluster
;
325 if (pFatInfo
&& *RecognizedFS
)
331 ExFreePool(BootFatX
);
334 DPRINT("VfatHasFileSystem done\n");
340 * FUNCTION: Mount the filesystem
345 PVFAT_IRP_CONTEXT IrpContext
)
347 PDEVICE_OBJECT DeviceObject
= NULL
;
348 PDEVICE_EXTENSION DeviceExt
= NULL
;
349 BOOLEAN RecognizedFS
;
352 PVFATFCB VolumeFcb
= NULL
;
354 PDEVICE_OBJECT DeviceToMount
;
356 UNICODE_STRING NameU
= RTL_CONSTANT_STRING(L
"\\$$Fat$$");
357 UNICODE_STRING VolumeNameU
= RTL_CONSTANT_STRING(L
"\\$$Volume$$");
362 DPRINT("VfatMount(IrpContext %p)\n", IrpContext
);
366 if (IrpContext
->DeviceObject
!= VfatGlobalData
->DeviceObject
)
368 Status
= STATUS_INVALID_DEVICE_REQUEST
;
372 DeviceToMount
= IrpContext
->Stack
->Parameters
.MountVolume
.DeviceObject
;
373 Vpb
= IrpContext
->Stack
->Parameters
.MountVolume
.Vpb
;
375 Status
= VfatHasFileSystem(DeviceToMount
, &RecognizedFS
, &FatInfo
, FALSE
);
376 if (!NT_SUCCESS(Status
))
381 if (RecognizedFS
== FALSE
)
383 DPRINT("VFAT: Unrecognized Volume\n");
384 Status
= STATUS_UNRECOGNIZED_VOLUME
;
388 /* Use prime numbers for the table size */
389 if (FatInfo
.FatType
== FAT12
)
391 HashTableSize
= 4099; // 4096 = 4 * 1024
393 else if (FatInfo
.FatType
== FAT16
||
394 FatInfo
.FatType
== FATX16
)
396 HashTableSize
= 16411; // 16384 = 16 * 1024
400 HashTableSize
= 65537; // 65536 = 64 * 1024;
402 DPRINT("VFAT: Recognized volume\n");
403 Status
= IoCreateDevice(VfatGlobalData
->DriverObject
,
404 ROUND_UP(sizeof (DEVICE_EXTENSION
), sizeof(ULONG
)) + sizeof(HASHENTRY
*) * HashTableSize
,
406 FILE_DEVICE_DISK_FILE_SYSTEM
,
407 DeviceToMount
->Characteristics
,
410 if (!NT_SUCCESS(Status
))
415 DeviceExt
= DeviceObject
->DeviceExtension
;
416 RtlZeroMemory(DeviceExt
, ROUND_UP(sizeof(DEVICE_EXTENSION
), sizeof(ULONG
)) + sizeof(HASHENTRY
*) * HashTableSize
);
417 DeviceExt
->FcbHashTable
= (HASHENTRY
**)((ULONG_PTR
)DeviceExt
+ ROUND_UP(sizeof(DEVICE_EXTENSION
), sizeof(ULONG
)));
418 DeviceExt
->HashTableSize
= HashTableSize
;
419 DeviceExt
->VolumeDevice
= DeviceObject
;
421 /* use same vpb as device disk */
422 DeviceObject
->Vpb
= Vpb
;
423 DeviceToMount
->Vpb
= Vpb
;
425 RtlCopyMemory(&DeviceExt
->FatInfo
, &FatInfo
, sizeof(FATINFO
));
427 DPRINT("BytesPerSector: %u\n", DeviceExt
->FatInfo
.BytesPerSector
);
428 DPRINT("SectorsPerCluster: %u\n", DeviceExt
->FatInfo
.SectorsPerCluster
);
429 DPRINT("FATCount: %u\n", DeviceExt
->FatInfo
.FATCount
);
430 DPRINT("FATSectors: %u\n", DeviceExt
->FatInfo
.FATSectors
);
431 DPRINT("RootStart: %u\n", DeviceExt
->FatInfo
.rootStart
);
432 DPRINT("DataStart: %u\n", DeviceExt
->FatInfo
.dataStart
);
433 if (DeviceExt
->FatInfo
.FatType
== FAT32
)
435 DPRINT("RootCluster: %u\n", DeviceExt
->FatInfo
.RootCluster
);
438 switch (DeviceExt
->FatInfo
.FatType
)
441 DeviceExt
->GetNextCluster
= FAT12GetNextCluster
;
442 DeviceExt
->FindAndMarkAvailableCluster
= FAT12FindAndMarkAvailableCluster
;
443 DeviceExt
->WriteCluster
= FAT12WriteCluster
;
444 DeviceExt
->CleanShutBitMask
= 0;
449 DeviceExt
->GetNextCluster
= FAT16GetNextCluster
;
450 DeviceExt
->FindAndMarkAvailableCluster
= FAT16FindAndMarkAvailableCluster
;
451 DeviceExt
->WriteCluster
= FAT16WriteCluster
;
452 DeviceExt
->CleanShutBitMask
= 0x8000;
457 DeviceExt
->GetNextCluster
= FAT32GetNextCluster
;
458 DeviceExt
->FindAndMarkAvailableCluster
= FAT32FindAndMarkAvailableCluster
;
459 DeviceExt
->WriteCluster
= FAT32WriteCluster
;
460 DeviceExt
->CleanShutBitMask
= 0x80000000;
464 if (DeviceExt
->FatInfo
.FatType
== FATX16
||
465 DeviceExt
->FatInfo
.FatType
== FATX32
)
467 DeviceExt
->Flags
|= VCB_IS_FATX
;
468 DeviceExt
->GetNextDirEntry
= FATXGetNextDirEntry
;
469 DeviceExt
->BaseDateYear
= 2000;
473 DeviceExt
->GetNextDirEntry
= FATGetNextDirEntry
;
474 DeviceExt
->BaseDateYear
= 1980;
477 DeviceExt
->StorageDevice
= DeviceToMount
;
478 DeviceExt
->StorageDevice
->Vpb
->DeviceObject
= DeviceObject
;
479 DeviceExt
->StorageDevice
->Vpb
->RealDevice
= DeviceExt
->StorageDevice
;
480 DeviceExt
->StorageDevice
->Vpb
->Flags
|= VPB_MOUNTED
;
481 DeviceObject
->StackSize
= DeviceExt
->StorageDevice
->StackSize
+ 1;
482 DeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
484 DPRINT("FsDeviceObject %p\n", DeviceObject
);
486 /* Initialize this resource early ... it's used in VfatCleanup */
487 ExInitializeResourceLite(&DeviceExt
->DirResource
);
489 DeviceExt
->IoVPB
= DeviceObject
->Vpb
;
490 DeviceExt
->SpareVPB
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(VPB
), TAG_VFAT
);
491 if (DeviceExt
->SpareVPB
== NULL
)
493 Status
= STATUS_INSUFFICIENT_RESOURCES
;
497 DeviceExt
->FATFileObject
= IoCreateStreamFileObject(NULL
, DeviceExt
->StorageDevice
);
498 Fcb
= vfatNewFCB(DeviceExt
, &NameU
);
501 Status
= STATUS_INSUFFICIENT_RESOURCES
;
505 Ccb
= ExAllocateFromNPagedLookasideList(&VfatGlobalData
->CcbLookasideList
);
508 Status
= STATUS_INSUFFICIENT_RESOURCES
;
512 RtlZeroMemory(Ccb
, sizeof (VFATCCB
));
513 DeviceExt
->FATFileObject
->FsContext
= Fcb
;
514 DeviceExt
->FATFileObject
->FsContext2
= Ccb
;
515 DeviceExt
->FATFileObject
->SectionObjectPointer
= &Fcb
->SectionObjectPointers
;
516 DeviceExt
->FATFileObject
->PrivateCacheMap
= NULL
;
517 DeviceExt
->FATFileObject
->Vpb
= DeviceObject
->Vpb
;
518 Fcb
->FileObject
= DeviceExt
->FATFileObject
;
520 Fcb
->Flags
|= FCB_IS_FAT
;
522 Fcb
->RFCB
.FileSize
.QuadPart
= DeviceExt
->FatInfo
.FATSectors
* DeviceExt
->FatInfo
.BytesPerSector
;
523 Fcb
->RFCB
.ValidDataLength
= Fcb
->RFCB
.FileSize
;
524 Fcb
->RFCB
.AllocationSize
= Fcb
->RFCB
.FileSize
;
528 CcInitializeCacheMap(DeviceExt
->FATFileObject
,
529 (PCC_FILE_SIZES
)(&Fcb
->RFCB
.AllocationSize
),
531 &VfatGlobalData
->CacheMgrCallbacks
,
534 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
536 Status
= _SEH2_GetExceptionCode();
541 DeviceExt
->LastAvailableCluster
= 2;
542 ExInitializeResourceLite(&DeviceExt
->FatResource
);
544 InitializeListHead(&DeviceExt
->FcbListHead
);
546 VolumeFcb
= vfatNewFCB(DeviceExt
, &VolumeNameU
);
547 if (VolumeFcb
== NULL
)
549 Status
= STATUS_INSUFFICIENT_RESOURCES
;
553 VolumeFcb
->Flags
= FCB_IS_VOLUME
;
554 VolumeFcb
->RFCB
.FileSize
.QuadPart
= (LONGLONG
) DeviceExt
->FatInfo
.Sectors
* DeviceExt
->FatInfo
.BytesPerSector
;
555 VolumeFcb
->RFCB
.ValidDataLength
= VolumeFcb
->RFCB
.FileSize
;
556 VolumeFcb
->RFCB
.AllocationSize
= VolumeFcb
->RFCB
.FileSize
;
557 DeviceExt
->VolumeFcb
= VolumeFcb
;
559 ExAcquireResourceExclusiveLite(&VfatGlobalData
->VolumeListLock
, TRUE
);
560 InsertHeadList(&VfatGlobalData
->VolumeListHead
, &DeviceExt
->VolumeListEntry
);
561 ExReleaseResourceLite(&VfatGlobalData
->VolumeListLock
);
563 /* read serial number */
564 DeviceObject
->Vpb
->SerialNumber
= DeviceExt
->FatInfo
.VolumeID
;
566 /* read volume label */
567 ReadVolumeLabel(DeviceExt
, DeviceObject
->Vpb
);
569 /* read clean shutdown bit status */
570 Status
= GetNextCluster(DeviceExt
, 1, &eocMark
);
571 if (NT_SUCCESS(Status
))
573 if (eocMark
& DeviceExt
->CleanShutBitMask
)
575 /* unset clean shutdown bit */
576 eocMark
&= ~DeviceExt
->CleanShutBitMask
;
577 WriteCluster(DeviceExt
, 1, eocMark
);
578 VolumeFcb
->Flags
|= VCB_CLEAR_DIRTY
;
582 VolumeFcb
->Flags
|= VCB_IS_DIRTY
;
584 FsRtlNotifyVolumeEvent(DeviceExt
->FATFileObject
, FSRTL_VOLUME_MOUNT
);
585 FsRtlNotifyInitializeSync(&DeviceExt
->NotifySync
);
586 InitializeListHead(&DeviceExt
->NotifyList
);
588 DPRINT("Mount success\n");
590 Status
= STATUS_SUCCESS
;
593 if (!NT_SUCCESS(Status
))
596 if (DeviceExt
&& DeviceExt
->FATFileObject
)
597 ObDereferenceObject (DeviceExt
->FATFileObject
);
598 if (DeviceExt
&& DeviceExt
->SpareVPB
)
599 ExFreePoolWithTag(DeviceExt
->SpareVPB
, TAG_VFAT
);
605 IoDeleteDevice(DeviceObject
);
613 * FUNCTION: Verify the filesystem
618 PVFAT_IRP_CONTEXT IrpContext
)
620 PDEVICE_OBJECT DeviceToVerify
;
621 NTSTATUS Status
= STATUS_SUCCESS
;
623 BOOLEAN RecognizedFS
;
624 PDEVICE_EXTENSION DeviceExt
;
626 DPRINT("VfatVerify(IrpContext %p)\n", IrpContext
);
628 DeviceToVerify
= IrpContext
->Stack
->Parameters
.VerifyVolume
.DeviceObject
;
629 DeviceExt
= DeviceToVerify
->DeviceExtension
;
630 Status
= VfatBlockDeviceIoControl(DeviceExt
->StorageDevice
,
631 IOCTL_DISK_CHECK_VERIFY
,
637 if (!NT_SUCCESS(Status
) && Status
!= STATUS_VERIFY_REQUIRED
)
639 DPRINT("VfatBlockDeviceIoControl() failed (Status %lx)\n", Status
);
640 Status
= STATUS_WRONG_VOLUME
;
644 Status
= VfatHasFileSystem(DeviceExt
->StorageDevice
, &RecognizedFS
, &FatInfo
, TRUE
);
645 if (!NT_SUCCESS(Status
) || RecognizedFS
== FALSE
)
647 Status
= STATUS_WRONG_VOLUME
;
649 else if (sizeof(FATINFO
) == RtlCompareMemory(&FatInfo
, &DeviceExt
->FatInfo
, sizeof(FATINFO
)))
651 DPRINT1("Same volume\n");
654 * Preformatted floppy disks have very often a serial number of 0000:0000.
655 * We should calculate a crc sum over the sectors from the root directory as secondary volume number.
656 * Each write to the root directory must update this crc sum.
659 if (!FatInfo
.FixedMedia
&& FatInfo
.FatType
>= FATX16
)
661 Status
= STATUS_WRONG_VOLUME
;
666 Status
= STATUS_WRONG_VOLUME
;
670 IrpContext
->Stack
->Parameters
.VerifyVolume
.Vpb
->RealDevice
->Flags
&= ~DO_VERIFY_VOLUME
;
679 PVFAT_IRP_CONTEXT IrpContext
)
681 DPRINT("VfatGetVolumeBitmap (IrpContext %p)\n", IrpContext
);
682 return STATUS_INVALID_DEVICE_REQUEST
;
688 VfatGetRetrievalPointers(
689 PVFAT_IRP_CONTEXT IrpContext
)
691 PIO_STACK_LOCATION Stack
;
693 PRETRIEVAL_POINTERS_BUFFER RetrievalPointers
;
694 PFILE_OBJECT FileObject
;
695 ULONG MaxExtentCount
;
697 PDEVICE_EXTENSION DeviceExt
;
699 ULONG CurrentCluster
;
703 DPRINT("VfatGetRetrievalPointers(IrpContext %p)\n", IrpContext
);
705 DeviceExt
= IrpContext
->DeviceExt
;
706 FileObject
= IrpContext
->FileObject
;
707 Stack
= IrpContext
->Stack
;
708 if (Stack
->Parameters
.DeviceIoControl
.InputBufferLength
< sizeof(STARTING_VCN_INPUT_BUFFER
) ||
709 Stack
->Parameters
.DeviceIoControl
.Type3InputBuffer
== NULL
)
711 return STATUS_INVALID_PARAMETER
;
714 if (IrpContext
->Irp
->UserBuffer
== NULL
||
715 Stack
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(RETRIEVAL_POINTERS_BUFFER
))
717 return STATUS_BUFFER_TOO_SMALL
;
720 Fcb
= FileObject
->FsContext
;
722 ExAcquireResourceSharedLite(&Fcb
->MainResource
, TRUE
);
724 Vcn
= ((PSTARTING_VCN_INPUT_BUFFER
)Stack
->Parameters
.DeviceIoControl
.Type3InputBuffer
)->StartingVcn
;
725 RetrievalPointers
= IrpContext
->Irp
->UserBuffer
;
727 MaxExtentCount
= ((Stack
->Parameters
.DeviceIoControl
.OutputBufferLength
- sizeof(RetrievalPointers
->ExtentCount
) - sizeof(RetrievalPointers
->StartingVcn
)) / sizeof(RetrievalPointers
->Extents
[0]));
729 if (Vcn
.QuadPart
>= Fcb
->RFCB
.AllocationSize
.QuadPart
/ DeviceExt
->FatInfo
.BytesPerCluster
)
731 Status
= STATUS_INVALID_PARAMETER
;
735 CurrentCluster
= FirstCluster
= vfatDirEntryGetFirstCluster(DeviceExt
, &Fcb
->entry
);
736 Status
= OffsetToCluster(DeviceExt
, FirstCluster
,
737 Vcn
.u
.LowPart
* DeviceExt
->FatInfo
.BytesPerCluster
,
738 &CurrentCluster
, FALSE
);
739 if (!NT_SUCCESS(Status
))
744 RetrievalPointers
->StartingVcn
= Vcn
;
745 RetrievalPointers
->ExtentCount
= 0;
746 RetrievalPointers
->Extents
[0].Lcn
.u
.HighPart
= 0;
747 RetrievalPointers
->Extents
[0].Lcn
.u
.LowPart
= CurrentCluster
- 2;
749 while (CurrentCluster
!= 0xffffffff && RetrievalPointers
->ExtentCount
< MaxExtentCount
)
751 LastCluster
= CurrentCluster
;
752 Status
= NextCluster(DeviceExt
, CurrentCluster
, &CurrentCluster
, FALSE
);
754 if (!NT_SUCCESS(Status
))
759 if (LastCluster
+ 1 != CurrentCluster
)
761 RetrievalPointers
->Extents
[RetrievalPointers
->ExtentCount
].NextVcn
= Vcn
;
762 RetrievalPointers
->ExtentCount
++;
763 if (RetrievalPointers
->ExtentCount
< MaxExtentCount
)
765 RetrievalPointers
->Extents
[RetrievalPointers
->ExtentCount
].Lcn
.u
.HighPart
= 0;
766 RetrievalPointers
->Extents
[RetrievalPointers
->ExtentCount
].Lcn
.u
.LowPart
= CurrentCluster
- 2;
771 IrpContext
->Irp
->IoStatus
.Information
= sizeof(RETRIEVAL_POINTERS_BUFFER
) + (sizeof(RetrievalPointers
->Extents
[0]) * (RetrievalPointers
->ExtentCount
- 1));
772 Status
= STATUS_SUCCESS
;
775 ExReleaseResourceLite(&Fcb
->MainResource
);
783 PVFAT_IRP_CONTEXT IrpContext
)
785 DPRINT("VfatMoveFile(IrpContext %p)\n", IrpContext
);
786 return STATUS_INVALID_DEVICE_REQUEST
;
792 PVFAT_IRP_CONTEXT IrpContext
)
796 DPRINT("VfatIsVolumeDirty(IrpContext %p)\n", IrpContext
);
798 if (IrpContext
->Stack
->Parameters
.FileSystemControl
.OutputBufferLength
!= sizeof(ULONG
))
799 return STATUS_INVALID_BUFFER_SIZE
;
800 else if (!IrpContext
->Irp
->AssociatedIrp
.SystemBuffer
)
801 return STATUS_INVALID_USER_BUFFER
;
803 Flags
= (PULONG
)IrpContext
->Irp
->AssociatedIrp
.SystemBuffer
;
806 if ((IrpContext
->DeviceExt
->VolumeFcb
->Flags
& VCB_IS_DIRTY
) &&
807 !(IrpContext
->DeviceExt
->VolumeFcb
->Flags
& VCB_CLEAR_DIRTY
))
809 *Flags
|= VOLUME_IS_DIRTY
;
812 return STATUS_SUCCESS
;
818 PVFAT_IRP_CONTEXT IrpContext
)
821 PDEVICE_EXTENSION DeviceExt
;
822 NTSTATUS Status
= STATUS_SUCCESS
;
824 DPRINT("VfatMarkVolumeDirty(IrpContext %p)\n", IrpContext
);
825 DeviceExt
= IrpContext
->DeviceExt
;
827 if (!(DeviceExt
->VolumeFcb
->Flags
& VCB_IS_DIRTY
))
829 Status
= GetNextCluster(DeviceExt
, 1, &eocMark
);
830 if (NT_SUCCESS(Status
))
832 /* unset clean shutdown bit */
833 eocMark
&= ~DeviceExt
->CleanShutBitMask
;
834 Status
= WriteCluster(DeviceExt
, 1, eocMark
);
838 DeviceExt
->VolumeFcb
->Flags
&= ~VCB_CLEAR_DIRTY
;
845 VfatLockOrUnlockVolume(
846 PVFAT_IRP_CONTEXT IrpContext
,
849 PFILE_OBJECT FileObject
;
850 PDEVICE_EXTENSION DeviceExt
;
853 DPRINT("VfatLockOrUnlockVolume(%p, %d)\n", IrpContext
, Lock
);
855 DeviceExt
= IrpContext
->DeviceExt
;
856 FileObject
= IrpContext
->FileObject
;
857 Fcb
= FileObject
->FsContext
;
859 /* Only allow locking with the volume open */
860 if (!(Fcb
->Flags
& FCB_IS_VOLUME
))
862 return STATUS_ACCESS_DENIED
;
865 /* Bail out if it's already in the demanded state */
866 if (((DeviceExt
->Flags
& VCB_VOLUME_LOCKED
) && Lock
) ||
867 (!(DeviceExt
->Flags
& VCB_VOLUME_LOCKED
) && !Lock
))
869 return STATUS_ACCESS_DENIED
;
872 /* Deny locking if we're not alone */
873 if (Lock
&& DeviceExt
->OpenHandleCount
!= 1)
875 return STATUS_ACCESS_DENIED
;
878 /* Finally, proceed */
881 DeviceExt
->Flags
|= VCB_VOLUME_LOCKED
;
885 DeviceExt
->Flags
&= ~VCB_VOLUME_LOCKED
;
888 return STATUS_SUCCESS
;
894 PVFAT_IRP_CONTEXT IrpContext
)
896 PDEVICE_EXTENSION DeviceExt
;
897 PLIST_ENTRY NextEntry
;
899 PFILE_OBJECT FileObject
;
903 DPRINT("VfatDismountVolume(%p)\n", IrpContext
);
905 DeviceExt
= IrpContext
->DeviceExt
;
906 FileObject
= IrpContext
->FileObject
;
908 /* We HAVE to be locked. Windows also allows dismount with no lock
909 * but we're here mainly for 1st stage, so KISS
911 if (!(DeviceExt
->Flags
& VCB_VOLUME_LOCKED
))
913 return STATUS_ACCESS_DENIED
;
916 /* Race condition? */
917 if (DeviceExt
->Flags
& VCB_DISMOUNT_PENDING
)
919 return STATUS_VOLUME_DISMOUNTED
;
922 /* Notify we'll dismount. Pass that point there's no reason we fail */
923 FsRtlNotifyVolumeEvent(IrpContext
->Stack
->FileObject
, FSRTL_VOLUME_DISMOUNT
);
925 ExAcquireResourceExclusiveLite(&DeviceExt
->FatResource
, TRUE
);
927 if (DeviceExt
->VolumeFcb
->Flags
& VCB_CLEAR_DIRTY
)
929 /* Set clean shutdown bit */
930 Status
= GetNextCluster(DeviceExt
, 1, &eocMark
);
931 if (NT_SUCCESS(Status
))
933 eocMark
|= DeviceExt
->CleanShutBitMask
;
934 if (NT_SUCCESS(WriteCluster(DeviceExt
, 1, eocMark
)))
935 DeviceExt
->VolumeFcb
->Flags
&= ~VCB_IS_DIRTY
;
939 /* Flush volume & files */
940 VfatFlushVolume(DeviceExt
, (PVFATFCB
)FileObject
->FsContext
);
942 /* Rebrowse the FCB in order to free them now */
943 while (!IsListEmpty(&DeviceExt
->FcbListHead
))
945 NextEntry
= RemoveHeadList(&DeviceExt
->FcbListHead
);
946 Fcb
= CONTAINING_RECORD(NextEntry
, VFATFCB
, FcbListEntry
);
950 /* Mark we're being dismounted */
951 DeviceExt
->Flags
|= VCB_DISMOUNT_PENDING
;
952 #ifndef ENABLE_SWAPOUT
953 IrpContext
->DeviceObject
->Vpb
->Flags
&= ~VPB_MOUNTED
;
956 ExReleaseResourceLite(&DeviceExt
->FatResource
);
958 /* Release a few resources and quit, we're done */
959 ExDeleteResourceLite(&DeviceExt
->DirResource
);
960 ExDeleteResourceLite(&DeviceExt
->FatResource
);
961 ObDereferenceObject(DeviceExt
->FATFileObject
);
963 return STATUS_SUCCESS
;
967 * FUNCTION: File system control
970 VfatFileSystemControl(
971 PVFAT_IRP_CONTEXT IrpContext
)
975 DPRINT("VfatFileSystemControl(IrpContext %p)\n", IrpContext
);
978 ASSERT(IrpContext
->Irp
);
979 ASSERT(IrpContext
->Stack
);
981 IrpContext
->Irp
->IoStatus
.Information
= 0;
983 switch (IrpContext
->MinorFunction
)
985 case IRP_MN_KERNEL_CALL
:
986 case IRP_MN_USER_FS_REQUEST
:
987 switch(IrpContext
->Stack
->Parameters
.DeviceIoControl
.IoControlCode
)
989 case FSCTL_GET_VOLUME_BITMAP
:
990 Status
= VfatGetVolumeBitmap(IrpContext
);
993 case FSCTL_GET_RETRIEVAL_POINTERS
:
994 Status
= VfatGetRetrievalPointers(IrpContext
);
997 case FSCTL_MOVE_FILE
:
998 Status
= VfatMoveFile(IrpContext
);
1001 case FSCTL_IS_VOLUME_DIRTY
:
1002 Status
= VfatIsVolumeDirty(IrpContext
);
1005 case FSCTL_MARK_VOLUME_DIRTY
:
1006 Status
= VfatMarkVolumeDirty(IrpContext
);
1009 case FSCTL_LOCK_VOLUME
:
1010 Status
= VfatLockOrUnlockVolume(IrpContext
, TRUE
);
1013 case FSCTL_UNLOCK_VOLUME
:
1014 Status
= VfatLockOrUnlockVolume(IrpContext
, FALSE
);
1017 case FSCTL_DISMOUNT_VOLUME
:
1018 Status
= VfatDismountVolume(IrpContext
);
1022 Status
= STATUS_INVALID_DEVICE_REQUEST
;
1026 case IRP_MN_MOUNT_VOLUME
:
1027 Status
= VfatMount(IrpContext
);
1030 case IRP_MN_VERIFY_VOLUME
:
1031 DPRINT("VFATFS: IRP_MN_VERIFY_VOLUME\n");
1032 Status
= VfatVerify(IrpContext
);
1036 DPRINT("VFAT FSC: MinorFunction %u\n", IrpContext
->MinorFunction
);
1037 Status
= STATUS_INVALID_DEVICE_REQUEST
;