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