[CLT2012]
[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 NTSTATUS Status;
328
329 DPRINT1("NtfsMountVolume() called\n");
330
331 if (DeviceObject != NtfsGlobalData->DeviceObject)
332 {
333 Status = STATUS_INVALID_DEVICE_REQUEST;
334 goto ByeBye;
335 }
336
337 Stack = IoGetCurrentIrpStackLocation(Irp);
338 DeviceToMount = Stack->Parameters.MountVolume.DeviceObject;
339
340 Status = NtfsHasFileSystem(DeviceToMount);
341 if (!NT_SUCCESS(Status))
342 {
343 goto ByeBye;
344 }
345
346 Status = IoCreateDevice(NtfsGlobalData->DriverObject,
347 sizeof(DEVICE_EXTENSION),
348 NULL,
349 FILE_DEVICE_DISK_FILE_SYSTEM,
350 0,
351 FALSE,
352 &NewDeviceObject);
353 if (!NT_SUCCESS(Status))
354 goto ByeBye;
355
356 NewDeviceObject->Flags |= DO_DIRECT_IO;
357 Vcb = (PVOID)NewDeviceObject->DeviceExtension;
358 RtlZeroMemory(Vcb, sizeof(NTFS_VCB));
359
360 Vcb->Identifier.Type = NTFS_TYPE_VCB;
361 Vcb->Identifier.Size = sizeof(NTFS_TYPE_VCB);
362
363 Status = NtfsGetVolumeData(DeviceToMount,
364 Vcb);
365 if (!NT_SUCCESS(Status))
366 goto ByeBye;
367
368 NewDeviceObject->Vpb = DeviceToMount->Vpb;
369
370 Vcb->StorageDevice = DeviceToMount;
371 Vcb->StorageDevice->Vpb->DeviceObject = NewDeviceObject;
372 Vcb->StorageDevice->Vpb->RealDevice = Vcb->StorageDevice;
373 Vcb->StorageDevice->Vpb->Flags |= VPB_MOUNTED;
374 NewDeviceObject->StackSize = Vcb->StorageDevice->StackSize + 1;
375 NewDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
376
377 Vcb->StreamFileObject = IoCreateStreamFileObject(NULL,
378 Vcb->StorageDevice);
379
380 InitializeListHead(&Vcb->FcbListHead);
381
382 Fcb = NtfsCreateFCB(NULL, Vcb);
383 if (Fcb == NULL)
384 {
385 Status = STATUS_INSUFFICIENT_RESOURCES;
386 goto ByeBye;
387 }
388
389 Ccb = ExAllocatePoolWithTag(NonPagedPool,
390 sizeof(NTFS_CCB),
391 TAG_CCB);
392 if (Ccb == NULL)
393 {
394 Status = STATUS_INSUFFICIENT_RESOURCES;
395 goto ByeBye;
396 }
397 RtlZeroMemory(Ccb, sizeof(NTFS_CCB));
398
399 Ccb->Identifier.Type = NTFS_TYPE_CCB;
400 Ccb->Identifier.Size = sizeof(NTFS_TYPE_CCB);
401
402 Vcb->StreamFileObject->FsContext = Fcb;
403 Vcb->StreamFileObject->FsContext2 = Ccb;
404 Vcb->StreamFileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
405 Vcb->StreamFileObject->PrivateCacheMap = NULL;
406 Vcb->StreamFileObject->Vpb = Vcb->Vpb;
407 Ccb->PtrFileObject = Vcb->StreamFileObject;
408 Fcb->FileObject = Vcb->StreamFileObject;
409 Fcb->Vcb = (PDEVICE_EXTENSION)Vcb->StorageDevice;
410
411 Fcb->Flags = FCB_IS_VOLUME_STREAM;
412
413 Fcb->RFCB.FileSize.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector;
414 Fcb->RFCB.ValidDataLength.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector;
415 Fcb->RFCB.AllocationSize.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector; /* Correct? */
416
417 // Fcb->Entry.ExtentLocationL = 0;
418 // Fcb->Entry.DataLengthL = DeviceExt->CdInfo.VolumeSpaceSize * BLOCKSIZE;
419
420 CcInitializeCacheMap(Vcb->StreamFileObject,
421 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
422 FALSE,
423 &(NtfsGlobalData->CacheMgrCallbacks),
424 Fcb);
425
426 ExInitializeResourceLite(&Vcb->DirResource);
427
428 KeInitializeSpinLock(&Vcb->FcbListLock);
429
430 /* Get serial number */
431 NewDeviceObject->Vpb->SerialNumber = Vcb->NtfsInfo.SerialNumber;
432
433 /* Get volume label */
434 NewDeviceObject->Vpb->VolumeLabelLength = Vcb->NtfsInfo.VolumeLabelLength;
435 RtlCopyMemory(NewDeviceObject->Vpb->VolumeLabel,
436 Vcb->NtfsInfo.VolumeLabel,
437 Vcb->NtfsInfo.VolumeLabelLength);
438
439 Status = STATUS_SUCCESS;
440
441 ByeBye:
442 if (!NT_SUCCESS(Status))
443 {
444 /* Cleanup */
445 if (Vcb && Vcb->StreamFileObject)
446 ObDereferenceObject(Vcb->StreamFileObject);
447 if (Fcb)
448 ExFreePool(Fcb);
449 if (Ccb)
450 ExFreePool(Ccb);
451 if (NewDeviceObject)
452 IoDeleteDevice(NewDeviceObject);
453 }
454
455 DPRINT("NtfsMountVolume() done (Status: %lx)\n", Status);
456
457 return Status;
458 }
459
460
461 static NTSTATUS
462 NtfsVerifyVolume(PDEVICE_OBJECT DeviceObject,
463 PIRP Irp)
464 {
465 DPRINT1("NtfsVerifyVolume() called\n");
466
467 return STATUS_WRONG_VOLUME;
468 }
469
470
471 NTSTATUS NTAPI
472 NtfsFsdFileSystemControl(PDEVICE_OBJECT DeviceObject,
473 PIRP Irp)
474 {
475 PIO_STACK_LOCATION Stack;
476 NTSTATUS Status;
477
478 DPRINT1("NtfsFileSystemControl() called\n");
479
480 Stack = IoGetCurrentIrpStackLocation(Irp);
481
482 switch (Stack->MinorFunction)
483 {
484 case IRP_MN_KERNEL_CALL:
485 case IRP_MN_USER_FS_REQUEST:
486 DPRINT("NTFS: IRP_MN_USER_FS_REQUEST/IRP_MN_KERNEL_CALL\n");
487 Status = STATUS_INVALID_DEVICE_REQUEST;
488 break;
489
490 case IRP_MN_MOUNT_VOLUME:
491 DPRINT("NTFS: IRP_MN_MOUNT_VOLUME\n");
492 Status = NtfsMountVolume(DeviceObject, Irp);
493 break;
494
495 case IRP_MN_VERIFY_VOLUME:
496 DPRINT1("NTFS: IRP_MN_VERIFY_VOLUME\n");
497 Status = NtfsVerifyVolume(DeviceObject, Irp);
498 break;
499
500 default:
501 DPRINT("NTFS FSC: MinorFunction %d\n", Stack->MinorFunction);
502 Status = STATUS_INVALID_DEVICE_REQUEST;
503 break;
504 }
505
506 Irp->IoStatus.Status = Status;
507 Irp->IoStatus.Information = 0;
508
509 IoCompleteRequest(Irp, IO_NO_INCREMENT);
510
511 return(Status);
512 }
513
514 /* EOF */