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