[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 /* 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 VolumeRecord;
177 PVOLINFO_ATTRIBUTE VolumeInfo;
178 PBOOT_SECTOR BootSector;
179 ULONG Size;
180 PNTFS_INFO NtfsInfo = &DeviceExt->NtfsInfo;
181 NTSTATUS Status;
182 PNTFS_ATTR_CONTEXT AttrCtxt;
183 PNTFS_ATTR_RECORD Attribute;
184 PNTFS_FCB VolumeFcb;
185 PWSTR VolumeNameU;
186
187 DPRINT("NtfsGetVolumeData() called\n");
188
189 Size = sizeof(DISK_GEOMETRY);
190 Status = NtfsDeviceIoControl(DeviceObject,
191 IOCTL_DISK_GET_DRIVE_GEOMETRY,
192 NULL,
193 0,
194 &DiskGeometry,
195 &Size,
196 TRUE);
197 if (!NT_SUCCESS(Status))
198 {
199 DPRINT("NtfsDeviceIoControl() failed (Status %lx)\n", Status);
200 return Status;
201 }
202
203 DPRINT("BytesPerSector: %lu\n", DiskGeometry.BytesPerSector);
204 BootSector = ExAllocatePoolWithTag(NonPagedPool,
205 DiskGeometry.BytesPerSector,
206 TAG_NTFS);
207 if (BootSector == NULL)
208 {
209 return STATUS_INSUFFICIENT_RESOURCES;
210 }
211
212 Status = NtfsReadSectors(DeviceObject,
213 0, /* Partition boot sector */
214 1,
215 DiskGeometry.BytesPerSector,
216 (PVOID)BootSector,
217 TRUE);
218 if (!NT_SUCCESS(Status))
219 {
220 ExFreePool(BootSector);
221 return Status;
222 }
223
224 /* Read data from the bootsector */
225 NtfsInfo->BytesPerSector = BootSector->BPB.BytesPerSector;
226 NtfsInfo->SectorsPerCluster = BootSector->BPB.SectorsPerCluster;
227 NtfsInfo->BytesPerCluster = BootSector->BPB.BytesPerSector * BootSector->BPB.SectorsPerCluster;
228 NtfsInfo->SectorCount = BootSector->EBPB.SectorCount;
229
230 NtfsInfo->MftStart.QuadPart = BootSector->EBPB.MftLocation;
231 NtfsInfo->MftMirrStart.QuadPart = BootSector->EBPB.MftMirrLocation;
232 NtfsInfo->SerialNumber = BootSector->EBPB.SerialNumber;
233 if (BootSector->EBPB.ClustersPerMftRecord > 0)
234 NtfsInfo->BytesPerFileRecord = BootSector->EBPB.ClustersPerMftRecord * NtfsInfo->BytesPerCluster;
235 else
236 NtfsInfo->BytesPerFileRecord = 1 << (-BootSector->EBPB.ClustersPerMftRecord);
237 if (BootSector->EBPB.ClustersPerIndexRecord > 0)
238 NtfsInfo->BytesPerIndexRecord = BootSector->EBPB.ClustersPerIndexRecord * NtfsInfo->BytesPerCluster;
239 else
240 NtfsInfo->BytesPerIndexRecord = 1 << (-BootSector->EBPB.ClustersPerIndexRecord);
241
242 DPRINT("Boot sector information:\n");
243 DPRINT(" BytesPerSector: %hu\n", BootSector->BPB.BytesPerSector);
244 DPRINT(" SectorsPerCluster: %hu\n", BootSector->BPB.SectorsPerCluster);
245 DPRINT(" SectorCount: %I64u\n", BootSector->EBPB.SectorCount);
246 DPRINT(" MftStart: %I64u\n", BootSector->EBPB.MftLocation);
247 DPRINT(" MftMirrStart: %I64u\n", BootSector->EBPB.MftMirrLocation);
248 DPRINT(" ClustersPerMftRecord: %lx\n", BootSector->EBPB.ClustersPerMftRecord);
249 DPRINT(" ClustersPerIndexRecord: %lx\n", BootSector->EBPB.ClustersPerIndexRecord);
250 DPRINT(" SerialNumber: %I64x\n", BootSector->EBPB.SerialNumber);
251
252 ExFreePool(BootSector);
253
254 DeviceExt->MasterFileTable = ExAllocatePoolWithTag(NonPagedPool,
255 NtfsInfo->BytesPerFileRecord,
256 TAG_NTFS);
257 if (DeviceExt->MasterFileTable == NULL)
258 {
259 return STATUS_INSUFFICIENT_RESOURCES;
260 }
261
262 Status = NtfsReadSectors(DeviceObject,
263 NtfsInfo->MftStart.u.LowPart * NtfsInfo->SectorsPerCluster,
264 NtfsInfo->BytesPerFileRecord / NtfsInfo->BytesPerSector,
265 NtfsInfo->BytesPerSector,
266 (PVOID)DeviceExt->MasterFileTable,
267 TRUE);
268 if (!NT_SUCCESS(Status))
269 {
270 DPRINT1("Failed reading MFT.\n");
271 ExFreePool(DeviceExt->MasterFileTable);
272 return Status;
273 }
274
275 Status = FindAttribute(DeviceExt, DeviceExt->MasterFileTable, AttributeData, L"", 0, &DeviceExt->MFTContext);
276 if (!NT_SUCCESS(Status))
277 {
278 DPRINT1("Can't find data attribute for Master File Table.\n");
279 ExFreePool(DeviceExt->MasterFileTable);
280 return Status;
281 }
282
283 VolumeRecord = ExAllocatePoolWithTag(NonPagedPool,
284 NtfsInfo->BytesPerFileRecord,
285 TAG_NTFS);
286 if (VolumeRecord == NULL)
287 {
288 DPRINT1("Allocation failed for volume record\n");
289 ExFreePool(DeviceExt->MasterFileTable);
290 return STATUS_INSUFFICIENT_RESOURCES;
291 }
292
293 /* Read Volume File (MFT index 3) */
294 DeviceExt->StorageDevice = DeviceObject;
295 Status = ReadFileRecord(DeviceExt,
296 NTFS_FILE_VOLUME,
297 VolumeRecord);
298 if (!NT_SUCCESS(Status))
299 {
300 DPRINT1("Failed reading volume file\n");
301 ExFreePool(VolumeRecord);
302 ExFreePool(DeviceExt->MasterFileTable);
303 return Status;
304 }
305
306 /* Enumerate attributes */
307 NtfsDumpFileAttributes(DeviceExt->MasterFileTable);
308
309 /* Enumerate attributes */
310 NtfsDumpFileAttributes(VolumeRecord);
311
312 /* Get volume name */
313 Status = FindAttribute(DeviceExt, VolumeRecord, AttributeVolumeName, L"", 0, &AttrCtxt);
314
315 if (NT_SUCCESS(Status) && AttrCtxt->Record.Resident.ValueLength != 0)
316 {
317 Attribute = &AttrCtxt->Record;
318 DPRINT("Data length %lu\n", AttributeDataLength(Attribute));
319 NtfsInfo->VolumeLabelLength =
320 min (Attribute->Resident.ValueLength, MAXIMUM_VOLUME_LABEL_LENGTH);
321 RtlCopyMemory(NtfsInfo->VolumeLabel,
322 (PVOID)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset),
323 NtfsInfo->VolumeLabelLength);
324 VolumeNameU = NtfsInfo->VolumeLabel;
325 }
326 else
327 {
328 NtfsInfo->VolumeLabelLength = 0;
329 VolumeNameU = L"\0";
330 }
331
332 VolumeFcb = NtfsCreateFCB(VolumeNameU, DeviceExt);
333 if (VolumeFcb == NULL)
334 {
335 DPRINT1("Failed allocating volume FCB\n");
336 ExFreePool(VolumeRecord);
337 ExFreePool(DeviceExt->MasterFileTable);
338 return STATUS_INSUFFICIENT_RESOURCES;
339 }
340
341 VolumeFcb->Flags = FCB_IS_VOLUME;
342 VolumeFcb->RFCB.FileSize.QuadPart = DeviceExt->NtfsInfo.SectorCount * DeviceExt->NtfsInfo.BytesPerSector;
343 VolumeFcb->RFCB.ValidDataLength = VolumeFcb->RFCB.FileSize;
344 VolumeFcb->RFCB.AllocationSize = VolumeFcb->RFCB.FileSize;
345 VolumeFcb->MFTIndex = 0;
346 DeviceExt->VolumeFcb = VolumeFcb;
347
348 /* Get volume information */
349 Status = FindAttribute(DeviceExt, VolumeRecord, AttributeVolumeInformation, L"", 0, &AttrCtxt);
350
351 if (NT_SUCCESS(Status) && AttrCtxt->Record.Resident.ValueLength != 0)
352 {
353 Attribute = &AttrCtxt->Record;
354 DPRINT("Data length %lu\n", AttributeDataLength (Attribute));
355 VolumeInfo = (PVOID)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
356
357 NtfsInfo->MajorVersion = VolumeInfo->MajorVersion;
358 NtfsInfo->MinorVersion = VolumeInfo->MinorVersion;
359 NtfsInfo->Flags = VolumeInfo->Flags;
360 }
361
362 ExFreePool(VolumeRecord);
363
364 return Status;
365 }
366
367
368 static
369 NTSTATUS
370 NtfsMountVolume(PDEVICE_OBJECT DeviceObject,
371 PIRP Irp)
372 {
373 PDEVICE_OBJECT NewDeviceObject = NULL;
374 PDEVICE_OBJECT DeviceToMount;
375 PIO_STACK_LOCATION Stack;
376 PNTFS_FCB Fcb = NULL;
377 PNTFS_CCB Ccb = NULL;
378 PNTFS_VCB Vcb = NULL;
379 NTSTATUS Status;
380
381 DPRINT1("NtfsMountVolume() called\n");
382
383 if (DeviceObject != NtfsGlobalData->DeviceObject)
384 {
385 Status = STATUS_INVALID_DEVICE_REQUEST;
386 goto ByeBye;
387 }
388
389 Stack = IoGetCurrentIrpStackLocation(Irp);
390 DeviceToMount = Stack->Parameters.MountVolume.DeviceObject;
391
392 Status = NtfsHasFileSystem(DeviceToMount);
393 if (!NT_SUCCESS(Status))
394 {
395 goto ByeBye;
396 }
397
398 Status = IoCreateDevice(NtfsGlobalData->DriverObject,
399 sizeof(DEVICE_EXTENSION),
400 NULL,
401 FILE_DEVICE_DISK_FILE_SYSTEM,
402 0,
403 FALSE,
404 &NewDeviceObject);
405 if (!NT_SUCCESS(Status))
406 goto ByeBye;
407
408 NewDeviceObject->Flags |= DO_DIRECT_IO;
409 Vcb = (PVOID)NewDeviceObject->DeviceExtension;
410 RtlZeroMemory(Vcb, sizeof(NTFS_VCB));
411
412 Vcb->Identifier.Type = NTFS_TYPE_VCB;
413 Vcb->Identifier.Size = sizeof(NTFS_TYPE_VCB);
414
415 Status = NtfsGetVolumeData(DeviceToMount,
416 Vcb);
417 if (!NT_SUCCESS(Status))
418 goto ByeBye;
419
420 NewDeviceObject->Vpb = DeviceToMount->Vpb;
421
422 Vcb->StorageDevice = DeviceToMount;
423 Vcb->StorageDevice->Vpb->DeviceObject = NewDeviceObject;
424 Vcb->StorageDevice->Vpb->RealDevice = Vcb->StorageDevice;
425 Vcb->StorageDevice->Vpb->Flags |= VPB_MOUNTED;
426 NewDeviceObject->StackSize = Vcb->StorageDevice->StackSize + 1;
427 NewDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
428
429 Vcb->StreamFileObject = IoCreateStreamFileObject(NULL,
430 Vcb->StorageDevice);
431
432 InitializeListHead(&Vcb->FcbListHead);
433
434 Fcb = NtfsCreateFCB(NULL, Vcb);
435 if (Fcb == NULL)
436 {
437 Status = STATUS_INSUFFICIENT_RESOURCES;
438 goto ByeBye;
439 }
440
441 Ccb = ExAllocatePoolWithTag(NonPagedPool,
442 sizeof(NTFS_CCB),
443 TAG_CCB);
444 if (Ccb == NULL)
445 {
446 Status = STATUS_INSUFFICIENT_RESOURCES;
447 goto ByeBye;
448 }
449
450 RtlZeroMemory(Ccb, sizeof(NTFS_CCB));
451
452 Ccb->Identifier.Type = NTFS_TYPE_CCB;
453 Ccb->Identifier.Size = sizeof(NTFS_TYPE_CCB);
454
455 Vcb->StreamFileObject->FsContext = Fcb;
456 Vcb->StreamFileObject->FsContext2 = Ccb;
457 Vcb->StreamFileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
458 Vcb->StreamFileObject->PrivateCacheMap = NULL;
459 Vcb->StreamFileObject->Vpb = Vcb->Vpb;
460 Ccb->PtrFileObject = Vcb->StreamFileObject;
461 Fcb->FileObject = Vcb->StreamFileObject;
462 Fcb->Vcb = (PDEVICE_EXTENSION)Vcb->StorageDevice;
463
464 Fcb->Flags = FCB_IS_VOLUME_STREAM;
465
466 Fcb->RFCB.FileSize.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector;
467 Fcb->RFCB.ValidDataLength.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector;
468 Fcb->RFCB.AllocationSize.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector; /* Correct? */
469
470 // Fcb->Entry.ExtentLocationL = 0;
471 // Fcb->Entry.DataLengthL = DeviceExt->CdInfo.VolumeSpaceSize * BLOCKSIZE;
472
473 CcInitializeCacheMap(Vcb->StreamFileObject,
474 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
475 FALSE,
476 &(NtfsGlobalData->CacheMgrCallbacks),
477 Fcb);
478
479 ExInitializeResourceLite(&Vcb->DirResource);
480
481 KeInitializeSpinLock(&Vcb->FcbListLock);
482
483 /* Get serial number */
484 NewDeviceObject->Vpb->SerialNumber = Vcb->NtfsInfo.SerialNumber;
485
486 /* Get volume label */
487 NewDeviceObject->Vpb->VolumeLabelLength = Vcb->NtfsInfo.VolumeLabelLength;
488 RtlCopyMemory(NewDeviceObject->Vpb->VolumeLabel,
489 Vcb->NtfsInfo.VolumeLabel,
490 Vcb->NtfsInfo.VolumeLabelLength);
491
492 Status = STATUS_SUCCESS;
493
494 ByeBye:
495 if (!NT_SUCCESS(Status))
496 {
497 /* Cleanup */
498 if (Vcb && Vcb->StreamFileObject)
499 ObDereferenceObject(Vcb->StreamFileObject);
500
501 if (Fcb)
502 ExFreePool(Fcb);
503
504 if (Ccb)
505 ExFreePool(Ccb);
506
507 if (NewDeviceObject)
508 IoDeleteDevice(NewDeviceObject);
509 }
510
511 DPRINT("NtfsMountVolume() done (Status: %lx)\n", Status);
512
513 return Status;
514 }
515
516
517 static
518 NTSTATUS
519 NtfsVerifyVolume(PDEVICE_OBJECT DeviceObject,
520 PIRP Irp)
521 {
522 UNREFERENCED_PARAMETER(DeviceObject);
523 UNREFERENCED_PARAMETER(Irp);
524 DPRINT1("NtfsVerifyVolume() called\n");
525 return STATUS_WRONG_VOLUME;
526 }
527
528
529 NTSTATUS
530 NTAPI
531 NtfsFsdFileSystemControl(PDEVICE_OBJECT DeviceObject,
532 PIRP Irp)
533 {
534 PIO_STACK_LOCATION Stack;
535 NTSTATUS Status;
536
537 DPRINT1("NtfsFileSystemControl() called\n");
538
539 Stack = IoGetCurrentIrpStackLocation(Irp);
540
541 switch (Stack->MinorFunction)
542 {
543 case IRP_MN_KERNEL_CALL:
544 case IRP_MN_USER_FS_REQUEST:
545 DPRINT("NTFS: IRP_MN_USER_FS_REQUEST/IRP_MN_KERNEL_CALL\n");
546 Status = STATUS_INVALID_DEVICE_REQUEST;
547 break;
548
549 case IRP_MN_MOUNT_VOLUME:
550 DPRINT("NTFS: IRP_MN_MOUNT_VOLUME\n");
551 Status = NtfsMountVolume(DeviceObject, Irp);
552 break;
553
554 case IRP_MN_VERIFY_VOLUME:
555 DPRINT1("NTFS: IRP_MN_VERIFY_VOLUME\n");
556 Status = NtfsVerifyVolume(DeviceObject, Irp);
557 break;
558
559 default:
560 DPRINT("NTFS FSC: MinorFunction %d\n", Stack->MinorFunction);
561 Status = STATUS_INVALID_DEVICE_REQUEST;
562 break;
563 }
564
565 Irp->IoStatus.Status = Status;
566 Irp->IoStatus.Information = 0;
567
568 IoCompleteRequest(Irp, IO_NO_INCREMENT);
569
570 return Status;
571 }
572
573 /* EOF */