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