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 *****************************************************************/
37 UNICODE_STRING EmptyName
= RTL_CONSTANT_STRING(L
"");
39 /* FUNCTIONS ****************************************************************/
42 * FUNCTION: Tests if the device contains a filesystem that can be mounted
47 NtfsHasFileSystem(PDEVICE_OBJECT DeviceToMount
)
49 PARTITION_INFORMATION PartitionInfo
;
50 DISK_GEOMETRY DiskGeometry
;
51 ULONG ClusterSize
, Size
, k
;
52 PBOOT_SECTOR BootSector
;
55 DPRINT1("NtfsHasFileSystem() called\n");
57 Size
= sizeof(DISK_GEOMETRY
);
58 Status
= NtfsDeviceIoControl(DeviceToMount
,
59 IOCTL_DISK_GET_DRIVE_GEOMETRY
,
65 if (!NT_SUCCESS(Status
))
67 DPRINT1("NtfsDeviceIoControl() failed (Status %lx)\n", Status
);
71 if (DiskGeometry
.MediaType
== FixedMedia
)
73 /* We have found a hard disk */
74 Size
= sizeof(PARTITION_INFORMATION
);
75 Status
= NtfsDeviceIoControl(DeviceToMount
,
76 IOCTL_DISK_GET_PARTITION_INFO
,
82 if (!NT_SUCCESS(Status
))
84 DPRINT1("NtfsDeviceIoControl() failed (Status %lx)\n", Status
);
88 if (PartitionInfo
.PartitionType
!= PARTITION_IFS
)
90 DPRINT1("Invalid partition type\n");
91 return STATUS_UNRECOGNIZED_VOLUME
;
95 DPRINT1("BytesPerSector: %lu\n", DiskGeometry
.BytesPerSector
);
96 BootSector
= ExAllocatePoolWithTag(NonPagedPool
,
97 DiskGeometry
.BytesPerSector
,
99 if (BootSector
== NULL
)
101 return STATUS_INSUFFICIENT_RESOURCES
;
104 Status
= NtfsReadSectors(DeviceToMount
,
107 DiskGeometry
.BytesPerSector
,
110 if (!NT_SUCCESS(Status
))
116 * Check values of different fields. If those fields have not expected
117 * values, we fail, to avoid mounting partitions that Windows won't mount.
120 /* OEMID: this field must be NTFS */
121 if (RtlCompareMemory(BootSector
->OEMID
, "NTFS ", 8) != 8)
123 DPRINT1("Failed with NTFS-identifier: [%.8s]\n", BootSector
->OEMID
);
124 Status
= STATUS_UNRECOGNIZED_VOLUME
;
128 /* Unused0: this field must be COMPLETELY null */
129 for (k
= 0; k
< 7; k
++)
131 if (BootSector
->BPB
.Unused0
[k
] != 0)
133 DPRINT1("Failed in field Unused0: [%.7s]\n", BootSector
->BPB
.Unused0
);
134 Status
= STATUS_UNRECOGNIZED_VOLUME
;
139 /* Unused3: this field must be COMPLETELY null */
140 for (k
= 0; k
< 4; k
++)
142 if (BootSector
->BPB
.Unused3
[k
] != 0)
144 DPRINT1("Failed in field Unused3: [%.4s]\n", BootSector
->BPB
.Unused3
);
145 Status
= STATUS_UNRECOGNIZED_VOLUME
;
150 /* Check cluster size */
151 ClusterSize
= BootSector
->BPB
.BytesPerSector
* BootSector
->BPB
.SectorsPerCluster
;
152 if (ClusterSize
!= 512 && ClusterSize
!= 1024 &&
153 ClusterSize
!= 2048 && ClusterSize
!= 4096 &&
154 ClusterSize
!= 8192 && ClusterSize
!= 16384 &&
155 ClusterSize
!= 32768 && ClusterSize
!= 65536)
157 DPRINT1("Cluster size failed: %hu, %hu, %hu\n",
158 BootSector
->BPB
.BytesPerSector
,
159 BootSector
->BPB
.SectorsPerCluster
,
161 Status
= STATUS_UNRECOGNIZED_VOLUME
;
166 ExFreePool(BootSector
);
174 NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject
,
175 PDEVICE_EXTENSION DeviceExt
)
177 DISK_GEOMETRY DiskGeometry
;
178 PFILE_RECORD_HEADER VolumeRecord
;
179 PVOLINFO_ATTRIBUTE VolumeInfo
;
180 PBOOT_SECTOR BootSector
;
182 PNTFS_INFO NtfsInfo
= &DeviceExt
->NtfsInfo
;
184 PNTFS_ATTR_CONTEXT AttrCtxt
;
185 PNTFS_ATTR_RECORD Attribute
;
189 DPRINT("NtfsGetVolumeData() called\n");
191 Size
= sizeof(DISK_GEOMETRY
);
192 Status
= NtfsDeviceIoControl(DeviceObject
,
193 IOCTL_DISK_GET_DRIVE_GEOMETRY
,
199 if (!NT_SUCCESS(Status
))
201 DPRINT("NtfsDeviceIoControl() failed (Status %lx)\n", Status
);
205 DPRINT("BytesPerSector: %lu\n", DiskGeometry
.BytesPerSector
);
206 BootSector
= ExAllocatePoolWithTag(NonPagedPool
,
207 DiskGeometry
.BytesPerSector
,
209 if (BootSector
== NULL
)
211 return STATUS_INSUFFICIENT_RESOURCES
;
214 Status
= NtfsReadSectors(DeviceObject
,
215 0, /* Partition boot sector */
217 DiskGeometry
.BytesPerSector
,
220 if (!NT_SUCCESS(Status
))
222 ExFreePool(BootSector
);
226 /* Read data from the bootsector */
227 NtfsInfo
->BytesPerSector
= BootSector
->BPB
.BytesPerSector
;
228 NtfsInfo
->SectorsPerCluster
= BootSector
->BPB
.SectorsPerCluster
;
229 NtfsInfo
->BytesPerCluster
= BootSector
->BPB
.BytesPerSector
* BootSector
->BPB
.SectorsPerCluster
;
230 NtfsInfo
->SectorCount
= BootSector
->EBPB
.SectorCount
;
232 NtfsInfo
->MftStart
.QuadPart
= BootSector
->EBPB
.MftLocation
;
233 NtfsInfo
->MftMirrStart
.QuadPart
= BootSector
->EBPB
.MftMirrLocation
;
234 NtfsInfo
->SerialNumber
= BootSector
->EBPB
.SerialNumber
;
235 if (BootSector
->EBPB
.ClustersPerMftRecord
> 0)
236 NtfsInfo
->BytesPerFileRecord
= BootSector
->EBPB
.ClustersPerMftRecord
* NtfsInfo
->BytesPerCluster
;
238 NtfsInfo
->BytesPerFileRecord
= 1 << (-BootSector
->EBPB
.ClustersPerMftRecord
);
239 if (BootSector
->EBPB
.ClustersPerIndexRecord
> 0)
240 NtfsInfo
->BytesPerIndexRecord
= BootSector
->EBPB
.ClustersPerIndexRecord
* NtfsInfo
->BytesPerCluster
;
242 NtfsInfo
->BytesPerIndexRecord
= 1 << (-BootSector
->EBPB
.ClustersPerIndexRecord
);
244 DPRINT("Boot sector information:\n");
245 DPRINT(" BytesPerSector: %hu\n", BootSector
->BPB
.BytesPerSector
);
246 DPRINT(" SectorsPerCluster: %hu\n", BootSector
->BPB
.SectorsPerCluster
);
247 DPRINT(" SectorCount: %I64u\n", BootSector
->EBPB
.SectorCount
);
248 DPRINT(" MftStart: %I64u\n", BootSector
->EBPB
.MftLocation
);
249 DPRINT(" MftMirrStart: %I64u\n", BootSector
->EBPB
.MftMirrLocation
);
250 DPRINT(" ClustersPerMftRecord: %lx\n", BootSector
->EBPB
.ClustersPerMftRecord
);
251 DPRINT(" ClustersPerIndexRecord: %lx\n", BootSector
->EBPB
.ClustersPerIndexRecord
);
252 DPRINT(" SerialNumber: %I64x\n", BootSector
->EBPB
.SerialNumber
);
254 ExFreePool(BootSector
);
256 DeviceExt
->MasterFileTable
= ExAllocatePoolWithTag(NonPagedPool
,
257 NtfsInfo
->BytesPerFileRecord
,
259 if (DeviceExt
->MasterFileTable
== NULL
)
261 return STATUS_INSUFFICIENT_RESOURCES
;
264 Status
= NtfsReadSectors(DeviceObject
,
265 NtfsInfo
->MftStart
.u
.LowPart
* NtfsInfo
->SectorsPerCluster
,
266 NtfsInfo
->BytesPerFileRecord
/ NtfsInfo
->BytesPerSector
,
267 NtfsInfo
->BytesPerSector
,
268 (PVOID
)DeviceExt
->MasterFileTable
,
270 if (!NT_SUCCESS(Status
))
272 DPRINT1("Failed reading MFT.\n");
273 ExFreePool(DeviceExt
->MasterFileTable
);
277 Status
= FindAttribute(DeviceExt
, DeviceExt
->MasterFileTable
, AttributeData
, &EmptyName
, &DeviceExt
->MFTContext
);
278 if (!NT_SUCCESS(Status
))
280 DPRINT1("Can't find data attribute for Master File Table.\n");
281 ExFreePool(DeviceExt
->MasterFileTable
);
285 VolumeRecord
= ExAllocatePoolWithTag(NonPagedPool
,
286 NtfsInfo
->BytesPerFileRecord
,
288 if (VolumeRecord
== NULL
)
290 DPRINT1("Allocation failed for volume record\n");
291 ExFreePool(DeviceExt
->MasterFileTable
);
292 return STATUS_INSUFFICIENT_RESOURCES
;
295 /* Read Volume File (MFT index 3) */
296 DeviceExt
->StorageDevice
= DeviceObject
;
297 Status
= ReadFileRecord(DeviceExt
,
300 if (!NT_SUCCESS(Status
))
302 DPRINT1("Failed reading volume file\n");
303 ExFreePool(VolumeRecord
);
304 ExFreePool(DeviceExt
->MasterFileTable
);
308 /* Enumerate attributes */
309 NtfsDumpFileAttributes(DeviceExt
->MasterFileTable
);
311 /* Enumerate attributes */
312 NtfsDumpFileAttributes(VolumeRecord
);
314 /* Get volume name */
315 Status
= FindAttribute(DeviceExt
, VolumeRecord
, AttributeVolumeName
, &EmptyName
, &AttrCtxt
);
317 if (NT_SUCCESS(Status
) && AttrCtxt
->Record
.Resident
.ValueLength
!= 0)
319 Attribute
= &AttrCtxt
->Record
;
320 DPRINT("Data length %lu\n", AttributeDataLength(Attribute
));
321 NtfsInfo
->VolumeLabelLength
=
322 min (Attribute
->Resident
.ValueLength
, MAXIMUM_VOLUME_LABEL_LENGTH
);
323 RtlCopyMemory(NtfsInfo
->VolumeLabel
,
324 (PVOID
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
),
325 NtfsInfo
->VolumeLabelLength
);
326 VolumeNameU
= NtfsInfo
->VolumeLabel
;
330 NtfsInfo
->VolumeLabelLength
= 0;
334 VolumeFcb
= NtfsCreateFCB(VolumeNameU
, DeviceExt
);
335 if (VolumeFcb
== NULL
)
337 DPRINT1("Failed allocating volume FCB\n");
338 ExFreePool(VolumeRecord
);
339 ExFreePool(DeviceExt
->MasterFileTable
);
340 return STATUS_INSUFFICIENT_RESOURCES
;
343 VolumeFcb
->Flags
= FCB_IS_VOLUME
;
344 VolumeFcb
->RFCB
.FileSize
.QuadPart
= DeviceExt
->NtfsInfo
.SectorCount
* DeviceExt
->NtfsInfo
.BytesPerSector
;
345 VolumeFcb
->RFCB
.ValidDataLength
= VolumeFcb
->RFCB
.FileSize
;
346 VolumeFcb
->RFCB
.AllocationSize
= VolumeFcb
->RFCB
.FileSize
;
347 VolumeFcb
->MFTIndex
= 0;
348 DeviceExt
->VolumeFcb
= VolumeFcb
;
350 /* Get volume information */
351 Status
= FindAttribute(DeviceExt
, VolumeRecord
, AttributeVolumeInformation
, &EmptyName
, &AttrCtxt
);
353 if (NT_SUCCESS(Status
) && AttrCtxt
->Record
.Resident
.ValueLength
!= 0)
355 Attribute
= &AttrCtxt
->Record
;
356 DPRINT("Data length %lu\n", AttributeDataLength (Attribute
));
357 VolumeInfo
= (PVOID
)((ULONG_PTR
)Attribute
+ Attribute
->Resident
.ValueOffset
);
359 NtfsInfo
->MajorVersion
= VolumeInfo
->MajorVersion
;
360 NtfsInfo
->MinorVersion
= VolumeInfo
->MinorVersion
;
361 NtfsInfo
->Flags
= VolumeInfo
->Flags
;
364 ExFreePool(VolumeRecord
);
372 NtfsMountVolume(PDEVICE_OBJECT DeviceObject
,
375 PDEVICE_OBJECT NewDeviceObject
= NULL
;
376 PDEVICE_OBJECT DeviceToMount
;
377 PIO_STACK_LOCATION Stack
;
378 PNTFS_FCB Fcb
= NULL
;
379 PNTFS_CCB Ccb
= NULL
;
380 PNTFS_VCB Vcb
= NULL
;
383 DPRINT1("NtfsMountVolume() called\n");
385 if (DeviceObject
!= NtfsGlobalData
->DeviceObject
)
387 Status
= STATUS_INVALID_DEVICE_REQUEST
;
391 Stack
= IoGetCurrentIrpStackLocation(Irp
);
392 DeviceToMount
= Stack
->Parameters
.MountVolume
.DeviceObject
;
394 Status
= NtfsHasFileSystem(DeviceToMount
);
395 if (!NT_SUCCESS(Status
))
400 Status
= IoCreateDevice(NtfsGlobalData
->DriverObject
,
401 sizeof(DEVICE_EXTENSION
),
403 FILE_DEVICE_DISK_FILE_SYSTEM
,
407 if (!NT_SUCCESS(Status
))
410 NewDeviceObject
->Flags
|= DO_DIRECT_IO
;
411 Vcb
= (PVOID
)NewDeviceObject
->DeviceExtension
;
412 RtlZeroMemory(Vcb
, sizeof(NTFS_VCB
));
414 Vcb
->Identifier
.Type
= NTFS_TYPE_VCB
;
415 Vcb
->Identifier
.Size
= sizeof(NTFS_TYPE_VCB
);
417 Status
= NtfsGetVolumeData(DeviceToMount
,
419 if (!NT_SUCCESS(Status
))
422 NewDeviceObject
->Vpb
= DeviceToMount
->Vpb
;
424 Vcb
->StorageDevice
= DeviceToMount
;
425 Vcb
->StorageDevice
->Vpb
->DeviceObject
= NewDeviceObject
;
426 Vcb
->StorageDevice
->Vpb
->RealDevice
= Vcb
->StorageDevice
;
427 Vcb
->StorageDevice
->Vpb
->Flags
|= VPB_MOUNTED
;
428 NewDeviceObject
->StackSize
= Vcb
->StorageDevice
->StackSize
+ 1;
429 NewDeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
431 Vcb
->StreamFileObject
= IoCreateStreamFileObject(NULL
,
434 InitializeListHead(&Vcb
->FcbListHead
);
436 Fcb
= NtfsCreateFCB(NULL
, Vcb
);
439 Status
= STATUS_INSUFFICIENT_RESOURCES
;
443 Ccb
= ExAllocatePoolWithTag(NonPagedPool
,
448 Status
= STATUS_INSUFFICIENT_RESOURCES
;
452 RtlZeroMemory(Ccb
, sizeof(NTFS_CCB
));
454 Ccb
->Identifier
.Type
= NTFS_TYPE_CCB
;
455 Ccb
->Identifier
.Size
= sizeof(NTFS_TYPE_CCB
);
457 Vcb
->StreamFileObject
->FsContext
= Fcb
;
458 Vcb
->StreamFileObject
->FsContext2
= Ccb
;
459 Vcb
->StreamFileObject
->SectionObjectPointer
= &Fcb
->SectionObjectPointers
;
460 Vcb
->StreamFileObject
->PrivateCacheMap
= NULL
;
461 Vcb
->StreamFileObject
->Vpb
= Vcb
->Vpb
;
462 Ccb
->PtrFileObject
= Vcb
->StreamFileObject
;
463 Fcb
->FileObject
= Vcb
->StreamFileObject
;
464 Fcb
->Vcb
= (PDEVICE_EXTENSION
)Vcb
->StorageDevice
;
466 Fcb
->Flags
= FCB_IS_VOLUME_STREAM
;
468 Fcb
->RFCB
.FileSize
.QuadPart
= Vcb
->NtfsInfo
.SectorCount
* Vcb
->NtfsInfo
.BytesPerSector
;
469 Fcb
->RFCB
.ValidDataLength
.QuadPart
= Vcb
->NtfsInfo
.SectorCount
* Vcb
->NtfsInfo
.BytesPerSector
;
470 Fcb
->RFCB
.AllocationSize
.QuadPart
= Vcb
->NtfsInfo
.SectorCount
* Vcb
->NtfsInfo
.BytesPerSector
; /* Correct? */
472 // Fcb->Entry.ExtentLocationL = 0;
473 // Fcb->Entry.DataLengthL = DeviceExt->CdInfo.VolumeSpaceSize * BLOCKSIZE;
475 CcInitializeCacheMap(Vcb
->StreamFileObject
,
476 (PCC_FILE_SIZES
)(&Fcb
->RFCB
.AllocationSize
),
478 &(NtfsGlobalData
->CacheMgrCallbacks
),
481 ExInitializeResourceLite(&Vcb
->DirResource
);
483 KeInitializeSpinLock(&Vcb
->FcbListLock
);
485 /* Get serial number */
486 NewDeviceObject
->Vpb
->SerialNumber
= Vcb
->NtfsInfo
.SerialNumber
;
488 /* Get volume label */
489 NewDeviceObject
->Vpb
->VolumeLabelLength
= Vcb
->NtfsInfo
.VolumeLabelLength
;
490 RtlCopyMemory(NewDeviceObject
->Vpb
->VolumeLabel
,
491 Vcb
->NtfsInfo
.VolumeLabel
,
492 Vcb
->NtfsInfo
.VolumeLabelLength
);
494 Status
= STATUS_SUCCESS
;
497 if (!NT_SUCCESS(Status
))
500 if (Vcb
&& Vcb
->StreamFileObject
)
501 ObDereferenceObject(Vcb
->StreamFileObject
);
510 IoDeleteDevice(NewDeviceObject
);
513 DPRINT("NtfsMountVolume() done (Status: %lx)\n", Status
);
521 NtfsVerifyVolume(PDEVICE_OBJECT DeviceObject
,
524 UNREFERENCED_PARAMETER(DeviceObject
);
525 UNREFERENCED_PARAMETER(Irp
);
526 DPRINT1("NtfsVerifyVolume() called\n");
527 return STATUS_WRONG_VOLUME
;
533 NtfsFsdFileSystemControl(PDEVICE_OBJECT DeviceObject
,
536 PIO_STACK_LOCATION Stack
;
539 DPRINT1("NtfsFileSystemControl() called\n");
541 Stack
= IoGetCurrentIrpStackLocation(Irp
);
543 switch (Stack
->MinorFunction
)
545 case IRP_MN_KERNEL_CALL
:
546 case IRP_MN_USER_FS_REQUEST
:
547 DPRINT("NTFS: IRP_MN_USER_FS_REQUEST/IRP_MN_KERNEL_CALL\n");
548 Status
= STATUS_INVALID_DEVICE_REQUEST
;
551 case IRP_MN_MOUNT_VOLUME
:
552 DPRINT("NTFS: IRP_MN_MOUNT_VOLUME\n");
553 Status
= NtfsMountVolume(DeviceObject
, Irp
);
556 case IRP_MN_VERIFY_VOLUME
:
557 DPRINT1("NTFS: IRP_MN_VERIFY_VOLUME\n");
558 Status
= NtfsVerifyVolume(DeviceObject
, Irp
);
562 DPRINT("NTFS FSC: MinorFunction %d\n", Stack
->MinorFunction
);
563 Status
= STATUS_INVALID_DEVICE_REQUEST
;
567 Irp
->IoStatus
.Status
= Status
;
568 Irp
->IoStatus
.Information
= 0;
570 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);