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 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 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 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 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 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 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 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 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