1 /* 2 * PROJECT: VFAT Filesystem 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: File creation routines 5 * COPYRIGHT: Copyright 1998 Jason Filby <jasonfilby@yahoo.com> 6 * Copyright 2010-2019 Pierre Schweitzer <pierre@reactos.org> 7 */ 8 9 /* INCLUDES *****************************************************************/ 10 11 #include "vfat.h" 12 13 #define NDEBUG 14 #include <debug.h> 15 16 /* FUNCTIONS *****************************************************************/ 17 18 VOID 19 vfat8Dot3ToString( 20 PFAT_DIR_ENTRY pEntry, 21 PUNICODE_STRING NameU) 22 { 23 OEM_STRING StringA; 24 USHORT Length; 25 CHAR cString[12]; 26 27 RtlCopyMemory(cString, pEntry->ShortName, 11); 28 cString[11] = 0; 29 if (cString[0] == 0x05) 30 { 31 cString[0] = 0xe5; 32 } 33 34 StringA.Buffer = cString; 35 for (StringA.Length = 0; 36 StringA.Length < 8 && StringA.Buffer[StringA.Length] != ' '; 37 StringA.Length++); 38 StringA.MaximumLength = StringA.Length; 39 40 RtlOemStringToUnicodeString(NameU, &StringA, FALSE); 41 42 if (BooleanFlagOn(pEntry->lCase, VFAT_CASE_LOWER_BASE)) 43 { 44 RtlDowncaseUnicodeString(NameU, NameU, FALSE); 45 } 46 47 if (cString[8] != ' ') 48 { 49 Length = NameU->Length; 50 NameU->Buffer += Length / sizeof(WCHAR); 51 if (!FAT_ENTRY_VOLUME(pEntry)) 52 { 53 Length += sizeof(WCHAR); 54 NameU->Buffer[0] = L'.'; 55 NameU->Buffer++; 56 } 57 NameU->Length = 0; 58 NameU->MaximumLength -= Length; 59 60 StringA.Buffer = &cString[8]; 61 for (StringA.Length = 0; 62 StringA.Length < 3 && StringA.Buffer[StringA.Length] != ' '; 63 StringA.Length++); 64 StringA.MaximumLength = StringA.Length; 65 RtlOemStringToUnicodeString(NameU, &StringA, FALSE); 66 if (BooleanFlagOn(pEntry->lCase, VFAT_CASE_LOWER_EXT)) 67 { 68 RtlDowncaseUnicodeString(NameU, NameU, FALSE); 69 } 70 NameU->Buffer -= Length / sizeof(WCHAR); 71 NameU->Length += Length; 72 NameU->MaximumLength += Length; 73 } 74 75 NameU->Buffer[NameU->Length / sizeof(WCHAR)] = 0; 76 DPRINT("'%wZ'\n", NameU); 77 } 78 79 /* 80 * FUNCTION: Find a file 81 */ 82 NTSTATUS 83 FindFile( 84 PDEVICE_EXTENSION DeviceExt, 85 PVFATFCB Parent, 86 PUNICODE_STRING FileToFindU, 87 PVFAT_DIRENTRY_CONTEXT DirContext, 88 BOOLEAN First) 89 { 90 PWCHAR PathNameBuffer; 91 USHORT PathNameBufferLength; 92 NTSTATUS Status; 93 PVOID Context = NULL; 94 PVOID Page; 95 PVFATFCB rcFcb; 96 BOOLEAN Found; 97 UNICODE_STRING PathNameU; 98 UNICODE_STRING FileToFindUpcase; 99 BOOLEAN WildCard; 100 BOOLEAN IsFatX = vfatVolumeIsFatX(DeviceExt); 101 102 DPRINT("FindFile(Parent %p, FileToFind '%wZ', DirIndex: %u)\n", 103 Parent, FileToFindU, DirContext->DirIndex); 104 DPRINT("FindFile: Path %wZ\n",&Parent->PathNameU); 105 106 PathNameBufferLength = LONGNAME_MAX_LENGTH * sizeof(WCHAR); 107 PathNameBuffer = ExAllocatePoolWithTag(NonPagedPool, PathNameBufferLength + sizeof(WCHAR), TAG_NAME); 108 if (!PathNameBuffer) 109 { 110 return STATUS_INSUFFICIENT_RESOURCES; 111 } 112 113 PathNameU.Buffer = PathNameBuffer; 114 PathNameU.Length = 0; 115 PathNameU.MaximumLength = PathNameBufferLength; 116 117 DirContext->LongNameU.Length = 0; 118 DirContext->ShortNameU.Length = 0; 119 120 WildCard = FsRtlDoesNameContainWildCards(FileToFindU); 121 122 if (WildCard == FALSE) 123 { 124 /* if there is no '*?' in the search name, than look first for an existing fcb */ 125 RtlCopyUnicodeString(&PathNameU, &Parent->PathNameU); 126 if (!vfatFCBIsRoot(Parent)) 127 { 128 PathNameU.Buffer[PathNameU.Length / sizeof(WCHAR)] = L'\\'; 129 PathNameU.Length += sizeof(WCHAR); 130 } 131 RtlAppendUnicodeStringToString(&PathNameU, FileToFindU); 132 PathNameU.Buffer[PathNameU.Length / sizeof(WCHAR)] = 0; 133 rcFcb = vfatGrabFCBFromTable(DeviceExt, &PathNameU); 134 if (rcFcb) 135 { 136 ULONG startIndex = rcFcb->startIndex; 137 if (IsFatX && !vfatFCBIsRoot(Parent)) 138 { 139 startIndex += 2; 140 } 141 if(startIndex >= DirContext->DirIndex) 142 { 143 RtlCopyUnicodeString(&DirContext->LongNameU, &rcFcb->LongNameU); 144 RtlCopyUnicodeString(&DirContext->ShortNameU, &rcFcb->ShortNameU); 145 RtlCopyMemory(&DirContext->DirEntry, &rcFcb->entry, sizeof(DIR_ENTRY)); 146 DirContext->StartIndex = rcFcb->startIndex; 147 DirContext->DirIndex = rcFcb->dirIndex; 148 DPRINT("FindFile: new Name %wZ, DirIndex %u (%u)\n", 149 &DirContext->LongNameU, DirContext->DirIndex, DirContext->StartIndex); 150 Status = STATUS_SUCCESS; 151 } 152 else 153 { 154 DPRINT("FCB not found for %wZ\n", &PathNameU); 155 Status = STATUS_UNSUCCESSFUL; 156 } 157 vfatReleaseFCB(DeviceExt, rcFcb); 158 ExFreePoolWithTag(PathNameBuffer, TAG_NAME); 159 return Status; 160 } 161 } 162 163 /* FsRtlIsNameInExpression need the searched string to be upcase, 164 * even if IgnoreCase is specified */ 165 Status = RtlUpcaseUnicodeString(&FileToFindUpcase, FileToFindU, TRUE); 166 if (!NT_SUCCESS(Status)) 167 { 168 ExFreePoolWithTag(PathNameBuffer, TAG_NAME); 169 return Status; 170 } 171 172 while (TRUE) 173 { 174 Status = VfatGetNextDirEntry(DeviceExt, &Context, &Page, Parent, DirContext, First); 175 First = FALSE; 176 if (Status == STATUS_NO_MORE_ENTRIES) 177 { 178 break; 179 } 180 if (ENTRY_VOLUME(IsFatX, &DirContext->DirEntry)) 181 { 182 DirContext->DirIndex++; 183 continue; 184 } 185 if (DirContext->LongNameU.Length == 0 || 186 DirContext->ShortNameU.Length == 0) 187 { 188 DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n"); 189 if (VfatGlobalData->Flags & VFAT_BREAK_ON_CORRUPTION) 190 { 191 ASSERT(DirContext->LongNameU.Length != 0 && 192 DirContext->ShortNameU.Length != 0); 193 } 194 DirContext->DirIndex++; 195 continue; 196 } 197 if (WildCard) 198 { 199 Found = FsRtlIsNameInExpression(&FileToFindUpcase, &DirContext->LongNameU, TRUE, NULL) || 200 FsRtlIsNameInExpression(&FileToFindUpcase, &DirContext->ShortNameU, TRUE, NULL); 201 } 202 else 203 { 204 Found = FsRtlAreNamesEqual(&DirContext->LongNameU, FileToFindU, TRUE, NULL) || 205 FsRtlAreNamesEqual(&DirContext->ShortNameU, FileToFindU, TRUE, NULL); 206 } 207 208 if (Found) 209 { 210 if (WildCard) 211 { 212 RtlCopyUnicodeString(&PathNameU, &Parent->PathNameU); 213 if (!vfatFCBIsRoot(Parent)) 214 { 215 PathNameU.Buffer[PathNameU.Length / sizeof(WCHAR)] = L'\\'; 216 PathNameU.Length += sizeof(WCHAR); 217 } 218 RtlAppendUnicodeStringToString(&PathNameU, &DirContext->LongNameU); 219 PathNameU.Buffer[PathNameU.Length / sizeof(WCHAR)] = 0; 220 rcFcb = vfatGrabFCBFromTable(DeviceExt, &PathNameU); 221 if (rcFcb != NULL) 222 { 223 RtlCopyMemory(&DirContext->DirEntry, &rcFcb->entry, sizeof(DIR_ENTRY)); 224 vfatReleaseFCB(DeviceExt, rcFcb); 225 } 226 } 227 DPRINT("%u\n", DirContext->LongNameU.Length); 228 DPRINT("FindFile: new Name %wZ, DirIndex %u\n", 229 &DirContext->LongNameU, DirContext->DirIndex); 230 231 if (Context) 232 { 233 CcUnpinData(Context); 234 } 235 RtlFreeUnicodeString(&FileToFindUpcase); 236 ExFreePoolWithTag(PathNameBuffer, TAG_NAME); 237 return STATUS_SUCCESS; 238 } 239 DirContext->DirIndex++; 240 } 241 242 if (Context) 243 { 244 CcUnpinData(Context); 245 } 246 247 RtlFreeUnicodeString(&FileToFindUpcase); 248 ExFreePoolWithTag(PathNameBuffer, TAG_NAME); 249 return Status; 250 } 251 252 /* 253 * FUNCTION: Opens a file 254 */ 255 static 256 NTSTATUS 257 VfatOpenFile( 258 PDEVICE_EXTENSION DeviceExt, 259 PUNICODE_STRING PathNameU, 260 PFILE_OBJECT FileObject, 261 ULONG RequestedDisposition, 262 ULONG RequestedOptions, 263 PVFATFCB *ParentFcb) 264 { 265 PVFATFCB Fcb; 266 NTSTATUS Status; 267 268 DPRINT("VfatOpenFile(%p, '%wZ', %p, %p)\n", DeviceExt, PathNameU, FileObject, ParentFcb); 269 270 if (FileObject->RelatedFileObject) 271 { 272 DPRINT("'%wZ'\n", &FileObject->RelatedFileObject->FileName); 273 274 *ParentFcb = FileObject->RelatedFileObject->FsContext; 275 } 276 else 277 { 278 *ParentFcb = NULL; 279 } 280 281 if (!DeviceExt->FatInfo.FixedMedia) 282 { 283 Status = VfatBlockDeviceIoControl(DeviceExt->StorageDevice, 284 IOCTL_DISK_CHECK_VERIFY, 285 NULL, 286 0, 287 NULL, 288 0, 289 FALSE); 290 if (!NT_SUCCESS(Status)) 291 { 292 DPRINT("Status %lx\n", Status); 293 *ParentFcb = NULL; 294 return Status; 295 } 296 } 297 298 if (*ParentFcb) 299 { 300 vfatGrabFCB(DeviceExt, *ParentFcb); 301 } 302 303 /* try first to find an existing FCB in memory */ 304 DPRINT("Checking for existing FCB in memory\n"); 305 306 Status = vfatGetFCBForFile(DeviceExt, ParentFcb, &Fcb, PathNameU); 307 if (!NT_SUCCESS(Status)) 308 { 309 DPRINT ("Could not make a new FCB, status: %x\n", Status); 310 return Status; 311 } 312 313 /* Fail, if we try to overwrite an existing directory */ 314 if ((!BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE) && vfatFCBIsDirectory(Fcb)) && 315 (RequestedDisposition == FILE_OVERWRITE || 316 RequestedDisposition == FILE_OVERWRITE_IF || 317 RequestedDisposition == FILE_SUPERSEDE)) 318 { 319 vfatReleaseFCB(DeviceExt, Fcb); 320 return STATUS_OBJECT_NAME_COLLISION; 321 } 322 323 if (BooleanFlagOn(Fcb->Flags, FCB_DELETE_PENDING)) 324 { 325 vfatReleaseFCB(DeviceExt, Fcb); 326 return STATUS_DELETE_PENDING; 327 } 328 329 /* Fail, if we try to overwrite a read-only file */ 330 if (vfatFCBIsReadOnly(Fcb) && 331 (RequestedDisposition == FILE_OVERWRITE || 332 RequestedDisposition == FILE_OVERWRITE_IF)) 333 { 334 vfatReleaseFCB(DeviceExt, Fcb); 335 return STATUS_ACCESS_DENIED; 336 } 337 338 if (vfatFCBIsReadOnly(Fcb) && 339 (RequestedOptions & FILE_DELETE_ON_CLOSE)) 340 { 341 vfatReleaseFCB(DeviceExt, Fcb); 342 return STATUS_CANNOT_DELETE; 343 } 344 345 if ((vfatFCBIsRoot(Fcb) || IsDotOrDotDot(&Fcb->LongNameU)) && 346 BooleanFlagOn(RequestedOptions, FILE_DELETE_ON_CLOSE)) 347 { 348 // we cannot delete a '.', '..' or the root directory 349 vfatReleaseFCB(DeviceExt, Fcb); 350 return STATUS_CANNOT_DELETE; 351 } 352 353 /* If that one was marked for closing, remove it */ 354 if (BooleanFlagOn(Fcb->Flags, FCB_DELAYED_CLOSE)) 355 { 356 BOOLEAN ConcurrentDeletion; 357 PVFAT_CLOSE_CONTEXT CloseContext; 358 359 /* Get the context */ 360 CloseContext = Fcb->CloseContext; 361 /* Is someone already taking over? */ 362 if (CloseContext != NULL) 363 { 364 ConcurrentDeletion = FALSE; 365 /* Lock list */ 366 ExAcquireFastMutex(&VfatGlobalData->CloseMutex); 367 /* Check whether it was already removed, if not, do it */ 368 if (!IsListEmpty(&CloseContext->CloseListEntry)) 369 { 370 RemoveEntryList(&CloseContext->CloseListEntry); 371 --VfatGlobalData->CloseCount; 372 ConcurrentDeletion = TRUE; 373 } 374 ExReleaseFastMutex(&VfatGlobalData->CloseMutex); 375 376 /* It's not delayed anymore! */ 377 ClearFlag(Fcb->Flags, FCB_DELAYED_CLOSE); 378 /* Release the extra reference (would have been removed by IRP_MJ_CLOSE) */ 379 vfatReleaseFCB(DeviceExt, Fcb); 380 Fcb->CloseContext = NULL; 381 /* If no concurrent deletion, free work item */ 382 if (!ConcurrentDeletion) 383 { 384 ExFreeToPagedLookasideList(&VfatGlobalData->CloseContextLookasideList, CloseContext); 385 } 386 } 387 388 DPRINT("Reusing delayed close FCB for %wZ\n", &Fcb->PathNameU); 389 } 390 391 DPRINT("Attaching FCB to fileObject\n"); 392 Status = vfatAttachFCBToFileObject(DeviceExt, Fcb, FileObject); 393 if (!NT_SUCCESS(Status)) 394 { 395 vfatReleaseFCB(DeviceExt, Fcb); 396 } 397 return Status; 398 } 399 400 /* 401 * FUNCTION: Create or open a file 402 */ 403 static NTSTATUS 404 VfatCreateFile( 405 PDEVICE_OBJECT DeviceObject, 406 PIRP Irp) 407 { 408 PIO_STACK_LOCATION Stack; 409 PFILE_OBJECT FileObject; 410 NTSTATUS Status = STATUS_SUCCESS; 411 PDEVICE_EXTENSION DeviceExt; 412 ULONG RequestedDisposition, RequestedOptions; 413 PVFATFCB pFcb = NULL; 414 PVFATFCB ParentFcb = NULL; 415 PVFATCCB pCcb = NULL; 416 PWCHAR c, last; 417 BOOLEAN PagingFileCreate; 418 BOOLEAN Dots; 419 BOOLEAN OpenTargetDir; 420 BOOLEAN TrailingBackslash; 421 UNICODE_STRING FileNameU; 422 UNICODE_STRING PathNameU; 423 ULONG Attributes; 424 425 /* Unpack the various parameters. */ 426 Stack = IoGetCurrentIrpStackLocation(Irp); 427 RequestedDisposition = ((Stack->Parameters.Create.Options >> 24) & 0xff); 428 RequestedOptions = Stack->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS; 429 PagingFileCreate = BooleanFlagOn(Stack->Flags, SL_OPEN_PAGING_FILE); 430 OpenTargetDir = BooleanFlagOn(Stack->Flags, SL_OPEN_TARGET_DIRECTORY); 431 432 FileObject = Stack->FileObject; 433 DeviceExt = DeviceObject->DeviceExtension; 434 435 if (BooleanFlagOn(Stack->Parameters.Create.Options, FILE_OPEN_BY_FILE_ID)) 436 { 437 return STATUS_NOT_IMPLEMENTED; 438 } 439 440 /* Check their validity. */ 441 if (BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE) && 442 RequestedDisposition == FILE_SUPERSEDE) 443 { 444 return STATUS_INVALID_PARAMETER; 445 } 446 447 if (BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE) && 448 BooleanFlagOn(RequestedOptions, FILE_NON_DIRECTORY_FILE)) 449 { 450 return STATUS_INVALID_PARAMETER; 451 } 452 453 /* Deny create if the volume is locked */ 454 if (BooleanFlagOn(DeviceExt->Flags, VCB_VOLUME_LOCKED)) 455 { 456 return STATUS_ACCESS_DENIED; 457 } 458 459 /* This a open operation for the volume itself */ 460 if (FileObject->FileName.Length == 0 && 461 (FileObject->RelatedFileObject == NULL || 462 FileObject->RelatedFileObject->FsContext2 != NULL || 463 FileObject->RelatedFileObject->FsContext == DeviceExt->VolumeFcb)) 464 { 465 DPRINT("Volume opening\n"); 466 467 if (RequestedDisposition != FILE_OPEN && 468 RequestedDisposition != FILE_OPEN_IF) 469 { 470 return STATUS_ACCESS_DENIED; 471 } 472 473 if (BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE) && 474 (FileObject->RelatedFileObject == NULL || FileObject->RelatedFileObject->FsContext2 == NULL || FileObject->RelatedFileObject->FsContext == DeviceExt->VolumeFcb)) 475 { 476 return STATUS_NOT_A_DIRECTORY; 477 } 478 479 if (OpenTargetDir) 480 { 481 return STATUS_INVALID_PARAMETER; 482 } 483 484 if (BooleanFlagOn(RequestedOptions, FILE_DELETE_ON_CLOSE)) 485 { 486 return STATUS_CANNOT_DELETE; 487 } 488 489 vfatAddToStat(DeviceExt, Fat.CreateHits, 1); 490 491 pFcb = DeviceExt->VolumeFcb; 492 493 if (pFcb->OpenHandleCount == 0) 494 { 495 IoSetShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess, 496 Stack->Parameters.Create.ShareAccess, 497 FileObject, 498 &pFcb->FCBShareAccess); 499 } 500 else 501 { 502 Status = IoCheckShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess, 503 Stack->Parameters.Create.ShareAccess, 504 FileObject, 505 &pFcb->FCBShareAccess, 506 TRUE); 507 if (!NT_SUCCESS(Status)) 508 { 509 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1); 510 return Status; 511 } 512 } 513 514 vfatAttachFCBToFileObject(DeviceExt, pFcb, FileObject); 515 DeviceExt->OpenHandleCount++; 516 pFcb->OpenHandleCount++; 517 vfatAddToStat(DeviceExt, Fat.SuccessfulCreates, 1); 518 519 Irp->IoStatus.Information = FILE_OPENED; 520 return STATUS_SUCCESS; 521 } 522 523 if (FileObject->RelatedFileObject != NULL && 524 FileObject->RelatedFileObject->FsContext == DeviceExt->VolumeFcb) 525 { 526 ASSERT(FileObject->FileName.Length != 0); 527 return STATUS_OBJECT_PATH_NOT_FOUND; 528 } 529 530 /* Check for illegal characters and illegal dot sequences in the file name */ 531 PathNameU = FileObject->FileName; 532 c = PathNameU.Buffer + PathNameU.Length / sizeof(WCHAR); 533 last = c - 1; 534 535 Dots = TRUE; 536 while (c-- > PathNameU.Buffer) 537 { 538 if (*c == L'\\' || c == PathNameU.Buffer) 539 { 540 if (Dots && last > c) 541 { 542 return STATUS_OBJECT_NAME_INVALID; 543 } 544 if (*c == L'\\' && (c - 1) > PathNameU.Buffer && 545 *(c - 1) == L'\\') 546 { 547 return STATUS_OBJECT_NAME_INVALID; 548 } 549 550 last = c - 1; 551 Dots = TRUE; 552 } 553 else if (*c != L'.') 554 { 555 Dots = FALSE; 556 } 557 558 if (*c != '\\' && vfatIsLongIllegal(*c)) 559 { 560 return STATUS_OBJECT_NAME_INVALID; 561 } 562 } 563 564 /* Check if we try to open target directory of root dir */ 565 if (OpenTargetDir && FileObject->RelatedFileObject == NULL && PathNameU.Length == sizeof(WCHAR) && 566 PathNameU.Buffer[0] == L'\\') 567 { 568 return STATUS_INVALID_PARAMETER; 569 } 570 571 if (FileObject->RelatedFileObject && PathNameU.Length >= sizeof(WCHAR) && PathNameU.Buffer[0] == L'\\') 572 { 573 return STATUS_OBJECT_NAME_INVALID; 574 } 575 576 TrailingBackslash = FALSE; 577 if (PathNameU.Length > sizeof(WCHAR) && PathNameU.Buffer[PathNameU.Length/sizeof(WCHAR)-1] == L'\\') 578 { 579 PathNameU.Length -= sizeof(WCHAR); 580 TrailingBackslash = TRUE; 581 } 582 583 if (PathNameU.Length > sizeof(WCHAR) && PathNameU.Buffer[PathNameU.Length/sizeof(WCHAR)-1] == L'\\') 584 { 585 return STATUS_OBJECT_NAME_INVALID; 586 } 587 588 /* Try opening the file. */ 589 if (!OpenTargetDir) 590 { 591 vfatAddToStat(DeviceExt, Fat.CreateHits, 1); 592 593 Status = VfatOpenFile(DeviceExt, &PathNameU, FileObject, RequestedDisposition, RequestedOptions, &ParentFcb); 594 } 595 else 596 { 597 PVFATFCB TargetFcb; 598 LONG idx, FileNameLen; 599 600 vfatAddToStat(DeviceExt, Fat.CreateHits, 1); 601 602 ParentFcb = (FileObject->RelatedFileObject != NULL) ? FileObject->RelatedFileObject->FsContext : NULL; 603 if (ParentFcb) 604 { 605 vfatGrabFCB(DeviceExt, ParentFcb); 606 } 607 608 Status = vfatGetFCBForFile(DeviceExt, &ParentFcb, &TargetFcb, &PathNameU); 609 if (NT_SUCCESS(Status)) 610 { 611 vfatReleaseFCB(DeviceExt, TargetFcb); 612 Irp->IoStatus.Information = FILE_EXISTS; 613 } 614 else 615 { 616 Irp->IoStatus.Information = FILE_DOES_NOT_EXIST; 617 } 618 619 idx = FileObject->FileName.Length / sizeof(WCHAR) - 1; 620 621 /* Skip trailing \ - if any */ 622 if (PathNameU.Buffer[idx] == L'\\') 623 { 624 --idx; 625 PathNameU.Length -= sizeof(WCHAR); 626 } 627 628 /* Get file name */ 629 while (idx >= 0 && PathNameU.Buffer[idx] != L'\\') 630 { 631 --idx; 632 } 633 634 if (idx > 0 || PathNameU.Buffer[0] == L'\\') 635 { 636 /* We don't want to include / in the name */ 637 FileNameLen = PathNameU.Length - ((idx + 1) * sizeof(WCHAR)); 638 639 /* Update FO just to keep file name */ 640 /* Skip first slash */ 641 ++idx; 642 FileObject->FileName.Length = FileNameLen; 643 RtlMoveMemory(&PathNameU.Buffer[0], &PathNameU.Buffer[idx], FileObject->FileName.Length); 644 #if 0 645 /* Terminate the string at the last backslash */ 646 PathNameU.Buffer[idx + 1] = UNICODE_NULL; 647 PathNameU.Length = (idx + 1) * sizeof(WCHAR); 648 PathNameU.MaximumLength = PathNameU.Length + sizeof(WCHAR); 649 650 /* Update the file object as well */ 651 FileObject->FileName.Length = PathNameU.Length; 652 FileObject->FileName.MaximumLength = PathNameU.MaximumLength; 653 #endif 654 } 655 else 656 { 657 /* This is a relative open and we have only the filename, so open the parent directory 658 * It is in RelatedFileObject 659 */ 660 ASSERT(FileObject->RelatedFileObject != NULL); 661 662 /* No need to modify the FO, it already has the name */ 663 } 664 665 /* We're done with opening! */ 666 if (ParentFcb != NULL) 667 { 668 Status = vfatAttachFCBToFileObject(DeviceExt, ParentFcb, FileObject); 669 } 670 671 if (NT_SUCCESS(Status)) 672 { 673 pFcb = FileObject->FsContext; 674 ASSERT(pFcb == ParentFcb); 675 676 if (pFcb->OpenHandleCount == 0) 677 { 678 IoSetShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess, 679 Stack->Parameters.Create.ShareAccess, 680 FileObject, 681 &pFcb->FCBShareAccess); 682 } 683 else 684 { 685 Status = IoCheckShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess, 686 Stack->Parameters.Create.ShareAccess, 687 FileObject, 688 &pFcb->FCBShareAccess, 689 FALSE); 690 if (!NT_SUCCESS(Status)) 691 { 692 VfatCloseFile(DeviceExt, FileObject); 693 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1); 694 return Status; 695 } 696 } 697 698 pCcb = FileObject->FsContext2; 699 if (BooleanFlagOn(RequestedOptions, FILE_DELETE_ON_CLOSE)) 700 { 701 pCcb->Flags |= CCB_DELETE_ON_CLOSE; 702 } 703 704 pFcb->OpenHandleCount++; 705 DeviceExt->OpenHandleCount++; 706 } 707 else if (ParentFcb != NULL) 708 { 709 vfatReleaseFCB(DeviceExt, ParentFcb); 710 } 711 712 if (NT_SUCCESS(Status)) 713 { 714 vfatAddToStat(DeviceExt, Fat.SuccessfulCreates, 1); 715 } 716 else 717 { 718 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1); 719 } 720 721 return Status; 722 } 723 724 /* 725 * If the directory containing the file to open doesn't exist then 726 * fail immediately 727 */ 728 if (Status == STATUS_OBJECT_PATH_NOT_FOUND || 729 Status == STATUS_INVALID_PARAMETER || 730 Status == STATUS_DELETE_PENDING || 731 Status == STATUS_ACCESS_DENIED || 732 Status == STATUS_OBJECT_NAME_COLLISION) 733 { 734 if (ParentFcb) 735 { 736 vfatReleaseFCB(DeviceExt, ParentFcb); 737 } 738 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1); 739 return Status; 740 } 741 742 if (!NT_SUCCESS(Status) && ParentFcb == NULL) 743 { 744 DPRINT1("VfatOpenFile failed for '%wZ', status %x\n", &PathNameU, Status); 745 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1); 746 return Status; 747 } 748 749 Attributes = (Stack->Parameters.Create.FileAttributes & (FILE_ATTRIBUTE_ARCHIVE | 750 FILE_ATTRIBUTE_SYSTEM | 751 FILE_ATTRIBUTE_HIDDEN | 752 FILE_ATTRIBUTE_DIRECTORY | 753 FILE_ATTRIBUTE_READONLY)); 754 755 /* If the file open failed then create the required file */ 756 if (!NT_SUCCESS (Status)) 757 { 758 if (RequestedDisposition == FILE_CREATE || 759 RequestedDisposition == FILE_OPEN_IF || 760 RequestedDisposition == FILE_OVERWRITE_IF || 761 RequestedDisposition == FILE_SUPERSEDE) 762 { 763 if (!BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE)) 764 { 765 if (TrailingBackslash) 766 { 767 vfatReleaseFCB(DeviceExt, ParentFcb); 768 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1); 769 return STATUS_OBJECT_NAME_INVALID; 770 } 771 Attributes |= FILE_ATTRIBUTE_ARCHIVE; 772 } 773 vfatSplitPathName(&PathNameU, NULL, &FileNameU); 774 775 if (IsDotOrDotDot(&FileNameU)) 776 { 777 vfatReleaseFCB(DeviceExt, ParentFcb); 778 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1); 779 return STATUS_OBJECT_NAME_INVALID; 780 } 781 Status = VfatAddEntry(DeviceExt, &FileNameU, &pFcb, ParentFcb, RequestedOptions, 782 Attributes, NULL); 783 vfatReleaseFCB(DeviceExt, ParentFcb); 784 if (NT_SUCCESS(Status)) 785 { 786 Status = vfatAttachFCBToFileObject(DeviceExt, pFcb, FileObject); 787 if (!NT_SUCCESS(Status)) 788 { 789 vfatReleaseFCB(DeviceExt, pFcb); 790 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1); 791 return Status; 792 } 793 794 Irp->IoStatus.Information = FILE_CREATED; 795 VfatSetAllocationSizeInformation(FileObject, 796 pFcb, 797 DeviceExt, 798 &Irp->Overlay.AllocationSize); 799 VfatSetExtendedAttributes(FileObject, 800 Irp->AssociatedIrp.SystemBuffer, 801 Stack->Parameters.Create.EaLength); 802 803 if (PagingFileCreate) 804 { 805 pFcb->Flags |= FCB_IS_PAGE_FILE; 806 SetFlag(DeviceExt->Flags, VCB_IS_SYS_OR_HAS_PAGE); 807 } 808 } 809 else 810 { 811 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1); 812 return Status; 813 } 814 } 815 else 816 { 817 vfatReleaseFCB(DeviceExt, ParentFcb); 818 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1); 819 return Status; 820 } 821 } 822 else 823 { 824 if (ParentFcb) 825 { 826 vfatReleaseFCB(DeviceExt, ParentFcb); 827 } 828 829 pFcb = FileObject->FsContext; 830 831 /* Otherwise fail if the caller wanted to create a new file */ 832 if (RequestedDisposition == FILE_CREATE) 833 { 834 VfatCloseFile(DeviceExt, FileObject); 835 if (TrailingBackslash && !vfatFCBIsDirectory(pFcb)) 836 { 837 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1); 838 return STATUS_OBJECT_NAME_INVALID; 839 } 840 Irp->IoStatus.Information = FILE_EXISTS; 841 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1); 842 return STATUS_OBJECT_NAME_COLLISION; 843 } 844 845 if (pFcb->OpenHandleCount != 0) 846 { 847 Status = IoCheckShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess, 848 Stack->Parameters.Create.ShareAccess, 849 FileObject, 850 &pFcb->FCBShareAccess, 851 FALSE); 852 if (!NT_SUCCESS(Status)) 853 { 854 VfatCloseFile(DeviceExt, FileObject); 855 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1); 856 return Status; 857 } 858 } 859 860 /* 861 * Check the file has the requested attributes 862 */ 863 if (BooleanFlagOn(RequestedOptions, FILE_NON_DIRECTORY_FILE) && 864 vfatFCBIsDirectory(pFcb)) 865 { 866 VfatCloseFile (DeviceExt, FileObject); 867 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1); 868 return STATUS_FILE_IS_A_DIRECTORY; 869 } 870 if (BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE) && 871 !vfatFCBIsDirectory(pFcb)) 872 { 873 VfatCloseFile (DeviceExt, FileObject); 874 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1); 875 return STATUS_NOT_A_DIRECTORY; 876 } 877 if (TrailingBackslash && !vfatFCBIsDirectory(pFcb)) 878 { 879 VfatCloseFile (DeviceExt, FileObject); 880 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1); 881 return STATUS_OBJECT_NAME_INVALID; 882 } 883 #ifndef USE_ROS_CC_AND_FS 884 if (!vfatFCBIsDirectory(pFcb)) 885 { 886 if (BooleanFlagOn(Stack->Parameters.Create.SecurityContext->DesiredAccess, FILE_WRITE_DATA) || 887 RequestedDisposition == FILE_OVERWRITE || 888 RequestedDisposition == FILE_OVERWRITE_IF || 889 (RequestedOptions & FILE_DELETE_ON_CLOSE)) 890 { 891 if (!MmFlushImageSection(&pFcb->SectionObjectPointers, MmFlushForWrite)) 892 { 893 DPRINT1("%wZ\n", &pFcb->PathNameU); 894 DPRINT1("%d %d %d\n", Stack->Parameters.Create.SecurityContext->DesiredAccess & FILE_WRITE_DATA, 895 RequestedDisposition == FILE_OVERWRITE, RequestedDisposition == FILE_OVERWRITE_IF); 896 VfatCloseFile (DeviceExt, FileObject); 897 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1); 898 return (BooleanFlagOn(RequestedOptions, FILE_DELETE_ON_CLOSE)) ? STATUS_CANNOT_DELETE 899 : STATUS_SHARING_VIOLATION; 900 } 901 } 902 } 903 #endif 904 if (PagingFileCreate) 905 { 906 /* FIXME: 907 * Do more checking for page files. It is possible, 908 * that the file was opened and closed previously 909 * as a normal cached file. In this case, the cache 910 * manager has referenced the fileobject and the fcb 911 * is held in memory. Try to remove the fileobject 912 * from cache manager and use the fcb. 913 */ 914 if (pFcb->RefCount > 1) 915 { 916 if(!BooleanFlagOn(pFcb->Flags, FCB_IS_PAGE_FILE)) 917 { 918 VfatCloseFile(DeviceExt, FileObject); 919 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1); 920 return STATUS_INVALID_PARAMETER; 921 } 922 } 923 else 924 { 925 pFcb->Flags |= FCB_IS_PAGE_FILE; 926 SetFlag(DeviceExt->Flags, VCB_IS_SYS_OR_HAS_PAGE); 927 } 928 } 929 else 930 { 931 if (BooleanFlagOn(pFcb->Flags, FCB_IS_PAGE_FILE)) 932 { 933 VfatCloseFile(DeviceExt, FileObject); 934 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1); 935 return STATUS_INVALID_PARAMETER; 936 } 937 } 938 939 if (RequestedDisposition == FILE_OVERWRITE || 940 RequestedDisposition == FILE_OVERWRITE_IF || 941 RequestedDisposition == FILE_SUPERSEDE) 942 { 943 if ((BooleanFlagOn(*pFcb->Attributes, FILE_ATTRIBUTE_HIDDEN) && !BooleanFlagOn(Attributes, FILE_ATTRIBUTE_HIDDEN)) || 944 (BooleanFlagOn(*pFcb->Attributes, FILE_ATTRIBUTE_SYSTEM) && !BooleanFlagOn(Attributes, FILE_ATTRIBUTE_SYSTEM))) 945 { 946 VfatCloseFile(DeviceExt, FileObject); 947 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1); 948 return STATUS_ACCESS_DENIED; 949 } 950 951 if (!vfatFCBIsDirectory(pFcb)) 952 { 953 LARGE_INTEGER SystemTime; 954 955 if (RequestedDisposition == FILE_SUPERSEDE) 956 { 957 *pFcb->Attributes = Attributes; 958 } 959 else 960 { 961 *pFcb->Attributes |= Attributes; 962 } 963 *pFcb->Attributes |= FILE_ATTRIBUTE_ARCHIVE; 964 965 KeQuerySystemTime(&SystemTime); 966 if (vfatVolumeIsFatX(DeviceExt)) 967 { 968 FsdSystemTimeToDosDateTime(DeviceExt, 969 &SystemTime, &pFcb->entry.FatX.UpdateDate, 970 &pFcb->entry.FatX.UpdateTime); 971 } 972 else 973 { 974 FsdSystemTimeToDosDateTime(DeviceExt, 975 &SystemTime, &pFcb->entry.Fat.UpdateDate, 976 &pFcb->entry.Fat.UpdateTime); 977 } 978 979 VfatUpdateEntry(DeviceExt, pFcb); 980 } 981 982 ExAcquireResourceExclusiveLite(&(pFcb->MainResource), TRUE); 983 Status = VfatSetAllocationSizeInformation(FileObject, 984 pFcb, 985 DeviceExt, 986 &Irp->Overlay.AllocationSize); 987 ExReleaseResourceLite(&(pFcb->MainResource)); 988 if (!NT_SUCCESS (Status)) 989 { 990 VfatCloseFile(DeviceExt, FileObject); 991 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1); 992 return Status; 993 } 994 } 995 996 if (RequestedDisposition == FILE_SUPERSEDE) 997 { 998 Irp->IoStatus.Information = FILE_SUPERSEDED; 999 } 1000 else if (RequestedDisposition == FILE_OVERWRITE || 1001 RequestedDisposition == FILE_OVERWRITE_IF) 1002 { 1003 Irp->IoStatus.Information = FILE_OVERWRITTEN; 1004 } 1005 else 1006 { 1007 Irp->IoStatus.Information = FILE_OPENED; 1008 } 1009 } 1010 1011 if (pFcb->OpenHandleCount == 0) 1012 { 1013 IoSetShareAccess(Stack->Parameters.Create.SecurityContext->DesiredAccess, 1014 Stack->Parameters.Create.ShareAccess, 1015 FileObject, 1016 &pFcb->FCBShareAccess); 1017 } 1018 else 1019 { 1020 IoUpdateShareAccess(FileObject, 1021 &pFcb->FCBShareAccess); 1022 } 1023 1024 pCcb = FileObject->FsContext2; 1025 if (BooleanFlagOn(RequestedOptions, FILE_DELETE_ON_CLOSE)) 1026 { 1027 pCcb->Flags |= CCB_DELETE_ON_CLOSE; 1028 } 1029 1030 if (Irp->IoStatus.Information == FILE_CREATED) 1031 { 1032 vfatReportChange(DeviceExt, 1033 pFcb, 1034 (vfatFCBIsDirectory(pFcb) ? 1035 FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME), 1036 FILE_ACTION_ADDED); 1037 } 1038 else if (Irp->IoStatus.Information == FILE_OVERWRITTEN || 1039 Irp->IoStatus.Information == FILE_SUPERSEDED) 1040 { 1041 vfatReportChange(DeviceExt, 1042 pFcb, 1043 FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE, 1044 FILE_ACTION_MODIFIED); 1045 } 1046 1047 pFcb->OpenHandleCount++; 1048 DeviceExt->OpenHandleCount++; 1049 1050 /* FIXME : test write access if requested */ 1051 1052 /* FIXME: That is broken, we cannot reach this code path with failure */ 1053 ASSERT(NT_SUCCESS(Status)); 1054 if (NT_SUCCESS(Status)) 1055 { 1056 vfatAddToStat(DeviceExt, Fat.SuccessfulCreates, 1); 1057 } 1058 else 1059 { 1060 vfatAddToStat(DeviceExt, Fat.FailedCreates, 1); 1061 } 1062 1063 return Status; 1064 } 1065 1066 /* 1067 * FUNCTION: Create or open a file 1068 */ 1069 NTSTATUS 1070 VfatCreate( 1071 PVFAT_IRP_CONTEXT IrpContext) 1072 { 1073 NTSTATUS Status; 1074 1075 ASSERT(IrpContext); 1076 1077 if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject) 1078 { 1079 /* DeviceObject represents FileSystem instead of logical volume */ 1080 DPRINT ("FsdCreate called with file system\n"); 1081 IrpContext->Irp->IoStatus.Information = FILE_OPENED; 1082 IrpContext->PriorityBoost = IO_DISK_INCREMENT; 1083 1084 return STATUS_SUCCESS; 1085 } 1086 1087 IrpContext->Irp->IoStatus.Information = 0; 1088 ExAcquireResourceExclusiveLite(&IrpContext->DeviceExt->DirResource, TRUE); 1089 Status = VfatCreateFile(IrpContext->DeviceObject, IrpContext->Irp); 1090 ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource); 1091 1092 if (NT_SUCCESS(Status)) 1093 IrpContext->PriorityBoost = IO_DISK_INCREMENT; 1094 1095 return Status; 1096 } 1097 1098 /* EOF */ 1099