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