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
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
19 * COPYRIGHT: See COPYING in the top level directory
20 * PROJECT: ReactOS kernel
21 * FILE: drivers/filesystems/ntfs/fsctl.c
22 * PURPOSE: NTFS filesystem driver
23 * PROGRAMMER: Eric Kohl
28 /* INCLUDES *****************************************************************/
35 /* GLOBALS *****************************************************************/
37 /* FUNCTIONS ****************************************************************/
40 NtfsHasFileSystem(PDEVICE_OBJECT DeviceToMount
)
42 * FUNCTION: Tests if the device contains a filesystem that can be mounted
46 PARTITION_INFORMATION PartitionInfo
;
47 DISK_GEOMETRY DiskGeometry
;
48 ULONG ClusterSize
, Size
, k
;
49 PBOOT_SECTOR BootSector
;
52 DPRINT1("NtfsHasFileSystem() called\n");
54 Size
= sizeof(DISK_GEOMETRY
);
55 Status
= NtfsDeviceIoControl(DeviceToMount
,
56 IOCTL_DISK_GET_DRIVE_GEOMETRY
,
62 if (!NT_SUCCESS(Status
))
64 DPRINT1("NtfsDeviceIoControl() failed (Status %lx)\n", Status
);
68 if (DiskGeometry
.MediaType
== FixedMedia
)
70 /* We have found a hard disk */
71 Size
= sizeof(PARTITION_INFORMATION
);
72 Status
= NtfsDeviceIoControl(DeviceToMount
,
73 IOCTL_DISK_GET_PARTITION_INFO
,
79 if (!NT_SUCCESS(Status
))
81 DPRINT1("NtfsDeviceIoControl() failed (Status %lx)\n", Status
);
85 if (PartitionInfo
.PartitionType
!= PARTITION_IFS
)
87 DPRINT1("Invalid partition type\n");
88 return(STATUS_UNRECOGNIZED_VOLUME
);
92 DPRINT1("BytesPerSector: %lu\n", DiskGeometry
.BytesPerSector
);
93 BootSector
= ExAllocatePoolWithTag(NonPagedPool
,
94 DiskGeometry
.BytesPerSector
, TAG_NTFS
);
95 if (BootSector
== NULL
)
97 return(STATUS_INSUFFICIENT_RESOURCES
);
100 Status
= NtfsReadSectors (DeviceToMount
,
103 DiskGeometry
.BytesPerSector
,
106 if (!NT_SUCCESS(Status
))
111 /* Check values of different fields. If those fields have not expected
112 * values, we fail, to avoid mounting partitions that Windows won't mount.
114 /* OEMID: this field must be NTFS */
115 if (RtlCompareMemory(BootSector
->OEMID
, "NTFS ", 8) != 8)
117 DPRINT1("Failed with NTFS-identifier: [%.8s]\n", BootSector
->OEMID
);
118 Status
= STATUS_UNRECOGNIZED_VOLUME
;
121 /* Unused0: this field must be COMPLETELY null */
124 if (BootSector
->BPB
.Unused0
[k
] != 0)
126 DPRINT1("Failed in field Unused0: [%.7s]\n", BootSector
->BPB
.Unused0
);
127 Status
= STATUS_UNRECOGNIZED_VOLUME
;
131 /* Unused3: this field must be COMPLETELY null */
134 if (BootSector
->BPB
.Unused3
[k
] != 0)
136 DPRINT1("Failed in field Unused3: [%.4s]\n", BootSector
->BPB
.Unused3
);
137 Status
= STATUS_UNRECOGNIZED_VOLUME
;
141 /* Check cluster size */
142 ClusterSize
= BootSector
->BPB
.BytesPerSector
* BootSector
->BPB
.SectorsPerCluster
;
143 if (ClusterSize
!= 512 && ClusterSize
!= 1024 &&
144 ClusterSize
!= 2048 && ClusterSize
!= 4096 &&
145 ClusterSize
!= 8192 && ClusterSize
!= 16384 &&
146 ClusterSize
!= 32768 && ClusterSize
!= 65536)
148 DPRINT1("Cluster size failed: %hu, %hu, %hu\n", BootSector
->BPB
.BytesPerSector
,
149 BootSector
->BPB
.SectorsPerCluster
,
151 Status
= STATUS_UNRECOGNIZED_VOLUME
;
156 ExFreePool(BootSector
);
162 NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject
,
163 PDEVICE_EXTENSION DeviceExt
)
165 DISK_GEOMETRY DiskGeometry
;
166 PFILE_RECORD_HEADER MftRecord
;
167 PFILE_RECORD_HEADER VolumeRecord
;
168 PVOLINFO_ATTRIBUTE VolumeInfo
;
169 PBOOT_SECTOR BootSector
;
170 PATTRIBUTE Attribute
;
173 PNTFS_INFO NtfsInfo
= &DeviceExt
->NtfsInfo
;
175 DPRINT("NtfsGetVolumeData() called\n");
177 Size
= sizeof(DISK_GEOMETRY
);
178 Status
= NtfsDeviceIoControl(DeviceObject
,
179 IOCTL_DISK_GET_DRIVE_GEOMETRY
,
185 if (!NT_SUCCESS(Status
))
187 DPRINT("NtfsDeviceIoControl() failed (Status %lx)\n", Status
);
191 DPRINT("BytesPerSector: %lu\n", DiskGeometry
.BytesPerSector
);
192 BootSector
= ExAllocatePoolWithTag(NonPagedPool
,
193 DiskGeometry
.BytesPerSector
, TAG_NTFS
);
194 if (BootSector
== NULL
)
196 return(STATUS_INSUFFICIENT_RESOURCES
);
199 Status
= NtfsReadSectors(DeviceObject
,
200 0, /* Partition boot sector */
202 DiskGeometry
.BytesPerSector
,
205 if (!NT_SUCCESS(Status
))
207 ExFreePool(BootSector
);
211 /* Read data from the bootsector */
212 NtfsInfo
->BytesPerSector
= BootSector
->BPB
.BytesPerSector
;
213 NtfsInfo
->SectorsPerCluster
= BootSector
->BPB
.SectorsPerCluster
;
214 NtfsInfo
->BytesPerCluster
= BootSector
->BPB
.BytesPerSector
* BootSector
->BPB
.SectorsPerCluster
;
215 NtfsInfo
->SectorCount
= BootSector
->EBPB
.SectorCount
;
217 NtfsInfo
->MftStart
.QuadPart
= BootSector
->EBPB
.MftLocation
;
218 NtfsInfo
->MftMirrStart
.QuadPart
= BootSector
->EBPB
.MftMirrLocation
;
219 NtfsInfo
->SerialNumber
= BootSector
->EBPB
.SerialNumber
;
220 if (BootSector
->EBPB
.ClustersPerMftRecord
> 0)
221 NtfsInfo
->BytesPerFileRecord
= BootSector
->EBPB
.ClustersPerMftRecord
* NtfsInfo
->BytesPerCluster
;
223 NtfsInfo
->BytesPerFileRecord
= 1 << (-BootSector
->EBPB
.ClustersPerMftRecord
);
225 DPRINT("Boot sector information:\n");
226 DPRINT(" BytesPerSector: %hu\n", BootSector
->BPB
.BytesPerSector
);
227 DPRINT(" SectorsPerCluster: %hu\n", BootSector
->BPB
.SectorsPerCluster
);
228 DPRINT(" SectorCount: %I64u\n", BootSector
->EBPB
.SectorCount
);
229 DPRINT(" MftStart: %I64u\n", BootSector
->EBPB
.MftLocation
);
230 DPRINT(" MftMirrStart: %I64u\n", BootSector
->EBPB
.MftMirrLocation
);
231 DPRINT(" ClustersPerMftRecord: %lx\n", BootSector
->EBPB
.ClustersPerMftRecord
);
232 DPRINT(" ClustersPerIndexRecord: %lx\n", BootSector
->EBPB
.ClustersPerIndexRecord
);
233 DPRINT(" SerialNumber: %I64x\n", BootSector
->EBPB
.SerialNumber
);
235 ExFreePool(BootSector
);
237 MftRecord
= ExAllocatePoolWithTag(NonPagedPool
,
238 NtfsInfo
->BytesPerFileRecord
, TAG_NTFS
);
239 if (MftRecord
== NULL
)
241 return STATUS_INSUFFICIENT_RESOURCES
;
244 Status
= NtfsReadSectors(DeviceObject
,
245 NtfsInfo
->MftStart
.u
.LowPart
* NtfsInfo
->SectorsPerCluster
,
246 NtfsInfo
->BytesPerFileRecord
/ NtfsInfo
->BytesPerSector
,
247 NtfsInfo
->BytesPerSector
,
250 if (!NT_SUCCESS(Status
))
252 ExFreePool(MftRecord
);
256 VolumeRecord
= ExAllocatePoolWithTag(NonPagedPool
, NtfsInfo
->BytesPerFileRecord
, TAG_NTFS
);
257 if (VolumeRecord
== NULL
)
259 ExFreePool (MftRecord
);
260 return STATUS_INSUFFICIENT_RESOURCES
;
263 /* Read Volume File (MFT index 3) */
264 DeviceExt
->StorageDevice
= DeviceObject
;
265 Status
= ReadFileRecord(DeviceExt
, 3, VolumeRecord
, MftRecord
);
266 if (!NT_SUCCESS(Status
))
268 ExFreePool(MftRecord
);
272 /* Enumerate attributes */
273 NtfsDumpFileAttributes (MftRecord
);
275 /* Enumerate attributes */
276 NtfsDumpFileAttributes (VolumeRecord
);
278 /* Get volume name */
279 Attribute
= FindAttribute (VolumeRecord
, AttributeVolumeName
, NULL
);
280 DPRINT("Attribute %p\n", Attribute
);
282 if (Attribute
!= NULL
&& ((PRESIDENT_ATTRIBUTE
)Attribute
)->ValueLength
!= 0)
284 DPRINT("Data length %lu\n", AttributeDataLength (Attribute
));
285 NtfsInfo
->VolumeLabelLength
=
286 min (((PRESIDENT_ATTRIBUTE
)Attribute
)->ValueLength
, MAXIMUM_VOLUME_LABEL_LENGTH
);
287 RtlCopyMemory (NtfsInfo
->VolumeLabel
,
288 (PVOID
)((ULONG_PTR
)Attribute
+ ((PRESIDENT_ATTRIBUTE
)Attribute
)->ValueOffset
),
289 NtfsInfo
->VolumeLabelLength
);
293 NtfsInfo
->VolumeLabelLength
= 0;
296 /* Get volume information */
297 Attribute
= FindAttribute (VolumeRecord
, AttributeVolumeInformation
, NULL
);
298 DPRINT("Attribute %p\n", Attribute
);
300 if (Attribute
!= NULL
&& ((PRESIDENT_ATTRIBUTE
)Attribute
)->ValueLength
!= 0)
302 DPRINT("Data length %lu\n", AttributeDataLength (Attribute
));
303 VolumeInfo
= (PVOID
)((ULONG_PTR
)Attribute
+ ((PRESIDENT_ATTRIBUTE
)Attribute
)->ValueOffset
);
305 NtfsInfo
->MajorVersion
= VolumeInfo
->MajorVersion
;
306 NtfsInfo
->MinorVersion
= VolumeInfo
->MinorVersion
;
307 NtfsInfo
->Flags
= VolumeInfo
->Flags
;
310 ExFreePool(MftRecord
);
311 ExFreePool(VolumeRecord
);
318 NtfsMountVolume(PDEVICE_OBJECT DeviceObject
,
321 PDEVICE_OBJECT NewDeviceObject
= NULL
;
322 PDEVICE_OBJECT DeviceToMount
;
323 PIO_STACK_LOCATION Stack
;
324 PNTFS_FCB Fcb
= NULL
;
325 PNTFS_CCB Ccb
= NULL
;
326 PNTFS_VCB Vcb
= NULL
;
329 DPRINT1("NtfsMountVolume() called\n");
331 if (DeviceObject
!= NtfsGlobalData
->DeviceObject
)
333 Status
= STATUS_INVALID_DEVICE_REQUEST
;
337 Stack
= IoGetCurrentIrpStackLocation(Irp
);
338 DeviceToMount
= Stack
->Parameters
.MountVolume
.DeviceObject
;
340 Status
= NtfsHasFileSystem(DeviceToMount
);
341 if (!NT_SUCCESS(Status
))
346 Status
= IoCreateDevice(NtfsGlobalData
->DriverObject
,
347 sizeof(DEVICE_EXTENSION
),
349 FILE_DEVICE_DISK_FILE_SYSTEM
,
353 if (!NT_SUCCESS(Status
))
356 NewDeviceObject
->Flags
|= DO_DIRECT_IO
;
357 Vcb
= (PVOID
)NewDeviceObject
->DeviceExtension
;
358 RtlZeroMemory(Vcb
, sizeof(NTFS_VCB
));
360 Vcb
->Identifier
.Type
= NTFS_TYPE_VCB
;
361 Vcb
->Identifier
.Size
= sizeof(NTFS_TYPE_VCB
);
363 Status
= NtfsGetVolumeData(DeviceToMount
,
365 if (!NT_SUCCESS(Status
))
368 NewDeviceObject
->Vpb
= DeviceToMount
->Vpb
;
370 Vcb
->StorageDevice
= DeviceToMount
;
371 Vcb
->StorageDevice
->Vpb
->DeviceObject
= NewDeviceObject
;
372 Vcb
->StorageDevice
->Vpb
->RealDevice
= Vcb
->StorageDevice
;
373 Vcb
->StorageDevice
->Vpb
->Flags
|= VPB_MOUNTED
;
374 NewDeviceObject
->StackSize
= Vcb
->StorageDevice
->StackSize
+ 1;
375 NewDeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
377 Vcb
->StreamFileObject
= IoCreateStreamFileObject(NULL
,
380 InitializeListHead(&Vcb
->FcbListHead
);
382 Fcb
= NtfsCreateFCB(NULL
, Vcb
);
385 Status
= STATUS_INSUFFICIENT_RESOURCES
;
389 Ccb
= ExAllocatePoolWithTag(NonPagedPool
,
394 Status
= STATUS_INSUFFICIENT_RESOURCES
;
397 RtlZeroMemory(Ccb
, sizeof(NTFS_CCB
));
399 Ccb
->Identifier
.Type
= NTFS_TYPE_CCB
;
400 Ccb
->Identifier
.Size
= sizeof(NTFS_TYPE_CCB
);
402 Vcb
->StreamFileObject
->FsContext
= Fcb
;
403 Vcb
->StreamFileObject
->FsContext2
= Ccb
;
404 Vcb
->StreamFileObject
->SectionObjectPointer
= &Fcb
->SectionObjectPointers
;
405 Vcb
->StreamFileObject
->PrivateCacheMap
= NULL
;
406 Vcb
->StreamFileObject
->Vpb
= Vcb
->Vpb
;
407 Ccb
->PtrFileObject
= Vcb
->StreamFileObject
;
408 Fcb
->FileObject
= Vcb
->StreamFileObject
;
409 Fcb
->Vcb
= (PDEVICE_EXTENSION
)Vcb
->StorageDevice
;
411 Fcb
->Flags
= FCB_IS_VOLUME_STREAM
;
413 Fcb
->RFCB
.FileSize
.QuadPart
= Vcb
->NtfsInfo
.SectorCount
* Vcb
->NtfsInfo
.BytesPerSector
;
414 Fcb
->RFCB
.ValidDataLength
.QuadPart
= Vcb
->NtfsInfo
.SectorCount
* Vcb
->NtfsInfo
.BytesPerSector
;
415 Fcb
->RFCB
.AllocationSize
.QuadPart
= Vcb
->NtfsInfo
.SectorCount
* Vcb
->NtfsInfo
.BytesPerSector
; /* Correct? */
417 // Fcb->Entry.ExtentLocationL = 0;
418 // Fcb->Entry.DataLengthL = DeviceExt->CdInfo.VolumeSpaceSize * BLOCKSIZE;
420 CcInitializeCacheMap(Vcb
->StreamFileObject
,
421 (PCC_FILE_SIZES
)(&Fcb
->RFCB
.AllocationSize
),
423 &(NtfsGlobalData
->CacheMgrCallbacks
),
426 ExInitializeResourceLite(&Vcb
->DirResource
);
428 KeInitializeSpinLock(&Vcb
->FcbListLock
);
430 /* Get serial number */
431 NewDeviceObject
->Vpb
->SerialNumber
= Vcb
->NtfsInfo
.SerialNumber
;
433 /* Get volume label */
434 NewDeviceObject
->Vpb
->VolumeLabelLength
= Vcb
->NtfsInfo
.VolumeLabelLength
;
435 RtlCopyMemory(NewDeviceObject
->Vpb
->VolumeLabel
,
436 Vcb
->NtfsInfo
.VolumeLabel
,
437 Vcb
->NtfsInfo
.VolumeLabelLength
);
439 Status
= STATUS_SUCCESS
;
442 if (!NT_SUCCESS(Status
))
445 if (Vcb
&& Vcb
->StreamFileObject
)
446 ObDereferenceObject(Vcb
->StreamFileObject
);
452 IoDeleteDevice(NewDeviceObject
);
455 DPRINT("NtfsMountVolume() done (Status: %lx)\n", Status
);
462 NtfsVerifyVolume(PDEVICE_OBJECT DeviceObject
,
465 DPRINT1("NtfsVerifyVolume() called\n");
467 return STATUS_WRONG_VOLUME
;
472 NtfsFsdFileSystemControl(PDEVICE_OBJECT DeviceObject
,
475 PIO_STACK_LOCATION Stack
;
478 DPRINT1("NtfsFileSystemControl() called\n");
480 Stack
= IoGetCurrentIrpStackLocation(Irp
);
482 switch (Stack
->MinorFunction
)
484 case IRP_MN_KERNEL_CALL
:
485 case IRP_MN_USER_FS_REQUEST
:
486 DPRINT("NTFS: IRP_MN_USER_FS_REQUEST/IRP_MN_KERNEL_CALL\n");
487 Status
= STATUS_INVALID_DEVICE_REQUEST
;
490 case IRP_MN_MOUNT_VOLUME
:
491 DPRINT("NTFS: IRP_MN_MOUNT_VOLUME\n");
492 Status
= NtfsMountVolume(DeviceObject
, Irp
);
495 case IRP_MN_VERIFY_VOLUME
:
496 DPRINT1("NTFS: IRP_MN_VERIFY_VOLUME\n");
497 Status
= NtfsVerifyVolume(DeviceObject
, Irp
);
501 DPRINT("NTFS FSC: MinorFunction %d\n", Stack
->MinorFunction
);
502 Status
= STATUS_INVALID_DEVICE_REQUEST
;
506 Irp
->IoStatus
.Status
= Status
;
507 Irp
->IoStatus
.Information
= 0;
509 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);