1 /* 2 * PROJECT: VFAT Filesystem 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: File information routines 5 * COPYRIGHT: Copyright 1998 Jason Filby <jasonfilby@yahoo.com> 6 * Copyright 2005 Hervé Poussineau <hpoussin@reactos.org> 7 * Copyright 2008-2018 Pierre Schweitzer <pierre@reactos.org> 8 */ 9 10 /* INCLUDES *****************************************************************/ 11 12 #include "vfat.h" 13 14 #define NDEBUG 15 #include <debug.h> 16 17 #define NASSERTS_RENAME 18 19 /* GLOBALS ******************************************************************/ 20 21 const char* FileInformationClassNames[] = 22 { 23 "??????", 24 "FileDirectoryInformation", 25 "FileFullDirectoryInformation", 26 "FileBothDirectoryInformation", 27 "FileBasicInformation", 28 "FileStandardInformation", 29 "FileInternalInformation", 30 "FileEaInformation", 31 "FileAccessInformation", 32 "FileNameInformation", 33 "FileRenameInformation", 34 "FileLinkInformation", 35 "FileNamesInformation", 36 "FileDispositionInformation", 37 "FilePositionInformation", 38 "FileFullEaInformation", 39 "FileModeInformation", 40 "FileAlignmentInformation", 41 "FileAllInformation", 42 "FileAllocationInformation", 43 "FileEndOfFileInformation", 44 "FileAlternateNameInformation", 45 "FileStreamInformation", 46 "FilePipeInformation", 47 "FilePipeLocalInformation", 48 "FilePipeRemoteInformation", 49 "FileMailslotQueryInformation", 50 "FileMailslotSetInformation", 51 "FileCompressionInformation", 52 "FileObjectIdInformation", 53 "FileCompletionInformation", 54 "FileMoveClusterInformation", 55 "FileQuotaInformation", 56 "FileReparsePointInformation", 57 "FileNetworkOpenInformation", 58 "FileAttributeTagInformation", 59 "FileTrackingInformation", 60 "FileIdBothDirectoryInformation", 61 "FileIdFullDirectoryInformation", 62 "FileValidDataLengthInformation", 63 "FileShortNameInformation", 64 "FileMaximumInformation" 65 }; 66 67 /* FUNCTIONS ****************************************************************/ 68 69 /* 70 * FUNCTION: Retrieve the standard file information 71 */ 72 NTSTATUS 73 VfatGetStandardInformation( 74 PVFATFCB FCB, 75 PFILE_STANDARD_INFORMATION StandardInfo, 76 PULONG BufferLength) 77 { 78 if (*BufferLength < sizeof(FILE_STANDARD_INFORMATION)) 79 return STATUS_BUFFER_OVERFLOW; 80 81 /* PRECONDITION */ 82 ASSERT(StandardInfo != NULL); 83 ASSERT(FCB != NULL); 84 85 if (vfatFCBIsDirectory(FCB)) 86 { 87 StandardInfo->AllocationSize.QuadPart = 0; 88 StandardInfo->EndOfFile.QuadPart = 0; 89 StandardInfo->Directory = TRUE; 90 } 91 else 92 { 93 StandardInfo->AllocationSize = FCB->RFCB.AllocationSize; 94 StandardInfo->EndOfFile = FCB->RFCB.FileSize; 95 StandardInfo->Directory = FALSE; 96 } 97 StandardInfo->NumberOfLinks = 1; 98 StandardInfo->DeletePending = BooleanFlagOn(FCB->Flags, FCB_DELETE_PENDING); 99 100 *BufferLength -= sizeof(FILE_STANDARD_INFORMATION); 101 return STATUS_SUCCESS; 102 } 103 104 static 105 NTSTATUS 106 VfatSetPositionInformation( 107 PFILE_OBJECT FileObject, 108 PFILE_POSITION_INFORMATION PositionInfo) 109 { 110 DPRINT("FsdSetPositionInformation()\n"); 111 112 DPRINT("PositionInfo %p\n", PositionInfo); 113 DPRINT("Setting position %u\n", PositionInfo->CurrentByteOffset.u.LowPart); 114 115 FileObject->CurrentByteOffset.QuadPart = 116 PositionInfo->CurrentByteOffset.QuadPart; 117 118 return STATUS_SUCCESS; 119 } 120 121 static 122 NTSTATUS 123 VfatGetPositionInformation( 124 PFILE_OBJECT FileObject, 125 PVFATFCB FCB, 126 PDEVICE_EXTENSION DeviceExt, 127 PFILE_POSITION_INFORMATION PositionInfo, 128 PULONG BufferLength) 129 { 130 UNREFERENCED_PARAMETER(FileObject); 131 UNREFERENCED_PARAMETER(FCB); 132 UNREFERENCED_PARAMETER(DeviceExt); 133 134 DPRINT("VfatGetPositionInformation()\n"); 135 136 if (*BufferLength < sizeof(FILE_POSITION_INFORMATION)) 137 return STATUS_BUFFER_OVERFLOW; 138 139 PositionInfo->CurrentByteOffset.QuadPart = 140 FileObject->CurrentByteOffset.QuadPart; 141 142 DPRINT("Getting position %I64x\n", 143 PositionInfo->CurrentByteOffset.QuadPart); 144 145 *BufferLength -= sizeof(FILE_POSITION_INFORMATION); 146 return STATUS_SUCCESS; 147 } 148 149 static 150 NTSTATUS 151 VfatSetBasicInformation( 152 PFILE_OBJECT FileObject, 153 PVFATFCB FCB, 154 PDEVICE_EXTENSION DeviceExt, 155 PFILE_BASIC_INFORMATION BasicInfo) 156 { 157 ULONG NotifyFilter; 158 159 DPRINT("VfatSetBasicInformation()\n"); 160 161 ASSERT(NULL != FileObject); 162 ASSERT(NULL != FCB); 163 ASSERT(NULL != DeviceExt); 164 ASSERT(NULL != BasicInfo); 165 /* Check volume label bit */ 166 ASSERT(0 == (*FCB->Attributes & _A_VOLID)); 167 168 NotifyFilter = 0; 169 170 if (BasicInfo->FileAttributes != 0) 171 { 172 UCHAR Attributes; 173 174 Attributes = (BasicInfo->FileAttributes & (FILE_ATTRIBUTE_ARCHIVE | 175 FILE_ATTRIBUTE_SYSTEM | 176 FILE_ATTRIBUTE_HIDDEN | 177 FILE_ATTRIBUTE_DIRECTORY | 178 FILE_ATTRIBUTE_READONLY)); 179 180 if (vfatFCBIsDirectory(FCB)) 181 { 182 if (BooleanFlagOn(BasicInfo->FileAttributes, FILE_ATTRIBUTE_TEMPORARY)) 183 { 184 DPRINT("Setting temporary attribute on a directory!\n"); 185 return STATUS_INVALID_PARAMETER; 186 } 187 188 Attributes |= FILE_ATTRIBUTE_DIRECTORY; 189 } 190 else 191 { 192 if (BooleanFlagOn(BasicInfo->FileAttributes, FILE_ATTRIBUTE_DIRECTORY)) 193 { 194 DPRINT("Setting directory attribute on a file!\n"); 195 return STATUS_INVALID_PARAMETER; 196 } 197 } 198 199 if (Attributes != *FCB->Attributes) 200 { 201 *FCB->Attributes = Attributes; 202 DPRINT("Setting attributes 0x%02x\n", *FCB->Attributes); 203 NotifyFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES; 204 } 205 } 206 207 if (vfatVolumeIsFatX(DeviceExt)) 208 { 209 if (BasicInfo->CreationTime.QuadPart != 0 && BasicInfo->CreationTime.QuadPart != -1) 210 { 211 FsdSystemTimeToDosDateTime(DeviceExt, 212 &BasicInfo->CreationTime, 213 &FCB->entry.FatX.CreationDate, 214 &FCB->entry.FatX.CreationTime); 215 NotifyFilter |= FILE_NOTIFY_CHANGE_CREATION; 216 } 217 218 if (BasicInfo->LastAccessTime.QuadPart != 0 && BasicInfo->LastAccessTime.QuadPart != -1) 219 { 220 FsdSystemTimeToDosDateTime(DeviceExt, 221 &BasicInfo->LastAccessTime, 222 &FCB->entry.FatX.AccessDate, 223 &FCB->entry.FatX.AccessTime); 224 NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_ACCESS; 225 } 226 227 if (BasicInfo->LastWriteTime.QuadPart != 0 && BasicInfo->LastWriteTime.QuadPart != -1) 228 { 229 FsdSystemTimeToDosDateTime(DeviceExt, 230 &BasicInfo->LastWriteTime, 231 &FCB->entry.FatX.UpdateDate, 232 &FCB->entry.FatX.UpdateTime); 233 NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE; 234 } 235 } 236 else 237 { 238 if (BasicInfo->CreationTime.QuadPart != 0 && BasicInfo->CreationTime.QuadPart != -1) 239 { 240 FsdSystemTimeToDosDateTime(DeviceExt, 241 &BasicInfo->CreationTime, 242 &FCB->entry.Fat.CreationDate, 243 &FCB->entry.Fat.CreationTime); 244 NotifyFilter |= FILE_NOTIFY_CHANGE_CREATION; 245 } 246 247 if (BasicInfo->LastAccessTime.QuadPart != 0 && BasicInfo->LastAccessTime.QuadPart != -1) 248 { 249 FsdSystemTimeToDosDateTime(DeviceExt, 250 &BasicInfo->LastAccessTime, 251 &FCB->entry.Fat.AccessDate, 252 NULL); 253 NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_ACCESS; 254 } 255 256 if (BasicInfo->LastWriteTime.QuadPart != 0 && BasicInfo->LastWriteTime.QuadPart != -1) 257 { 258 FsdSystemTimeToDosDateTime(DeviceExt, 259 &BasicInfo->LastWriteTime, 260 &FCB->entry.Fat.UpdateDate, 261 &FCB->entry.Fat.UpdateTime); 262 NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE; 263 } 264 } 265 266 VfatUpdateEntry(DeviceExt, FCB); 267 268 if (NotifyFilter != 0) 269 { 270 vfatReportChange(DeviceExt, 271 FCB, 272 NotifyFilter, 273 FILE_ACTION_MODIFIED); 274 } 275 276 return STATUS_SUCCESS; 277 } 278 279 NTSTATUS 280 VfatGetBasicInformation( 281 PFILE_OBJECT FileObject, 282 PVFATFCB FCB, 283 PDEVICE_EXTENSION DeviceExt, 284 PFILE_BASIC_INFORMATION BasicInfo, 285 PULONG BufferLength) 286 { 287 UNREFERENCED_PARAMETER(FileObject); 288 289 DPRINT("VfatGetBasicInformation()\n"); 290 291 if (*BufferLength < sizeof(FILE_BASIC_INFORMATION)) 292 return STATUS_BUFFER_OVERFLOW; 293 294 RtlZeroMemory(BasicInfo, sizeof(FILE_BASIC_INFORMATION)); 295 296 if (vfatVolumeIsFatX(DeviceExt)) 297 { 298 FsdDosDateTimeToSystemTime(DeviceExt, 299 FCB->entry.FatX.CreationDate, 300 FCB->entry.FatX.CreationTime, 301 &BasicInfo->CreationTime); 302 FsdDosDateTimeToSystemTime(DeviceExt, 303 FCB->entry.FatX.AccessDate, 304 FCB->entry.FatX.AccessTime, 305 &BasicInfo->LastAccessTime); 306 FsdDosDateTimeToSystemTime(DeviceExt, 307 FCB->entry.FatX.UpdateDate, 308 FCB->entry.FatX.UpdateTime, 309 &BasicInfo->LastWriteTime); 310 BasicInfo->ChangeTime = BasicInfo->LastWriteTime; 311 } 312 else 313 { 314 FsdDosDateTimeToSystemTime(DeviceExt, 315 FCB->entry.Fat.CreationDate, 316 FCB->entry.Fat.CreationTime, 317 &BasicInfo->CreationTime); 318 FsdDosDateTimeToSystemTime(DeviceExt, 319 FCB->entry.Fat.AccessDate, 320 0, 321 &BasicInfo->LastAccessTime); 322 FsdDosDateTimeToSystemTime(DeviceExt, 323 FCB->entry.Fat.UpdateDate, 324 FCB->entry.Fat.UpdateTime, 325 &BasicInfo->LastWriteTime); 326 BasicInfo->ChangeTime = BasicInfo->LastWriteTime; 327 } 328 329 BasicInfo->FileAttributes = *FCB->Attributes & 0x3f; 330 /* Synthesize FILE_ATTRIBUTE_NORMAL */ 331 if (0 == (BasicInfo->FileAttributes & (FILE_ATTRIBUTE_DIRECTORY | 332 FILE_ATTRIBUTE_ARCHIVE | 333 FILE_ATTRIBUTE_SYSTEM | 334 FILE_ATTRIBUTE_HIDDEN | 335 FILE_ATTRIBUTE_READONLY))) 336 { 337 DPRINT("Synthesizing FILE_ATTRIBUTE_NORMAL\n"); 338 BasicInfo->FileAttributes |= FILE_ATTRIBUTE_NORMAL; 339 } 340 DPRINT("Getting attributes 0x%02x\n", BasicInfo->FileAttributes); 341 342 *BufferLength -= sizeof(FILE_BASIC_INFORMATION); 343 return STATUS_SUCCESS; 344 } 345 346 347 static 348 NTSTATUS 349 VfatSetDispositionInformation( 350 PFILE_OBJECT FileObject, 351 PVFATFCB FCB, 352 PDEVICE_EXTENSION DeviceExt, 353 PFILE_DISPOSITION_INFORMATION DispositionInfo) 354 { 355 DPRINT("FsdSetDispositionInformation(<%wZ>, Delete %u)\n", &FCB->PathNameU, DispositionInfo->DeleteFile); 356 357 ASSERT(DeviceExt != NULL); 358 ASSERT(DeviceExt->FatInfo.BytesPerCluster != 0); 359 ASSERT(FCB != NULL); 360 361 if (!DispositionInfo->DeleteFile) 362 { 363 /* undelete the file */ 364 FCB->Flags &= ~FCB_DELETE_PENDING; 365 FileObject->DeletePending = FALSE; 366 return STATUS_SUCCESS; 367 } 368 369 if (BooleanFlagOn(FCB->Flags, FCB_DELETE_PENDING)) 370 { 371 /* stream already marked for deletion. just update the file object */ 372 FileObject->DeletePending = TRUE; 373 return STATUS_SUCCESS; 374 } 375 376 if (vfatFCBIsReadOnly(FCB)) 377 { 378 return STATUS_CANNOT_DELETE; 379 } 380 381 if (vfatFCBIsRoot(FCB) || IsDotOrDotDot(&FCB->LongNameU)) 382 { 383 /* we cannot delete a '.', '..' or the root directory */ 384 return STATUS_ACCESS_DENIED; 385 } 386 387 if (!MmFlushImageSection (FileObject->SectionObjectPointer, MmFlushForDelete)) 388 { 389 /* can't delete a file if its mapped into a process */ 390 391 DPRINT("MmFlushImageSection returned FALSE\n"); 392 return STATUS_CANNOT_DELETE; 393 } 394 395 if (vfatFCBIsDirectory(FCB) && !VfatIsDirectoryEmpty(DeviceExt, FCB)) 396 { 397 /* can't delete a non-empty directory */ 398 399 return STATUS_DIRECTORY_NOT_EMPTY; 400 } 401 402 /* all good */ 403 FCB->Flags |= FCB_DELETE_PENDING; 404 FileObject->DeletePending = TRUE; 405 406 return STATUS_SUCCESS; 407 } 408 409 static NTSTATUS 410 vfatPrepareTargetForRename( 411 IN PDEVICE_EXTENSION DeviceExt, 412 IN PVFATFCB * ParentFCB, 413 IN PUNICODE_STRING NewName, 414 IN BOOLEAN ReplaceIfExists, 415 IN PUNICODE_STRING ParentName, 416 OUT PBOOLEAN Deleted) 417 { 418 NTSTATUS Status; 419 PVFATFCB TargetFcb; 420 421 DPRINT("vfatPrepareTargetForRename(%p, %p, %wZ, %d, %wZ, %p)\n", DeviceExt, ParentFCB, NewName, ReplaceIfExists, ParentName); 422 423 *Deleted = FALSE; 424 /* Try to open target */ 425 Status = vfatGetFCBForFile(DeviceExt, ParentFCB, &TargetFcb, NewName); 426 /* If it exists */ 427 if (NT_SUCCESS(Status)) 428 { 429 DPRINT("Target file %wZ exists. FCB Flags %08x\n", NewName, TargetFcb->Flags); 430 /* Check whether we are allowed to replace */ 431 if (ReplaceIfExists) 432 { 433 /* If that's a directory or a read-only file, we're not allowed */ 434 if (vfatFCBIsDirectory(TargetFcb) || vfatFCBIsReadOnly(TargetFcb)) 435 { 436 DPRINT("And this is a readonly file!\n"); 437 vfatReleaseFCB(DeviceExt, *ParentFCB); 438 *ParentFCB = NULL; 439 vfatReleaseFCB(DeviceExt, TargetFcb); 440 return STATUS_OBJECT_NAME_COLLISION; 441 } 442 443 444 /* If we still have a file object, close it. */ 445 if (TargetFcb->FileObject) 446 { 447 if (!MmFlushImageSection(TargetFcb->FileObject->SectionObjectPointer, MmFlushForDelete)) 448 { 449 DPRINT("MmFlushImageSection failed.\n"); 450 vfatReleaseFCB(DeviceExt, *ParentFCB); 451 *ParentFCB = NULL; 452 vfatReleaseFCB(DeviceExt, TargetFcb); 453 return STATUS_ACCESS_DENIED; 454 } 455 456 TargetFcb->FileObject->DeletePending = TRUE; 457 VfatCloseFile(DeviceExt, TargetFcb->FileObject); 458 } 459 460 /* If we are here, ensure the file isn't open by anyone! */ 461 if (TargetFcb->OpenHandleCount != 0) 462 { 463 DPRINT("There are still open handles for this file.\n"); 464 vfatReleaseFCB(DeviceExt, *ParentFCB); 465 *ParentFCB = NULL; 466 vfatReleaseFCB(DeviceExt, TargetFcb); 467 return STATUS_ACCESS_DENIED; 468 } 469 470 /* Effectively delete old file to allow renaming */ 471 DPRINT("Effectively deleting the file.\n"); 472 VfatDelEntry(DeviceExt, TargetFcb, NULL); 473 vfatReleaseFCB(DeviceExt, TargetFcb); 474 *Deleted = TRUE; 475 return STATUS_SUCCESS; 476 } 477 else 478 { 479 vfatReleaseFCB(DeviceExt, *ParentFCB); 480 *ParentFCB = NULL; 481 vfatReleaseFCB(DeviceExt, TargetFcb); 482 return STATUS_OBJECT_NAME_COLLISION; 483 } 484 } 485 else if (*ParentFCB != NULL) 486 { 487 return STATUS_SUCCESS; 488 } 489 490 /* Failure */ 491 return Status; 492 } 493 494 static 495 BOOLEAN 496 IsThereAChildOpened(PVFATFCB FCB) 497 { 498 PLIST_ENTRY Entry; 499 PVFATFCB VolFCB; 500 501 for (Entry = FCB->ParentListHead.Flink; Entry != &FCB->ParentListHead; Entry = Entry->Flink) 502 { 503 VolFCB = CONTAINING_RECORD(Entry, VFATFCB, ParentListEntry); 504 if (VolFCB->OpenHandleCount != 0) 505 { 506 ASSERT(VolFCB->parentFcb == FCB); 507 DPRINT1("At least one children file opened! %wZ (%u, %u)\n", &VolFCB->PathNameU, VolFCB->RefCount, VolFCB->OpenHandleCount); 508 return TRUE; 509 } 510 511 if (vfatFCBIsDirectory(VolFCB) && !IsListEmpty(&VolFCB->ParentListHead)) 512 { 513 if (IsThereAChildOpened(VolFCB)) 514 { 515 return TRUE; 516 } 517 } 518 } 519 520 return FALSE; 521 } 522 523 static 524 VOID 525 VfatRenameChildFCB( 526 PDEVICE_EXTENSION DeviceExt, 527 PVFATFCB FCB) 528 { 529 PLIST_ENTRY Entry; 530 PVFATFCB Child; 531 532 if (IsListEmpty(&FCB->ParentListHead)) 533 return; 534 535 for (Entry = FCB->ParentListHead.Flink; Entry != &FCB->ParentListHead; Entry = Entry->Flink) 536 { 537 NTSTATUS Status; 538 539 Child = CONTAINING_RECORD(Entry, VFATFCB, ParentListEntry); 540 DPRINT("Found %wZ with still %lu references (parent: %lu)!\n", &Child->PathNameU, Child->RefCount, FCB->RefCount); 541 542 Status = vfatSetFCBNewDirName(DeviceExt, Child, FCB); 543 if (!NT_SUCCESS(Status)) 544 continue; 545 546 if (vfatFCBIsDirectory(Child)) 547 { 548 VfatRenameChildFCB(DeviceExt, Child); 549 } 550 } 551 } 552 553 /* 554 * FUNCTION: Set the file name information 555 */ 556 static 557 NTSTATUS 558 VfatSetRenameInformation( 559 PFILE_OBJECT FileObject, 560 PVFATFCB FCB, 561 PDEVICE_EXTENSION DeviceExt, 562 PFILE_RENAME_INFORMATION RenameInfo, 563 PFILE_OBJECT TargetFileObject) 564 { 565 #ifdef NASSERTS_RENAME 566 #pragma push_macro("ASSERT") 567 #undef ASSERT 568 #define ASSERT(x) ((VOID) 0) 569 #endif 570 NTSTATUS Status; 571 UNICODE_STRING NewName; 572 UNICODE_STRING SourcePath; 573 UNICODE_STRING SourceFile; 574 UNICODE_STRING NewPath; 575 UNICODE_STRING NewFile; 576 PFILE_OBJECT RootFileObject; 577 PVFATFCB RootFCB; 578 UNICODE_STRING RenameInfoString; 579 PVFATFCB ParentFCB; 580 IO_STATUS_BLOCK IoStatusBlock; 581 OBJECT_ATTRIBUTES ObjectAttributes; 582 HANDLE TargetHandle; 583 BOOLEAN DeletedTarget; 584 ULONG OldReferences, NewReferences; 585 PVFATFCB OldParent; 586 587 DPRINT("VfatSetRenameInfo(%p, %p, %p, %p, %p)\n", FileObject, FCB, DeviceExt, RenameInfo, TargetFileObject); 588 589 /* Disallow renaming root */ 590 if (vfatFCBIsRoot(FCB)) 591 { 592 return STATUS_INVALID_PARAMETER; 593 } 594 595 OldReferences = FCB->parentFcb->RefCount; 596 #ifdef NASSERTS_RENAME 597 UNREFERENCED_PARAMETER(OldReferences); 598 #endif 599 600 /* If we are performing relative opening for rename, get FO for getting FCB and path name */ 601 if (RenameInfo->RootDirectory != NULL) 602 { 603 /* We cannot tolerate relative opening with a full path */ 604 if (RenameInfo->FileName[0] == L'\\') 605 { 606 return STATUS_OBJECT_NAME_INVALID; 607 } 608 609 Status = ObReferenceObjectByHandle(RenameInfo->RootDirectory, 610 FILE_READ_DATA, 611 *IoFileObjectType, 612 ExGetPreviousMode(), 613 (PVOID *)&RootFileObject, 614 NULL); 615 if (!NT_SUCCESS(Status)) 616 { 617 return Status; 618 } 619 620 RootFCB = RootFileObject->FsContext; 621 } 622 623 RtlInitEmptyUnicodeString(&NewName, NULL, 0); 624 ParentFCB = NULL; 625 626 if (TargetFileObject == NULL) 627 { 628 /* If we don't have target file object, construct paths thanks to relative FCB, if any, and with 629 * information supplied by the user 630 */ 631 632 /* First, setup a string we'll work on */ 633 RenameInfoString.Length = RenameInfo->FileNameLength; 634 RenameInfoString.MaximumLength = RenameInfo->FileNameLength; 635 RenameInfoString.Buffer = RenameInfo->FileName; 636 637 /* Check whether we have FQN */ 638 if (RenameInfoString.Length > 6 * sizeof(WCHAR)) 639 { 640 if (RenameInfoString.Buffer[0] == L'\\' && RenameInfoString.Buffer[1] == L'?' && 641 RenameInfoString.Buffer[2] == L'?' && RenameInfoString.Buffer[3] == L'\\' && 642 RenameInfoString.Buffer[5] == L':' && (RenameInfoString.Buffer[4] >= L'A' && 643 RenameInfoString.Buffer[4] <= L'Z')) 644 { 645 /* If so, open its target directory */ 646 InitializeObjectAttributes(&ObjectAttributes, 647 &RenameInfoString, 648 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 649 NULL, NULL); 650 651 Status = IoCreateFile(&TargetHandle, 652 FILE_WRITE_DATA | SYNCHRONIZE, 653 &ObjectAttributes, 654 &IoStatusBlock, 655 NULL, 0, 656 FILE_SHARE_READ | FILE_SHARE_WRITE, 657 FILE_OPEN, 658 FILE_OPEN_FOR_BACKUP_INTENT, 659 NULL, 0, 660 CreateFileTypeNone, 661 NULL, 662 IO_FORCE_ACCESS_CHECK | IO_OPEN_TARGET_DIRECTORY); 663 if (!NT_SUCCESS(Status)) 664 { 665 goto Cleanup; 666 } 667 668 /* Get its FO to get the FCB */ 669 Status = ObReferenceObjectByHandle(TargetHandle, 670 FILE_WRITE_DATA, 671 *IoFileObjectType, 672 KernelMode, 673 (PVOID *)&TargetFileObject, 674 NULL); 675 if (!NT_SUCCESS(Status)) 676 { 677 ZwClose(TargetHandle); 678 goto Cleanup; 679 } 680 681 /* Are we working on the same volume? */ 682 if (IoGetRelatedDeviceObject(TargetFileObject) != IoGetRelatedDeviceObject(FileObject)) 683 { 684 ObDereferenceObject(TargetFileObject); 685 ZwClose(TargetHandle); 686 TargetFileObject = NULL; 687 Status = STATUS_NOT_SAME_DEVICE; 688 goto Cleanup; 689 } 690 } 691 } 692 693 NewName.Length = 0; 694 NewName.MaximumLength = RenameInfo->FileNameLength; 695 if (RenameInfo->RootDirectory != NULL) 696 { 697 NewName.MaximumLength += sizeof(WCHAR) + RootFCB->PathNameU.Length; 698 } 699 else if (RenameInfo->FileName[0] != L'\\') 700 { 701 /* We don't have full path, and we don't have root directory: 702 * => we move inside the same directory 703 */ 704 NewName.MaximumLength += sizeof(WCHAR) + FCB->DirNameU.Length; 705 } 706 else if (TargetFileObject != NULL) 707 { 708 /* We had a FQN: 709 * => we need to use its correct path 710 */ 711 NewName.MaximumLength += sizeof(WCHAR) + ((PVFATFCB)TargetFileObject->FsContext)->PathNameU.Length; 712 } 713 714 NewName.Buffer = ExAllocatePoolWithTag(NonPagedPool, NewName.MaximumLength, TAG_NAME); 715 if (NewName.Buffer == NULL) 716 { 717 if (TargetFileObject != NULL) 718 { 719 ObDereferenceObject(TargetFileObject); 720 ZwClose(TargetHandle); 721 TargetFileObject = NULL; 722 } 723 Status = STATUS_INSUFFICIENT_RESOURCES; 724 goto Cleanup; 725 } 726 727 if (RenameInfo->RootDirectory != NULL) 728 { 729 /* Here, copy first absolute and then append relative */ 730 RtlCopyUnicodeString(&NewName, &RootFCB->PathNameU); 731 NewName.Buffer[NewName.Length / sizeof(WCHAR)] = L'\\'; 732 NewName.Length += sizeof(WCHAR); 733 RtlAppendUnicodeStringToString(&NewName, &RenameInfoString); 734 } 735 else if (RenameInfo->FileName[0] != L'\\') 736 { 737 /* Here, copy first work directory and then append filename */ 738 RtlCopyUnicodeString(&NewName, &FCB->DirNameU); 739 NewName.Buffer[NewName.Length / sizeof(WCHAR)] = L'\\'; 740 NewName.Length += sizeof(WCHAR); 741 RtlAppendUnicodeStringToString(&NewName, &RenameInfoString); 742 } 743 else if (TargetFileObject != NULL) 744 { 745 /* Here, copy first path name and then append filename */ 746 RtlCopyUnicodeString(&NewName, &((PVFATFCB)TargetFileObject->FsContext)->PathNameU); 747 NewName.Buffer[NewName.Length / sizeof(WCHAR)] = L'\\'; 748 NewName.Length += sizeof(WCHAR); 749 RtlAppendUnicodeStringToString(&NewName, &RenameInfoString); 750 } 751 else 752 { 753 /* Here we should have full path, so simply copy it */ 754 RtlCopyUnicodeString(&NewName, &RenameInfoString); 755 } 756 757 /* Do we have to cleanup some stuff? */ 758 if (TargetFileObject != NULL) 759 { 760 ObDereferenceObject(TargetFileObject); 761 ZwClose(TargetHandle); 762 TargetFileObject = NULL; 763 } 764 } 765 else 766 { 767 /* At that point, we shouldn't care about whether we are relative opening 768 * Target FO FCB should already have full path 769 */ 770 771 /* Before constructing string, just make a sanity check (just to be sure!) */ 772 if (IoGetRelatedDeviceObject(TargetFileObject) != IoGetRelatedDeviceObject(FileObject)) 773 { 774 Status = STATUS_NOT_SAME_DEVICE; 775 goto Cleanup; 776 } 777 778 NewName.Length = 0; 779 NewName.MaximumLength = TargetFileObject->FileName.Length + ((PVFATFCB)TargetFileObject->FsContext)->PathNameU.Length + sizeof(WCHAR); 780 NewName.Buffer = ExAllocatePoolWithTag(NonPagedPool, NewName.MaximumLength, TAG_NAME); 781 if (NewName.Buffer == NULL) 782 { 783 Status = STATUS_INSUFFICIENT_RESOURCES; 784 goto Cleanup; 785 } 786 787 RtlCopyUnicodeString(&NewName, &((PVFATFCB)TargetFileObject->FsContext)->PathNameU); 788 /* If \, it's already backslash terminated, don't add it */ 789 if (!vfatFCBIsRoot(TargetFileObject->FsContext)) 790 { 791 NewName.Buffer[NewName.Length / sizeof(WCHAR)] = L'\\'; 792 NewName.Length += sizeof(WCHAR); 793 } 794 RtlAppendUnicodeStringToString(&NewName, &TargetFileObject->FileName); 795 } 796 797 /* Explode our paths to get path & filename */ 798 vfatSplitPathName(&FCB->PathNameU, &SourcePath, &SourceFile); 799 DPRINT("Old dir: %wZ, Old file: %wZ\n", &SourcePath, &SourceFile); 800 vfatSplitPathName(&NewName, &NewPath, &NewFile); 801 DPRINT("New dir: %wZ, New file: %wZ\n", &NewPath, &NewFile); 802 803 if (IsDotOrDotDot(&NewFile)) 804 { 805 Status = STATUS_OBJECT_NAME_INVALID; 806 goto Cleanup; 807 } 808 809 if (vfatFCBIsDirectory(FCB) && !IsListEmpty(&FCB->ParentListHead)) 810 { 811 if (IsThereAChildOpened(FCB)) 812 { 813 Status = STATUS_ACCESS_DENIED; 814 ASSERT(OldReferences == FCB->parentFcb->RefCount); 815 goto Cleanup; 816 } 817 } 818 819 /* Are we working in place? */ 820 if (FsRtlAreNamesEqual(&SourcePath, &NewPath, TRUE, NULL)) 821 { 822 if (FsRtlAreNamesEqual(&SourceFile, &NewFile, FALSE, NULL)) 823 { 824 Status = STATUS_SUCCESS; 825 ASSERT(OldReferences == FCB->parentFcb->RefCount); 826 goto Cleanup; 827 } 828 829 if (FsRtlAreNamesEqual(&SourceFile, &NewFile, TRUE, NULL)) 830 { 831 vfatReportChange(DeviceExt, 832 FCB, 833 (vfatFCBIsDirectory(FCB) ? 834 FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME), 835 FILE_ACTION_RENAMED_OLD_NAME); 836 Status = vfatRenameEntry(DeviceExt, FCB, &NewFile, TRUE); 837 if (NT_SUCCESS(Status)) 838 { 839 vfatReportChange(DeviceExt, 840 FCB, 841 (vfatFCBIsDirectory(FCB) ? 842 FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME), 843 FILE_ACTION_RENAMED_NEW_NAME); 844 } 845 } 846 else 847 { 848 /* Try to find target */ 849 ParentFCB = FCB->parentFcb; 850 vfatGrabFCB(DeviceExt, ParentFCB); 851 Status = vfatPrepareTargetForRename(DeviceExt, 852 &ParentFCB, 853 &NewFile, 854 RenameInfo->ReplaceIfExists, 855 &NewPath, 856 &DeletedTarget); 857 if (!NT_SUCCESS(Status)) 858 { 859 ASSERT(OldReferences == FCB->parentFcb->RefCount - 1); 860 ASSERT(OldReferences == ParentFCB->RefCount - 1); 861 goto Cleanup; 862 } 863 864 vfatReportChange(DeviceExt, 865 FCB, 866 (vfatFCBIsDirectory(FCB) ? 867 FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME), 868 (DeletedTarget ? FILE_ACTION_REMOVED : FILE_ACTION_RENAMED_OLD_NAME)); 869 Status = vfatRenameEntry(DeviceExt, FCB, &NewFile, FALSE); 870 if (NT_SUCCESS(Status)) 871 { 872 if (DeletedTarget) 873 { 874 vfatReportChange(DeviceExt, 875 FCB, 876 FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE 877 | FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_EA, 878 FILE_ACTION_MODIFIED); 879 } 880 else 881 { 882 vfatReportChange(DeviceExt, 883 FCB, 884 (vfatFCBIsDirectory(FCB) ? 885 FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME), 886 FILE_ACTION_RENAMED_NEW_NAME); 887 } 888 } 889 } 890 891 ASSERT(OldReferences == FCB->parentFcb->RefCount - 1); // extra grab 892 ASSERT(OldReferences == ParentFCB->RefCount - 1); // extra grab 893 } 894 else 895 { 896 897 /* Try to find target */ 898 ParentFCB = NULL; 899 OldParent = FCB->parentFcb; 900 #ifdef NASSERTS_RENAME 901 UNREFERENCED_PARAMETER(OldParent); 902 #endif 903 Status = vfatPrepareTargetForRename(DeviceExt, 904 &ParentFCB, 905 &NewName, 906 RenameInfo->ReplaceIfExists, 907 &NewPath, 908 &DeletedTarget); 909 if (!NT_SUCCESS(Status)) 910 { 911 ASSERT(OldReferences == FCB->parentFcb->RefCount); 912 goto Cleanup; 913 } 914 915 NewReferences = ParentFCB->RefCount; 916 #ifdef NASSERTS_RENAME 917 UNREFERENCED_PARAMETER(NewReferences); 918 #endif 919 920 vfatReportChange(DeviceExt, 921 FCB, 922 (vfatFCBIsDirectory(FCB) ? 923 FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME), 924 FILE_ACTION_REMOVED); 925 Status = VfatMoveEntry(DeviceExt, FCB, &NewFile, ParentFCB); 926 if (NT_SUCCESS(Status)) 927 { 928 if (DeletedTarget) 929 { 930 vfatReportChange(DeviceExt, 931 FCB, 932 FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE 933 | FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_EA, 934 FILE_ACTION_MODIFIED); 935 } 936 else 937 { 938 vfatReportChange(DeviceExt, 939 FCB, 940 (vfatFCBIsDirectory(FCB) ? 941 FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME), 942 FILE_ACTION_ADDED); 943 } 944 } 945 } 946 947 if (NT_SUCCESS(Status) && vfatFCBIsDirectory(FCB)) 948 { 949 VfatRenameChildFCB(DeviceExt, FCB); 950 } 951 952 ASSERT(OldReferences == OldParent->RefCount + 1); // removed file 953 ASSERT(NewReferences == ParentFCB->RefCount - 1); // new file 954 Cleanup: 955 if (ParentFCB != NULL) vfatReleaseFCB(DeviceExt, ParentFCB); 956 if (NewName.Buffer != NULL) ExFreePoolWithTag(NewName.Buffer, TAG_NAME); 957 if (RenameInfo->RootDirectory != NULL) ObDereferenceObject(RootFileObject); 958 959 return Status; 960 #ifdef NASSERTS_RENAME 961 #pragma pop_macro("ASSERT") 962 #endif 963 } 964 965 /* 966 * FUNCTION: Retrieve the file name information 967 */ 968 static 969 NTSTATUS 970 VfatGetNameInformation( 971 PFILE_OBJECT FileObject, 972 PVFATFCB FCB, 973 PDEVICE_EXTENSION DeviceExt, 974 PFILE_NAME_INFORMATION NameInfo, 975 PULONG BufferLength) 976 { 977 ULONG BytesToCopy; 978 979 UNREFERENCED_PARAMETER(FileObject); 980 UNREFERENCED_PARAMETER(DeviceExt); 981 982 ASSERT(NameInfo != NULL); 983 ASSERT(FCB != NULL); 984 985 /* If buffer can't hold at least the file name length, bail out */ 986 if (*BufferLength < (ULONG)FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0])) 987 return STATUS_BUFFER_OVERFLOW; 988 989 /* Save file name length, and as much file len, as buffer length allows */ 990 NameInfo->FileNameLength = FCB->PathNameU.Length; 991 992 /* Calculate amount of bytes to copy not to overflow the buffer */ 993 BytesToCopy = min(FCB->PathNameU.Length, 994 *BufferLength - FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0])); 995 996 /* Fill in the bytes */ 997 RtlCopyMemory(NameInfo->FileName, FCB->PathNameU.Buffer, BytesToCopy); 998 999 /* Check if we could write more but are not able to */ 1000 if (*BufferLength < FCB->PathNameU.Length + (ULONG)FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0])) 1001 { 1002 /* Return number of bytes written */ 1003 *BufferLength -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]) + BytesToCopy; 1004 return STATUS_BUFFER_OVERFLOW; 1005 } 1006 1007 /* We filled up as many bytes, as needed */ 1008 *BufferLength -= (FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]) + FCB->PathNameU.Length); 1009 1010 return STATUS_SUCCESS; 1011 } 1012 1013 static 1014 NTSTATUS 1015 VfatGetInternalInformation( 1016 PVFATFCB Fcb, 1017 PDEVICE_EXTENSION DeviceExt, 1018 PFILE_INTERNAL_INFORMATION InternalInfo, 1019 PULONG BufferLength) 1020 { 1021 ASSERT(InternalInfo); 1022 ASSERT(Fcb); 1023 1024 if (*BufferLength < sizeof(FILE_INTERNAL_INFORMATION)) 1025 return STATUS_BUFFER_OVERFLOW; 1026 1027 InternalInfo->IndexNumber.QuadPart = (LONGLONG)vfatDirEntryGetFirstCluster(DeviceExt, &Fcb->entry) * DeviceExt->FatInfo.BytesPerCluster; 1028 1029 *BufferLength -= sizeof(FILE_INTERNAL_INFORMATION); 1030 return STATUS_SUCCESS; 1031 } 1032 1033 1034 /* 1035 * FUNCTION: Retrieve the file network open information 1036 */ 1037 static 1038 NTSTATUS 1039 VfatGetNetworkOpenInformation( 1040 PVFATFCB Fcb, 1041 PDEVICE_EXTENSION DeviceExt, 1042 PFILE_NETWORK_OPEN_INFORMATION NetworkInfo, 1043 PULONG BufferLength) 1044 { 1045 ASSERT(NetworkInfo); 1046 ASSERT(Fcb); 1047 1048 if (*BufferLength < sizeof(FILE_NETWORK_OPEN_INFORMATION)) 1049 return(STATUS_BUFFER_OVERFLOW); 1050 1051 if (vfatVolumeIsFatX(DeviceExt)) 1052 { 1053 FsdDosDateTimeToSystemTime(DeviceExt, 1054 Fcb->entry.FatX.CreationDate, 1055 Fcb->entry.FatX.CreationTime, 1056 &NetworkInfo->CreationTime); 1057 FsdDosDateTimeToSystemTime(DeviceExt, 1058 Fcb->entry.FatX.AccessDate, 1059 Fcb->entry.FatX.AccessTime, 1060 &NetworkInfo->LastAccessTime); 1061 FsdDosDateTimeToSystemTime(DeviceExt, 1062 Fcb->entry.FatX.UpdateDate, 1063 Fcb->entry.FatX.UpdateTime, 1064 &NetworkInfo->LastWriteTime); 1065 NetworkInfo->ChangeTime.QuadPart = NetworkInfo->LastWriteTime.QuadPart; 1066 } 1067 else 1068 { 1069 FsdDosDateTimeToSystemTime(DeviceExt, 1070 Fcb->entry.Fat.CreationDate, 1071 Fcb->entry.Fat.CreationTime, 1072 &NetworkInfo->CreationTime); 1073 FsdDosDateTimeToSystemTime(DeviceExt, 1074 Fcb->entry.Fat.AccessDate, 1075 0, 1076 &NetworkInfo->LastAccessTime); 1077 FsdDosDateTimeToSystemTime(DeviceExt, 1078 Fcb->entry.Fat.UpdateDate, 1079 Fcb->entry.Fat.UpdateTime, 1080 &NetworkInfo->LastWriteTime); 1081 NetworkInfo->ChangeTime.QuadPart = NetworkInfo->LastWriteTime.QuadPart; 1082 } 1083 1084 if (vfatFCBIsDirectory(Fcb)) 1085 { 1086 NetworkInfo->EndOfFile.QuadPart = 0L; 1087 NetworkInfo->AllocationSize.QuadPart = 0L; 1088 } 1089 else 1090 { 1091 NetworkInfo->AllocationSize = Fcb->RFCB.AllocationSize; 1092 NetworkInfo->EndOfFile = Fcb->RFCB.FileSize; 1093 } 1094 1095 NetworkInfo->FileAttributes = *Fcb->Attributes & 0x3f; 1096 /* Synthesize FILE_ATTRIBUTE_NORMAL */ 1097 if (0 == (NetworkInfo->FileAttributes & (FILE_ATTRIBUTE_DIRECTORY | 1098 FILE_ATTRIBUTE_ARCHIVE | 1099 FILE_ATTRIBUTE_SYSTEM | 1100 FILE_ATTRIBUTE_HIDDEN | 1101 FILE_ATTRIBUTE_READONLY))) 1102 { 1103 DPRINT("Synthesizing FILE_ATTRIBUTE_NORMAL\n"); 1104 NetworkInfo->FileAttributes |= FILE_ATTRIBUTE_NORMAL; 1105 } 1106 1107 *BufferLength -= sizeof(FILE_NETWORK_OPEN_INFORMATION); 1108 return STATUS_SUCCESS; 1109 } 1110 1111 1112 static 1113 NTSTATUS 1114 VfatGetEaInformation( 1115 PFILE_OBJECT FileObject, 1116 PVFATFCB Fcb, 1117 PDEVICE_EXTENSION DeviceExt, 1118 PFILE_EA_INFORMATION Info, 1119 PULONG BufferLength) 1120 { 1121 UNREFERENCED_PARAMETER(FileObject); 1122 UNREFERENCED_PARAMETER(Fcb); 1123 1124 /* FIXME - use SEH to access the buffer! */ 1125 Info->EaSize = 0; 1126 *BufferLength -= sizeof(*Info); 1127 if (DeviceExt->FatInfo.FatType == FAT12 || 1128 DeviceExt->FatInfo.FatType == FAT16) 1129 { 1130 /* FIXME */ 1131 DPRINT1("VFAT: FileEaInformation not implemented!\n"); 1132 } 1133 return STATUS_SUCCESS; 1134 } 1135 1136 1137 /* 1138 * FUNCTION: Retrieve the all file information 1139 */ 1140 static 1141 NTSTATUS 1142 VfatGetAllInformation( 1143 PFILE_OBJECT FileObject, 1144 PVFATFCB Fcb, 1145 PDEVICE_EXTENSION DeviceExt, 1146 PFILE_ALL_INFORMATION Info, 1147 PULONG BufferLength) 1148 { 1149 NTSTATUS Status; 1150 1151 ASSERT(Info); 1152 ASSERT(Fcb); 1153 1154 if (*BufferLength < FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName)) 1155 return STATUS_BUFFER_OVERFLOW; 1156 1157 *BufferLength -= (sizeof(FILE_ACCESS_INFORMATION) + sizeof(FILE_MODE_INFORMATION) + sizeof(FILE_ALIGNMENT_INFORMATION)); 1158 1159 /* Basic Information */ 1160 Status = VfatGetBasicInformation(FileObject, Fcb, DeviceExt, &Info->BasicInformation, BufferLength); 1161 if (!NT_SUCCESS(Status)) return Status; 1162 /* Standard Information */ 1163 Status = VfatGetStandardInformation(Fcb, &Info->StandardInformation, BufferLength); 1164 if (!NT_SUCCESS(Status)) return Status; 1165 /* Internal Information */ 1166 Status = VfatGetInternalInformation(Fcb, DeviceExt, &Info->InternalInformation, BufferLength); 1167 if (!NT_SUCCESS(Status)) return Status; 1168 /* EA Information */ 1169 Status = VfatGetEaInformation(FileObject, Fcb, DeviceExt, &Info->EaInformation, BufferLength); 1170 if (!NT_SUCCESS(Status)) return Status; 1171 /* Position Information */ 1172 Status = VfatGetPositionInformation(FileObject, Fcb, DeviceExt, &Info->PositionInformation, BufferLength); 1173 if (!NT_SUCCESS(Status)) return Status; 1174 /* Name Information */ 1175 Status = VfatGetNameInformation(FileObject, Fcb, DeviceExt, &Info->NameInformation, BufferLength); 1176 1177 return Status; 1178 } 1179 1180 static 1181 VOID 1182 UpdateFileSize( 1183 PFILE_OBJECT FileObject, 1184 PVFATFCB Fcb, 1185 ULONG Size, 1186 ULONG ClusterSize, 1187 BOOLEAN IsFatX) 1188 { 1189 if (Size > 0) 1190 { 1191 Fcb->RFCB.AllocationSize.QuadPart = ROUND_UP_64(Size, ClusterSize); 1192 } 1193 else 1194 { 1195 Fcb->RFCB.AllocationSize.QuadPart = (LONGLONG)0; 1196 } 1197 if (!vfatFCBIsDirectory(Fcb)) 1198 { 1199 if (IsFatX) 1200 Fcb->entry.FatX.FileSize = Size; 1201 else 1202 Fcb->entry.Fat.FileSize = Size; 1203 } 1204 Fcb->RFCB.FileSize.QuadPart = Size; 1205 Fcb->RFCB.ValidDataLength.QuadPart = Size; 1206 1207 CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&Fcb->RFCB.AllocationSize); 1208 } 1209 1210 NTSTATUS 1211 VfatSetAllocationSizeInformation( 1212 PFILE_OBJECT FileObject, 1213 PVFATFCB Fcb, 1214 PDEVICE_EXTENSION DeviceExt, 1215 PLARGE_INTEGER AllocationSize) 1216 { 1217 ULONG OldSize; 1218 ULONG Cluster, FirstCluster; 1219 NTSTATUS Status; 1220 1221 ULONG ClusterSize = DeviceExt->FatInfo.BytesPerCluster; 1222 ULONG NewSize = AllocationSize->u.LowPart; 1223 ULONG NCluster; 1224 BOOLEAN AllocSizeChanged = FALSE, IsFatX = vfatVolumeIsFatX(DeviceExt); 1225 1226 DPRINT("VfatSetAllocationSizeInformation(File <%wZ>, AllocationSize %d %u)\n", 1227 &Fcb->PathNameU, AllocationSize->HighPart, AllocationSize->LowPart); 1228 1229 if (IsFatX) 1230 OldSize = Fcb->entry.FatX.FileSize; 1231 else 1232 OldSize = Fcb->entry.Fat.FileSize; 1233 1234 if (AllocationSize->u.HighPart > 0) 1235 { 1236 return STATUS_INVALID_PARAMETER; 1237 } 1238 1239 if (OldSize == NewSize) 1240 { 1241 return STATUS_SUCCESS; 1242 } 1243 1244 FirstCluster = vfatDirEntryGetFirstCluster(DeviceExt, &Fcb->entry); 1245 1246 if (NewSize > Fcb->RFCB.AllocationSize.u.LowPart) 1247 { 1248 AllocSizeChanged = TRUE; 1249 if (FirstCluster == 0) 1250 { 1251 Fcb->LastCluster = Fcb->LastOffset = 0; 1252 Status = NextCluster(DeviceExt, FirstCluster, &FirstCluster, TRUE); 1253 if (!NT_SUCCESS(Status)) 1254 { 1255 DPRINT1("NextCluster failed. Status = %x\n", Status); 1256 return Status; 1257 } 1258 1259 if (FirstCluster == 0xffffffff) 1260 { 1261 return STATUS_DISK_FULL; 1262 } 1263 1264 Status = OffsetToCluster(DeviceExt, FirstCluster, 1265 ROUND_DOWN(NewSize - 1, ClusterSize), 1266 &NCluster, TRUE); 1267 if (NCluster == 0xffffffff || !NT_SUCCESS(Status)) 1268 { 1269 /* disk is full */ 1270 NCluster = Cluster = FirstCluster; 1271 Status = STATUS_SUCCESS; 1272 while (NT_SUCCESS(Status) && Cluster != 0xffffffff && Cluster > 1) 1273 { 1274 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE); 1275 WriteCluster(DeviceExt, Cluster, 0); 1276 Cluster = NCluster; 1277 } 1278 return STATUS_DISK_FULL; 1279 } 1280 1281 if (IsFatX) 1282 { 1283 Fcb->entry.FatX.FirstCluster = FirstCluster; 1284 } 1285 else 1286 { 1287 if (DeviceExt->FatInfo.FatType == FAT32) 1288 { 1289 Fcb->entry.Fat.FirstCluster = (unsigned short)(FirstCluster & 0x0000FFFF); 1290 Fcb->entry.Fat.FirstClusterHigh = FirstCluster >> 16; 1291 } 1292 else 1293 { 1294 ASSERT((FirstCluster >> 16) == 0); 1295 Fcb->entry.Fat.FirstCluster = (unsigned short)(FirstCluster & 0x0000FFFF); 1296 } 1297 } 1298 } 1299 else 1300 { 1301 if (Fcb->LastCluster > 0) 1302 { 1303 if (Fcb->RFCB.AllocationSize.u.LowPart - ClusterSize == Fcb->LastOffset) 1304 { 1305 Cluster = Fcb->LastCluster; 1306 Status = STATUS_SUCCESS; 1307 } 1308 else 1309 { 1310 Status = OffsetToCluster(DeviceExt, Fcb->LastCluster, 1311 Fcb->RFCB.AllocationSize.u.LowPart - ClusterSize - Fcb->LastOffset, 1312 &Cluster, FALSE); 1313 } 1314 } 1315 else 1316 { 1317 Status = OffsetToCluster(DeviceExt, FirstCluster, 1318 Fcb->RFCB.AllocationSize.u.LowPart - ClusterSize, 1319 &Cluster, FALSE); 1320 } 1321 1322 if (!NT_SUCCESS(Status)) 1323 { 1324 return Status; 1325 } 1326 1327 Fcb->LastCluster = Cluster; 1328 Fcb->LastOffset = Fcb->RFCB.AllocationSize.u.LowPart - ClusterSize; 1329 1330 /* FIXME: Check status */ 1331 /* Cluster points now to the last cluster within the chain */ 1332 Status = OffsetToCluster(DeviceExt, Cluster, 1333 ROUND_DOWN(NewSize - 1, ClusterSize) - Fcb->LastOffset, 1334 &NCluster, TRUE); 1335 if (NCluster == 0xffffffff || !NT_SUCCESS(Status)) 1336 { 1337 /* disk is full */ 1338 NCluster = Cluster; 1339 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE); 1340 WriteCluster(DeviceExt, Cluster, 0xffffffff); 1341 Cluster = NCluster; 1342 while (NT_SUCCESS(Status) && Cluster != 0xffffffff && Cluster > 1) 1343 { 1344 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE); 1345 WriteCluster(DeviceExt, Cluster, 0); 1346 Cluster = NCluster; 1347 } 1348 return STATUS_DISK_FULL; 1349 } 1350 } 1351 UpdateFileSize(FileObject, Fcb, NewSize, ClusterSize, vfatVolumeIsFatX(DeviceExt)); 1352 } 1353 else if (NewSize + ClusterSize <= Fcb->RFCB.AllocationSize.u.LowPart) 1354 { 1355 DPRINT("Check for the ability to set file size\n"); 1356 if (!MmCanFileBeTruncated(FileObject->SectionObjectPointer, 1357 (PLARGE_INTEGER)AllocationSize)) 1358 { 1359 DPRINT("Couldn't set file size!\n"); 1360 return STATUS_USER_MAPPED_FILE; 1361 } 1362 DPRINT("Can set file size\n"); 1363 1364 AllocSizeChanged = TRUE; 1365 /* FIXME: Use the cached cluster/offset better way. */ 1366 Fcb->LastCluster = Fcb->LastOffset = 0; 1367 UpdateFileSize(FileObject, Fcb, NewSize, ClusterSize, vfatVolumeIsFatX(DeviceExt)); 1368 if (NewSize > 0) 1369 { 1370 Status = OffsetToCluster(DeviceExt, FirstCluster, 1371 ROUND_DOWN(NewSize - 1, ClusterSize), 1372 &Cluster, FALSE); 1373 1374 NCluster = Cluster; 1375 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE); 1376 WriteCluster(DeviceExt, Cluster, 0xffffffff); 1377 Cluster = NCluster; 1378 } 1379 else 1380 { 1381 if (IsFatX) 1382 { 1383 Fcb->entry.FatX.FirstCluster = 0; 1384 } 1385 else 1386 { 1387 if (DeviceExt->FatInfo.FatType == FAT32) 1388 { 1389 Fcb->entry.Fat.FirstCluster = 0; 1390 Fcb->entry.Fat.FirstClusterHigh = 0; 1391 } 1392 else 1393 { 1394 Fcb->entry.Fat.FirstCluster = 0; 1395 } 1396 } 1397 1398 NCluster = Cluster = FirstCluster; 1399 Status = STATUS_SUCCESS; 1400 } 1401 1402 while (NT_SUCCESS(Status) && 0xffffffff != Cluster && Cluster > 1) 1403 { 1404 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE); 1405 WriteCluster(DeviceExt, Cluster, 0); 1406 Cluster = NCluster; 1407 } 1408 1409 if (DeviceExt->FatInfo.FatType == FAT32) 1410 { 1411 FAT32UpdateFreeClustersCount(DeviceExt); 1412 } 1413 } 1414 else 1415 { 1416 UpdateFileSize(FileObject, Fcb, NewSize, ClusterSize, vfatVolumeIsFatX(DeviceExt)); 1417 } 1418 1419 /* Update the on-disk directory entry */ 1420 Fcb->Flags |= FCB_IS_DIRTY; 1421 if (AllocSizeChanged) 1422 { 1423 VfatUpdateEntry(DeviceExt, Fcb); 1424 1425 vfatReportChange(DeviceExt, Fcb, FILE_NOTIFY_CHANGE_SIZE, FILE_ACTION_MODIFIED); 1426 } 1427 return STATUS_SUCCESS; 1428 } 1429 1430 /* 1431 * FUNCTION: Retrieve the specified file information 1432 */ 1433 NTSTATUS 1434 VfatQueryInformation( 1435 PVFAT_IRP_CONTEXT IrpContext) 1436 { 1437 FILE_INFORMATION_CLASS FileInformationClass; 1438 PVFATFCB FCB; 1439 1440 NTSTATUS Status = STATUS_SUCCESS; 1441 PVOID SystemBuffer; 1442 ULONG BufferLength; 1443 1444 /* PRECONDITION */ 1445 ASSERT(IrpContext); 1446 1447 /* INITIALIZATION */ 1448 FileInformationClass = IrpContext->Stack->Parameters.QueryFile.FileInformationClass; 1449 FCB = (PVFATFCB) IrpContext->FileObject->FsContext; 1450 1451 DPRINT("VfatQueryInformation is called for '%s'\n", 1452 FileInformationClass >= FileMaximumInformation - 1 ? "????" : FileInformationClassNames[FileInformationClass]); 1453 1454 if (FCB == NULL) 1455 { 1456 DPRINT1("IRP_MJ_QUERY_INFORMATION without FCB!\n"); 1457 IrpContext->Irp->IoStatus.Information = 0; 1458 return STATUS_INVALID_PARAMETER; 1459 } 1460 1461 SystemBuffer = IrpContext->Irp->AssociatedIrp.SystemBuffer; 1462 BufferLength = IrpContext->Stack->Parameters.QueryFile.Length; 1463 1464 if (!BooleanFlagOn(FCB->Flags, FCB_IS_PAGE_FILE)) 1465 { 1466 if (!ExAcquireResourceSharedLite(&FCB->MainResource, 1467 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT))) 1468 { 1469 return VfatMarkIrpContextForQueue(IrpContext); 1470 } 1471 } 1472 1473 switch (FileInformationClass) 1474 { 1475 case FileStandardInformation: 1476 Status = VfatGetStandardInformation(FCB, 1477 SystemBuffer, 1478 &BufferLength); 1479 break; 1480 1481 case FilePositionInformation: 1482 Status = VfatGetPositionInformation(IrpContext->FileObject, 1483 FCB, 1484 IrpContext->DeviceExt, 1485 SystemBuffer, 1486 &BufferLength); 1487 break; 1488 1489 case FileBasicInformation: 1490 Status = VfatGetBasicInformation(IrpContext->FileObject, 1491 FCB, 1492 IrpContext->DeviceExt, 1493 SystemBuffer, 1494 &BufferLength); 1495 break; 1496 1497 case FileNameInformation: 1498 Status = VfatGetNameInformation(IrpContext->FileObject, 1499 FCB, 1500 IrpContext->DeviceExt, 1501 SystemBuffer, 1502 &BufferLength); 1503 break; 1504 1505 case FileInternalInformation: 1506 Status = VfatGetInternalInformation(FCB, 1507 IrpContext->DeviceExt, 1508 SystemBuffer, 1509 &BufferLength); 1510 break; 1511 1512 case FileNetworkOpenInformation: 1513 Status = VfatGetNetworkOpenInformation(FCB, 1514 IrpContext->DeviceExt, 1515 SystemBuffer, 1516 &BufferLength); 1517 break; 1518 1519 case FileAllInformation: 1520 Status = VfatGetAllInformation(IrpContext->FileObject, 1521 FCB, 1522 IrpContext->DeviceExt, 1523 SystemBuffer, 1524 &BufferLength); 1525 break; 1526 1527 case FileEaInformation: 1528 Status = VfatGetEaInformation(IrpContext->FileObject, 1529 FCB, 1530 IrpContext->DeviceExt, 1531 SystemBuffer, 1532 &BufferLength); 1533 break; 1534 1535 case FileAlternateNameInformation: 1536 Status = STATUS_NOT_IMPLEMENTED; 1537 break; 1538 1539 default: 1540 Status = STATUS_INVALID_PARAMETER; 1541 } 1542 1543 if (!BooleanFlagOn(FCB->Flags, FCB_IS_PAGE_FILE)) 1544 { 1545 ExReleaseResourceLite(&FCB->MainResource); 1546 } 1547 1548 if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_OVERFLOW) 1549 IrpContext->Irp->IoStatus.Information = 1550 IrpContext->Stack->Parameters.QueryFile.Length - BufferLength; 1551 else 1552 IrpContext->Irp->IoStatus.Information = 0; 1553 1554 return Status; 1555 } 1556 1557 /* 1558 * FUNCTION: Retrieve the specified file information 1559 */ 1560 NTSTATUS 1561 VfatSetInformation( 1562 PVFAT_IRP_CONTEXT IrpContext) 1563 { 1564 FILE_INFORMATION_CLASS FileInformationClass; 1565 PVFATFCB FCB; 1566 NTSTATUS Status = STATUS_SUCCESS; 1567 PVOID SystemBuffer; 1568 BOOLEAN LockDir; 1569 1570 /* PRECONDITION */ 1571 ASSERT(IrpContext); 1572 1573 DPRINT("VfatSetInformation(IrpContext %p)\n", IrpContext); 1574 1575 /* INITIALIZATION */ 1576 FileInformationClass = 1577 IrpContext->Stack->Parameters.SetFile.FileInformationClass; 1578 FCB = (PVFATFCB) IrpContext->FileObject->FsContext; 1579 SystemBuffer = IrpContext->Irp->AssociatedIrp.SystemBuffer; 1580 1581 DPRINT("VfatSetInformation is called for '%s'\n", 1582 FileInformationClass >= FileMaximumInformation - 1 ? "????" : FileInformationClassNames[ FileInformationClass]); 1583 1584 DPRINT("FileInformationClass %d\n", FileInformationClass); 1585 DPRINT("SystemBuffer %p\n", SystemBuffer); 1586 1587 if (FCB == NULL) 1588 { 1589 DPRINT1("IRP_MJ_SET_INFORMATION without FCB!\n"); 1590 IrpContext->Irp->IoStatus.Information = 0; 1591 return STATUS_INVALID_PARAMETER; 1592 } 1593 1594 /* Special: We should call MmCanFileBeTruncated here to determine if changing 1595 the file size would be allowed. If not, we bail with the right error. 1596 We must do this before acquiring the lock. */ 1597 if (FileInformationClass == FileEndOfFileInformation) 1598 { 1599 DPRINT("Check for the ability to set file size\n"); 1600 if (!MmCanFileBeTruncated(IrpContext->FileObject->SectionObjectPointer, 1601 (PLARGE_INTEGER)SystemBuffer)) 1602 { 1603 DPRINT("Couldn't set file size!\n"); 1604 IrpContext->Irp->IoStatus.Information = 0; 1605 return STATUS_USER_MAPPED_FILE; 1606 } 1607 DPRINT("Can set file size\n"); 1608 } 1609 1610 LockDir = FALSE; 1611 if (FileInformationClass == FileRenameInformation || FileInformationClass == FileAllocationInformation || 1612 FileInformationClass == FileEndOfFileInformation || FileInformationClass == FileBasicInformation) 1613 { 1614 LockDir = TRUE; 1615 } 1616 1617 if (LockDir) 1618 { 1619 if (!ExAcquireResourceExclusiveLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource, 1620 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT))) 1621 { 1622 return VfatMarkIrpContextForQueue(IrpContext); 1623 } 1624 } 1625 1626 if (!BooleanFlagOn(FCB->Flags, FCB_IS_PAGE_FILE)) 1627 { 1628 if (!ExAcquireResourceExclusiveLite(&FCB->MainResource, 1629 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT))) 1630 { 1631 if (LockDir) 1632 { 1633 ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource); 1634 } 1635 1636 return VfatMarkIrpContextForQueue(IrpContext); 1637 } 1638 } 1639 1640 switch (FileInformationClass) 1641 { 1642 case FilePositionInformation: 1643 Status = VfatSetPositionInformation(IrpContext->FileObject, 1644 SystemBuffer); 1645 break; 1646 1647 case FileDispositionInformation: 1648 Status = VfatSetDispositionInformation(IrpContext->FileObject, 1649 FCB, 1650 IrpContext->DeviceExt, 1651 SystemBuffer); 1652 break; 1653 1654 case FileAllocationInformation: 1655 case FileEndOfFileInformation: 1656 Status = VfatSetAllocationSizeInformation(IrpContext->FileObject, 1657 FCB, 1658 IrpContext->DeviceExt, 1659 (PLARGE_INTEGER)SystemBuffer); 1660 break; 1661 1662 case FileBasicInformation: 1663 Status = VfatSetBasicInformation(IrpContext->FileObject, 1664 FCB, 1665 IrpContext->DeviceExt, 1666 SystemBuffer); 1667 break; 1668 1669 case FileRenameInformation: 1670 Status = VfatSetRenameInformation(IrpContext->FileObject, 1671 FCB, 1672 IrpContext->DeviceExt, 1673 SystemBuffer, 1674 IrpContext->Stack->Parameters.SetFile.FileObject); 1675 break; 1676 1677 default: 1678 Status = STATUS_NOT_SUPPORTED; 1679 } 1680 1681 if (!BooleanFlagOn(FCB->Flags, FCB_IS_PAGE_FILE)) 1682 { 1683 ExReleaseResourceLite(&FCB->MainResource); 1684 } 1685 1686 if (LockDir) 1687 { 1688 ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource); 1689 } 1690 1691 IrpContext->Irp->IoStatus.Information = 0; 1692 return Status; 1693 } 1694 1695 /* EOF */ 1696