6b7e598819a0eaa3d5c8f3b5c2ca0bb868ac0278
[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->pRecord);
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 PUCHAR BitmapData;
118 ULONGLONG FreeClusters = 0;
119 RTL_BITMAP Bitmap;
120 ULONG AssignedRun;
121 ULONG LengthWritten;
122
123 DPRINT1("NtfsAllocateClusters(%p, %lu, %lu, %p, %p)\n", DeviceExt, FirstDesiredCluster, DesiredClusters, FirstAssignedCluster, AssignedClusters);
124
125 BitmapRecord = ExAllocatePoolWithTag(NonPagedPool,
126 DeviceExt->NtfsInfo.BytesPerFileRecord,
127 TAG_NTFS);
128 if (BitmapRecord == NULL)
129 {
130 return STATUS_INSUFFICIENT_RESOURCES;
131 }
132
133 Status = ReadFileRecord(DeviceExt, NTFS_FILE_BITMAP, BitmapRecord);
134 if (!NT_SUCCESS(Status))
135 {
136 ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
137 return Status;
138 }
139
140 Status = FindAttribute(DeviceExt, BitmapRecord, AttributeData, L"", 0, &DataContext, NULL);
141 if (!NT_SUCCESS(Status))
142 {
143 ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
144 return Status;
145 }
146
147 BitmapDataSize = AttributeDataLength(DataContext->pRecord);
148 BitmapDataSize = min(BitmapDataSize, 0xffffffff);
149 ASSERT((BitmapDataSize * 8) >= DeviceExt->NtfsInfo.ClusterCount);
150 BitmapData = ExAllocatePoolWithTag(NonPagedPool, ROUND_UP(BitmapDataSize, DeviceExt->NtfsInfo.BytesPerSector), TAG_NTFS);
151 if (BitmapData == NULL)
152 {
153 ReleaseAttributeContext(DataContext);
154 ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
155 return STATUS_INSUFFICIENT_RESOURCES;
156 }
157
158 DPRINT1("Total clusters: %I64x\n", DeviceExt->NtfsInfo.ClusterCount);
159 DPRINT1("Total clusters in bitmap: %I64x\n", BitmapDataSize * 8);
160 DPRINT1("Diff in size: %I64d B\n", ((BitmapDataSize * 8) - DeviceExt->NtfsInfo.ClusterCount) * DeviceExt->NtfsInfo.SectorsPerCluster * DeviceExt->NtfsInfo.BytesPerSector);
161
162 ReadAttribute(DeviceExt, DataContext, 0, (PCHAR)BitmapData, (ULONG)BitmapDataSize);
163
164 RtlInitializeBitMap(&Bitmap, (PULONG)BitmapData, DeviceExt->NtfsInfo.ClusterCount);
165 FreeClusters = RtlNumberOfClearBits(&Bitmap);
166
167 if (FreeClusters < DesiredClusters)
168 {
169 ReleaseAttributeContext(DataContext);
170
171 ExFreePoolWithTag(BitmapData, TAG_NTFS);
172 ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
173 return STATUS_DISK_FULL;
174 }
175
176 // TODO: Observe MFT reservation zone
177
178 // Can we get one contiguous run?
179 AssignedRun = RtlFindClearBitsAndSet(&Bitmap, DesiredClusters, FirstDesiredCluster);
180
181 if (AssignedRun != 0xFFFFFFFF)
182 {
183 *FirstAssignedCluster = AssignedRun;
184 *AssignedClusters = DesiredClusters;
185 }
186 else
187 {
188 // we can't get one contiguous run
189 *AssignedClusters = RtlFindNextForwardRunClear(&Bitmap, FirstDesiredCluster, FirstAssignedCluster);
190
191 if (*AssignedClusters == 0)
192 {
193 // we couldn't find any runs starting at DesiredFirstCluster
194 *AssignedClusters = RtlFindLongestRunClear(&Bitmap, FirstAssignedCluster);
195 }
196
197 }
198
199 Status = WriteAttribute(DeviceExt, DataContext, 0, BitmapData, (ULONG)BitmapDataSize, &LengthWritten, BitmapRecord);
200
201 ReleaseAttributeContext(DataContext);
202
203 ExFreePoolWithTag(BitmapData, TAG_NTFS);
204 ExFreePoolWithTag(BitmapRecord, TAG_NTFS);
205
206 return Status;
207 }
208
209 static
210 NTSTATUS
211 NtfsGetFsVolumeInformation(PDEVICE_OBJECT DeviceObject,
212 PFILE_FS_VOLUME_INFORMATION FsVolumeInfo,
213 PULONG BufferLength)
214 {
215 DPRINT("NtfsGetFsVolumeInformation() called\n");
216 DPRINT("FsVolumeInfo = %p\n", FsVolumeInfo);
217 DPRINT("BufferLength %lu\n", *BufferLength);
218
219 DPRINT("Vpb %p\n", DeviceObject->Vpb);
220
221 DPRINT("Required length %lu\n",
222 sizeof(FILE_FS_VOLUME_INFORMATION) + DeviceObject->Vpb->VolumeLabelLength);
223 DPRINT("LabelLength %hu\n",
224 DeviceObject->Vpb->VolumeLabelLength);
225 DPRINT("Label %.*S\n",
226 DeviceObject->Vpb->VolumeLabelLength / sizeof(WCHAR),
227 DeviceObject->Vpb->VolumeLabel);
228
229 if (*BufferLength < sizeof(FILE_FS_VOLUME_INFORMATION))
230 return STATUS_INFO_LENGTH_MISMATCH;
231
232 if (*BufferLength < (sizeof(FILE_FS_VOLUME_INFORMATION) + DeviceObject->Vpb->VolumeLabelLength))
233 return STATUS_BUFFER_OVERFLOW;
234
235 /* valid entries */
236 FsVolumeInfo->VolumeSerialNumber = DeviceObject->Vpb->SerialNumber;
237 FsVolumeInfo->VolumeLabelLength = DeviceObject->Vpb->VolumeLabelLength;
238 memcpy(FsVolumeInfo->VolumeLabel,
239 DeviceObject->Vpb->VolumeLabel,
240 DeviceObject->Vpb->VolumeLabelLength);
241
242 /* dummy entries */
243 FsVolumeInfo->VolumeCreationTime.QuadPart = 0;
244 FsVolumeInfo->SupportsObjects = FALSE;
245
246 *BufferLength -= (sizeof(FILE_FS_VOLUME_INFORMATION) + DeviceObject->Vpb->VolumeLabelLength);
247
248 DPRINT("BufferLength %lu\n", *BufferLength);
249 DPRINT("NtfsGetFsVolumeInformation() done\n");
250
251 return STATUS_SUCCESS;
252 }
253
254
255 static
256 NTSTATUS
257 NtfsGetFsAttributeInformation(PDEVICE_EXTENSION DeviceExt,
258 PFILE_FS_ATTRIBUTE_INFORMATION FsAttributeInfo,
259 PULONG BufferLength)
260 {
261 UNREFERENCED_PARAMETER(DeviceExt);
262
263 DPRINT("NtfsGetFsAttributeInformation()\n");
264 DPRINT("FsAttributeInfo = %p\n", FsAttributeInfo);
265 DPRINT("BufferLength %lu\n", *BufferLength);
266 DPRINT("Required length %lu\n", (sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + 8));
267
268 if (*BufferLength < sizeof (FILE_FS_ATTRIBUTE_INFORMATION))
269 return STATUS_INFO_LENGTH_MISMATCH;
270
271 if (*BufferLength < (sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + 8))
272 return STATUS_BUFFER_OVERFLOW;
273
274 FsAttributeInfo->FileSystemAttributes =
275 FILE_CASE_PRESERVED_NAMES | FILE_UNICODE_ON_DISK | FILE_READ_ONLY_VOLUME;
276 FsAttributeInfo->MaximumComponentNameLength = 255;
277 FsAttributeInfo->FileSystemNameLength = 8;
278
279 memcpy(FsAttributeInfo->FileSystemName, L"NTFS", 8);
280
281 DPRINT("Finished NtfsGetFsAttributeInformation()\n");
282
283 *BufferLength -= (sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + 8);
284 DPRINT("BufferLength %lu\n", *BufferLength);
285
286 return STATUS_SUCCESS;
287 }
288
289
290 static
291 NTSTATUS
292 NtfsGetFsSizeInformation(PDEVICE_OBJECT DeviceObject,
293 PFILE_FS_SIZE_INFORMATION FsSizeInfo,
294 PULONG BufferLength)
295 {
296 PDEVICE_EXTENSION DeviceExt;
297 NTSTATUS Status = STATUS_SUCCESS;
298
299 DPRINT("NtfsGetFsSizeInformation()\n");
300 DPRINT("FsSizeInfo = %p\n", FsSizeInfo);
301
302 if (*BufferLength < sizeof(FILE_FS_SIZE_INFORMATION))
303 return STATUS_BUFFER_OVERFLOW;
304
305 DeviceExt = DeviceObject->DeviceExtension;
306
307 FsSizeInfo->AvailableAllocationUnits.QuadPart = NtfsGetFreeClusters(DeviceExt);
308 FsSizeInfo->TotalAllocationUnits.QuadPart = DeviceExt->NtfsInfo.ClusterCount;
309 FsSizeInfo->SectorsPerAllocationUnit = DeviceExt->NtfsInfo.SectorsPerCluster;
310 FsSizeInfo->BytesPerSector = DeviceExt->NtfsInfo.BytesPerSector;
311
312 DPRINT("Finished NtfsGetFsSizeInformation()\n");
313 if (NT_SUCCESS(Status))
314 *BufferLength -= sizeof(FILE_FS_SIZE_INFORMATION);
315
316 return Status;
317 }
318
319
320 static
321 NTSTATUS
322 NtfsGetFsDeviceInformation(PDEVICE_OBJECT DeviceObject,
323 PFILE_FS_DEVICE_INFORMATION FsDeviceInfo,
324 PULONG BufferLength)
325 {
326 DPRINT("NtfsGetFsDeviceInformation()\n");
327 DPRINT("FsDeviceInfo = %p\n", FsDeviceInfo);
328 DPRINT("BufferLength %lu\n", *BufferLength);
329 DPRINT("Required length %lu\n", sizeof(FILE_FS_DEVICE_INFORMATION));
330
331 if (*BufferLength < sizeof(FILE_FS_DEVICE_INFORMATION))
332 return STATUS_BUFFER_OVERFLOW;
333
334 FsDeviceInfo->DeviceType = FILE_DEVICE_DISK;
335 FsDeviceInfo->Characteristics = DeviceObject->Characteristics;
336
337 DPRINT("NtfsGetFsDeviceInformation() finished.\n");
338
339 *BufferLength -= sizeof(FILE_FS_DEVICE_INFORMATION);
340 DPRINT("BufferLength %lu\n", *BufferLength);
341
342 return STATUS_SUCCESS;
343 }
344
345
346 NTSTATUS
347 NtfsQueryVolumeInformation(PNTFS_IRP_CONTEXT IrpContext)
348 {
349 PIRP Irp;
350 PDEVICE_OBJECT DeviceObject;
351 FS_INFORMATION_CLASS FsInformationClass;
352 PIO_STACK_LOCATION Stack;
353 NTSTATUS Status = STATUS_SUCCESS;
354 PVOID SystemBuffer;
355 ULONG BufferLength;
356 PDEVICE_EXTENSION DeviceExt;
357
358 DPRINT("NtfsQueryVolumeInformation() called\n");
359
360 ASSERT(IrpContext);
361
362 Irp = IrpContext->Irp;
363 DeviceObject = IrpContext->DeviceObject;
364 DeviceExt = DeviceObject->DeviceExtension;
365 Stack = IrpContext->Stack;
366
367 if (!ExAcquireResourceSharedLite(&DeviceExt->DirResource,
368 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
369 {
370 return NtfsMarkIrpContextForQueue(IrpContext);
371 }
372
373 FsInformationClass = Stack->Parameters.QueryVolume.FsInformationClass;
374 BufferLength = Stack->Parameters.QueryVolume.Length;
375 SystemBuffer = Irp->AssociatedIrp.SystemBuffer;
376 RtlZeroMemory(SystemBuffer, BufferLength);
377
378 DPRINT("FsInformationClass %d\n", FsInformationClass);
379 DPRINT("SystemBuffer %p\n", SystemBuffer);
380
381 switch (FsInformationClass)
382 {
383 case FileFsVolumeInformation:
384 Status = NtfsGetFsVolumeInformation(DeviceObject,
385 SystemBuffer,
386 &BufferLength);
387 break;
388
389 case FileFsAttributeInformation:
390 Status = NtfsGetFsAttributeInformation(DeviceObject->DeviceExtension,
391 SystemBuffer,
392 &BufferLength);
393 break;
394
395 case FileFsSizeInformation:
396 Status = NtfsGetFsSizeInformation(DeviceObject,
397 SystemBuffer,
398 &BufferLength);
399 break;
400
401 case FileFsDeviceInformation:
402 Status = NtfsGetFsDeviceInformation(DeviceObject,
403 SystemBuffer,
404 &BufferLength);
405 break;
406
407 default:
408 Status = STATUS_NOT_SUPPORTED;
409 }
410
411 ExReleaseResourceLite(&DeviceExt->DirResource);
412
413 if (NT_SUCCESS(Status))
414 Irp->IoStatus.Information =
415 Stack->Parameters.QueryVolume.Length - BufferLength;
416 else
417 Irp->IoStatus.Information = 0;
418
419 return Status;
420 }
421
422
423 NTSTATUS
424 NtfsSetVolumeInformation(PNTFS_IRP_CONTEXT IrpContext)
425 {
426 PIRP Irp;
427
428 DPRINT("NtfsSetVolumeInformation() called\n");
429
430 ASSERT(IrpContext);
431
432 Irp = IrpContext->Irp;
433 Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
434 Irp->IoStatus.Information = 0;
435
436 return STATUS_NOT_SUPPORTED;
437 }
438
439 /* EOF */