3 * Copyright (C) 2002, 2014 ReactOS Team
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.
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.
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.
19 * COPYRIGHT: See COPYING in the top level directory
20 * PROJECT: ReactOS kernel
21 * FILE: drivers/filesystem/ntfs/volume.c
22 * PURPOSE: NTFS filesystem driver
23 * PROGRAMMERS: Eric Kohl
24 * Pierre Schweitzer (pierre@reactos.org)
27 /* INCLUDES *****************************************************************/
34 /* FUNCTIONS ****************************************************************/
37 NtfsGetFreeClusters(PDEVICE_EXTENSION DeviceExt
)
40 PFILE_RECORD_HEADER BitmapRecord
;
41 PNTFS_ATTR_CONTEXT DataContext
;
42 ULONGLONG BitmapDataSize
;
44 ULONGLONG FreeClusters
= 0;
48 DPRINT("NtfsGetFreeClusters(%p)\n", DeviceExt
);
50 BitmapRecord
= ExAllocateFromNPagedLookasideList(&DeviceExt
->FileRecLookasideList
);
51 if (BitmapRecord
== NULL
)
56 Status
= ReadFileRecord(DeviceExt
, NTFS_FILE_BITMAP
, BitmapRecord
);
57 if (!NT_SUCCESS(Status
))
59 ExFreeToNPagedLookasideList(&DeviceExt
->FileRecLookasideList
, BitmapRecord
);
63 Status
= FindAttribute(DeviceExt
, BitmapRecord
, AttributeData
, L
"", 0, &DataContext
, NULL
);
64 if (!NT_SUCCESS(Status
))
66 ExFreeToNPagedLookasideList(&DeviceExt
->FileRecLookasideList
, BitmapRecord
);
70 BitmapDataSize
= AttributeDataLength(DataContext
->pRecord
);
71 ASSERT((BitmapDataSize
* 8) >= DeviceExt
->NtfsInfo
.ClusterCount
);
72 BitmapData
= ExAllocatePoolWithTag(NonPagedPool
, ROUND_UP(BitmapDataSize
, DeviceExt
->NtfsInfo
.BytesPerSector
), TAG_NTFS
);
73 if (BitmapData
== NULL
)
75 ReleaseAttributeContext(DataContext
);
76 ExFreeToNPagedLookasideList(&DeviceExt
->FileRecLookasideList
, BitmapRecord
);
80 /* FIXME: Totally underoptimized! */
81 for (; Read
< BitmapDataSize
; Read
+= DeviceExt
->NtfsInfo
.BytesPerSector
)
83 ReadAttribute(DeviceExt
, DataContext
, Read
, (PCHAR
)((ULONG_PTR
)BitmapData
+ Read
), DeviceExt
->NtfsInfo
.BytesPerSector
);
85 ReleaseAttributeContext(DataContext
);
87 DPRINT1("Total clusters: %I64x\n", DeviceExt
->NtfsInfo
.ClusterCount
);
88 DPRINT1("Total clusters in bitmap: %I64x\n", BitmapDataSize
* 8);
89 DPRINT1("Diff in size: %I64d B\n", ((BitmapDataSize
* 8) - DeviceExt
->NtfsInfo
.ClusterCount
) * DeviceExt
->NtfsInfo
.SectorsPerCluster
* DeviceExt
->NtfsInfo
.BytesPerSector
);
91 RtlInitializeBitMap(&Bitmap
, (PULONG
)BitmapData
, DeviceExt
->NtfsInfo
.ClusterCount
);
92 FreeClusters
= RtlNumberOfClearBits(&Bitmap
);
94 ExFreePoolWithTag(BitmapData
, TAG_NTFS
);
95 ExFreeToNPagedLookasideList(&DeviceExt
->FileRecLookasideList
, BitmapRecord
);
101 * NtfsAllocateClusters
102 * Allocates a run of clusters. The run allocated might be smaller than DesiredClusters.
105 NtfsAllocateClusters(PDEVICE_EXTENSION DeviceExt
,
106 ULONG FirstDesiredCluster
,
107 ULONG DesiredClusters
,
108 PULONG FirstAssignedCluster
,
109 PULONG AssignedClusters
)
112 PFILE_RECORD_HEADER BitmapRecord
;
113 PNTFS_ATTR_CONTEXT DataContext
;
114 ULONGLONG BitmapDataSize
;
116 ULONGLONG FreeClusters
= 0;
121 DPRINT("NtfsAllocateClusters(%p, %lu, %lu, %p, %p)\n", DeviceExt
, FirstDesiredCluster
, DesiredClusters
, FirstAssignedCluster
, AssignedClusters
);
123 BitmapRecord
= ExAllocateFromNPagedLookasideList(&DeviceExt
->FileRecLookasideList
);
124 if (BitmapRecord
== NULL
)
126 return STATUS_INSUFFICIENT_RESOURCES
;
129 Status
= ReadFileRecord(DeviceExt
, NTFS_FILE_BITMAP
, BitmapRecord
);
130 if (!NT_SUCCESS(Status
))
132 ExFreeToNPagedLookasideList(&DeviceExt
->FileRecLookasideList
, BitmapRecord
);
136 Status
= FindAttribute(DeviceExt
, BitmapRecord
, AttributeData
, L
"", 0, &DataContext
, NULL
);
137 if (!NT_SUCCESS(Status
))
139 ExFreeToNPagedLookasideList(&DeviceExt
->FileRecLookasideList
, BitmapRecord
);
143 BitmapDataSize
= AttributeDataLength(DataContext
->pRecord
);
144 BitmapDataSize
= min(BitmapDataSize
, 0xffffffff);
145 ASSERT((BitmapDataSize
* 8) >= DeviceExt
->NtfsInfo
.ClusterCount
);
146 BitmapData
= ExAllocatePoolWithTag(NonPagedPool
, ROUND_UP(BitmapDataSize
, DeviceExt
->NtfsInfo
.BytesPerSector
), TAG_NTFS
);
147 if (BitmapData
== NULL
)
149 ReleaseAttributeContext(DataContext
);
150 ExFreeToNPagedLookasideList(&DeviceExt
->FileRecLookasideList
, BitmapRecord
);
151 return STATUS_INSUFFICIENT_RESOURCES
;
154 DPRINT("Total clusters: %I64x\n", DeviceExt
->NtfsInfo
.ClusterCount
);
155 DPRINT("Total clusters in bitmap: %I64x\n", BitmapDataSize
* 8);
156 DPRINT("Diff in size: %I64d B\n", ((BitmapDataSize
* 8) - DeviceExt
->NtfsInfo
.ClusterCount
) * DeviceExt
->NtfsInfo
.SectorsPerCluster
* DeviceExt
->NtfsInfo
.BytesPerSector
);
158 ReadAttribute(DeviceExt
, DataContext
, 0, (PCHAR
)BitmapData
, (ULONG
)BitmapDataSize
);
160 RtlInitializeBitMap(&Bitmap
, (PULONG
)BitmapData
, DeviceExt
->NtfsInfo
.ClusterCount
);
161 FreeClusters
= RtlNumberOfClearBits(&Bitmap
);
163 if (FreeClusters
< DesiredClusters
)
165 ReleaseAttributeContext(DataContext
);
167 ExFreePoolWithTag(BitmapData
, TAG_NTFS
);
168 ExFreeToNPagedLookasideList(&DeviceExt
->FileRecLookasideList
, BitmapRecord
);
169 return STATUS_DISK_FULL
;
172 // TODO: Observe MFT reservation zone
174 // Can we get one contiguous run?
175 AssignedRun
= RtlFindClearBitsAndSet(&Bitmap
, DesiredClusters
, FirstDesiredCluster
);
177 if (AssignedRun
!= 0xFFFFFFFF)
179 *FirstAssignedCluster
= AssignedRun
;
180 *AssignedClusters
= DesiredClusters
;
184 // we can't get one contiguous run
185 *AssignedClusters
= RtlFindNextForwardRunClear(&Bitmap
, FirstDesiredCluster
, FirstAssignedCluster
);
187 if (*AssignedClusters
== 0)
189 // we couldn't find any runs starting at DesiredFirstCluster
190 *AssignedClusters
= RtlFindLongestRunClear(&Bitmap
, FirstAssignedCluster
);
195 Status
= WriteAttribute(DeviceExt
, DataContext
, 0, BitmapData
, (ULONG
)BitmapDataSize
, &LengthWritten
, BitmapRecord
);
197 ReleaseAttributeContext(DataContext
);
199 ExFreePoolWithTag(BitmapData
, TAG_NTFS
);
200 ExFreeToNPagedLookasideList(&DeviceExt
->FileRecLookasideList
, BitmapRecord
);
207 NtfsGetFsVolumeInformation(PDEVICE_OBJECT DeviceObject
,
208 PFILE_FS_VOLUME_INFORMATION FsVolumeInfo
,
211 DPRINT("NtfsGetFsVolumeInformation() called\n");
212 DPRINT("FsVolumeInfo = %p\n", FsVolumeInfo
);
213 DPRINT("BufferLength %lu\n", *BufferLength
);
215 DPRINT("Vpb %p\n", DeviceObject
->Vpb
);
217 DPRINT("Required length %lu\n",
218 sizeof(FILE_FS_VOLUME_INFORMATION
) + DeviceObject
->Vpb
->VolumeLabelLength
);
219 DPRINT("LabelLength %hu\n",
220 DeviceObject
->Vpb
->VolumeLabelLength
);
221 DPRINT("Label %.*S\n",
222 DeviceObject
->Vpb
->VolumeLabelLength
/ sizeof(WCHAR
),
223 DeviceObject
->Vpb
->VolumeLabel
);
225 if (*BufferLength
< sizeof(FILE_FS_VOLUME_INFORMATION
))
226 return STATUS_INFO_LENGTH_MISMATCH
;
228 if (*BufferLength
< (sizeof(FILE_FS_VOLUME_INFORMATION
) + DeviceObject
->Vpb
->VolumeLabelLength
))
229 return STATUS_BUFFER_OVERFLOW
;
232 FsVolumeInfo
->VolumeSerialNumber
= DeviceObject
->Vpb
->SerialNumber
;
233 FsVolumeInfo
->VolumeLabelLength
= DeviceObject
->Vpb
->VolumeLabelLength
;
234 memcpy(FsVolumeInfo
->VolumeLabel
,
235 DeviceObject
->Vpb
->VolumeLabel
,
236 DeviceObject
->Vpb
->VolumeLabelLength
);
239 FsVolumeInfo
->VolumeCreationTime
.QuadPart
= 0;
240 FsVolumeInfo
->SupportsObjects
= FALSE
;
242 *BufferLength
-= (sizeof(FILE_FS_VOLUME_INFORMATION
) + DeviceObject
->Vpb
->VolumeLabelLength
);
244 DPRINT("BufferLength %lu\n", *BufferLength
);
245 DPRINT("NtfsGetFsVolumeInformation() done\n");
247 return STATUS_SUCCESS
;
253 NtfsGetFsAttributeInformation(PDEVICE_EXTENSION DeviceExt
,
254 PFILE_FS_ATTRIBUTE_INFORMATION FsAttributeInfo
,
257 UNREFERENCED_PARAMETER(DeviceExt
);
259 DPRINT("NtfsGetFsAttributeInformation()\n");
260 DPRINT("FsAttributeInfo = %p\n", FsAttributeInfo
);
261 DPRINT("BufferLength %lu\n", *BufferLength
);
262 DPRINT("Required length %lu\n", (sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) + 8));
264 if (*BufferLength
< sizeof (FILE_FS_ATTRIBUTE_INFORMATION
))
265 return STATUS_INFO_LENGTH_MISMATCH
;
267 if (*BufferLength
< (sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) + 8))
268 return STATUS_BUFFER_OVERFLOW
;
270 FsAttributeInfo
->FileSystemAttributes
=
271 FILE_CASE_PRESERVED_NAMES
| FILE_UNICODE_ON_DISK
| FILE_READ_ONLY_VOLUME
;
272 FsAttributeInfo
->MaximumComponentNameLength
= 255;
273 FsAttributeInfo
->FileSystemNameLength
= 8;
275 memcpy(FsAttributeInfo
->FileSystemName
, L
"NTFS", 8);
277 DPRINT("Finished NtfsGetFsAttributeInformation()\n");
279 *BufferLength
-= (sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) + 8);
280 DPRINT("BufferLength %lu\n", *BufferLength
);
282 return STATUS_SUCCESS
;
288 NtfsGetFsSizeInformation(PDEVICE_OBJECT DeviceObject
,
289 PFILE_FS_SIZE_INFORMATION FsSizeInfo
,
292 PDEVICE_EXTENSION DeviceExt
;
293 NTSTATUS Status
= STATUS_SUCCESS
;
295 DPRINT("NtfsGetFsSizeInformation()\n");
296 DPRINT("FsSizeInfo = %p\n", FsSizeInfo
);
298 if (*BufferLength
< sizeof(FILE_FS_SIZE_INFORMATION
))
299 return STATUS_BUFFER_OVERFLOW
;
301 DeviceExt
= DeviceObject
->DeviceExtension
;
303 FsSizeInfo
->AvailableAllocationUnits
.QuadPart
= NtfsGetFreeClusters(DeviceExt
);
304 FsSizeInfo
->TotalAllocationUnits
.QuadPart
= DeviceExt
->NtfsInfo
.ClusterCount
;
305 FsSizeInfo
->SectorsPerAllocationUnit
= DeviceExt
->NtfsInfo
.SectorsPerCluster
;
306 FsSizeInfo
->BytesPerSector
= DeviceExt
->NtfsInfo
.BytesPerSector
;
308 DPRINT("Finished NtfsGetFsSizeInformation()\n");
309 if (NT_SUCCESS(Status
))
310 *BufferLength
-= sizeof(FILE_FS_SIZE_INFORMATION
);
318 NtfsGetFsDeviceInformation(PDEVICE_OBJECT DeviceObject
,
319 PFILE_FS_DEVICE_INFORMATION FsDeviceInfo
,
322 DPRINT("NtfsGetFsDeviceInformation()\n");
323 DPRINT("FsDeviceInfo = %p\n", FsDeviceInfo
);
324 DPRINT("BufferLength %lu\n", *BufferLength
);
325 DPRINT("Required length %lu\n", sizeof(FILE_FS_DEVICE_INFORMATION
));
327 if (*BufferLength
< sizeof(FILE_FS_DEVICE_INFORMATION
))
328 return STATUS_BUFFER_OVERFLOW
;
330 FsDeviceInfo
->DeviceType
= FILE_DEVICE_DISK
;
331 FsDeviceInfo
->Characteristics
= DeviceObject
->Characteristics
;
333 DPRINT("NtfsGetFsDeviceInformation() finished.\n");
335 *BufferLength
-= sizeof(FILE_FS_DEVICE_INFORMATION
);
336 DPRINT("BufferLength %lu\n", *BufferLength
);
338 return STATUS_SUCCESS
;
343 NtfsQueryVolumeInformation(PNTFS_IRP_CONTEXT IrpContext
)
346 PDEVICE_OBJECT DeviceObject
;
347 FS_INFORMATION_CLASS FsInformationClass
;
348 PIO_STACK_LOCATION Stack
;
349 NTSTATUS Status
= STATUS_SUCCESS
;
352 PDEVICE_EXTENSION DeviceExt
;
354 DPRINT("NtfsQueryVolumeInformation() called\n");
358 Irp
= IrpContext
->Irp
;
359 DeviceObject
= IrpContext
->DeviceObject
;
360 DeviceExt
= DeviceObject
->DeviceExtension
;
361 Stack
= IrpContext
->Stack
;
363 if (!ExAcquireResourceSharedLite(&DeviceExt
->DirResource
,
364 BooleanFlagOn(IrpContext
->Flags
, IRPCONTEXT_CANWAIT
)))
366 return NtfsMarkIrpContextForQueue(IrpContext
);
369 FsInformationClass
= Stack
->Parameters
.QueryVolume
.FsInformationClass
;
370 BufferLength
= Stack
->Parameters
.QueryVolume
.Length
;
371 SystemBuffer
= Irp
->AssociatedIrp
.SystemBuffer
;
372 RtlZeroMemory(SystemBuffer
, BufferLength
);
374 DPRINT("FsInformationClass %d\n", FsInformationClass
);
375 DPRINT("SystemBuffer %p\n", SystemBuffer
);
377 switch (FsInformationClass
)
379 case FileFsVolumeInformation
:
380 Status
= NtfsGetFsVolumeInformation(DeviceObject
,
385 case FileFsAttributeInformation
:
386 Status
= NtfsGetFsAttributeInformation(DeviceObject
->DeviceExtension
,
391 case FileFsSizeInformation
:
392 Status
= NtfsGetFsSizeInformation(DeviceObject
,
397 case FileFsDeviceInformation
:
398 Status
= NtfsGetFsDeviceInformation(DeviceObject
,
404 Status
= STATUS_NOT_SUPPORTED
;
407 ExReleaseResourceLite(&DeviceExt
->DirResource
);
409 if (NT_SUCCESS(Status
))
410 Irp
->IoStatus
.Information
=
411 Stack
->Parameters
.QueryVolume
.Length
- BufferLength
;
413 Irp
->IoStatus
.Information
= 0;
420 NtfsSetVolumeInformation(PNTFS_IRP_CONTEXT IrpContext
)
424 DPRINT("NtfsSetVolumeInformation() called\n");
428 Irp
= IrpContext
->Irp
;
429 Irp
->IoStatus
.Status
= STATUS_NOT_SUPPORTED
;
430 Irp
->IoStatus
.Information
= 0;
432 return STATUS_NOT_SUPPORTED
;