1 /* 2 * PROJECT: VFAT Filesystem 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Volume routines 5 * COPYRIGHT: Copyright 1998 Jason Filby <jasonfilby@yahoo.com> 6 * Copyright 2004-2022 Hervé Poussineau <hpoussin@reactos.org> 7 */ 8 9 /* INCLUDES *****************************************************************/ 10 11 #include "vfat.h" 12 13 #define NDEBUG 14 #include <debug.h> 15 16 /* FUNCTIONS ****************************************************************/ 17 18 static 19 NTSTATUS 20 FsdGetFsVolumeInformation( 21 PDEVICE_OBJECT DeviceObject, 22 PFILE_FS_VOLUME_INFORMATION FsVolumeInfo, 23 PULONG BufferLength) 24 { 25 NTSTATUS Status; 26 PDEVICE_EXTENSION DeviceExt; 27 28 DPRINT("FsdGetFsVolumeInformation()\n"); 29 DPRINT("FsVolumeInfo = %p\n", FsVolumeInfo); 30 DPRINT("BufferLength %lu\n", *BufferLength); 31 32 DPRINT("Required length %lu\n", FIELD_OFFSET(FILE_FS_VOLUME_INFORMATION, VolumeLabel) + DeviceObject->Vpb->VolumeLabelLength); 33 DPRINT("LabelLength %hu\n", DeviceObject->Vpb->VolumeLabelLength); 34 DPRINT("Label %.*S\n", DeviceObject->Vpb->VolumeLabelLength / sizeof(WCHAR), DeviceObject->Vpb->VolumeLabel); 35 36 ASSERT(*BufferLength >= sizeof(FILE_FS_VOLUME_INFORMATION)); 37 *BufferLength -= FIELD_OFFSET(FILE_FS_VOLUME_INFORMATION, VolumeLabel); 38 39 DeviceExt = DeviceObject->DeviceExtension; 40 41 /* valid entries */ 42 FsVolumeInfo->VolumeSerialNumber = DeviceObject->Vpb->SerialNumber; 43 FsVolumeInfo->VolumeLabelLength = DeviceObject->Vpb->VolumeLabelLength; 44 if (*BufferLength < DeviceObject->Vpb->VolumeLabelLength) 45 { 46 Status = STATUS_BUFFER_OVERFLOW; 47 RtlCopyMemory(FsVolumeInfo->VolumeLabel, 48 DeviceObject->Vpb->VolumeLabel, 49 *BufferLength); 50 } 51 else 52 { 53 Status = STATUS_SUCCESS; 54 RtlCopyMemory(FsVolumeInfo->VolumeLabel, 55 DeviceObject->Vpb->VolumeLabel, 56 FsVolumeInfo->VolumeLabelLength); 57 *BufferLength -= DeviceObject->Vpb->VolumeLabelLength; 58 } 59 60 if (vfatVolumeIsFatX(DeviceExt)) 61 { 62 FsdDosDateTimeToSystemTime(DeviceExt, 63 DeviceExt->VolumeFcb->entry.FatX.CreationDate, 64 DeviceExt->VolumeFcb->entry.FatX.CreationTime, 65 &FsVolumeInfo->VolumeCreationTime); 66 } 67 else 68 { 69 FsdDosDateTimeToSystemTime(DeviceExt, 70 DeviceExt->VolumeFcb->entry.Fat.CreationDate, 71 DeviceExt->VolumeFcb->entry.Fat.CreationTime, 72 &FsVolumeInfo->VolumeCreationTime); 73 } 74 75 FsVolumeInfo->SupportsObjects = FALSE; 76 77 DPRINT("Finished FsdGetFsVolumeInformation()\n"); 78 DPRINT("BufferLength %lu\n", *BufferLength); 79 80 return Status; 81 } 82 83 84 static 85 NTSTATUS 86 FsdGetFsAttributeInformation( 87 PDEVICE_EXTENSION DeviceExt, 88 PFILE_FS_ATTRIBUTE_INFORMATION FsAttributeInfo, 89 PULONG BufferLength) 90 { 91 NTSTATUS Status; 92 PCWSTR pName; 93 ULONG Length; 94 95 DPRINT("FsdGetFsAttributeInformation()\n"); 96 DPRINT("FsAttributeInfo = %p\n", FsAttributeInfo); 97 DPRINT("BufferLength %lu\n", *BufferLength); 98 99 ASSERT(*BufferLength >= sizeof(FILE_FS_ATTRIBUTE_INFORMATION)); 100 *BufferLength -= FIELD_OFFSET(FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName); 101 102 switch (DeviceExt->FatInfo.FatType) 103 { 104 case FAT12: pName = L"FAT"; break; 105 case FAT16: pName = L"FAT"; break; 106 case FAT32: pName = L"FAT32"; break; 107 case FATX16: pName = L"FATX"; break; 108 case FATX32: pName = L"FATX"; break; 109 default: return STATUS_NOT_SUPPORTED; 110 } 111 112 Length = wcslen(pName) * sizeof(WCHAR); 113 DPRINT("Required length %lu\n", (FIELD_OFFSET(FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName) + Length)); 114 115 if (*BufferLength < Length) 116 { 117 Status = STATUS_BUFFER_OVERFLOW; 118 Length = *BufferLength; 119 } 120 else 121 { 122 Status = STATUS_SUCCESS; 123 } 124 125 FsAttributeInfo->FileSystemAttributes = 126 FILE_CASE_PRESERVED_NAMES | FILE_UNICODE_ON_DISK; 127 128 FsAttributeInfo->MaximumComponentNameLength = 255; 129 130 FsAttributeInfo->FileSystemNameLength = Length; 131 132 RtlCopyMemory(FsAttributeInfo->FileSystemName, pName, Length); 133 134 DPRINT("Finished FsdGetFsAttributeInformation()\n"); 135 136 *BufferLength -= Length; 137 DPRINT("BufferLength %lu\n", *BufferLength); 138 139 return Status; 140 } 141 142 143 static 144 NTSTATUS 145 FsdGetFsSizeInformation( 146 PDEVICE_OBJECT DeviceObject, 147 PFILE_FS_SIZE_INFORMATION FsSizeInfo, 148 PULONG BufferLength) 149 { 150 PDEVICE_EXTENSION DeviceExt; 151 NTSTATUS Status; 152 153 DPRINT("FsdGetFsSizeInformation()\n"); 154 DPRINT("FsSizeInfo = %p\n", FsSizeInfo); 155 156 ASSERT(*BufferLength >= sizeof(FILE_FS_SIZE_INFORMATION)); 157 158 DeviceExt = DeviceObject->DeviceExtension; 159 Status = CountAvailableClusters(DeviceExt, &FsSizeInfo->AvailableAllocationUnits); 160 161 FsSizeInfo->TotalAllocationUnits.QuadPart = DeviceExt->FatInfo.NumberOfClusters; 162 FsSizeInfo->SectorsPerAllocationUnit = DeviceExt->FatInfo.SectorsPerCluster; 163 FsSizeInfo->BytesPerSector = DeviceExt->FatInfo.BytesPerSector; 164 165 DPRINT("Finished FsdGetFsSizeInformation()\n"); 166 if (NT_SUCCESS(Status)) 167 *BufferLength -= sizeof(FILE_FS_SIZE_INFORMATION); 168 169 return Status; 170 } 171 172 173 static 174 NTSTATUS 175 FsdGetFsDeviceInformation( 176 PDEVICE_OBJECT DeviceObject, 177 PFILE_FS_DEVICE_INFORMATION FsDeviceInfo, 178 PULONG BufferLength) 179 { 180 DPRINT("FsdGetFsDeviceInformation()\n"); 181 DPRINT("FsDeviceInfo = %p\n", FsDeviceInfo); 182 DPRINT("BufferLength %lu\n", *BufferLength); 183 DPRINT("Required length %lu\n", sizeof(FILE_FS_DEVICE_INFORMATION)); 184 185 ASSERT(*BufferLength >= sizeof(FILE_FS_DEVICE_INFORMATION)); 186 187 FsDeviceInfo->DeviceType = FILE_DEVICE_DISK; 188 FsDeviceInfo->Characteristics = DeviceObject->Characteristics; 189 190 DPRINT("FsdGetFsDeviceInformation() finished.\n"); 191 192 *BufferLength -= sizeof(FILE_FS_DEVICE_INFORMATION); 193 DPRINT("BufferLength %lu\n", *BufferLength); 194 195 return STATUS_SUCCESS; 196 } 197 198 199 static 200 NTSTATUS 201 FsdGetFsFullSizeInformation( 202 PDEVICE_OBJECT DeviceObject, 203 PFILE_FS_FULL_SIZE_INFORMATION FsSizeInfo, 204 PULONG BufferLength) 205 { 206 PDEVICE_EXTENSION DeviceExt; 207 NTSTATUS Status; 208 209 DPRINT("FsdGetFsFullSizeInformation()\n"); 210 DPRINT("FsSizeInfo = %p\n", FsSizeInfo); 211 212 ASSERT(*BufferLength >= sizeof(FILE_FS_FULL_SIZE_INFORMATION)); 213 214 DeviceExt = DeviceObject->DeviceExtension; 215 Status = CountAvailableClusters(DeviceExt, &FsSizeInfo->CallerAvailableAllocationUnits); 216 217 FsSizeInfo->TotalAllocationUnits.QuadPart = DeviceExt->FatInfo.NumberOfClusters; 218 FsSizeInfo->ActualAvailableAllocationUnits.QuadPart = FsSizeInfo->CallerAvailableAllocationUnits.QuadPart; 219 FsSizeInfo->SectorsPerAllocationUnit = DeviceExt->FatInfo.SectorsPerCluster; 220 FsSizeInfo->BytesPerSector = DeviceExt->FatInfo.BytesPerSector; 221 222 DPRINT("Finished FsdGetFsFullSizeInformation()\n"); 223 if (NT_SUCCESS(Status)) 224 *BufferLength -= sizeof(FILE_FS_FULL_SIZE_INFORMATION); 225 226 return Status; 227 } 228 229 230 static 231 NTSTATUS 232 FsdSetFsLabelInformation( 233 PDEVICE_OBJECT DeviceObject, 234 PFILE_FS_LABEL_INFORMATION FsLabelInfo) 235 { 236 PDEVICE_EXTENSION DeviceExt; 237 PVOID Context = NULL; 238 ULONG DirIndex = 0; 239 PDIR_ENTRY Entry; 240 PVFATFCB pRootFcb; 241 LARGE_INTEGER FileOffset; 242 BOOLEAN LabelFound = FALSE; 243 DIR_ENTRY VolumeLabelDirEntry; 244 ULONG VolumeLabelDirIndex; 245 ULONG LabelLen; 246 NTSTATUS Status = STATUS_UNSUCCESSFUL; 247 OEM_STRING StringO; 248 UNICODE_STRING StringW; 249 CHAR cString[43]; 250 ULONG SizeDirEntry; 251 ULONG EntriesPerPage; 252 BOOLEAN IsFatX; 253 254 DPRINT("FsdSetFsLabelInformation()\n"); 255 256 DeviceExt = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; 257 IsFatX = vfatVolumeIsFatX(DeviceExt); 258 259 if (sizeof(DeviceObject->Vpb->VolumeLabel) < FsLabelInfo->VolumeLabelLength) 260 { 261 return STATUS_NAME_TOO_LONG; 262 } 263 264 if (IsFatX) 265 { 266 if (FsLabelInfo->VolumeLabelLength / sizeof(WCHAR) > 42) 267 return STATUS_NAME_TOO_LONG; 268 269 SizeDirEntry = sizeof(FATX_DIR_ENTRY); 270 EntriesPerPage = FATX_ENTRIES_PER_PAGE; 271 } 272 else 273 { 274 if (FsLabelInfo->VolumeLabelLength / sizeof(WCHAR) > 11) 275 return STATUS_NAME_TOO_LONG; 276 277 SizeDirEntry = sizeof(FAT_DIR_ENTRY); 278 EntriesPerPage = FAT_ENTRIES_PER_PAGE; 279 } 280 281 /* Create Volume label dir entry */ 282 LabelLen = FsLabelInfo->VolumeLabelLength / sizeof(WCHAR); 283 RtlZeroMemory(&VolumeLabelDirEntry, SizeDirEntry); 284 StringW.Buffer = FsLabelInfo->VolumeLabel; 285 StringW.Length = StringW.MaximumLength = (USHORT)FsLabelInfo->VolumeLabelLength; 286 StringO.Buffer = cString; 287 StringO.Length = 0; 288 StringO.MaximumLength = 42; 289 Status = RtlUnicodeStringToOemString(&StringO, &StringW, FALSE); 290 if (!NT_SUCCESS(Status)) 291 return Status; 292 293 if (IsFatX) 294 { 295 RtlCopyMemory(VolumeLabelDirEntry.FatX.Filename, cString, LabelLen); 296 memset(&VolumeLabelDirEntry.FatX.Filename[LabelLen], ' ', 42 - LabelLen); 297 VolumeLabelDirEntry.FatX.Attrib = _A_VOLID; 298 } 299 else 300 { 301 RtlCopyMemory(VolumeLabelDirEntry.Fat.Filename, cString, max(sizeof(VolumeLabelDirEntry.Fat.Filename), LabelLen)); 302 if (LabelLen > sizeof(VolumeLabelDirEntry.Fat.Filename)) 303 { 304 memset(VolumeLabelDirEntry.Fat.Ext, ' ', sizeof(VolumeLabelDirEntry.Fat.Ext)); 305 RtlCopyMemory(VolumeLabelDirEntry.Fat.Ext, cString + sizeof(VolumeLabelDirEntry.Fat.Filename), LabelLen - sizeof(VolumeLabelDirEntry.Fat.Filename)); 306 } 307 else 308 { 309 memset(&VolumeLabelDirEntry.Fat.Filename[LabelLen], ' ', sizeof(VolumeLabelDirEntry.Fat.Filename) - LabelLen); 310 } 311 VolumeLabelDirEntry.Fat.Attrib = _A_VOLID; 312 } 313 314 pRootFcb = vfatOpenRootFCB(DeviceExt); 315 Status = vfatFCBInitializeCacheFromVolume(DeviceExt, pRootFcb); 316 if (!NT_SUCCESS(Status)) 317 { 318 return Status; 319 } 320 321 /* Search existing volume entry on disk */ 322 FileOffset.QuadPart = 0; 323 _SEH2_TRY 324 { 325 CcPinRead(pRootFcb->FileObject, &FileOffset, SizeDirEntry, PIN_WAIT, &Context, (PVOID*)&Entry); 326 } 327 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 328 { 329 Status = _SEH2_GetExceptionCode(); 330 } 331 _SEH2_END; 332 333 if (NT_SUCCESS(Status)) 334 { 335 while (TRUE) 336 { 337 if (ENTRY_VOLUME(IsFatX, Entry)) 338 { 339 /* Update entry */ 340 LabelFound = TRUE; 341 RtlCopyMemory(Entry, &VolumeLabelDirEntry, SizeDirEntry); 342 CcSetDirtyPinnedData(Context, NULL); 343 Status = STATUS_SUCCESS; 344 break; 345 } 346 347 if (ENTRY_END(IsFatX, Entry)) 348 { 349 break; 350 } 351 352 DirIndex++; 353 Entry = (PDIR_ENTRY)((ULONG_PTR)Entry + SizeDirEntry); 354 if ((DirIndex % EntriesPerPage) == 0) 355 { 356 CcUnpinData(Context); 357 FileOffset.u.LowPart += PAGE_SIZE; 358 _SEH2_TRY 359 { 360 CcPinRead(pRootFcb->FileObject, &FileOffset, SizeDirEntry, PIN_WAIT, &Context, (PVOID*)&Entry); 361 } 362 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 363 { 364 Status = _SEH2_GetExceptionCode(); 365 } 366 _SEH2_END; 367 368 if (!NT_SUCCESS(Status)) 369 { 370 Context = NULL; 371 break; 372 } 373 } 374 } 375 376 if (Context) 377 { 378 CcUnpinData(Context); 379 } 380 } 381 382 if (!LabelFound) 383 { 384 /* Add new entry for label */ 385 if (!vfatFindDirSpace(DeviceExt, pRootFcb, 1, &VolumeLabelDirIndex)) 386 Status = STATUS_DISK_FULL; 387 else 388 { 389 FileOffset.u.HighPart = 0; 390 FileOffset.u.LowPart = VolumeLabelDirIndex * SizeDirEntry; 391 392 Status = STATUS_SUCCESS; 393 _SEH2_TRY 394 { 395 CcPinRead(pRootFcb->FileObject, &FileOffset, SizeDirEntry, PIN_WAIT, &Context, (PVOID*)&Entry); 396 } 397 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 398 { 399 Status = _SEH2_GetExceptionCode(); 400 } 401 _SEH2_END; 402 403 if (NT_SUCCESS(Status)) 404 { 405 RtlCopyMemory(Entry, &VolumeLabelDirEntry, SizeDirEntry); 406 CcSetDirtyPinnedData(Context, NULL); 407 CcUnpinData(Context); 408 Status = STATUS_SUCCESS; 409 } 410 } 411 } 412 413 vfatReleaseFCB(DeviceExt, pRootFcb); 414 if (!NT_SUCCESS(Status)) 415 { 416 return Status; 417 } 418 419 /* Update volume label in memory */ 420 DeviceObject->Vpb->VolumeLabelLength = (USHORT)FsLabelInfo->VolumeLabelLength; 421 RtlCopyMemory(DeviceObject->Vpb->VolumeLabel, FsLabelInfo->VolumeLabel, DeviceObject->Vpb->VolumeLabelLength); 422 423 return Status; 424 } 425 426 427 /* 428 * FUNCTION: Retrieve the specified volume information 429 */ 430 NTSTATUS 431 VfatQueryVolumeInformation( 432 PVFAT_IRP_CONTEXT IrpContext) 433 { 434 FS_INFORMATION_CLASS FsInformationClass; 435 NTSTATUS RC = STATUS_SUCCESS; 436 PVOID SystemBuffer; 437 ULONG BufferLength; 438 439 /* PRECONDITION */ 440 ASSERT(IrpContext); 441 442 DPRINT("VfatQueryVolumeInformation(IrpContext %p)\n", IrpContext); 443 444 if (!ExAcquireResourceSharedLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource, 445 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT))) 446 { 447 DPRINT1("DirResource failed!\n"); 448 return VfatMarkIrpContextForQueue(IrpContext); 449 } 450 451 /* INITIALIZATION */ 452 FsInformationClass = IrpContext->Stack->Parameters.QueryVolume.FsInformationClass; 453 BufferLength = IrpContext->Stack->Parameters.QueryVolume.Length; 454 SystemBuffer = IrpContext->Irp->AssociatedIrp.SystemBuffer; 455 456 DPRINT("FsInformationClass %d\n", FsInformationClass); 457 DPRINT("SystemBuffer %p\n", SystemBuffer); 458 459 RtlZeroMemory(SystemBuffer, BufferLength); 460 461 switch (FsInformationClass) 462 { 463 case FileFsVolumeInformation: 464 RC = FsdGetFsVolumeInformation(IrpContext->DeviceObject, 465 SystemBuffer, 466 &BufferLength); 467 break; 468 469 case FileFsAttributeInformation: 470 RC = FsdGetFsAttributeInformation(IrpContext->DeviceObject->DeviceExtension, 471 SystemBuffer, 472 &BufferLength); 473 break; 474 475 case FileFsSizeInformation: 476 RC = FsdGetFsSizeInformation(IrpContext->DeviceObject, 477 SystemBuffer, 478 &BufferLength); 479 break; 480 481 case FileFsDeviceInformation: 482 RC = FsdGetFsDeviceInformation(IrpContext->DeviceObject, 483 SystemBuffer, 484 &BufferLength); 485 break; 486 487 case FileFsFullSizeInformation: 488 RC = FsdGetFsFullSizeInformation(IrpContext->DeviceObject, 489 SystemBuffer, 490 &BufferLength); 491 break; 492 493 default: 494 RC = STATUS_NOT_SUPPORTED; 495 } 496 497 ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource); 498 499 IrpContext->Irp->IoStatus.Information = 500 IrpContext->Stack->Parameters.QueryVolume.Length - BufferLength; 501 502 return RC; 503 } 504 505 506 /* 507 * FUNCTION: Set the specified volume information 508 */ 509 NTSTATUS 510 VfatSetVolumeInformation( 511 PVFAT_IRP_CONTEXT IrpContext) 512 { 513 FS_INFORMATION_CLASS FsInformationClass; 514 NTSTATUS Status = STATUS_SUCCESS; 515 PVOID SystemBuffer; 516 ULONG BufferLength; 517 PIO_STACK_LOCATION Stack = IrpContext->Stack; 518 519 /* PRECONDITION */ 520 ASSERT(IrpContext); 521 522 DPRINT("VfatSetVolumeInformation(IrpContext %p)\n", IrpContext); 523 524 if (!ExAcquireResourceExclusiveLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource, 525 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT))) 526 { 527 return VfatMarkIrpContextForQueue(IrpContext); 528 } 529 530 FsInformationClass = Stack->Parameters.SetVolume.FsInformationClass; 531 BufferLength = Stack->Parameters.SetVolume.Length; 532 SystemBuffer = IrpContext->Irp->AssociatedIrp.SystemBuffer; 533 534 DPRINT("FsInformationClass %d\n", FsInformationClass); 535 DPRINT("BufferLength %u\n", BufferLength); 536 DPRINT("SystemBuffer %p\n", SystemBuffer); 537 538 switch (FsInformationClass) 539 { 540 case FileFsLabelInformation: 541 Status = FsdSetFsLabelInformation(IrpContext->DeviceObject, 542 SystemBuffer); 543 break; 544 545 default: 546 Status = STATUS_NOT_SUPPORTED; 547 } 548 549 ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource); 550 IrpContext->Irp->IoStatus.Information = 0; 551 552 return Status; 553 } 554 555 /* EOF */ 556