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