76337ec4482ac539b5a15c049db35bd7d5309cef
[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 #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,
299 DeviceExt->MasterFileTable,
300 AttributeData,
301 L"",
302 0,
303 &DeviceExt->MFTContext,
304 &DeviceExt->MftDataOffset);
305 if (!NT_SUCCESS(Status))
306 {
307 DPRINT1("Can't find data attribute for Master File Table.\n");
308 ExFreePool(DeviceExt->MasterFileTable);
309 return Status;
310 }
311
312 VolumeRecord = ExAllocatePoolWithTag(NonPagedPool,
313 NtfsInfo->BytesPerFileRecord,
314 TAG_NTFS);
315 if (VolumeRecord == NULL)
316 {
317 DPRINT1("Allocation failed for volume record\n");
318 ExFreePool(DeviceExt->MasterFileTable);
319 return STATUS_INSUFFICIENT_RESOURCES;
320 }
321
322 /* Read Volume File (MFT index 3) */
323 DeviceExt->StorageDevice = DeviceObject;
324 Status = ReadFileRecord(DeviceExt,
325 NTFS_FILE_VOLUME,
326 VolumeRecord);
327 if (!NT_SUCCESS(Status))
328 {
329 DPRINT1("Failed reading volume file\n");
330 ExFreePool(VolumeRecord);
331 ExFreePool(DeviceExt->MasterFileTable);
332 return Status;
333 }
334
335 /* Enumerate attributes */
336 NtfsDumpFileAttributes(DeviceExt, DeviceExt->MasterFileTable);
337
338 /* Enumerate attributes */
339 NtfsDumpFileAttributes(DeviceExt, VolumeRecord);
340
341 /* Get volume name */
342 Status = FindAttribute(DeviceExt, VolumeRecord, AttributeVolumeName, L"", 0, &AttrCtxt, NULL);
343
344 if (NT_SUCCESS(Status) && AttrCtxt->pRecord->Resident.ValueLength != 0)
345 {
346 Attribute = AttrCtxt->pRecord;
347 DPRINT("Data length %lu\n", AttributeDataLength(Attribute));
348 NtfsInfo->VolumeLabelLength =
349 min (Attribute->Resident.ValueLength, MAXIMUM_VOLUME_LABEL_LENGTH);
350 RtlCopyMemory(NtfsInfo->VolumeLabel,
351 (PVOID)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset),
352 NtfsInfo->VolumeLabelLength);
353 VolumeNameU = NtfsInfo->VolumeLabel;
354 }
355 else
356 {
357 NtfsInfo->VolumeLabelLength = 0;
358 VolumeNameU = L"\0";
359 }
360
361 if (NT_SUCCESS(Status))
362 {
363 ReleaseAttributeContext(AttrCtxt);
364 }
365
366 VolumeFcb = NtfsCreateFCB(VolumeNameU, NULL, DeviceExt);
367 if (VolumeFcb == NULL)
368 {
369 DPRINT1("Failed allocating volume FCB\n");
370 ExFreePool(VolumeRecord);
371 ExFreePool(DeviceExt->MasterFileTable);
372 return STATUS_INSUFFICIENT_RESOURCES;
373 }
374
375 VolumeFcb->Flags = FCB_IS_VOLUME;
376 VolumeFcb->RFCB.FileSize.QuadPart = DeviceExt->NtfsInfo.SectorCount * DeviceExt->NtfsInfo.BytesPerSector;
377 VolumeFcb->RFCB.ValidDataLength = VolumeFcb->RFCB.FileSize;
378 VolumeFcb->RFCB.AllocationSize = VolumeFcb->RFCB.FileSize;
379 VolumeFcb->MFTIndex = 0;
380 DeviceExt->VolumeFcb = VolumeFcb;
381
382 /* Get volume information */
383 Status = FindAttribute(DeviceExt, VolumeRecord, AttributeVolumeInformation, L"", 0, &AttrCtxt, NULL);
384
385 if (NT_SUCCESS(Status) && AttrCtxt->pRecord->Resident.ValueLength != 0)
386 {
387 Attribute = AttrCtxt->pRecord;
388 DPRINT("Data length %lu\n", AttributeDataLength (Attribute));
389 VolumeInfo = (PVOID)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
390
391 NtfsInfo->MajorVersion = VolumeInfo->MajorVersion;
392 NtfsInfo->MinorVersion = VolumeInfo->MinorVersion;
393 NtfsInfo->Flags = VolumeInfo->Flags;
394 }
395
396 if (NT_SUCCESS(Status))
397 {
398 ReleaseAttributeContext(AttrCtxt);
399 }
400
401 ExFreePool(VolumeRecord);
402
403 NtfsInfo->MftZoneReservation = NtfsQueryMftZoneReservation();
404
405 return Status;
406 }
407
408
409 static
410 NTSTATUS
411 NtfsMountVolume(PDEVICE_OBJECT DeviceObject,
412 PIRP Irp)
413 {
414 PDEVICE_OBJECT NewDeviceObject = NULL;
415 PDEVICE_OBJECT DeviceToMount;
416 PIO_STACK_LOCATION Stack;
417 PNTFS_FCB Fcb = NULL;
418 PNTFS_CCB Ccb = NULL;
419 PNTFS_VCB Vcb = NULL;
420 NTSTATUS Status;
421
422 DPRINT1("NtfsMountVolume() called\n");
423
424 if (DeviceObject != NtfsGlobalData->DeviceObject)
425 {
426 Status = STATUS_INVALID_DEVICE_REQUEST;
427 goto ByeBye;
428 }
429
430 Stack = IoGetCurrentIrpStackLocation(Irp);
431 DeviceToMount = Stack->Parameters.MountVolume.DeviceObject;
432
433 Status = NtfsHasFileSystem(DeviceToMount);
434 if (!NT_SUCCESS(Status))
435 {
436 goto ByeBye;
437 }
438
439 Status = IoCreateDevice(NtfsGlobalData->DriverObject,
440 sizeof(DEVICE_EXTENSION),
441 NULL,
442 FILE_DEVICE_DISK_FILE_SYSTEM,
443 0,
444 FALSE,
445 &NewDeviceObject);
446 if (!NT_SUCCESS(Status))
447 goto ByeBye;
448
449 NewDeviceObject->Flags |= DO_DIRECT_IO;
450 Vcb = (PVOID)NewDeviceObject->DeviceExtension;
451 RtlZeroMemory(Vcb, sizeof(NTFS_VCB));
452
453 Vcb->Identifier.Type = NTFS_TYPE_VCB;
454 Vcb->Identifier.Size = sizeof(NTFS_TYPE_VCB);
455
456 Status = NtfsGetVolumeData(DeviceToMount,
457 Vcb);
458 if (!NT_SUCCESS(Status))
459 goto ByeBye;
460
461 NewDeviceObject->Vpb = DeviceToMount->Vpb;
462
463 Vcb->StorageDevice = DeviceToMount;
464 Vcb->StorageDevice->Vpb->DeviceObject = NewDeviceObject;
465 Vcb->StorageDevice->Vpb->RealDevice = Vcb->StorageDevice;
466 Vcb->StorageDevice->Vpb->Flags |= VPB_MOUNTED;
467 NewDeviceObject->StackSize = Vcb->StorageDevice->StackSize + 1;
468 NewDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
469
470 Vcb->StreamFileObject = IoCreateStreamFileObject(NULL,
471 Vcb->StorageDevice);
472
473 InitializeListHead(&Vcb->FcbListHead);
474
475 Fcb = NtfsCreateFCB(NULL, NULL, Vcb);
476 if (Fcb == NULL)
477 {
478 Status = STATUS_INSUFFICIENT_RESOURCES;
479 goto ByeBye;
480 }
481
482 Ccb = ExAllocatePoolWithTag(NonPagedPool,
483 sizeof(NTFS_CCB),
484 TAG_CCB);
485 if (Ccb == NULL)
486 {
487 Status = STATUS_INSUFFICIENT_RESOURCES;
488 goto ByeBye;
489 }
490
491 RtlZeroMemory(Ccb, sizeof(NTFS_CCB));
492
493 Ccb->Identifier.Type = NTFS_TYPE_CCB;
494 Ccb->Identifier.Size = sizeof(NTFS_TYPE_CCB);
495
496 Vcb->StreamFileObject->FsContext = Fcb;
497 Vcb->StreamFileObject->FsContext2 = Ccb;
498 Vcb->StreamFileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
499 Vcb->StreamFileObject->PrivateCacheMap = NULL;
500 Vcb->StreamFileObject->Vpb = Vcb->Vpb;
501 Ccb->PtrFileObject = Vcb->StreamFileObject;
502 Fcb->FileObject = Vcb->StreamFileObject;
503 Fcb->Vcb = (PDEVICE_EXTENSION)Vcb->StorageDevice;
504
505 Fcb->Flags = FCB_IS_VOLUME_STREAM;
506
507 Fcb->RFCB.FileSize.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector;
508 Fcb->RFCB.ValidDataLength.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector;
509 Fcb->RFCB.AllocationSize.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector; /* Correct? */
510
511 // Fcb->Entry.ExtentLocationL = 0;
512 // Fcb->Entry.DataLengthL = DeviceExt->CdInfo.VolumeSpaceSize * BLOCKSIZE;
513
514 _SEH2_TRY
515 {
516 CcInitializeCacheMap(Vcb->StreamFileObject,
517 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
518 TRUE,
519 &(NtfsGlobalData->CacheMgrCallbacks),
520 Fcb);
521 }
522 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
523 {
524 Status = _SEH2_GetExceptionCode();
525 goto ByeBye;
526 }
527 _SEH2_END;
528
529 ExInitializeResourceLite(&Vcb->DirResource);
530
531 KeInitializeSpinLock(&Vcb->FcbListLock);
532
533 /* Get serial number */
534 NewDeviceObject->Vpb->SerialNumber = Vcb->NtfsInfo.SerialNumber;
535
536 /* Get volume label */
537 NewDeviceObject->Vpb->VolumeLabelLength = Vcb->NtfsInfo.VolumeLabelLength;
538 RtlCopyMemory(NewDeviceObject->Vpb->VolumeLabel,
539 Vcb->NtfsInfo.VolumeLabel,
540 Vcb->NtfsInfo.VolumeLabelLength);
541
542 FsRtlNotifyVolumeEvent(Vcb->StreamFileObject, FSRTL_VOLUME_MOUNT);
543
544 Status = STATUS_SUCCESS;
545
546 ByeBye:
547 if (!NT_SUCCESS(Status))
548 {
549 /* Cleanup */
550 if (Vcb && Vcb->StreamFileObject)
551 ObDereferenceObject(Vcb->StreamFileObject);
552
553 if (Fcb)
554 NtfsDestroyFCB(Fcb);
555
556 if (Ccb)
557 ExFreePool(Ccb);
558
559 if (NewDeviceObject)
560 IoDeleteDevice(NewDeviceObject);
561 }
562
563 DPRINT("NtfsMountVolume() done (Status: %lx)\n", Status);
564
565 return Status;
566 }
567
568
569 static
570 NTSTATUS
571 NtfsVerifyVolume(PDEVICE_OBJECT DeviceObject,
572 PIRP Irp)
573 {
574 UNREFERENCED_PARAMETER(DeviceObject);
575 UNREFERENCED_PARAMETER(Irp);
576 DPRINT1("NtfsVerifyVolume() called\n");
577 return STATUS_WRONG_VOLUME;
578 }
579
580
581 static
582 NTSTATUS
583 GetNfsVolumeData(PDEVICE_EXTENSION DeviceExt,
584 PIRP Irp)
585 {
586 PIO_STACK_LOCATION Stack;
587 PNTFS_VOLUME_DATA_BUFFER DataBuffer;
588 PNTFS_ATTR_RECORD Attribute;
589 FIND_ATTR_CONTXT Context;
590 NTSTATUS Status;
591
592 DataBuffer = (PNTFS_VOLUME_DATA_BUFFER)Irp->AssociatedIrp.SystemBuffer;
593 Stack = IoGetCurrentIrpStackLocation(Irp);
594
595 if (Stack->Parameters.FileSystemControl.OutputBufferLength < sizeof(NTFS_VOLUME_DATA_BUFFER) ||
596 Irp->UserBuffer == NULL)
597 {
598 DPRINT1("Invalid output! %d %p\n", Stack->Parameters.FileSystemControl.OutputBufferLength, Irp->UserBuffer);
599 return STATUS_INVALID_PARAMETER;
600 }
601
602 DataBuffer->VolumeSerialNumber.QuadPart = DeviceExt->NtfsInfo.SerialNumber;
603 DataBuffer->NumberSectors.QuadPart = DeviceExt->NtfsInfo.SectorCount;
604 DataBuffer->TotalClusters.QuadPart = DeviceExt->NtfsInfo.ClusterCount;
605 DataBuffer->FreeClusters.QuadPart = NtfsGetFreeClusters(DeviceExt);
606 DataBuffer->TotalReserved.QuadPart = 0LL; // FIXME
607 DataBuffer->BytesPerSector = DeviceExt->NtfsInfo.BytesPerSector;
608 DataBuffer->BytesPerCluster = DeviceExt->NtfsInfo.BytesPerCluster;
609 DataBuffer->BytesPerFileRecordSegment = DeviceExt->NtfsInfo.BytesPerFileRecord;
610 DataBuffer->ClustersPerFileRecordSegment = DeviceExt->NtfsInfo.BytesPerFileRecord / DeviceExt->NtfsInfo.BytesPerCluster;
611 DataBuffer->MftStartLcn.QuadPart = DeviceExt->NtfsInfo.MftStart.QuadPart;
612 DataBuffer->Mft2StartLcn.QuadPart = DeviceExt->NtfsInfo.MftMirrStart.QuadPart;
613 DataBuffer->MftZoneStart.QuadPart = 0; // FIXME
614 DataBuffer->MftZoneEnd.QuadPart = 0; // FIXME
615
616 Status = FindFirstAttribute(&Context, DeviceExt, DeviceExt->MasterFileTable, FALSE, &Attribute);
617 while (NT_SUCCESS(Status))
618 {
619 if (Attribute->Type == AttributeData)
620 {
621 ASSERT(Attribute->IsNonResident);
622 DataBuffer->MftValidDataLength.QuadPart = Attribute->NonResident.DataSize;
623
624 break;
625 }
626
627 Status = FindNextAttribute(&Context, &Attribute);
628 }
629 FindCloseAttribute(&Context);
630
631 Irp->IoStatus.Information = sizeof(NTFS_VOLUME_DATA_BUFFER);
632
633 if (Stack->Parameters.FileSystemControl.OutputBufferLength >= sizeof(NTFS_EXTENDED_VOLUME_DATA) + sizeof(NTFS_VOLUME_DATA_BUFFER))
634 {
635 PNTFS_EXTENDED_VOLUME_DATA ExtendedData = (PNTFS_EXTENDED_VOLUME_DATA)((ULONG_PTR)Irp->UserBuffer + sizeof(NTFS_VOLUME_DATA_BUFFER));
636
637 ExtendedData->ByteCount = sizeof(NTFS_EXTENDED_VOLUME_DATA);
638 ExtendedData->MajorVersion = DeviceExt->NtfsInfo.MajorVersion;
639 ExtendedData->MinorVersion = DeviceExt->NtfsInfo.MinorVersion;
640 Irp->IoStatus.Information += sizeof(NTFS_EXTENDED_VOLUME_DATA);
641 }
642
643 return STATUS_SUCCESS;
644 }
645
646
647 static
648 NTSTATUS
649 GetNtfsFileRecord(PDEVICE_EXTENSION DeviceExt,
650 PIRP Irp)
651 {
652 NTSTATUS Status;
653 PIO_STACK_LOCATION Stack;
654 PNTFS_FILE_RECORD_INPUT_BUFFER InputBuffer;
655 PFILE_RECORD_HEADER FileRecord;
656 PNTFS_FILE_RECORD_OUTPUT_BUFFER OutputBuffer;
657 ULONGLONG MFTRecord;
658
659 Stack = IoGetCurrentIrpStackLocation(Irp);
660
661 if (Stack->Parameters.FileSystemControl.InputBufferLength < sizeof(NTFS_FILE_RECORD_INPUT_BUFFER) ||
662 Irp->AssociatedIrp.SystemBuffer == NULL)
663 {
664 DPRINT1("Invalid input! %d %p\n", Stack->Parameters.FileSystemControl.InputBufferLength, Irp->AssociatedIrp.SystemBuffer);
665 return STATUS_INVALID_PARAMETER;
666 }
667
668 if (Stack->Parameters.FileSystemControl.OutputBufferLength < (FIELD_OFFSET(NTFS_FILE_RECORD_OUTPUT_BUFFER, FileRecordBuffer) + DeviceExt->NtfsInfo.BytesPerFileRecord) ||
669 Irp->AssociatedIrp.SystemBuffer == NULL)
670 {
671 DPRINT1("Invalid output! %d %p\n", Stack->Parameters.FileSystemControl.OutputBufferLength, Irp->AssociatedIrp.SystemBuffer);
672 return STATUS_BUFFER_TOO_SMALL;
673 }
674
675 FileRecord = ExAllocatePoolWithTag(NonPagedPool,
676 DeviceExt->NtfsInfo.BytesPerFileRecord,
677 TAG_NTFS);
678 if (FileRecord == NULL)
679 {
680 return STATUS_INSUFFICIENT_RESOURCES;
681 }
682
683 InputBuffer = (PNTFS_FILE_RECORD_INPUT_BUFFER)Irp->AssociatedIrp.SystemBuffer;
684
685 MFTRecord = InputBuffer->FileReferenceNumber.QuadPart;
686 DPRINT1("Requesting: %I64x\n", MFTRecord);
687
688 do
689 {
690 Status = ReadFileRecord(DeviceExt, MFTRecord, FileRecord);
691 if (NT_SUCCESS(Status))
692 {
693 if (FileRecord->Flags & FRH_IN_USE)
694 {
695 break;
696 }
697 }
698
699 --MFTRecord;
700 } while (TRUE);
701
702 DPRINT1("Returning: %I64x\n", MFTRecord);
703 OutputBuffer = (PNTFS_FILE_RECORD_OUTPUT_BUFFER)Irp->AssociatedIrp.SystemBuffer;
704 OutputBuffer->FileReferenceNumber.QuadPart = MFTRecord;
705 OutputBuffer->FileRecordLength = DeviceExt->NtfsInfo.BytesPerFileRecord;
706 RtlCopyMemory(OutputBuffer->FileRecordBuffer, FileRecord, DeviceExt->NtfsInfo.BytesPerFileRecord);
707
708 ExFreePoolWithTag(FileRecord, TAG_NTFS);
709
710 Irp->IoStatus.Information = FIELD_OFFSET(NTFS_FILE_RECORD_OUTPUT_BUFFER, FileRecordBuffer) + DeviceExt->NtfsInfo.BytesPerFileRecord;
711
712 return STATUS_SUCCESS;
713 }
714
715
716 static
717 NTSTATUS
718 GetVolumeBitmap(PDEVICE_EXTENSION DeviceExt,
719 PIRP Irp)
720 {
721 NTSTATUS Status = STATUS_SUCCESS;
722 PIO_STACK_LOCATION Stack;
723 PVOLUME_BITMAP_BUFFER BitmapBuffer;
724 LONGLONG StartingLcn;
725 PFILE_RECORD_HEADER BitmapRecord;
726 PNTFS_ATTR_CONTEXT DataContext;
727 ULONGLONG TotalClusters;
728 ULONGLONG ToCopy;
729 BOOLEAN Overflow = FALSE;
730
731 DPRINT1("GetVolumeBitmap(%p, %p)\n", DeviceExt, Irp);
732
733 Stack = IoGetCurrentIrpStackLocation(Irp);
734
735 if (Stack->Parameters.FileSystemControl.InputBufferLength < sizeof(STARTING_LCN_INPUT_BUFFER))
736 {
737 DPRINT1("Invalid input! %d\n", Stack->Parameters.FileSystemControl.InputBufferLength);
738 return STATUS_INVALID_PARAMETER;
739 }
740
741 if (Stack->Parameters.FileSystemControl.OutputBufferLength < sizeof(VOLUME_BITMAP_BUFFER))
742 {
743 DPRINT1("Invalid output! %d\n", Stack->Parameters.FileSystemControl.OutputBufferLength);
744 return STATUS_BUFFER_TOO_SMALL;
745 }
746
747 BitmapBuffer = NtfsGetUserBuffer(Irp, FALSE);
748 if (Irp->RequestorMode == UserMode)
749 {
750 _SEH2_TRY
751 {
752 ProbeForRead(Stack->Parameters.FileSystemControl.Type3InputBuffer,
753 Stack->Parameters.FileSystemControl.InputBufferLength,
754 sizeof(CHAR));
755 ProbeForWrite(BitmapBuffer, Stack->Parameters.FileSystemControl.OutputBufferLength,
756 sizeof(CHAR));
757 }
758 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
759 {
760 Status = _SEH2_GetExceptionCode();
761 }
762 _SEH2_END;
763 }
764 else
765 {
766 if (Stack->Parameters.FileSystemControl.Type3InputBuffer == NULL ||
767 BitmapBuffer == NULL)
768 {
769 Status = STATUS_INVALID_PARAMETER;
770 }
771 }
772
773 if (!NT_SUCCESS(Status))
774 {
775 DPRINT1("Invalid buffer! %p %p\n", Stack->Parameters.FileSystemControl.Type3InputBuffer, BitmapBuffer);
776 return Status;
777 }
778
779 StartingLcn = ((PSTARTING_LCN_INPUT_BUFFER)Stack->Parameters.FileSystemControl.Type3InputBuffer)->StartingLcn.QuadPart;
780 if (StartingLcn > DeviceExt->NtfsInfo.ClusterCount)
781 {
782 DPRINT1("Requested bitmap start beyond partition end: %I64x %I64x\n", DeviceExt->NtfsInfo.ClusterCount, StartingLcn);
783 return STATUS_INVALID_PARAMETER;
784 }
785
786 /* Round down to a multiple of 8 */
787 StartingLcn = StartingLcn & ~7;
788 TotalClusters = DeviceExt->NtfsInfo.ClusterCount - StartingLcn;
789 ToCopy = TotalClusters / 8;
790 if ((ToCopy + FIELD_OFFSET(VOLUME_BITMAP_BUFFER, Buffer)) > Stack->Parameters.FileSystemControl.OutputBufferLength)
791 {
792 DPRINT1("Buffer too small: %x, needed: %x\n", Stack->Parameters.FileSystemControl.OutputBufferLength, (ToCopy + FIELD_OFFSET(VOLUME_BITMAP_BUFFER, Buffer)));
793 Overflow = TRUE;
794 ToCopy = Stack->Parameters.FileSystemControl.OutputBufferLength - FIELD_OFFSET(VOLUME_BITMAP_BUFFER, Buffer);
795 }
796
797 BitmapRecord = ExAllocatePoolWithTag(NonPagedPool,
798 DeviceExt->NtfsInfo.BytesPerFileRecord,
799 TAG_NTFS);
800 if (BitmapRecord == NULL)
801 {
802 return STATUS_INSUFFICIENT_RESOURCES;
803 }
804
805 Status = ReadFileRecord(DeviceExt, NTFS_FILE_BITMAP, BitmapRecord);
806 if (!NT_SUCCESS(Status))
807 {
808 DPRINT1("Failed reading volume bitmap: %lx\n", Status);
809 ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
810 return Status;
811 }
812
813 Status = FindAttribute(DeviceExt, BitmapRecord, AttributeData, L"", 0, &DataContext, NULL);
814 if (!NT_SUCCESS(Status))
815 {
816 DPRINT1("Failed find $DATA for bitmap: %lx\n", Status);
817 ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
818 return Status;
819 }
820
821 BitmapBuffer->StartingLcn.QuadPart = StartingLcn;
822 BitmapBuffer->BitmapSize.QuadPart = ToCopy * 8;
823
824 Irp->IoStatus.Information = FIELD_OFFSET(VOLUME_BITMAP_BUFFER, Buffer);
825 _SEH2_TRY
826 {
827 Irp->IoStatus.Information += ReadAttribute(DeviceExt, DataContext, StartingLcn / 8, (PCHAR)BitmapBuffer->Buffer, ToCopy);
828 Status = (Overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS);
829 }
830 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
831 {
832 Status = _SEH2_GetExceptionCode();
833 }
834 _SEH2_END;
835 ReleaseAttributeContext(DataContext);
836 ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
837
838 return Status;
839 }
840
841
842 static
843 NTSTATUS
844 LockOrUnlockVolume(PDEVICE_EXTENSION DeviceExt,
845 PIRP Irp,
846 BOOLEAN Lock)
847 {
848 PFILE_OBJECT FileObject;
849 PNTFS_FCB Fcb;
850 PIO_STACK_LOCATION Stack;
851
852 DPRINT("LockOrUnlockVolume(%p, %p, %d)\n", DeviceExt, Irp, Lock);
853
854 Stack = IoGetCurrentIrpStackLocation(Irp);
855 FileObject = Stack->FileObject;
856 Fcb = FileObject->FsContext;
857
858 /* Only allow locking with the volume open */
859 if (!(Fcb->Flags & FCB_IS_VOLUME))
860 {
861 return STATUS_ACCESS_DENIED;
862 }
863
864 /* Bail out if it's already in the demanded state */
865 if (((DeviceExt->Flags & VCB_VOLUME_LOCKED) && Lock) ||
866 (!(DeviceExt->Flags & VCB_VOLUME_LOCKED) && !Lock))
867 {
868 return STATUS_ACCESS_DENIED;
869 }
870
871 /* Deny locking if we're not alone */
872 if (Lock && DeviceExt->OpenHandleCount != 1)
873 {
874 return STATUS_ACCESS_DENIED;
875 }
876
877 /* Finally, proceed */
878 if (Lock)
879 {
880 DeviceExt->Flags |= VCB_VOLUME_LOCKED;
881 }
882 else
883 {
884 DeviceExt->Flags &= ~VCB_VOLUME_LOCKED;
885 }
886
887 return STATUS_SUCCESS;
888 }
889
890
891 static
892 NTSTATUS
893 NtfsUserFsRequest(PDEVICE_OBJECT DeviceObject,
894 PIRP Irp)
895 {
896 NTSTATUS Status;
897 PIO_STACK_LOCATION Stack;
898 PDEVICE_EXTENSION DeviceExt;
899
900 DPRINT1("NtfsUserFsRequest(%p, %p)\n", DeviceObject, Irp);
901
902 Stack = IoGetCurrentIrpStackLocation(Irp);
903 DeviceExt = DeviceObject->DeviceExtension;
904 switch (Stack->Parameters.FileSystemControl.FsControlCode)
905 {
906 case FSCTL_CREATE_USN_JOURNAL:
907 case FSCTL_DELETE_USN_JOURNAL:
908 case FSCTL_ENUM_USN_DATA:
909 case FSCTL_EXTEND_VOLUME:
910 //case FSCTL_GET_RETRIEVAL_POINTER_BASE:
911 case FSCTL_GET_RETRIEVAL_POINTERS:
912 //case FSCTL_LOOKUP_STREAM_FROM_CLUSTER:
913 case FSCTL_MARK_HANDLE:
914 case FSCTL_MOVE_FILE:
915 case FSCTL_QUERY_USN_JOURNAL:
916 case FSCTL_READ_FILE_USN_DATA:
917 case FSCTL_READ_USN_JOURNAL:
918 //case FSCTL_SHRINK_VOLUME:
919 case FSCTL_WRITE_USN_CLOSE_RECORD:
920 UNIMPLEMENTED;
921 DPRINT1("Unimplemented user request: %x\n", Stack->Parameters.FileSystemControl.FsControlCode);
922 Status = STATUS_NOT_IMPLEMENTED;
923 break;
924
925 case FSCTL_LOCK_VOLUME:
926 Status = LockOrUnlockVolume(DeviceExt, Irp, TRUE);
927 break;
928
929 case FSCTL_UNLOCK_VOLUME:
930 Status = LockOrUnlockVolume(DeviceExt, Irp, FALSE);
931 break;
932
933 case FSCTL_GET_NTFS_VOLUME_DATA:
934 Status = GetNfsVolumeData(DeviceExt, Irp);
935 break;
936
937 case FSCTL_GET_NTFS_FILE_RECORD:
938 Status = GetNtfsFileRecord(DeviceExt, Irp);
939 break;
940
941 case FSCTL_GET_VOLUME_BITMAP:
942 Status = GetVolumeBitmap(DeviceExt, Irp);
943 break;
944
945 default:
946 DPRINT("Invalid user request: %x\n", Stack->Parameters.FileSystemControl.FsControlCode);
947 Status = STATUS_INVALID_DEVICE_REQUEST;
948 break;
949 }
950
951 return Status;
952 }
953
954
955 NTSTATUS
956 NtfsFileSystemControl(PNTFS_IRP_CONTEXT IrpContext)
957 {
958 NTSTATUS Status;
959 PIRP Irp;
960 PDEVICE_OBJECT DeviceObject;
961
962 DPRINT1("NtfsFileSystemControl() called\n");
963
964 DeviceObject = IrpContext->DeviceObject;
965 Irp = IrpContext->Irp;
966 Irp->IoStatus.Information = 0;
967
968 switch (IrpContext->MinorFunction)
969 {
970 case IRP_MN_KERNEL_CALL:
971 DPRINT1("NTFS: IRP_MN_USER_FS_REQUEST\n");
972 Status = STATUS_INVALID_DEVICE_REQUEST;
973 break;
974
975 case IRP_MN_USER_FS_REQUEST:
976 Status = NtfsUserFsRequest(DeviceObject, Irp);
977 break;
978
979 case IRP_MN_MOUNT_VOLUME:
980 DPRINT("NTFS: IRP_MN_MOUNT_VOLUME\n");
981 Status = NtfsMountVolume(DeviceObject, Irp);
982 break;
983
984 case IRP_MN_VERIFY_VOLUME:
985 DPRINT1("NTFS: IRP_MN_VERIFY_VOLUME\n");
986 Status = NtfsVerifyVolume(DeviceObject, Irp);
987 break;
988
989 default:
990 DPRINT1("NTFS FSC: MinorFunction %d\n", IrpContext->MinorFunction);
991 Status = STATUS_INVALID_DEVICE_REQUEST;
992 break;
993 }
994
995 return Status;
996 }
997
998 /* EOF */