[NTFS]
[reactos.git] / reactos / 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 UNICODE_STRING EmptyName = RTL_CONSTANT_STRING(L"");
38
39 /* FUNCTIONS ****************************************************************/
40
41 /*
42 * FUNCTION: Tests if the device contains a filesystem that can be mounted
43 * by this fsd.
44 */
45 static
46 NTSTATUS
47 NtfsHasFileSystem(PDEVICE_OBJECT DeviceToMount)
48 {
49 PARTITION_INFORMATION PartitionInfo;
50 DISK_GEOMETRY DiskGeometry;
51 ULONG ClusterSize, Size, k;
52 PBOOT_SECTOR BootSector;
53 NTSTATUS Status;
54
55 DPRINT1("NtfsHasFileSystem() called\n");
56
57 Size = sizeof(DISK_GEOMETRY);
58 Status = NtfsDeviceIoControl(DeviceToMount,
59 IOCTL_DISK_GET_DRIVE_GEOMETRY,
60 NULL,
61 0,
62 &DiskGeometry,
63 &Size,
64 TRUE);
65 if (!NT_SUCCESS(Status))
66 {
67 DPRINT1("NtfsDeviceIoControl() failed (Status %lx)\n", Status);
68 return Status;
69 }
70
71 if (DiskGeometry.MediaType == FixedMedia)
72 {
73 /* We have found a hard disk */
74 Size = sizeof(PARTITION_INFORMATION);
75 Status = NtfsDeviceIoControl(DeviceToMount,
76 IOCTL_DISK_GET_PARTITION_INFO,
77 NULL,
78 0,
79 &PartitionInfo,
80 &Size,
81 TRUE);
82 if (!NT_SUCCESS(Status))
83 {
84 DPRINT1("NtfsDeviceIoControl() failed (Status %lx)\n", Status);
85 return Status;
86 }
87
88 if (PartitionInfo.PartitionType != PARTITION_IFS)
89 {
90 DPRINT1("Invalid partition type\n");
91 return STATUS_UNRECOGNIZED_VOLUME;
92 }
93 }
94
95 DPRINT1("BytesPerSector: %lu\n", DiskGeometry.BytesPerSector);
96 BootSector = ExAllocatePoolWithTag(NonPagedPool,
97 DiskGeometry.BytesPerSector,
98 TAG_NTFS);
99 if (BootSector == NULL)
100 {
101 return STATUS_INSUFFICIENT_RESOURCES;
102 }
103
104 Status = NtfsReadSectors(DeviceToMount,
105 0,
106 1,
107 DiskGeometry.BytesPerSector,
108 (PVOID)BootSector,
109 TRUE);
110 if (!NT_SUCCESS(Status))
111 {
112 goto ByeBye;
113 }
114
115 /*
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.
118 */
119
120 /* OEMID: this field must be NTFS */
121 if (RtlCompareMemory(BootSector->OEMID, "NTFS ", 8) != 8)
122 {
123 DPRINT1("Failed with NTFS-identifier: [%.8s]\n", BootSector->OEMID);
124 Status = STATUS_UNRECOGNIZED_VOLUME;
125 goto ByeBye;
126 }
127
128 /* Unused0: this field must be COMPLETELY null */
129 for (k = 0; k < 7; k++)
130 {
131 if (BootSector->BPB.Unused0[k] != 0)
132 {
133 DPRINT1("Failed in field Unused0: [%.7s]\n", BootSector->BPB.Unused0);
134 Status = STATUS_UNRECOGNIZED_VOLUME;
135 goto ByeBye;
136 }
137 }
138
139 /* Unused3: this field must be COMPLETELY null */
140 for (k = 0; k < 4; k++)
141 {
142 if (BootSector->BPB.Unused3[k] != 0)
143 {
144 DPRINT1("Failed in field Unused3: [%.4s]\n", BootSector->BPB.Unused3);
145 Status = STATUS_UNRECOGNIZED_VOLUME;
146 goto ByeBye;
147 }
148 }
149
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)
156 {
157 DPRINT1("Cluster size failed: %hu, %hu, %hu\n",
158 BootSector->BPB.BytesPerSector,
159 BootSector->BPB.SectorsPerCluster,
160 ClusterSize);
161 Status = STATUS_UNRECOGNIZED_VOLUME;
162 goto ByeBye;
163 }
164
165 ByeBye:
166 ExFreePool(BootSector);
167
168 return Status;
169 }
170
171
172 static
173 NTSTATUS
174 NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject,
175 PDEVICE_EXTENSION DeviceExt)
176 {
177 DISK_GEOMETRY DiskGeometry;
178 PFILE_RECORD_HEADER VolumeRecord;
179 PVOLINFO_ATTRIBUTE VolumeInfo;
180 PBOOT_SECTOR BootSector;
181 ULONG Size;
182 PNTFS_INFO NtfsInfo = &DeviceExt->NtfsInfo;
183 NTSTATUS Status;
184 PNTFS_ATTR_CONTEXT AttrCtxt;
185 PNTFS_ATTR_RECORD Attribute;
186 PNTFS_FCB VolumeFcb;
187 PWSTR VolumeNameU;
188
189 DPRINT("NtfsGetVolumeData() called\n");
190
191 Size = sizeof(DISK_GEOMETRY);
192 Status = NtfsDeviceIoControl(DeviceObject,
193 IOCTL_DISK_GET_DRIVE_GEOMETRY,
194 NULL,
195 0,
196 &DiskGeometry,
197 &Size,
198 TRUE);
199 if (!NT_SUCCESS(Status))
200 {
201 DPRINT("NtfsDeviceIoControl() failed (Status %lx)\n", Status);
202 return Status;
203 }
204
205 DPRINT("BytesPerSector: %lu\n", DiskGeometry.BytesPerSector);
206 BootSector = ExAllocatePoolWithTag(NonPagedPool,
207 DiskGeometry.BytesPerSector,
208 TAG_NTFS);
209 if (BootSector == NULL)
210 {
211 return STATUS_INSUFFICIENT_RESOURCES;
212 }
213
214 Status = NtfsReadSectors(DeviceObject,
215 0, /* Partition boot sector */
216 1,
217 DiskGeometry.BytesPerSector,
218 (PVOID)BootSector,
219 TRUE);
220 if (!NT_SUCCESS(Status))
221 {
222 ExFreePool(BootSector);
223 return Status;
224 }
225
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;
231
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;
237 else
238 NtfsInfo->BytesPerFileRecord = 1 << (-BootSector->EBPB.ClustersPerMftRecord);
239 if (BootSector->EBPB.ClustersPerIndexRecord > 0)
240 NtfsInfo->BytesPerIndexRecord = BootSector->EBPB.ClustersPerIndexRecord * NtfsInfo->BytesPerCluster;
241 else
242 NtfsInfo->BytesPerIndexRecord = 1 << (-BootSector->EBPB.ClustersPerIndexRecord);
243
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);
253
254 ExFreePool(BootSector);
255
256 DeviceExt->MasterFileTable = ExAllocatePoolWithTag(NonPagedPool,
257 NtfsInfo->BytesPerFileRecord,
258 TAG_NTFS);
259 if (DeviceExt->MasterFileTable == NULL)
260 {
261 return STATUS_INSUFFICIENT_RESOURCES;
262 }
263
264 Status = NtfsReadSectors(DeviceObject,
265 NtfsInfo->MftStart.u.LowPart * NtfsInfo->SectorsPerCluster,
266 NtfsInfo->BytesPerFileRecord / NtfsInfo->BytesPerSector,
267 NtfsInfo->BytesPerSector,
268 (PVOID)DeviceExt->MasterFileTable,
269 TRUE);
270 if (!NT_SUCCESS(Status))
271 {
272 DPRINT1("Failed reading MFT.\n");
273 ExFreePool(DeviceExt->MasterFileTable);
274 return Status;
275 }
276
277 Status = FindAttribute(DeviceExt, DeviceExt->MasterFileTable, AttributeData, &EmptyName, &DeviceExt->MFTContext);
278 if (!NT_SUCCESS(Status))
279 {
280 DPRINT1("Can't find data attribute for Master File Table.\n");
281 ExFreePool(DeviceExt->MasterFileTable);
282 return Status;
283 }
284
285 VolumeRecord = ExAllocatePoolWithTag(NonPagedPool,
286 NtfsInfo->BytesPerFileRecord,
287 TAG_NTFS);
288 if (VolumeRecord == NULL)
289 {
290 DPRINT1("Allocation failed for volume record\n");
291 ExFreePool(DeviceExt->MasterFileTable);
292 return STATUS_INSUFFICIENT_RESOURCES;
293 }
294
295 /* Read Volume File (MFT index 3) */
296 DeviceExt->StorageDevice = DeviceObject;
297 Status = ReadFileRecord(DeviceExt,
298 NTFS_FILE_VOLUME,
299 VolumeRecord);
300 if (!NT_SUCCESS(Status))
301 {
302 DPRINT1("Failed reading volume file\n");
303 ExFreePool(VolumeRecord);
304 ExFreePool(DeviceExt->MasterFileTable);
305 return Status;
306 }
307
308 /* Enumerate attributes */
309 NtfsDumpFileAttributes(DeviceExt->MasterFileTable);
310
311 /* Enumerate attributes */
312 NtfsDumpFileAttributes(VolumeRecord);
313
314 /* Get volume name */
315 Status = FindAttribute(DeviceExt, VolumeRecord, AttributeVolumeName, &EmptyName, &AttrCtxt);
316
317 if (NT_SUCCESS(Status) && AttrCtxt->Record.Resident.ValueLength != 0)
318 {
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;
327 }
328 else
329 {
330 NtfsInfo->VolumeLabelLength = 0;
331 VolumeNameU = L"\0";
332 }
333
334 VolumeFcb = NtfsCreateFCB(VolumeNameU, DeviceExt);
335 if (VolumeFcb == NULL)
336 {
337 DPRINT1("Failed allocating volume FCB\n");
338 ExFreePool(VolumeRecord);
339 ExFreePool(DeviceExt->MasterFileTable);
340 return STATUS_INSUFFICIENT_RESOURCES;
341 }
342
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;
349
350 /* Get volume information */
351 Status = FindAttribute(DeviceExt, VolumeRecord, AttributeVolumeInformation, &EmptyName, &AttrCtxt);
352
353 if (NT_SUCCESS(Status) && AttrCtxt->Record.Resident.ValueLength != 0)
354 {
355 Attribute = &AttrCtxt->Record;
356 DPRINT("Data length %lu\n", AttributeDataLength (Attribute));
357 VolumeInfo = (PVOID)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
358
359 NtfsInfo->MajorVersion = VolumeInfo->MajorVersion;
360 NtfsInfo->MinorVersion = VolumeInfo->MinorVersion;
361 NtfsInfo->Flags = VolumeInfo->Flags;
362 }
363
364 ExFreePool(VolumeRecord);
365
366 return Status;
367 }
368
369
370 static
371 NTSTATUS
372 NtfsMountVolume(PDEVICE_OBJECT DeviceObject,
373 PIRP Irp)
374 {
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;
381 NTSTATUS Status;
382
383 DPRINT1("NtfsMountVolume() called\n");
384
385 if (DeviceObject != NtfsGlobalData->DeviceObject)
386 {
387 Status = STATUS_INVALID_DEVICE_REQUEST;
388 goto ByeBye;
389 }
390
391 Stack = IoGetCurrentIrpStackLocation(Irp);
392 DeviceToMount = Stack->Parameters.MountVolume.DeviceObject;
393
394 Status = NtfsHasFileSystem(DeviceToMount);
395 if (!NT_SUCCESS(Status))
396 {
397 goto ByeBye;
398 }
399
400 Status = IoCreateDevice(NtfsGlobalData->DriverObject,
401 sizeof(DEVICE_EXTENSION),
402 NULL,
403 FILE_DEVICE_DISK_FILE_SYSTEM,
404 0,
405 FALSE,
406 &NewDeviceObject);
407 if (!NT_SUCCESS(Status))
408 goto ByeBye;
409
410 NewDeviceObject->Flags |= DO_DIRECT_IO;
411 Vcb = (PVOID)NewDeviceObject->DeviceExtension;
412 RtlZeroMemory(Vcb, sizeof(NTFS_VCB));
413
414 Vcb->Identifier.Type = NTFS_TYPE_VCB;
415 Vcb->Identifier.Size = sizeof(NTFS_TYPE_VCB);
416
417 Status = NtfsGetVolumeData(DeviceToMount,
418 Vcb);
419 if (!NT_SUCCESS(Status))
420 goto ByeBye;
421
422 NewDeviceObject->Vpb = DeviceToMount->Vpb;
423
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;
430
431 Vcb->StreamFileObject = IoCreateStreamFileObject(NULL,
432 Vcb->StorageDevice);
433
434 InitializeListHead(&Vcb->FcbListHead);
435
436 Fcb = NtfsCreateFCB(NULL, Vcb);
437 if (Fcb == NULL)
438 {
439 Status = STATUS_INSUFFICIENT_RESOURCES;
440 goto ByeBye;
441 }
442
443 Ccb = ExAllocatePoolWithTag(NonPagedPool,
444 sizeof(NTFS_CCB),
445 TAG_CCB);
446 if (Ccb == NULL)
447 {
448 Status = STATUS_INSUFFICIENT_RESOURCES;
449 goto ByeBye;
450 }
451
452 RtlZeroMemory(Ccb, sizeof(NTFS_CCB));
453
454 Ccb->Identifier.Type = NTFS_TYPE_CCB;
455 Ccb->Identifier.Size = sizeof(NTFS_TYPE_CCB);
456
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;
465
466 Fcb->Flags = FCB_IS_VOLUME_STREAM;
467
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? */
471
472 // Fcb->Entry.ExtentLocationL = 0;
473 // Fcb->Entry.DataLengthL = DeviceExt->CdInfo.VolumeSpaceSize * BLOCKSIZE;
474
475 CcInitializeCacheMap(Vcb->StreamFileObject,
476 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
477 FALSE,
478 &(NtfsGlobalData->CacheMgrCallbacks),
479 Fcb);
480
481 ExInitializeResourceLite(&Vcb->DirResource);
482
483 KeInitializeSpinLock(&Vcb->FcbListLock);
484
485 /* Get serial number */
486 NewDeviceObject->Vpb->SerialNumber = Vcb->NtfsInfo.SerialNumber;
487
488 /* Get volume label */
489 NewDeviceObject->Vpb->VolumeLabelLength = Vcb->NtfsInfo.VolumeLabelLength;
490 RtlCopyMemory(NewDeviceObject->Vpb->VolumeLabel,
491 Vcb->NtfsInfo.VolumeLabel,
492 Vcb->NtfsInfo.VolumeLabelLength);
493
494 Status = STATUS_SUCCESS;
495
496 ByeBye:
497 if (!NT_SUCCESS(Status))
498 {
499 /* Cleanup */
500 if (Vcb && Vcb->StreamFileObject)
501 ObDereferenceObject(Vcb->StreamFileObject);
502
503 if (Fcb)
504 ExFreePool(Fcb);
505
506 if (Ccb)
507 ExFreePool(Ccb);
508
509 if (NewDeviceObject)
510 IoDeleteDevice(NewDeviceObject);
511 }
512
513 DPRINT("NtfsMountVolume() done (Status: %lx)\n", Status);
514
515 return Status;
516 }
517
518
519 static
520 NTSTATUS
521 NtfsVerifyVolume(PDEVICE_OBJECT DeviceObject,
522 PIRP Irp)
523 {
524 UNREFERENCED_PARAMETER(DeviceObject);
525 UNREFERENCED_PARAMETER(Irp);
526 DPRINT1("NtfsVerifyVolume() called\n");
527 return STATUS_WRONG_VOLUME;
528 }
529
530
531 NTSTATUS
532 NTAPI
533 NtfsFsdFileSystemControl(PDEVICE_OBJECT DeviceObject,
534 PIRP Irp)
535 {
536 PIO_STACK_LOCATION Stack;
537 NTSTATUS Status;
538
539 DPRINT1("NtfsFileSystemControl() called\n");
540
541 Stack = IoGetCurrentIrpStackLocation(Irp);
542
543 switch (Stack->MinorFunction)
544 {
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;
549 break;
550
551 case IRP_MN_MOUNT_VOLUME:
552 DPRINT("NTFS: IRP_MN_MOUNT_VOLUME\n");
553 Status = NtfsMountVolume(DeviceObject, Irp);
554 break;
555
556 case IRP_MN_VERIFY_VOLUME:
557 DPRINT1("NTFS: IRP_MN_VERIFY_VOLUME\n");
558 Status = NtfsVerifyVolume(DeviceObject, Irp);
559 break;
560
561 default:
562 DPRINT("NTFS FSC: MinorFunction %d\n", Stack->MinorFunction);
563 Status = STATUS_INVALID_DEVICE_REQUEST;
564 break;
565 }
566
567 Irp->IoStatus.Status = Status;
568 Irp->IoStatus.Information = 0;
569
570 IoCompleteRequest(Irp, IO_NO_INCREMENT);
571
572 return Status;
573 }
574
575 /* EOF */