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