Sync with trunk r63647.
[reactos.git] / drivers / filesystems / ntfs / fsctl.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 2002 ReactOS Team
4 *
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.
9 *
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.
14 *
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.
18 *
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
24 * Valentin Verkhovsky
25 * Pierre Schweitzer
26 */
27
28 /* INCLUDES *****************************************************************/
29
30 #include "ntfs.h"
31
32 #include <ntdddisk.h>
33
34 #define NDEBUG
35 #include <debug.h>
36
37 /* FUNCTIONS ****************************************************************/
38
39 /*
40 * FUNCTION: Tests if the device contains a filesystem that can be mounted
41 * by this fsd.
42 */
43 static
44 NTSTATUS
45 NtfsHasFileSystem(PDEVICE_OBJECT DeviceToMount)
46 {
47 PARTITION_INFORMATION PartitionInfo;
48 DISK_GEOMETRY DiskGeometry;
49 ULONG ClusterSize, Size, k;
50 PBOOT_SECTOR BootSector;
51 NTSTATUS Status;
52
53 DPRINT1("NtfsHasFileSystem() called\n");
54
55 Size = sizeof(DISK_GEOMETRY);
56 Status = NtfsDeviceIoControl(DeviceToMount,
57 IOCTL_DISK_GET_DRIVE_GEOMETRY,
58 NULL,
59 0,
60 &DiskGeometry,
61 &Size,
62 TRUE);
63 if (!NT_SUCCESS(Status))
64 {
65 DPRINT1("NtfsDeviceIoControl() failed (Status %lx)\n", Status);
66 return Status;
67 }
68
69 if (DiskGeometry.MediaType == FixedMedia)
70 {
71 /* We have found a hard disk */
72 Size = sizeof(PARTITION_INFORMATION);
73 Status = NtfsDeviceIoControl(DeviceToMount,
74 IOCTL_DISK_GET_PARTITION_INFO,
75 NULL,
76 0,
77 &PartitionInfo,
78 &Size,
79 TRUE);
80 if (!NT_SUCCESS(Status))
81 {
82 DPRINT1("NtfsDeviceIoControl() failed (Status %lx)\n", Status);
83 return Status;
84 }
85
86 if (PartitionInfo.PartitionType != PARTITION_IFS)
87 {
88 DPRINT1("Invalid partition type\n");
89 return STATUS_UNRECOGNIZED_VOLUME;
90 }
91 }
92
93 DPRINT1("BytesPerSector: %lu\n", DiskGeometry.BytesPerSector);
94 BootSector = ExAllocatePoolWithTag(NonPagedPool,
95 DiskGeometry.BytesPerSector,
96 TAG_NTFS);
97 if (BootSector == NULL)
98 {
99 return STATUS_INSUFFICIENT_RESOURCES;
100 }
101
102 Status = NtfsReadSectors(DeviceToMount,
103 0,
104 1,
105 DiskGeometry.BytesPerSector,
106 (PVOID)BootSector,
107 TRUE);
108 if (!NT_SUCCESS(Status))
109 {
110 goto ByeBye;
111 }
112
113 /*
114 * Check values of different fields. If those fields have not expected
115 * values, we fail, to avoid mounting partitions that Windows won't mount.
116 */
117
118 /* OEMID: this field must be NTFS */
119 if (RtlCompareMemory(BootSector->OEMID, "NTFS ", 8) != 8)
120 {
121 DPRINT1("Failed with NTFS-identifier: [%.8s]\n", BootSector->OEMID);
122 Status = STATUS_UNRECOGNIZED_VOLUME;
123 goto ByeBye;
124 }
125
126 /* Unused0: this field must be COMPLETELY null */
127 for (k = 0; k < 7; k++)
128 {
129 if (BootSector->BPB.Unused0[k] != 0)
130 {
131 DPRINT1("Failed in field Unused0: [%.7s]\n", BootSector->BPB.Unused0);
132 Status = STATUS_UNRECOGNIZED_VOLUME;
133 goto ByeBye;
134 }
135 }
136
137 /* Unused3: this field must be COMPLETELY null */
138 for (k = 0; k < 4; k++)
139 {
140 if (BootSector->BPB.Unused3[k] != 0)
141 {
142 DPRINT1("Failed in field Unused3: [%.4s]\n", BootSector->BPB.Unused3);
143 Status = STATUS_UNRECOGNIZED_VOLUME;
144 goto ByeBye;
145 }
146 }
147
148 /* Check cluster size */
149 ClusterSize = BootSector->BPB.BytesPerSector * BootSector->BPB.SectorsPerCluster;
150 if (ClusterSize != 512 && ClusterSize != 1024 &&
151 ClusterSize != 2048 && ClusterSize != 4096 &&
152 ClusterSize != 8192 && ClusterSize != 16384 &&
153 ClusterSize != 32768 && ClusterSize != 65536)
154 {
155 DPRINT1("Cluster size failed: %hu, %hu, %hu\n",
156 BootSector->BPB.BytesPerSector,
157 BootSector->BPB.SectorsPerCluster,
158 ClusterSize);
159 Status = STATUS_UNRECOGNIZED_VOLUME;
160 goto ByeBye;
161 }
162
163 ByeBye:
164 ExFreePool(BootSector);
165
166 return Status;
167 }
168
169
170 static
171 NTSTATUS
172 NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject,
173 PDEVICE_EXTENSION DeviceExt)
174 {
175 DISK_GEOMETRY DiskGeometry;
176 PFILE_RECORD_HEADER MftRecord;
177 PFILE_RECORD_HEADER VolumeRecord;
178 PVOLINFO_ATTRIBUTE VolumeInfo;
179 PBOOT_SECTOR BootSector;
180 PATTRIBUTE Attribute;
181 ULONG Size;
182 PNTFS_INFO NtfsInfo = &DeviceExt->NtfsInfo;
183 NTSTATUS Status;
184
185 DPRINT("NtfsGetVolumeData() called\n");
186
187 Size = sizeof(DISK_GEOMETRY);
188 Status = NtfsDeviceIoControl(DeviceObject,
189 IOCTL_DISK_GET_DRIVE_GEOMETRY,
190 NULL,
191 0,
192 &DiskGeometry,
193 &Size,
194 TRUE);
195 if (!NT_SUCCESS(Status))
196 {
197 DPRINT("NtfsDeviceIoControl() failed (Status %lx)\n", Status);
198 return Status;
199 }
200
201 DPRINT("BytesPerSector: %lu\n", DiskGeometry.BytesPerSector);
202 BootSector = ExAllocatePoolWithTag(NonPagedPool,
203 DiskGeometry.BytesPerSector,
204 TAG_NTFS);
205 if (BootSector == NULL)
206 {
207 return STATUS_INSUFFICIENT_RESOURCES;
208 }
209
210 Status = NtfsReadSectors(DeviceObject,
211 0, /* Partition boot sector */
212 1,
213 DiskGeometry.BytesPerSector,
214 (PVOID)BootSector,
215 TRUE);
216 if (!NT_SUCCESS(Status))
217 {
218 ExFreePool(BootSector);
219 return Status;
220 }
221
222 /* Read data from the bootsector */
223 NtfsInfo->BytesPerSector = BootSector->BPB.BytesPerSector;
224 NtfsInfo->SectorsPerCluster = BootSector->BPB.SectorsPerCluster;
225 NtfsInfo->BytesPerCluster = BootSector->BPB.BytesPerSector * BootSector->BPB.SectorsPerCluster;
226 NtfsInfo->SectorCount = BootSector->EBPB.SectorCount;
227
228 NtfsInfo->MftStart.QuadPart = BootSector->EBPB.MftLocation;
229 NtfsInfo->MftMirrStart.QuadPart = BootSector->EBPB.MftMirrLocation;
230 NtfsInfo->SerialNumber = BootSector->EBPB.SerialNumber;
231 if (BootSector->EBPB.ClustersPerMftRecord > 0)
232 NtfsInfo->BytesPerFileRecord = BootSector->EBPB.ClustersPerMftRecord * NtfsInfo->BytesPerCluster;
233 else
234 NtfsInfo->BytesPerFileRecord = 1 << (-BootSector->EBPB.ClustersPerMftRecord);
235
236 DPRINT("Boot sector information:\n");
237 DPRINT(" BytesPerSector: %hu\n", BootSector->BPB.BytesPerSector);
238 DPRINT(" SectorsPerCluster: %hu\n", BootSector->BPB.SectorsPerCluster);
239 DPRINT(" SectorCount: %I64u\n", BootSector->EBPB.SectorCount);
240 DPRINT(" MftStart: %I64u\n", BootSector->EBPB.MftLocation);
241 DPRINT(" MftMirrStart: %I64u\n", BootSector->EBPB.MftMirrLocation);
242 DPRINT(" ClustersPerMftRecord: %lx\n", BootSector->EBPB.ClustersPerMftRecord);
243 DPRINT(" ClustersPerIndexRecord: %lx\n", BootSector->EBPB.ClustersPerIndexRecord);
244 DPRINT(" SerialNumber: %I64x\n", BootSector->EBPB.SerialNumber);
245
246 ExFreePool(BootSector);
247
248 MftRecord = ExAllocatePoolWithTag(NonPagedPool,
249 NtfsInfo->BytesPerFileRecord,
250 TAG_NTFS);
251 if (MftRecord == NULL)
252 {
253 return STATUS_INSUFFICIENT_RESOURCES;
254 }
255
256 Status = NtfsReadSectors(DeviceObject,
257 NtfsInfo->MftStart.u.LowPart * NtfsInfo->SectorsPerCluster,
258 NtfsInfo->BytesPerFileRecord / NtfsInfo->BytesPerSector,
259 NtfsInfo->BytesPerSector,
260 (PVOID)MftRecord,
261 TRUE);
262 if (!NT_SUCCESS(Status))
263 {
264 ExFreePool(MftRecord);
265 return Status;
266 }
267
268 VolumeRecord = ExAllocatePoolWithTag(NonPagedPool,
269 NtfsInfo->BytesPerFileRecord,
270 TAG_NTFS);
271 if (VolumeRecord == NULL)
272 {
273 ExFreePool(MftRecord);
274 return STATUS_INSUFFICIENT_RESOURCES;
275 }
276
277 /* Read Volume File (MFT index 3) */
278 DeviceExt->StorageDevice = DeviceObject;
279 Status = ReadFileRecord(DeviceExt,
280 3,
281 VolumeRecord,
282 MftRecord);
283 if (!NT_SUCCESS(Status))
284 {
285 ExFreePool(MftRecord);
286 return Status;
287 }
288
289 /* Enumerate attributes */
290 NtfsDumpFileAttributes (MftRecord);
291
292 /* Enumerate attributes */
293 NtfsDumpFileAttributes (VolumeRecord);
294
295 /* Get volume name */
296 Attribute = FindAttribute (VolumeRecord, AttributeVolumeName, NULL);
297 DPRINT("Attribute %p\n", Attribute);
298
299 if (Attribute != NULL && ((PRESIDENT_ATTRIBUTE)Attribute)->ValueLength != 0)
300 {
301 DPRINT("Data length %lu\n", AttributeDataLength (Attribute));
302 NtfsInfo->VolumeLabelLength =
303 min (((PRESIDENT_ATTRIBUTE)Attribute)->ValueLength, MAXIMUM_VOLUME_LABEL_LENGTH);
304 RtlCopyMemory(NtfsInfo->VolumeLabel,
305 (PVOID)((ULONG_PTR)Attribute + ((PRESIDENT_ATTRIBUTE)Attribute)->ValueOffset),
306 NtfsInfo->VolumeLabelLength);
307 }
308 else
309 {
310 NtfsInfo->VolumeLabelLength = 0;
311 }
312
313 /* Get volume information */
314 Attribute = FindAttribute (VolumeRecord, AttributeVolumeInformation, NULL);
315 DPRINT("Attribute %p\n", Attribute);
316
317 if (Attribute != NULL && ((PRESIDENT_ATTRIBUTE)Attribute)->ValueLength != 0)
318 {
319 DPRINT("Data length %lu\n", AttributeDataLength (Attribute));
320 VolumeInfo = (PVOID)((ULONG_PTR)Attribute + ((PRESIDENT_ATTRIBUTE)Attribute)->ValueOffset);
321
322 NtfsInfo->MajorVersion = VolumeInfo->MajorVersion;
323 NtfsInfo->MinorVersion = VolumeInfo->MinorVersion;
324 NtfsInfo->Flags = VolumeInfo->Flags;
325 }
326
327 ExFreePool(MftRecord);
328 ExFreePool(VolumeRecord);
329
330 return Status;
331 }
332
333
334 static
335 NTSTATUS
336 NtfsMountVolume(PDEVICE_OBJECT DeviceObject,
337 PIRP Irp)
338 {
339 PDEVICE_OBJECT NewDeviceObject = NULL;
340 PDEVICE_OBJECT DeviceToMount;
341 PIO_STACK_LOCATION Stack;
342 PNTFS_FCB Fcb = NULL;
343 PNTFS_CCB Ccb = NULL;
344 PNTFS_VCB Vcb = NULL;
345 NTSTATUS Status;
346
347 DPRINT1("NtfsMountVolume() called\n");
348
349 if (DeviceObject != NtfsGlobalData->DeviceObject)
350 {
351 Status = STATUS_INVALID_DEVICE_REQUEST;
352 goto ByeBye;
353 }
354
355 Stack = IoGetCurrentIrpStackLocation(Irp);
356 DeviceToMount = Stack->Parameters.MountVolume.DeviceObject;
357
358 Status = NtfsHasFileSystem(DeviceToMount);
359 if (!NT_SUCCESS(Status))
360 {
361 goto ByeBye;
362 }
363
364 Status = IoCreateDevice(NtfsGlobalData->DriverObject,
365 sizeof(DEVICE_EXTENSION),
366 NULL,
367 FILE_DEVICE_DISK_FILE_SYSTEM,
368 0,
369 FALSE,
370 &NewDeviceObject);
371 if (!NT_SUCCESS(Status))
372 goto ByeBye;
373
374 NewDeviceObject->Flags |= DO_DIRECT_IO;
375 Vcb = (PVOID)NewDeviceObject->DeviceExtension;
376 RtlZeroMemory(Vcb, sizeof(NTFS_VCB));
377
378 Vcb->Identifier.Type = NTFS_TYPE_VCB;
379 Vcb->Identifier.Size = sizeof(NTFS_TYPE_VCB);
380
381 Status = NtfsGetVolumeData(DeviceToMount,
382 Vcb);
383 if (!NT_SUCCESS(Status))
384 goto ByeBye;
385
386 NewDeviceObject->Vpb = DeviceToMount->Vpb;
387
388 Vcb->StorageDevice = DeviceToMount;
389 Vcb->StorageDevice->Vpb->DeviceObject = NewDeviceObject;
390 Vcb->StorageDevice->Vpb->RealDevice = Vcb->StorageDevice;
391 Vcb->StorageDevice->Vpb->Flags |= VPB_MOUNTED;
392 NewDeviceObject->StackSize = Vcb->StorageDevice->StackSize + 1;
393 NewDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
394
395 Vcb->StreamFileObject = IoCreateStreamFileObject(NULL,
396 Vcb->StorageDevice);
397
398 InitializeListHead(&Vcb->FcbListHead);
399
400 Fcb = NtfsCreateFCB(NULL, Vcb);
401 if (Fcb == NULL)
402 {
403 Status = STATUS_INSUFFICIENT_RESOURCES;
404 goto ByeBye;
405 }
406
407 Ccb = ExAllocatePoolWithTag(NonPagedPool,
408 sizeof(NTFS_CCB),
409 TAG_CCB);
410 if (Ccb == NULL)
411 {
412 Status = STATUS_INSUFFICIENT_RESOURCES;
413 goto ByeBye;
414 }
415
416 RtlZeroMemory(Ccb, sizeof(NTFS_CCB));
417
418 Ccb->Identifier.Type = NTFS_TYPE_CCB;
419 Ccb->Identifier.Size = sizeof(NTFS_TYPE_CCB);
420
421 Vcb->StreamFileObject->FsContext = Fcb;
422 Vcb->StreamFileObject->FsContext2 = Ccb;
423 Vcb->StreamFileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
424 Vcb->StreamFileObject->PrivateCacheMap = NULL;
425 Vcb->StreamFileObject->Vpb = Vcb->Vpb;
426 Ccb->PtrFileObject = Vcb->StreamFileObject;
427 Fcb->FileObject = Vcb->StreamFileObject;
428 Fcb->Vcb = (PDEVICE_EXTENSION)Vcb->StorageDevice;
429
430 Fcb->Flags = FCB_IS_VOLUME_STREAM;
431
432 Fcb->RFCB.FileSize.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector;
433 Fcb->RFCB.ValidDataLength.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector;
434 Fcb->RFCB.AllocationSize.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector; /* Correct? */
435
436 // Fcb->Entry.ExtentLocationL = 0;
437 // Fcb->Entry.DataLengthL = DeviceExt->CdInfo.VolumeSpaceSize * BLOCKSIZE;
438
439 CcInitializeCacheMap(Vcb->StreamFileObject,
440 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
441 FALSE,
442 &(NtfsGlobalData->CacheMgrCallbacks),
443 Fcb);
444
445 ExInitializeResourceLite(&Vcb->DirResource);
446
447 KeInitializeSpinLock(&Vcb->FcbListLock);
448
449 /* Get serial number */
450 NewDeviceObject->Vpb->SerialNumber = Vcb->NtfsInfo.SerialNumber;
451
452 /* Get volume label */
453 NewDeviceObject->Vpb->VolumeLabelLength = Vcb->NtfsInfo.VolumeLabelLength;
454 RtlCopyMemory(NewDeviceObject->Vpb->VolumeLabel,
455 Vcb->NtfsInfo.VolumeLabel,
456 Vcb->NtfsInfo.VolumeLabelLength);
457
458 Status = STATUS_SUCCESS;
459
460 ByeBye:
461 if (!NT_SUCCESS(Status))
462 {
463 /* Cleanup */
464 if (Vcb && Vcb->StreamFileObject)
465 ObDereferenceObject(Vcb->StreamFileObject);
466
467 if (Fcb)
468 ExFreePool(Fcb);
469
470 if (Ccb)
471 ExFreePool(Ccb);
472
473 if (NewDeviceObject)
474 IoDeleteDevice(NewDeviceObject);
475 }
476
477 DPRINT("NtfsMountVolume() done (Status: %lx)\n", Status);
478
479 return Status;
480 }
481
482
483 static
484 NTSTATUS
485 NtfsVerifyVolume(PDEVICE_OBJECT DeviceObject,
486 PIRP Irp)
487 {
488 UNREFERENCED_PARAMETER(DeviceObject);
489 UNREFERENCED_PARAMETER(Irp);
490 DPRINT1("NtfsVerifyVolume() called\n");
491 return STATUS_WRONG_VOLUME;
492 }
493
494
495 NTSTATUS
496 NTAPI
497 NtfsFsdFileSystemControl(PDEVICE_OBJECT DeviceObject,
498 PIRP Irp)
499 {
500 PIO_STACK_LOCATION Stack;
501 NTSTATUS Status;
502
503 DPRINT1("NtfsFileSystemControl() called\n");
504
505 Stack = IoGetCurrentIrpStackLocation(Irp);
506
507 switch (Stack->MinorFunction)
508 {
509 case IRP_MN_KERNEL_CALL:
510 case IRP_MN_USER_FS_REQUEST:
511 DPRINT("NTFS: IRP_MN_USER_FS_REQUEST/IRP_MN_KERNEL_CALL\n");
512 Status = STATUS_INVALID_DEVICE_REQUEST;
513 break;
514
515 case IRP_MN_MOUNT_VOLUME:
516 DPRINT("NTFS: IRP_MN_MOUNT_VOLUME\n");
517 Status = NtfsMountVolume(DeviceObject, Irp);
518 break;
519
520 case IRP_MN_VERIFY_VOLUME:
521 DPRINT1("NTFS: IRP_MN_VERIFY_VOLUME\n");
522 Status = NtfsVerifyVolume(DeviceObject, Irp);
523 break;
524
525 default:
526 DPRINT("NTFS FSC: MinorFunction %d\n", Stack->MinorFunction);
527 Status = STATUS_INVALID_DEVICE_REQUEST;
528 break;
529 }
530
531 Irp->IoStatus.Status = Status;
532 Irp->IoStatus.Information = 0;
533
534 IoCompleteRequest(Irp, IO_NO_INCREMENT);
535
536 return Status;
537 }
538
539 /* EOF */