[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 else if (DiskGeometry.MediaType == Unknown)
93 {
94 DiskGeometry.BytesPerSector = 512;
95 }
96
97 DPRINT1("BytesPerSector: %lu\n", DiskGeometry.BytesPerSector);
98 BootSector = ExAllocatePoolWithTag(NonPagedPool,
99 DiskGeometry.BytesPerSector,
100 TAG_NTFS);
101 if (BootSector == NULL)
102 {
103 return STATUS_INSUFFICIENT_RESOURCES;
104 }
105
106 Status = NtfsReadSectors(DeviceToMount,
107 0,
108 1,
109 DiskGeometry.BytesPerSector,
110 (PVOID)BootSector,
111 TRUE);
112 if (!NT_SUCCESS(Status))
113 {
114 goto ByeBye;
115 }
116
117 /*
118 * Check values of different fields. If those fields have not expected
119 * values, we fail, to avoid mounting partitions that Windows won't mount.
120 */
121
122 /* OEMID: this field must be NTFS */
123 if (RtlCompareMemory(BootSector->OEMID, "NTFS ", 8) != 8)
124 {
125 DPRINT1("Failed with NTFS-identifier: [%.8s]\n", BootSector->OEMID);
126 Status = STATUS_UNRECOGNIZED_VOLUME;
127 goto ByeBye;
128 }
129
130 /* Unused0: this field must be COMPLETELY null */
131 for (k = 0; k < 7; k++)
132 {
133 if (BootSector->BPB.Unused0[k] != 0)
134 {
135 DPRINT1("Failed in field Unused0: [%.7s]\n", BootSector->BPB.Unused0);
136 Status = STATUS_UNRECOGNIZED_VOLUME;
137 goto ByeBye;
138 }
139 }
140
141 /* Unused3: this field must be COMPLETELY null */
142 for (k = 0; k < 4; k++)
143 {
144 if (BootSector->BPB.Unused3[k] != 0)
145 {
146 DPRINT1("Failed in field Unused3: [%.4s]\n", BootSector->BPB.Unused3);
147 Status = STATUS_UNRECOGNIZED_VOLUME;
148 goto ByeBye;
149 }
150 }
151
152 /* Check cluster size */
153 ClusterSize = BootSector->BPB.BytesPerSector * BootSector->BPB.SectorsPerCluster;
154 if (ClusterSize != 512 && ClusterSize != 1024 &&
155 ClusterSize != 2048 && ClusterSize != 4096 &&
156 ClusterSize != 8192 && ClusterSize != 16384 &&
157 ClusterSize != 32768 && ClusterSize != 65536)
158 {
159 DPRINT1("Cluster size failed: %hu, %hu, %hu\n",
160 BootSector->BPB.BytesPerSector,
161 BootSector->BPB.SectorsPerCluster,
162 ClusterSize);
163 Status = STATUS_UNRECOGNIZED_VOLUME;
164 goto ByeBye;
165 }
166
167 ByeBye:
168 ExFreePool(BootSector);
169
170 return Status;
171 }
172
173
174 static
175 ULONG
176 NtfsQueryMftZoneReservation(VOID)
177 {
178 ULONG ZoneReservation = 1;
179 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
180
181 RtlZeroMemory(QueryTable, sizeof(QueryTable));
182 QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
183 QueryTable[0].Name = L"NtfsMftZoneReservation";
184 QueryTable[0].EntryContext = &ZoneReservation;
185
186 RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
187 L"FileSystem",
188 QueryTable,
189 NULL,
190 NULL);
191
192 return ZoneReservation;
193 }
194
195
196 static
197 NTSTATUS
198 NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject,
199 PDEVICE_EXTENSION DeviceExt)
200 {
201 DISK_GEOMETRY DiskGeometry;
202 PFILE_RECORD_HEADER VolumeRecord;
203 PVOLINFO_ATTRIBUTE VolumeInfo;
204 PBOOT_SECTOR BootSector;
205 ULONG Size;
206 PNTFS_INFO NtfsInfo = &DeviceExt->NtfsInfo;
207 NTSTATUS Status;
208 PNTFS_ATTR_CONTEXT AttrCtxt;
209 PNTFS_ATTR_RECORD Attribute;
210 PNTFS_FCB VolumeFcb;
211 PWSTR VolumeNameU;
212
213 DPRINT("NtfsGetVolumeData() called\n");
214
215 Size = sizeof(DISK_GEOMETRY);
216 Status = NtfsDeviceIoControl(DeviceObject,
217 IOCTL_DISK_GET_DRIVE_GEOMETRY,
218 NULL,
219 0,
220 &DiskGeometry,
221 &Size,
222 TRUE);
223 if (!NT_SUCCESS(Status))
224 {
225 DPRINT("NtfsDeviceIoControl() failed (Status %lx)\n", Status);
226 return Status;
227 }
228
229 DPRINT("BytesPerSector: %lu\n", DiskGeometry.BytesPerSector);
230 BootSector = ExAllocatePoolWithTag(NonPagedPool,
231 DiskGeometry.BytesPerSector,
232 TAG_NTFS);
233 if (BootSector == NULL)
234 {
235 return STATUS_INSUFFICIENT_RESOURCES;
236 }
237
238 Status = NtfsReadSectors(DeviceObject,
239 0, /* Partition boot sector */
240 1,
241 DiskGeometry.BytesPerSector,
242 (PVOID)BootSector,
243 TRUE);
244 if (!NT_SUCCESS(Status))
245 {
246 ExFreePool(BootSector);
247 return Status;
248 }
249
250 /* Read data from the bootsector */
251 NtfsInfo->BytesPerSector = BootSector->BPB.BytesPerSector;
252 NtfsInfo->SectorsPerCluster = BootSector->BPB.SectorsPerCluster;
253 NtfsInfo->BytesPerCluster = BootSector->BPB.BytesPerSector * BootSector->BPB.SectorsPerCluster;
254 NtfsInfo->SectorCount = BootSector->EBPB.SectorCount;
255 NtfsInfo->ClusterCount = DeviceExt->NtfsInfo.SectorCount / (ULONGLONG)DeviceExt->NtfsInfo.SectorsPerCluster;
256
257 NtfsInfo->MftStart.QuadPart = BootSector->EBPB.MftLocation;
258 NtfsInfo->MftMirrStart.QuadPart = BootSector->EBPB.MftMirrLocation;
259 NtfsInfo->SerialNumber = BootSector->EBPB.SerialNumber;
260 if (BootSector->EBPB.ClustersPerMftRecord > 0)
261 NtfsInfo->BytesPerFileRecord = BootSector->EBPB.ClustersPerMftRecord * NtfsInfo->BytesPerCluster;
262 else
263 NtfsInfo->BytesPerFileRecord = 1 << (-BootSector->EBPB.ClustersPerMftRecord);
264 if (BootSector->EBPB.ClustersPerIndexRecord > 0)
265 NtfsInfo->BytesPerIndexRecord = BootSector->EBPB.ClustersPerIndexRecord * NtfsInfo->BytesPerCluster;
266 else
267 NtfsInfo->BytesPerIndexRecord = 1 << (-BootSector->EBPB.ClustersPerIndexRecord);
268
269 DPRINT("Boot sector information:\n");
270 DPRINT(" BytesPerSector: %hu\n", BootSector->BPB.BytesPerSector);
271 DPRINT(" SectorsPerCluster: %hu\n", BootSector->BPB.SectorsPerCluster);
272 DPRINT(" SectorCount: %I64u\n", BootSector->EBPB.SectorCount);
273 DPRINT(" MftStart: %I64u\n", BootSector->EBPB.MftLocation);
274 DPRINT(" MftMirrStart: %I64u\n", BootSector->EBPB.MftMirrLocation);
275 DPRINT(" ClustersPerMftRecord: %lx\n", BootSector->EBPB.ClustersPerMftRecord);
276 DPRINT(" ClustersPerIndexRecord: %lx\n", BootSector->EBPB.ClustersPerIndexRecord);
277 DPRINT(" SerialNumber: %I64x\n", BootSector->EBPB.SerialNumber);
278
279 ExFreePool(BootSector);
280
281 DeviceExt->MasterFileTable = ExAllocatePoolWithTag(NonPagedPool,
282 NtfsInfo->BytesPerFileRecord,
283 TAG_NTFS);
284 if (DeviceExt->MasterFileTable == NULL)
285 {
286 return STATUS_INSUFFICIENT_RESOURCES;
287 }
288
289 Status = NtfsReadSectors(DeviceObject,
290 NtfsInfo->MftStart.u.LowPart * NtfsInfo->SectorsPerCluster,
291 NtfsInfo->BytesPerFileRecord / NtfsInfo->BytesPerSector,
292 NtfsInfo->BytesPerSector,
293 (PVOID)DeviceExt->MasterFileTable,
294 TRUE);
295 if (!NT_SUCCESS(Status))
296 {
297 DPRINT1("Failed reading MFT.\n");
298 ExFreePool(DeviceExt->MasterFileTable);
299 return Status;
300 }
301
302 Status = FindAttribute(DeviceExt, DeviceExt->MasterFileTable, AttributeData, L"", 0, &DeviceExt->MFTContext);
303 if (!NT_SUCCESS(Status))
304 {
305 DPRINT1("Can't find data attribute for Master File Table.\n");
306 ExFreePool(DeviceExt->MasterFileTable);
307 return Status;
308 }
309
310 VolumeRecord = ExAllocatePoolWithTag(NonPagedPool,
311 NtfsInfo->BytesPerFileRecord,
312 TAG_NTFS);
313 if (VolumeRecord == NULL)
314 {
315 DPRINT1("Allocation failed for volume record\n");
316 ExFreePool(DeviceExt->MasterFileTable);
317 return STATUS_INSUFFICIENT_RESOURCES;
318 }
319
320 /* Read Volume File (MFT index 3) */
321 DeviceExt->StorageDevice = DeviceObject;
322 Status = ReadFileRecord(DeviceExt,
323 NTFS_FILE_VOLUME,
324 VolumeRecord);
325 if (!NT_SUCCESS(Status))
326 {
327 DPRINT1("Failed reading volume file\n");
328 ExFreePool(VolumeRecord);
329 ExFreePool(DeviceExt->MasterFileTable);
330 return Status;
331 }
332
333 /* Enumerate attributes */
334 NtfsDumpFileAttributes(DeviceExt, DeviceExt->MasterFileTable);
335
336 /* Enumerate attributes */
337 NtfsDumpFileAttributes(DeviceExt, VolumeRecord);
338
339 /* Get volume name */
340 Status = FindAttribute(DeviceExt, VolumeRecord, AttributeVolumeName, L"", 0, &AttrCtxt);
341
342 if (NT_SUCCESS(Status) && AttrCtxt->Record.Resident.ValueLength != 0)
343 {
344 Attribute = &AttrCtxt->Record;
345 DPRINT("Data length %lu\n", AttributeDataLength(Attribute));
346 NtfsInfo->VolumeLabelLength =
347 min (Attribute->Resident.ValueLength, MAXIMUM_VOLUME_LABEL_LENGTH);
348 RtlCopyMemory(NtfsInfo->VolumeLabel,
349 (PVOID)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset),
350 NtfsInfo->VolumeLabelLength);
351 VolumeNameU = NtfsInfo->VolumeLabel;
352 }
353 else
354 {
355 NtfsInfo->VolumeLabelLength = 0;
356 VolumeNameU = L"\0";
357 }
358
359 if (NT_SUCCESS(Status))
360 {
361 ReleaseAttributeContext(AttrCtxt);
362 }
363
364 VolumeFcb = NtfsCreateFCB(VolumeNameU, NULL, DeviceExt);
365 if (VolumeFcb == NULL)
366 {
367 DPRINT1("Failed allocating volume FCB\n");
368 ExFreePool(VolumeRecord);
369 ExFreePool(DeviceExt->MasterFileTable);
370 return STATUS_INSUFFICIENT_RESOURCES;
371 }
372
373 VolumeFcb->Flags = FCB_IS_VOLUME;
374 VolumeFcb->RFCB.FileSize.QuadPart = DeviceExt->NtfsInfo.SectorCount * DeviceExt->NtfsInfo.BytesPerSector;
375 VolumeFcb->RFCB.ValidDataLength = VolumeFcb->RFCB.FileSize;
376 VolumeFcb->RFCB.AllocationSize = VolumeFcb->RFCB.FileSize;
377 VolumeFcb->MFTIndex = 0;
378 DeviceExt->VolumeFcb = VolumeFcb;
379
380 /* Get volume information */
381 Status = FindAttribute(DeviceExt, VolumeRecord, AttributeVolumeInformation, L"", 0, &AttrCtxt);
382
383 if (NT_SUCCESS(Status) && AttrCtxt->Record.Resident.ValueLength != 0)
384 {
385 Attribute = &AttrCtxt->Record;
386 DPRINT("Data length %lu\n", AttributeDataLength (Attribute));
387 VolumeInfo = (PVOID)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
388
389 NtfsInfo->MajorVersion = VolumeInfo->MajorVersion;
390 NtfsInfo->MinorVersion = VolumeInfo->MinorVersion;
391 NtfsInfo->Flags = VolumeInfo->Flags;
392 }
393
394 if (NT_SUCCESS(Status))
395 {
396 ReleaseAttributeContext(AttrCtxt);
397 }
398
399 ExFreePool(VolumeRecord);
400
401 NtfsInfo->MftZoneReservation = NtfsQueryMftZoneReservation();
402
403 return Status;
404 }
405
406
407 static
408 NTSTATUS
409 NtfsMountVolume(PDEVICE_OBJECT DeviceObject,
410 PIRP Irp)
411 {
412 PDEVICE_OBJECT NewDeviceObject = NULL;
413 PDEVICE_OBJECT DeviceToMount;
414 PIO_STACK_LOCATION Stack;
415 PNTFS_FCB Fcb = NULL;
416 PNTFS_CCB Ccb = NULL;
417 PNTFS_VCB Vcb = NULL;
418 NTSTATUS Status;
419
420 DPRINT1("NtfsMountVolume() called\n");
421
422 if (DeviceObject != NtfsGlobalData->DeviceObject)
423 {
424 Status = STATUS_INVALID_DEVICE_REQUEST;
425 goto ByeBye;
426 }
427
428 Stack = IoGetCurrentIrpStackLocation(Irp);
429 DeviceToMount = Stack->Parameters.MountVolume.DeviceObject;
430
431 Status = NtfsHasFileSystem(DeviceToMount);
432 if (!NT_SUCCESS(Status))
433 {
434 goto ByeBye;
435 }
436
437 Status = IoCreateDevice(NtfsGlobalData->DriverObject,
438 sizeof(DEVICE_EXTENSION),
439 NULL,
440 FILE_DEVICE_DISK_FILE_SYSTEM,
441 0,
442 FALSE,
443 &NewDeviceObject);
444 if (!NT_SUCCESS(Status))
445 goto ByeBye;
446
447 NewDeviceObject->Flags |= DO_DIRECT_IO;
448 Vcb = (PVOID)NewDeviceObject->DeviceExtension;
449 RtlZeroMemory(Vcb, sizeof(NTFS_VCB));
450
451 Vcb->Identifier.Type = NTFS_TYPE_VCB;
452 Vcb->Identifier.Size = sizeof(NTFS_TYPE_VCB);
453
454 Status = NtfsGetVolumeData(DeviceToMount,
455 Vcb);
456 if (!NT_SUCCESS(Status))
457 goto ByeBye;
458
459 NewDeviceObject->Vpb = DeviceToMount->Vpb;
460
461 Vcb->StorageDevice = DeviceToMount;
462 Vcb->StorageDevice->Vpb->DeviceObject = NewDeviceObject;
463 Vcb->StorageDevice->Vpb->RealDevice = Vcb->StorageDevice;
464 Vcb->StorageDevice->Vpb->Flags |= VPB_MOUNTED;
465 NewDeviceObject->StackSize = Vcb->StorageDevice->StackSize + 1;
466 NewDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
467
468 Vcb->StreamFileObject = IoCreateStreamFileObject(NULL,
469 Vcb->StorageDevice);
470
471 InitializeListHead(&Vcb->FcbListHead);
472
473 Fcb = NtfsCreateFCB(NULL, NULL, Vcb);
474 if (Fcb == NULL)
475 {
476 Status = STATUS_INSUFFICIENT_RESOURCES;
477 goto ByeBye;
478 }
479
480 Ccb = ExAllocatePoolWithTag(NonPagedPool,
481 sizeof(NTFS_CCB),
482 TAG_CCB);
483 if (Ccb == NULL)
484 {
485 Status = STATUS_INSUFFICIENT_RESOURCES;
486 goto ByeBye;
487 }
488
489 RtlZeroMemory(Ccb, sizeof(NTFS_CCB));
490
491 Ccb->Identifier.Type = NTFS_TYPE_CCB;
492 Ccb->Identifier.Size = sizeof(NTFS_TYPE_CCB);
493
494 Vcb->StreamFileObject->FsContext = Fcb;
495 Vcb->StreamFileObject->FsContext2 = Ccb;
496 Vcb->StreamFileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
497 Vcb->StreamFileObject->PrivateCacheMap = NULL;
498 Vcb->StreamFileObject->Vpb = Vcb->Vpb;
499 Ccb->PtrFileObject = Vcb->StreamFileObject;
500 Fcb->FileObject = Vcb->StreamFileObject;
501 Fcb->Vcb = (PDEVICE_EXTENSION)Vcb->StorageDevice;
502
503 Fcb->Flags = FCB_IS_VOLUME_STREAM;
504
505 Fcb->RFCB.FileSize.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector;
506 Fcb->RFCB.ValidDataLength.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector;
507 Fcb->RFCB.AllocationSize.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector; /* Correct? */
508
509 // Fcb->Entry.ExtentLocationL = 0;
510 // Fcb->Entry.DataLengthL = DeviceExt->CdInfo.VolumeSpaceSize * BLOCKSIZE;
511
512 _SEH2_TRY
513 {
514 CcInitializeCacheMap(Vcb->StreamFileObject,
515 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
516 TRUE,
517 &(NtfsGlobalData->CacheMgrCallbacks),
518 Fcb);
519 }
520 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
521 {
522 Status = _SEH2_GetExceptionCode();
523 goto ByeBye;
524 }
525 _SEH2_END;
526
527 ExInitializeResourceLite(&Vcb->DirResource);
528
529 KeInitializeSpinLock(&Vcb->FcbListLock);
530
531 /* Get serial number */
532 NewDeviceObject->Vpb->SerialNumber = Vcb->NtfsInfo.SerialNumber;
533
534 /* Get volume label */
535 NewDeviceObject->Vpb->VolumeLabelLength = Vcb->NtfsInfo.VolumeLabelLength;
536 RtlCopyMemory(NewDeviceObject->Vpb->VolumeLabel,
537 Vcb->NtfsInfo.VolumeLabel,
538 Vcb->NtfsInfo.VolumeLabelLength);
539
540 FsRtlNotifyVolumeEvent(Vcb->StreamFileObject, FSRTL_VOLUME_MOUNT);
541
542 Status = STATUS_SUCCESS;
543
544 ByeBye:
545 if (!NT_SUCCESS(Status))
546 {
547 /* Cleanup */
548 if (Vcb && Vcb->StreamFileObject)
549 ObDereferenceObject(Vcb->StreamFileObject);
550
551 if (Fcb)
552 NtfsDestroyFCB(Fcb);
553
554 if (Ccb)
555 ExFreePool(Ccb);
556
557 if (NewDeviceObject)
558 IoDeleteDevice(NewDeviceObject);
559 }
560
561 DPRINT("NtfsMountVolume() done (Status: %lx)\n", Status);
562
563 return Status;
564 }
565
566
567 static
568 NTSTATUS
569 NtfsVerifyVolume(PDEVICE_OBJECT DeviceObject,
570 PIRP Irp)
571 {
572 UNREFERENCED_PARAMETER(DeviceObject);
573 UNREFERENCED_PARAMETER(Irp);
574 DPRINT1("NtfsVerifyVolume() called\n");
575 return STATUS_WRONG_VOLUME;
576 }
577
578
579 static
580 NTSTATUS
581 GetNfsVolumeData(PDEVICE_EXTENSION DeviceExt,
582 PIRP Irp)
583 {
584 PIO_STACK_LOCATION Stack;
585 PNTFS_VOLUME_DATA_BUFFER DataBuffer;
586 PNTFS_ATTR_RECORD Attribute;
587 FIND_ATTR_CONTXT Context;
588 NTSTATUS Status;
589
590 DataBuffer = (PNTFS_VOLUME_DATA_BUFFER)Irp->AssociatedIrp.SystemBuffer;
591 Stack = IoGetCurrentIrpStackLocation(Irp);
592
593 if (Stack->Parameters.FileSystemControl.OutputBufferLength < sizeof(NTFS_VOLUME_DATA_BUFFER) ||
594 Irp->UserBuffer == NULL)
595 {
596 DPRINT1("Invalid output! %d %p\n", Stack->Parameters.FileSystemControl.OutputBufferLength, Irp->UserBuffer);
597 return STATUS_INVALID_PARAMETER;
598 }
599
600 DataBuffer->VolumeSerialNumber.QuadPart = DeviceExt->NtfsInfo.SerialNumber;
601 DataBuffer->NumberSectors.QuadPart = DeviceExt->NtfsInfo.SectorCount;
602 DataBuffer->TotalClusters.QuadPart = DeviceExt->NtfsInfo.ClusterCount;
603 DataBuffer->FreeClusters.QuadPart = NtfsGetFreeClusters(DeviceExt);
604 DataBuffer->TotalReserved.QuadPart = 0LL; // FIXME
605 DataBuffer->BytesPerSector = DeviceExt->NtfsInfo.BytesPerSector;
606 DataBuffer->BytesPerCluster = DeviceExt->NtfsInfo.BytesPerCluster;
607 DataBuffer->BytesPerFileRecordSegment = DeviceExt->NtfsInfo.BytesPerFileRecord;
608 DataBuffer->ClustersPerFileRecordSegment = DeviceExt->NtfsInfo.BytesPerFileRecord / DeviceExt->NtfsInfo.BytesPerCluster;
609 DataBuffer->MftStartLcn.QuadPart = DeviceExt->NtfsInfo.MftStart.QuadPart;
610 DataBuffer->Mft2StartLcn.QuadPart = DeviceExt->NtfsInfo.MftMirrStart.QuadPart;
611 DataBuffer->MftZoneStart.QuadPart = 0; // FIXME
612 DataBuffer->MftZoneEnd.QuadPart = 0; // FIXME
613
614 Status = FindFirstAttribute(&Context, DeviceExt, DeviceExt->MasterFileTable, FALSE, &Attribute);
615 while (NT_SUCCESS(Status))
616 {
617 if (Attribute->Type == AttributeData)
618 {
619 ASSERT(Attribute->IsNonResident);
620 DataBuffer->MftValidDataLength.QuadPart = Attribute->NonResident.DataSize;
621
622 break;
623 }
624
625 Status = FindNextAttribute(&Context, &Attribute);
626 }
627 FindCloseAttribute(&Context);
628
629 Irp->IoStatus.Information = sizeof(NTFS_VOLUME_DATA_BUFFER);
630
631 if (Stack->Parameters.FileSystemControl.OutputBufferLength >= sizeof(NTFS_EXTENDED_VOLUME_DATA) + sizeof(NTFS_VOLUME_DATA_BUFFER))
632 {
633 PNTFS_EXTENDED_VOLUME_DATA ExtendedData = (PNTFS_EXTENDED_VOLUME_DATA)((ULONG_PTR)Irp->UserBuffer + sizeof(NTFS_VOLUME_DATA_BUFFER));
634
635 ExtendedData->ByteCount = sizeof(NTFS_EXTENDED_VOLUME_DATA);
636 ExtendedData->MajorVersion = DeviceExt->NtfsInfo.MajorVersion;
637 ExtendedData->MinorVersion = DeviceExt->NtfsInfo.MinorVersion;
638 Irp->IoStatus.Information += sizeof(NTFS_EXTENDED_VOLUME_DATA);
639 }
640
641 return STATUS_SUCCESS;
642 }
643
644
645 static
646 NTSTATUS
647 GetNtfsFileRecord(PDEVICE_EXTENSION DeviceExt,
648 PIRP Irp)
649 {
650 NTSTATUS Status;
651 PIO_STACK_LOCATION Stack;
652 PNTFS_FILE_RECORD_INPUT_BUFFER InputBuffer;
653 PFILE_RECORD_HEADER FileRecord;
654 PNTFS_FILE_RECORD_OUTPUT_BUFFER OutputBuffer;
655 ULONGLONG MFTRecord;
656
657 Stack = IoGetCurrentIrpStackLocation(Irp);
658
659 if (Stack->Parameters.FileSystemControl.InputBufferLength < sizeof(NTFS_FILE_RECORD_INPUT_BUFFER) ||
660 Irp->AssociatedIrp.SystemBuffer == NULL)
661 {
662 DPRINT1("Invalid input! %d %p\n", Stack->Parameters.FileSystemControl.InputBufferLength, Irp->AssociatedIrp.SystemBuffer);
663 return STATUS_INVALID_PARAMETER;
664 }
665
666 if (Stack->Parameters.FileSystemControl.OutputBufferLength < (FIELD_OFFSET(NTFS_FILE_RECORD_OUTPUT_BUFFER, FileRecordBuffer) + DeviceExt->NtfsInfo.BytesPerFileRecord) ||
667 Irp->AssociatedIrp.SystemBuffer == NULL)
668 {
669 DPRINT1("Invalid output! %d %p\n", Stack->Parameters.FileSystemControl.OutputBufferLength, Irp->AssociatedIrp.SystemBuffer);
670 return STATUS_BUFFER_TOO_SMALL;
671 }
672
673 FileRecord = ExAllocatePoolWithTag(NonPagedPool,
674 DeviceExt->NtfsInfo.BytesPerFileRecord,
675 TAG_NTFS);
676 if (FileRecord == NULL)
677 {
678 return STATUS_INSUFFICIENT_RESOURCES;
679 }
680
681 InputBuffer = (PNTFS_FILE_RECORD_INPUT_BUFFER)Irp->AssociatedIrp.SystemBuffer;
682
683 MFTRecord = InputBuffer->FileReferenceNumber.QuadPart;
684 DPRINT1("Requesting: %I64x\n", MFTRecord);
685
686 do
687 {
688 Status = ReadFileRecord(DeviceExt, MFTRecord, FileRecord);
689 if (NT_SUCCESS(Status))
690 {
691 if (FileRecord->Flags & FRH_IN_USE)
692 {
693 break;
694 }
695 }
696
697 --MFTRecord;
698 } while (TRUE);
699
700 DPRINT1("Returning: %I64x\n", MFTRecord);
701 OutputBuffer = (PNTFS_FILE_RECORD_OUTPUT_BUFFER)Irp->AssociatedIrp.SystemBuffer;
702 OutputBuffer->FileReferenceNumber.QuadPart = MFTRecord;
703 OutputBuffer->FileRecordLength = DeviceExt->NtfsInfo.BytesPerFileRecord;
704 RtlCopyMemory(OutputBuffer->FileRecordBuffer, FileRecord, DeviceExt->NtfsInfo.BytesPerFileRecord);
705
706 ExFreePoolWithTag(FileRecord, TAG_NTFS);
707
708 Irp->IoStatus.Information = FIELD_OFFSET(NTFS_FILE_RECORD_OUTPUT_BUFFER, FileRecordBuffer) + DeviceExt->NtfsInfo.BytesPerFileRecord;
709
710 return STATUS_SUCCESS;
711 }
712
713
714 static
715 NTSTATUS
716 GetVolumeBitmap(PDEVICE_EXTENSION DeviceExt,
717 PIRP Irp)
718 {
719 NTSTATUS Status = STATUS_SUCCESS;
720 PIO_STACK_LOCATION Stack;
721 PVOLUME_BITMAP_BUFFER BitmapBuffer;
722 LONGLONG StartingLcn;
723 PFILE_RECORD_HEADER BitmapRecord;
724 PNTFS_ATTR_CONTEXT DataContext;
725 ULONGLONG TotalClusters;
726 ULONGLONG ToCopy;
727 BOOLEAN Overflow = FALSE;
728
729 DPRINT1("GetVolumeBitmap(%p, %p)\n", DeviceExt, Irp);
730
731 Stack = IoGetCurrentIrpStackLocation(Irp);
732
733 if (Stack->Parameters.FileSystemControl.InputBufferLength < sizeof(STARTING_LCN_INPUT_BUFFER))
734 {
735 DPRINT1("Invalid input! %d\n", Stack->Parameters.FileSystemControl.InputBufferLength);
736 return STATUS_INVALID_PARAMETER;
737 }
738
739 if (Stack->Parameters.FileSystemControl.OutputBufferLength < sizeof(VOLUME_BITMAP_BUFFER))
740 {
741 DPRINT1("Invalid output! %d\n", Stack->Parameters.FileSystemControl.OutputBufferLength);
742 return STATUS_BUFFER_TOO_SMALL;
743 }
744
745 BitmapBuffer = NtfsGetUserBuffer(Irp, FALSE);
746 if (Irp->RequestorMode == UserMode)
747 {
748 _SEH2_TRY
749 {
750 ProbeForRead(Stack->Parameters.FileSystemControl.Type3InputBuffer,
751 Stack->Parameters.FileSystemControl.InputBufferLength,
752 sizeof(CHAR));
753 ProbeForWrite(BitmapBuffer, Stack->Parameters.FileSystemControl.OutputBufferLength,
754 sizeof(CHAR));
755 }
756 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
757 {
758 Status = _SEH2_GetExceptionCode();
759 }
760 _SEH2_END;
761 }
762 else
763 {
764 if (Stack->Parameters.FileSystemControl.Type3InputBuffer == NULL ||
765 BitmapBuffer == NULL)
766 {
767 Status = STATUS_INVALID_PARAMETER;
768 }
769 }
770
771 if (!NT_SUCCESS(Status))
772 {
773 DPRINT1("Invalid buffer! %p %p\n", Stack->Parameters.FileSystemControl.Type3InputBuffer, BitmapBuffer);
774 return Status;
775 }
776
777 StartingLcn = ((PSTARTING_LCN_INPUT_BUFFER)Stack->Parameters.FileSystemControl.Type3InputBuffer)->StartingLcn.QuadPart;
778 if (StartingLcn > DeviceExt->NtfsInfo.ClusterCount)
779 {
780 DPRINT1("Requested bitmap start beyond partition end: %I64x %I64x\n", DeviceExt->NtfsInfo.ClusterCount, StartingLcn);
781 return STATUS_INVALID_PARAMETER;
782 }
783
784 /* Round down to a multiple of 8 */
785 StartingLcn = StartingLcn & ~7;
786 TotalClusters = DeviceExt->NtfsInfo.ClusterCount - StartingLcn;
787 ToCopy = TotalClusters / 8;
788 if ((ToCopy + FIELD_OFFSET(VOLUME_BITMAP_BUFFER, Buffer)) > Stack->Parameters.FileSystemControl.OutputBufferLength)
789 {
790 DPRINT1("Buffer too small: %x, needed: %x\n", Stack->Parameters.FileSystemControl.OutputBufferLength, (ToCopy + FIELD_OFFSET(VOLUME_BITMAP_BUFFER, Buffer)));
791 Overflow = TRUE;
792 ToCopy = Stack->Parameters.FileSystemControl.OutputBufferLength - FIELD_OFFSET(VOLUME_BITMAP_BUFFER, Buffer);
793 }
794
795 BitmapRecord = ExAllocatePoolWithTag(NonPagedPool,
796 DeviceExt->NtfsInfo.BytesPerFileRecord,
797 TAG_NTFS);
798 if (BitmapRecord == NULL)
799 {
800 return STATUS_INSUFFICIENT_RESOURCES;
801 }
802
803 Status = ReadFileRecord(DeviceExt, NTFS_FILE_BITMAP, BitmapRecord);
804 if (!NT_SUCCESS(Status))
805 {
806 DPRINT1("Failed reading volume bitmap: %lx\n", Status);
807 ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
808 return Status;
809 }
810
811 Status = FindAttribute(DeviceExt, BitmapRecord, AttributeData, L"", 0, &DataContext);
812 if (!NT_SUCCESS(Status))
813 {
814 DPRINT1("Failed find $DATA for bitmap: %lx\n", Status);
815 ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
816 return Status;
817 }
818
819 BitmapBuffer->StartingLcn.QuadPart = StartingLcn;
820 BitmapBuffer->BitmapSize.QuadPart = ToCopy * 8;
821
822 Irp->IoStatus.Information = FIELD_OFFSET(VOLUME_BITMAP_BUFFER, Buffer);
823 _SEH2_TRY
824 {
825 Irp->IoStatus.Information += ReadAttribute(DeviceExt, DataContext, StartingLcn / 8, (PCHAR)BitmapBuffer->Buffer, ToCopy);
826 Status = (Overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS);
827 }
828 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
829 {
830 Status = _SEH2_GetExceptionCode();
831 }
832 _SEH2_END;
833 ReleaseAttributeContext(DataContext);
834 ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
835
836 return Status;
837 }
838
839
840 static
841 NTSTATUS
842 NtfsUserFsRequest(PDEVICE_OBJECT DeviceObject,
843 PIRP Irp)
844 {
845 NTSTATUS Status;
846 PIO_STACK_LOCATION Stack;
847 PDEVICE_EXTENSION DeviceExt;
848
849 DPRINT1("NtfsUserFsRequest(%p, %p)\n", DeviceObject, Irp);
850
851 Stack = IoGetCurrentIrpStackLocation(Irp);
852 DeviceExt = DeviceObject->DeviceExtension;
853 switch (Stack->Parameters.FileSystemControl.FsControlCode)
854 {
855 case FSCTL_CREATE_USN_JOURNAL:
856 case FSCTL_DELETE_USN_JOURNAL:
857 case FSCTL_ENUM_USN_DATA:
858 case FSCTL_EXTEND_VOLUME:
859 //case FSCTL_GET_RETRIEVAL_POINTER_BASE:
860 case FSCTL_GET_RETRIEVAL_POINTERS:
861 case FSCTL_LOCK_VOLUME:
862 //case FSCTL_LOOKUP_STREAM_FROM_CLUSTER:
863 case FSCTL_MARK_HANDLE:
864 case FSCTL_MOVE_FILE:
865 case FSCTL_QUERY_USN_JOURNAL:
866 case FSCTL_READ_FILE_USN_DATA:
867 case FSCTL_READ_USN_JOURNAL:
868 //case FSCTL_SHRINK_VOLUME:
869 case FSCTL_UNLOCK_VOLUME:
870 case FSCTL_WRITE_USN_CLOSE_RECORD:
871 UNIMPLEMENTED;
872 DPRINT1("Unimplemented user request: %x\n", Stack->Parameters.FileSystemControl.FsControlCode);
873 Status = STATUS_NOT_IMPLEMENTED;
874 break;
875
876 case FSCTL_GET_NTFS_VOLUME_DATA:
877 Status = GetNfsVolumeData(DeviceExt, Irp);
878 break;
879
880 case FSCTL_GET_NTFS_FILE_RECORD:
881 Status = GetNtfsFileRecord(DeviceExt, Irp);
882 break;
883
884 case FSCTL_GET_VOLUME_BITMAP:
885 Status = GetVolumeBitmap(DeviceExt, Irp);
886 break;
887
888 default:
889 DPRINT("Invalid user request: %x\n", Stack->Parameters.FileSystemControl.FsControlCode);
890 Status = STATUS_INVALID_DEVICE_REQUEST;
891 break;
892 }
893
894 return Status;
895 }
896
897
898 NTSTATUS
899 NtfsFileSystemControl(PNTFS_IRP_CONTEXT IrpContext)
900 {
901 NTSTATUS Status;
902 PIRP Irp;
903 PDEVICE_OBJECT DeviceObject;
904
905 DPRINT1("NtfsFileSystemControl() called\n");
906
907 DeviceObject = IrpContext->DeviceObject;
908 Irp = IrpContext->Irp;
909 Irp->IoStatus.Information = 0;
910
911 switch (IrpContext->MinorFunction)
912 {
913 case IRP_MN_KERNEL_CALL:
914 DPRINT1("NTFS: IRP_MN_USER_FS_REQUEST\n");
915 Status = STATUS_INVALID_DEVICE_REQUEST;
916 break;
917
918 case IRP_MN_USER_FS_REQUEST:
919 Status = NtfsUserFsRequest(DeviceObject, Irp);
920 break;
921
922 case IRP_MN_MOUNT_VOLUME:
923 DPRINT("NTFS: IRP_MN_MOUNT_VOLUME\n");
924 Status = NtfsMountVolume(DeviceObject, Irp);
925 break;
926
927 case IRP_MN_VERIFY_VOLUME:
928 DPRINT1("NTFS: IRP_MN_VERIFY_VOLUME\n");
929 Status = NtfsVerifyVolume(DeviceObject, Irp);
930 break;
931
932 default:
933 DPRINT1("NTFS FSC: MinorFunction %d\n", IrpContext->MinorFunction);
934 Status = STATUS_INVALID_DEVICE_REQUEST;
935 break;
936 }
937
938 return Status;
939 }
940
941 /* EOF */