d1505a5fb2459eb64966d62808cd3d02190e6e72
[reactos.git] / drivers / filesystems / ntfs / volinfo.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 2002, 2014 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/filesystem/ntfs/volume.c
22 * PURPOSE: NTFS filesystem driver
23 * PROGRAMMERS: Eric Kohl
24 * Pierre Schweitzer (pierre@reactos.org)
25 */
26
27 /* INCLUDES *****************************************************************/
28
29 #include "ntfs.h"
30
31 #define NDEBUG
32 #include <debug.h>
33
34 /* FUNCTIONS ****************************************************************/
35
36 ULONGLONG
37 NtfsGetFreeClusters(PDEVICE_EXTENSION DeviceExt)
38 {
39 NTSTATUS Status;
40 PFILE_RECORD_HEADER BitmapRecord;
41 PNTFS_ATTR_CONTEXT DataContext;
42 ULONGLONG BitmapDataSize;
43 PCHAR BitmapData;
44 ULONGLONG FreeClusters = 0;
45 ULONG Read = 0;
46 RTL_BITMAP Bitmap;
47
48 DPRINT1("NtfsGetFreeClusters(%p)\n", DeviceExt);
49
50 BitmapRecord = ExAllocatePoolWithTag(NonPagedPool,
51 DeviceExt->NtfsInfo.BytesPerFileRecord,
52 TAG_NTFS);
53 if (BitmapRecord == NULL)
54 {
55 return 0;
56 }
57
58 Status = ReadFileRecord(DeviceExt, NTFS_FILE_BITMAP, BitmapRecord);
59 if (!NT_SUCCESS(Status))
60 {
61 ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
62 return 0;
63 }
64
65 Status = FindAttribute(DeviceExt, BitmapRecord, AttributeData, L"", 0, &DataContext, NULL);
66 if (!NT_SUCCESS(Status))
67 {
68 ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
69 return 0;
70 }
71
72 BitmapDataSize = AttributeDataLength(&DataContext->Record);
73 ASSERT((BitmapDataSize * 8) >= DeviceExt->NtfsInfo.ClusterCount);
74 BitmapData = ExAllocatePoolWithTag(NonPagedPool, ROUND_UP(BitmapDataSize, DeviceExt->NtfsInfo.BytesPerSector), TAG_NTFS);
75 if (BitmapData == NULL)
76 {
77 ReleaseAttributeContext(DataContext);
78 ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
79 return 0;
80 }
81
82 /* FIXME: Totally underoptimized! */
83 for (; Read < BitmapDataSize; Read += DeviceExt->NtfsInfo.BytesPerSector)
84 {
85 ReadAttribute(DeviceExt, DataContext, Read, (PCHAR)((ULONG_PTR)BitmapData + Read), DeviceExt->NtfsInfo.BytesPerSector);
86 }
87 ReleaseAttributeContext(DataContext);
88
89 DPRINT1("Total clusters: %I64x\n", DeviceExt->NtfsInfo.ClusterCount);
90 DPRINT1("Total clusters in bitmap: %I64x\n", BitmapDataSize * 8);
91 DPRINT1("Diff in size: %I64d B\n", ((BitmapDataSize * 8) - DeviceExt->NtfsInfo.ClusterCount) * DeviceExt->NtfsInfo.SectorsPerCluster * DeviceExt->NtfsInfo.BytesPerSector);
92
93 RtlInitializeBitMap(&Bitmap, (PULONG)BitmapData, DeviceExt->NtfsInfo.ClusterCount);
94 FreeClusters = RtlNumberOfClearBits(&Bitmap);
95
96 ExFreePoolWithTag(BitmapData, TAG_NTFS);
97 ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
98
99 return FreeClusters;
100 }
101
102 /**
103 * NtfsAllocateClusters
104 * Allocates a run of clusters. The run allocated might be smaller than DesiredClusters.
105 */
106 NTSTATUS
107 NtfsAllocateClusters(PDEVICE_EXTENSION DeviceExt,
108 ULONG FirstDesiredCluster,
109 ULONG DesiredClusters,
110 PULONG FirstAssignedCluster,
111 PULONG AssignedClusters)
112 {
113 NTSTATUS Status;
114 PFILE_RECORD_HEADER BitmapRecord;
115 PNTFS_ATTR_CONTEXT DataContext;
116 ULONGLONG BitmapDataSize;
117 PCHAR BitmapData;
118 ULONGLONG FreeClusters = 0;
119 RTL_BITMAP Bitmap;
120
121 DPRINT1("NtfsAllocateClusters(%p, %lu, %lu, %p, %p)\n", DeviceExt, FirstDesiredCluster, DesiredClusters, FirstAssignedCluster, AssignedClusters);
122
123 BitmapRecord = ExAllocatePoolWithTag(NonPagedPool,
124 DeviceExt->NtfsInfo.BytesPerFileRecord,
125 TAG_NTFS);
126 if (BitmapRecord == NULL)
127 {
128 return 0;
129 }
130
131 Status = ReadFileRecord(DeviceExt, NTFS_FILE_BITMAP, BitmapRecord);
132 if (!NT_SUCCESS(Status))
133 {
134 ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
135 return 0;
136 }
137
138 Status = FindAttribute(DeviceExt, BitmapRecord, AttributeData, L"", 0, &DataContext, NULL);
139 if (!NT_SUCCESS(Status))
140 {
141 ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
142 return 0;
143 }
144
145 BitmapDataSize = AttributeDataLength(&DataContext->Record);
146 BitmapDataSize = min(BitmapDataSize, 0xffffffff);
147 ASSERT((BitmapDataSize * 8) >= DeviceExt->NtfsInfo.ClusterCount);
148 BitmapData = ExAllocatePoolWithTag(NonPagedPool, ROUND_UP(BitmapDataSize, DeviceExt->NtfsInfo.BytesPerSector), TAG_NTFS);
149 if (BitmapData == NULL)
150 {
151 ReleaseAttributeContext(DataContext);
152 ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
153 return 0;
154 }
155
156 DPRINT1("Total clusters: %I64x\n", DeviceExt->NtfsInfo.ClusterCount);
157 DPRINT1("Total clusters in bitmap: %I64x\n", BitmapDataSize * 8);
158 DPRINT1("Diff in size: %I64d B\n", ((BitmapDataSize * 8) - DeviceExt->NtfsInfo.ClusterCount) * DeviceExt->NtfsInfo.SectorsPerCluster * DeviceExt->NtfsInfo.BytesPerSector);
159
160 ReadAttribute(DeviceExt, DataContext, 0, (PCHAR)BitmapData, (ULONG)BitmapDataSize);
161
162 RtlInitializeBitMap(&Bitmap, (PULONG)BitmapData, DeviceExt->NtfsInfo.ClusterCount);
163 FreeClusters = RtlNumberOfClearBits(&Bitmap);
164
165 if (FreeClusters < DesiredClusters)
166 {
167 ReleaseAttributeContext(DataContext);
168
169 ExFreePoolWithTag(BitmapData, TAG_NTFS);
170 ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
171 return STATUS_DISK_FULL;
172 }
173
174 // TODO: Observe MFT reservation zone
175
176 // Can we get one contiguous run?
177 ULONG AssignedRun = RtlFindClearBitsAndSet(&Bitmap, DesiredClusters, FirstDesiredCluster);
178 ULONG LengthWritten;
179
180 if (AssignedRun != 0xFFFFFFFF)
181 {
182 *FirstAssignedCluster = AssignedRun;
183 *AssignedClusters = DesiredClusters;
184 }
185 else
186 {
187 // we can't get one contiguous run
188 *AssignedClusters = RtlFindNextForwardRunClear(&Bitmap, FirstDesiredCluster, FirstAssignedCluster);
189
190 if (*AssignedClusters == 0)
191 {
192 // we couldn't find any runs starting at DesiredFirstCluster
193 *AssignedClusters = RtlFindLongestRunClear(&Bitmap, FirstAssignedCluster);
194 }
195
196 }
197
198 Status = WriteAttribute(DeviceExt, DataContext, 0, BitmapData, (ULONG)BitmapDataSize, &LengthWritten);
199
200 ReleaseAttributeContext(DataContext);
201
202 ExFreePoolWithTag(BitmapData, TAG_NTFS);
203 ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
204
205 return Status;
206 }
207
208 static
209 NTSTATUS
210 NtfsGetFsVolumeInformation(PDEVICE_OBJECT DeviceObject,
211 PFILE_FS_VOLUME_INFORMATION FsVolumeInfo,
212 PULONG BufferLength)
213 {
214 DPRINT("NtfsGetFsVolumeInformation() called\n");
215 DPRINT("FsVolumeInfo = %p\n", FsVolumeInfo);
216 DPRINT("BufferLength %lu\n", *BufferLength);
217
218 DPRINT("Vpb %p\n", DeviceObject->Vpb);
219
220 DPRINT("Required length %lu\n",
221 sizeof(FILE_FS_VOLUME_INFORMATION) + DeviceObject->Vpb->VolumeLabelLength);
222 DPRINT("LabelLength %hu\n",
223 DeviceObject->Vpb->VolumeLabelLength);
224 DPRINT("Label %.*S\n",
225 DeviceObject->Vpb->VolumeLabelLength / sizeof(WCHAR),
226 DeviceObject->Vpb->VolumeLabel);
227
228 if (*BufferLength < sizeof(FILE_FS_VOLUME_INFORMATION))
229 return STATUS_INFO_LENGTH_MISMATCH;
230
231 if (*BufferLength < (sizeof(FILE_FS_VOLUME_INFORMATION) + DeviceObject->Vpb->VolumeLabelLength))
232 return STATUS_BUFFER_OVERFLOW;
233
234 /* valid entries */
235 FsVolumeInfo->VolumeSerialNumber = DeviceObject->Vpb->SerialNumber;
236 FsVolumeInfo->VolumeLabelLength = DeviceObject->Vpb->VolumeLabelLength;
237 memcpy(FsVolumeInfo->VolumeLabel,
238 DeviceObject->Vpb->VolumeLabel,
239 DeviceObject->Vpb->VolumeLabelLength);
240
241 /* dummy entries */
242 FsVolumeInfo->VolumeCreationTime.QuadPart = 0;
243 FsVolumeInfo->SupportsObjects = FALSE;
244
245 *BufferLength -= (sizeof(FILE_FS_VOLUME_INFORMATION) + DeviceObject->Vpb->VolumeLabelLength);
246
247 DPRINT("BufferLength %lu\n", *BufferLength);
248 DPRINT("NtfsGetFsVolumeInformation() done\n");
249
250 return STATUS_SUCCESS;
251 }
252
253
254 static
255 NTSTATUS
256 NtfsGetFsAttributeInformation(PDEVICE_EXTENSION DeviceExt,
257 PFILE_FS_ATTRIBUTE_INFORMATION FsAttributeInfo,
258 PULONG BufferLength)
259 {
260 UNREFERENCED_PARAMETER(DeviceExt);
261
262 DPRINT("NtfsGetFsAttributeInformation()\n");
263 DPRINT("FsAttributeInfo = %p\n", FsAttributeInfo);
264 DPRINT("BufferLength %lu\n", *BufferLength);
265 DPRINT("Required length %lu\n", (sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + 8));
266
267 if (*BufferLength < sizeof (FILE_FS_ATTRIBUTE_INFORMATION))
268 return STATUS_INFO_LENGTH_MISMATCH;
269
270 if (*BufferLength < (sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + 8))
271 return STATUS_BUFFER_OVERFLOW;
272
273 FsAttributeInfo->FileSystemAttributes =
274 FILE_CASE_PRESERVED_NAMES | FILE_UNICODE_ON_DISK | FILE_READ_ONLY_VOLUME;
275 FsAttributeInfo->MaximumComponentNameLength = 255;
276 FsAttributeInfo->FileSystemNameLength = 8;
277
278 memcpy(FsAttributeInfo->FileSystemName, L"NTFS", 8);
279
280 DPRINT("Finished NtfsGetFsAttributeInformation()\n");
281
282 *BufferLength -= (sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + 8);
283 DPRINT("BufferLength %lu\n", *BufferLength);
284
285 return STATUS_SUCCESS;
286 }
287
288
289 static
290 NTSTATUS
291 NtfsGetFsSizeInformation(PDEVICE_OBJECT DeviceObject,
292 PFILE_FS_SIZE_INFORMATION FsSizeInfo,
293 PULONG BufferLength)
294 {
295 PDEVICE_EXTENSION DeviceExt;
296 NTSTATUS Status = STATUS_SUCCESS;
297
298 DPRINT("NtfsGetFsSizeInformation()\n");
299 DPRINT("FsSizeInfo = %p\n", FsSizeInfo);
300
301 if (*BufferLength < sizeof(FILE_FS_SIZE_INFORMATION))
302 return STATUS_BUFFER_OVERFLOW;
303
304 DeviceExt = DeviceObject->DeviceExtension;
305
306 FsSizeInfo->AvailableAllocationUnits.QuadPart = NtfsGetFreeClusters(DeviceExt);
307 FsSizeInfo->TotalAllocationUnits.QuadPart = DeviceExt->NtfsInfo.ClusterCount;
308 FsSizeInfo->SectorsPerAllocationUnit = DeviceExt->NtfsInfo.SectorsPerCluster;
309 FsSizeInfo->BytesPerSector = DeviceExt->NtfsInfo.BytesPerSector;
310
311 DPRINT("Finished NtfsGetFsSizeInformation()\n");
312 if (NT_SUCCESS(Status))
313 *BufferLength -= sizeof(FILE_FS_SIZE_INFORMATION);
314
315 return Status;
316 }
317
318
319 static
320 NTSTATUS
321 NtfsGetFsDeviceInformation(PDEVICE_OBJECT DeviceObject,
322 PFILE_FS_DEVICE_INFORMATION FsDeviceInfo,
323 PULONG BufferLength)
324 {
325 DPRINT("NtfsGetFsDeviceInformation()\n");
326 DPRINT("FsDeviceInfo = %p\n", FsDeviceInfo);
327 DPRINT("BufferLength %lu\n", *BufferLength);
328 DPRINT("Required length %lu\n", sizeof(FILE_FS_DEVICE_INFORMATION));
329
330 if (*BufferLength < sizeof(FILE_FS_DEVICE_INFORMATION))
331 return STATUS_BUFFER_OVERFLOW;
332
333 FsDeviceInfo->DeviceType = FILE_DEVICE_DISK;
334 FsDeviceInfo->Characteristics = DeviceObject->Characteristics;
335
336 DPRINT("NtfsGetFsDeviceInformation() finished.\n");
337
338 *BufferLength -= sizeof(FILE_FS_DEVICE_INFORMATION);
339 DPRINT("BufferLength %lu\n", *BufferLength);
340
341 return STATUS_SUCCESS;
342 }
343
344
345 NTSTATUS
346 NtfsQueryVolumeInformation(PNTFS_IRP_CONTEXT IrpContext)
347 {
348 PIRP Irp;
349 PDEVICE_OBJECT DeviceObject;
350 FS_INFORMATION_CLASS FsInformationClass;
351 PIO_STACK_LOCATION Stack;
352 NTSTATUS Status = STATUS_SUCCESS;
353 PVOID SystemBuffer;
354 ULONG BufferLength;
355 PDEVICE_EXTENSION DeviceExt;
356
357 DPRINT("NtfsQueryVolumeInformation() called\n");
358
359 ASSERT(IrpContext);
360
361 Irp = IrpContext->Irp;
362 DeviceObject = IrpContext->DeviceObject;
363 DeviceExt = DeviceObject->DeviceExtension;
364 Stack = IrpContext->Stack;
365
366 if (!ExAcquireResourceSharedLite(&DeviceExt->DirResource,
367 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
368 {
369 return NtfsMarkIrpContextForQueue(IrpContext);
370 }
371
372 FsInformationClass = Stack->Parameters.QueryVolume.FsInformationClass;
373 BufferLength = Stack->Parameters.QueryVolume.Length;
374 SystemBuffer = Irp->AssociatedIrp.SystemBuffer;
375 RtlZeroMemory(SystemBuffer, BufferLength);
376
377 DPRINT("FsInformationClass %d\n", FsInformationClass);
378 DPRINT("SystemBuffer %p\n", SystemBuffer);
379
380 switch (FsInformationClass)
381 {
382 case FileFsVolumeInformation:
383 Status = NtfsGetFsVolumeInformation(DeviceObject,
384 SystemBuffer,
385 &BufferLength);
386 break;
387
388 case FileFsAttributeInformation:
389 Status = NtfsGetFsAttributeInformation(DeviceObject->DeviceExtension,
390 SystemBuffer,
391 &BufferLength);
392 break;
393
394 case FileFsSizeInformation:
395 Status = NtfsGetFsSizeInformation(DeviceObject,
396 SystemBuffer,
397 &BufferLength);
398 break;
399
400 case FileFsDeviceInformation:
401 Status = NtfsGetFsDeviceInformation(DeviceObject,
402 SystemBuffer,
403 &BufferLength);
404 break;
405
406 default:
407 Status = STATUS_NOT_SUPPORTED;
408 }
409
410 ExReleaseResourceLite(&DeviceExt->DirResource);
411
412 if (NT_SUCCESS(Status))
413 Irp->IoStatus.Information =
414 Stack->Parameters.QueryVolume.Length - BufferLength;
415 else
416 Irp->IoStatus.Information = 0;
417
418 return Status;
419 }
420
421
422 NTSTATUS
423 NtfsSetVolumeInformation(PNTFS_IRP_CONTEXT IrpContext)
424 {
425 PIRP Irp;
426
427 DPRINT("NtfsSetVolumeInformation() called\n");
428
429 ASSERT(IrpContext);
430
431 Irp = IrpContext->Irp;
432 Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
433 Irp->IoStatus.Information = 0;
434
435 return STATUS_NOT_SUPPORTED;
436 }
437
438 /* EOF */