1 /* 2 * ReactOS kernel 3 * Copyright (C) 2002, 2014 ReactOS Team 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 18 * 19 * COPYRIGHT: See COPYING in the top level directory 20 * PROJECT: ReactOS kernel 21 * FILE: drivers/filesystem/ntfs/create.c 22 * PURPOSE: NTFS filesystem driver 23 * PROGRAMMERS: Eric Kohl 24 * Pierre Schweitzer (pierre@reactos.org) 25 */ 26 27 /* INCLUDES *****************************************************************/ 28 29 #include "ntfs.h" 30 31 #define NDEBUG 32 #include <debug.h> 33 34 static PCWSTR MftIdToName[] = { 35 L"$MFT", 36 L"$MFTMirr", 37 L"$LogFile", 38 L"$Volume", 39 L"AttrDef", 40 L".", 41 L"$Bitmap", 42 L"$Boot", 43 L"$BadClus", 44 L"$Quota", 45 L"$UpCase", 46 L"$Extended", 47 }; 48 49 /* FUNCTIONS ****************************************************************/ 50 51 static 52 NTSTATUS 53 NtfsMakeAbsoluteFilename(PFILE_OBJECT pFileObject, 54 PWSTR pRelativeFileName, 55 PWSTR *pAbsoluteFilename) 56 { 57 PWSTR rcName; 58 PNTFS_FCB Fcb; 59 60 DPRINT("try related for %S\n", pRelativeFileName); 61 Fcb = pFileObject->FsContext; 62 ASSERT(Fcb); 63 64 if (Fcb->Flags & FCB_IS_VOLUME) 65 { 66 /* This is likely to be an opening by ID, return ourselves */ 67 if (pRelativeFileName[0] == L'\\') 68 { 69 *pAbsoluteFilename = NULL; 70 return STATUS_SUCCESS; 71 } 72 73 return STATUS_INVALID_PARAMETER; 74 } 75 76 /* verify related object is a directory and target name 77 don't start with \. */ 78 if (NtfsFCBIsDirectory(Fcb) == FALSE || 79 pRelativeFileName[0] == L'\\') 80 { 81 return STATUS_INVALID_PARAMETER; 82 } 83 84 /* construct absolute path name */ 85 ASSERT(wcslen (Fcb->PathName) + 1 + wcslen (pRelativeFileName) + 1 <= MAX_PATH); 86 rcName = ExAllocatePoolWithTag(NonPagedPool, MAX_PATH * sizeof(WCHAR), TAG_NTFS); 87 if (!rcName) 88 { 89 return STATUS_INSUFFICIENT_RESOURCES; 90 } 91 92 wcscpy(rcName, Fcb->PathName); 93 if (!NtfsFCBIsRoot(Fcb)) 94 wcscat (rcName, L"\\"); 95 wcscat (rcName, pRelativeFileName); 96 *pAbsoluteFilename = rcName; 97 98 return STATUS_SUCCESS; 99 } 100 101 102 static 103 NTSTATUS 104 NtfsMoonWalkID(PDEVICE_EXTENSION DeviceExt, 105 ULONGLONG Id, 106 PUNICODE_STRING OutPath) 107 { 108 NTSTATUS Status; 109 PFILE_RECORD_HEADER MftRecord; 110 PFILENAME_ATTRIBUTE FileName; 111 WCHAR FullPath[MAX_PATH]; 112 ULONG WritePosition = MAX_PATH - 1; 113 114 DPRINT("NtfsMoonWalkID(%p, %I64x, %p)\n", DeviceExt, Id, OutPath); 115 116 RtlZeroMemory(FullPath, sizeof(FullPath)); 117 MftRecord = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList); 118 if (MftRecord == NULL) 119 { 120 return STATUS_INSUFFICIENT_RESOURCES; 121 } 122 123 while (TRUE) 124 { 125 Status = ReadFileRecord(DeviceExt, Id, MftRecord); 126 if (!NT_SUCCESS(Status)) 127 break; 128 129 ASSERT(MftRecord->Ntfs.Type == NRH_FILE_TYPE); 130 if (!(MftRecord->Flags & FRH_IN_USE)) 131 { 132 Status = STATUS_OBJECT_PATH_NOT_FOUND; 133 break; 134 } 135 136 FileName = GetBestFileNameFromRecord(DeviceExt, MftRecord); 137 if (FileName == NULL) 138 { 139 DPRINT1("$FILE_NAME attribute not found for %I64x\n", Id); 140 Status = STATUS_OBJECT_PATH_NOT_FOUND; 141 break; 142 } 143 144 WritePosition -= FileName->NameLength; 145 ASSERT(WritePosition < MAX_PATH); 146 RtlCopyMemory(FullPath + WritePosition, FileName->Name, FileName->NameLength * sizeof(WCHAR)); 147 WritePosition -= 1; 148 ASSERT(WritePosition < MAX_PATH); 149 FullPath[WritePosition] = L'\\'; 150 151 Id = FileName->DirectoryFileReferenceNumber & NTFS_MFT_MASK; 152 if (Id == NTFS_FILE_ROOT) 153 break; 154 } 155 156 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, MftRecord); 157 158 if (!NT_SUCCESS(Status)) 159 return Status; 160 161 OutPath->Length = (MAX_PATH - WritePosition - 1) * sizeof(WCHAR); 162 OutPath->MaximumLength = (MAX_PATH - WritePosition) * sizeof(WCHAR); 163 OutPath->Buffer = ExAllocatePoolWithTag(NonPagedPool, OutPath->MaximumLength, TAG_NTFS); 164 if (OutPath->Buffer == NULL) 165 { 166 return STATUS_INSUFFICIENT_RESOURCES; 167 } 168 RtlCopyMemory(OutPath->Buffer, FullPath + WritePosition, OutPath->MaximumLength); 169 170 return Status; 171 } 172 173 static 174 NTSTATUS 175 NtfsOpenFileById(PDEVICE_EXTENSION DeviceExt, 176 PFILE_OBJECT FileObject, 177 ULONGLONG MftId, 178 PNTFS_FCB * FoundFCB) 179 { 180 NTSTATUS Status; 181 PNTFS_FCB FCB; 182 PFILE_RECORD_HEADER MftRecord; 183 184 DPRINT("NtfsOpenFileById(%p, %p, %I64x, %p)\n", DeviceExt, FileObject, MftId, FoundFCB); 185 186 ASSERT(MftId < NTFS_FILE_FIRST_USER_FILE); 187 if (MftId > 0xb) /* No entries are used yet beyond this */ 188 { 189 return STATUS_OBJECT_NAME_NOT_FOUND; 190 } 191 192 MftRecord = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList); 193 if (MftRecord == NULL) 194 { 195 return STATUS_INSUFFICIENT_RESOURCES; 196 } 197 198 Status = ReadFileRecord(DeviceExt, MftId, MftRecord); 199 if (!NT_SUCCESS(Status)) 200 { 201 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, MftRecord); 202 return Status; 203 } 204 205 if (!(MftRecord->Flags & FRH_IN_USE)) 206 { 207 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, MftRecord); 208 return STATUS_OBJECT_PATH_NOT_FOUND; 209 } 210 211 FCB = NtfsGrabFCBFromTable(DeviceExt, MftIdToName[MftId]); 212 if (FCB == NULL) 213 { 214 UNICODE_STRING Name; 215 216 RtlInitUnicodeString(&Name, MftIdToName[MftId]); 217 Status = NtfsMakeFCBFromDirEntry(DeviceExt, NULL, &Name, NULL, MftRecord, MftId, &FCB); 218 if (!NT_SUCCESS(Status)) 219 { 220 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, MftRecord); 221 return Status; 222 } 223 } 224 225 ASSERT(FCB != NULL); 226 227 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, MftRecord); 228 229 Status = NtfsAttachFCBToFileObject(DeviceExt, 230 FCB, 231 FileObject); 232 *FoundFCB = FCB; 233 234 return Status; 235 } 236 237 /* 238 * FUNCTION: Opens a file 239 */ 240 static 241 NTSTATUS 242 NtfsOpenFile(PDEVICE_EXTENSION DeviceExt, 243 PFILE_OBJECT FileObject, 244 PWSTR FileName, 245 BOOLEAN CaseSensitive, 246 PNTFS_FCB * FoundFCB) 247 { 248 PNTFS_FCB ParentFcb; 249 PNTFS_FCB Fcb; 250 NTSTATUS Status; 251 PWSTR AbsFileName = NULL; 252 253 DPRINT("NtfsOpenFile(%p, %p, %S, %s, %p)\n", 254 DeviceExt, 255 FileObject, 256 FileName, 257 CaseSensitive ? "TRUE" : "FALSE", 258 FoundFCB); 259 260 *FoundFCB = NULL; 261 262 if (FileObject->RelatedFileObject) 263 { 264 DPRINT("Converting relative filename to absolute filename\n"); 265 266 Status = NtfsMakeAbsoluteFilename(FileObject->RelatedFileObject, 267 FileName, 268 &AbsFileName); 269 if (AbsFileName) FileName = AbsFileName; 270 if (!NT_SUCCESS(Status)) 271 { 272 return Status; 273 } 274 } 275 276 //FIXME: Get canonical path name (remove .'s, ..'s and extra separators) 277 278 DPRINT("PathName to open: %S\n", FileName); 279 280 /* try first to find an existing FCB in memory */ 281 DPRINT("Checking for existing FCB in memory\n"); 282 Fcb = NtfsGrabFCBFromTable(DeviceExt, 283 FileName); 284 if (Fcb == NULL) 285 { 286 DPRINT("No existing FCB found, making a new one if file exists.\n"); 287 Status = NtfsGetFCBForFile(DeviceExt, 288 &ParentFcb, 289 &Fcb, 290 FileName, 291 CaseSensitive); 292 if (ParentFcb != NULL) 293 { 294 NtfsReleaseFCB(DeviceExt, 295 ParentFcb); 296 } 297 298 if (!NT_SUCCESS(Status)) 299 { 300 DPRINT("Could not make a new FCB, status: %x\n", Status); 301 302 if (AbsFileName) 303 ExFreePoolWithTag(AbsFileName, TAG_NTFS); 304 305 return Status; 306 } 307 } 308 309 DPRINT("Attaching FCB to fileObject\n"); 310 Status = NtfsAttachFCBToFileObject(DeviceExt, 311 Fcb, 312 FileObject); 313 314 if (AbsFileName) 315 ExFreePool(AbsFileName); 316 317 *FoundFCB = Fcb; 318 319 return Status; 320 } 321 322 323 /* 324 * FUNCTION: Opens a file 325 */ 326 static 327 NTSTATUS 328 NtfsCreateFile(PDEVICE_OBJECT DeviceObject, 329 PNTFS_IRP_CONTEXT IrpContext) 330 { 331 PDEVICE_EXTENSION DeviceExt; 332 PIO_STACK_LOCATION Stack; 333 PFILE_OBJECT FileObject; 334 ULONG RequestedDisposition; 335 ULONG RequestedOptions; 336 PNTFS_FCB Fcb = NULL; 337 // PWSTR FileName; 338 NTSTATUS Status; 339 UNICODE_STRING FullPath; 340 PIRP Irp = IrpContext->Irp; 341 342 DPRINT("NtfsCreateFile(%p, %p) called\n", DeviceObject, IrpContext); 343 344 DeviceExt = DeviceObject->DeviceExtension; 345 ASSERT(DeviceExt); 346 Stack = IoGetCurrentIrpStackLocation(Irp); 347 ASSERT(Stack); 348 349 RequestedDisposition = ((Stack->Parameters.Create.Options >> 24) & 0xff); 350 RequestedOptions = Stack->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS; 351 // PagingFileCreate = (Stack->Flags & SL_OPEN_PAGING_FILE) ? TRUE : FALSE; 352 if (RequestedOptions & FILE_DIRECTORY_FILE && 353 RequestedDisposition == FILE_SUPERSEDE) 354 { 355 return STATUS_INVALID_PARAMETER; 356 } 357 358 /* Deny create if the volume is locked */ 359 if (DeviceExt->Flags & VCB_VOLUME_LOCKED) 360 { 361 return STATUS_ACCESS_DENIED; 362 } 363 364 FileObject = Stack->FileObject; 365 366 if ((RequestedOptions & FILE_OPEN_BY_FILE_ID) == FILE_OPEN_BY_FILE_ID) 367 { 368 ULONGLONG MFTId; 369 370 if (FileObject->FileName.Length != sizeof(ULONGLONG)) 371 return STATUS_INVALID_PARAMETER; 372 373 MFTId = (*(PULONGLONG)FileObject->FileName.Buffer) & NTFS_MFT_MASK; 374 if (MFTId < NTFS_FILE_FIRST_USER_FILE) 375 { 376 Status = NtfsOpenFileById(DeviceExt, FileObject, MFTId, &Fcb); 377 } 378 else 379 { 380 Status = NtfsMoonWalkID(DeviceExt, MFTId, &FullPath); 381 } 382 383 if (!NT_SUCCESS(Status)) 384 { 385 return Status; 386 } 387 388 DPRINT1("Open by ID: %I64x -> %wZ\n", (*(PULONGLONG)FileObject->FileName.Buffer) & NTFS_MFT_MASK, &FullPath); 389 } 390 391 /* This a open operation for the volume itself */ 392 if (FileObject->FileName.Length == 0 && 393 (FileObject->RelatedFileObject == NULL || FileObject->RelatedFileObject->FsContext2 != NULL)) 394 { 395 if (RequestedDisposition != FILE_OPEN && 396 RequestedDisposition != FILE_OPEN_IF) 397 { 398 return STATUS_ACCESS_DENIED; 399 } 400 401 if (RequestedOptions & FILE_DIRECTORY_FILE) 402 { 403 return STATUS_NOT_A_DIRECTORY; 404 } 405 406 NtfsAttachFCBToFileObject(DeviceExt, DeviceExt->VolumeFcb, FileObject); 407 DeviceExt->VolumeFcb->RefCount++; 408 409 Irp->IoStatus.Information = FILE_OPENED; 410 return STATUS_SUCCESS; 411 } 412 413 if (Fcb == NULL) 414 { 415 Status = NtfsOpenFile(DeviceExt, 416 FileObject, 417 ((RequestedOptions & FILE_OPEN_BY_FILE_ID) ? FullPath.Buffer : FileObject->FileName.Buffer), 418 BooleanFlagOn(Stack->Flags, SL_CASE_SENSITIVE), 419 &Fcb); 420 421 if (RequestedOptions & FILE_OPEN_BY_FILE_ID) 422 { 423 ExFreePoolWithTag(FullPath.Buffer, TAG_NTFS); 424 } 425 } 426 427 if (NT_SUCCESS(Status)) 428 { 429 if (RequestedDisposition == FILE_CREATE) 430 { 431 Irp->IoStatus.Information = FILE_EXISTS; 432 NtfsCloseFile(DeviceExt, FileObject); 433 return STATUS_OBJECT_NAME_COLLISION; 434 } 435 436 if (RequestedOptions & FILE_NON_DIRECTORY_FILE && 437 NtfsFCBIsDirectory(Fcb)) 438 { 439 NtfsCloseFile(DeviceExt, FileObject); 440 return STATUS_FILE_IS_A_DIRECTORY; 441 } 442 443 if (RequestedOptions & FILE_DIRECTORY_FILE && 444 !NtfsFCBIsDirectory(Fcb)) 445 { 446 NtfsCloseFile(DeviceExt, FileObject); 447 return STATUS_NOT_A_DIRECTORY; 448 } 449 450 /* 451 * If it is a reparse point & FILE_OPEN_REPARSE_POINT, then allow opening it 452 * as a normal file. 453 * Otherwise, attempt to read reparse data and hand them to the Io manager 454 * with status reparse to force a reparse. 455 */ 456 if (NtfsFCBIsReparsePoint(Fcb) && 457 ((RequestedOptions & FILE_OPEN_REPARSE_POINT) != FILE_OPEN_REPARSE_POINT)) 458 { 459 PREPARSE_DATA_BUFFER ReparseData = NULL; 460 461 Status = NtfsReadFCBAttribute(DeviceExt, Fcb, 462 AttributeReparsePoint, L"", 0, 463 (PVOID *)&Irp->Tail.Overlay.AuxiliaryBuffer); 464 if (NT_SUCCESS(Status)) 465 { 466 ReparseData = (PREPARSE_DATA_BUFFER)Irp->Tail.Overlay.AuxiliaryBuffer; 467 if (ReparseData->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) 468 { 469 Status = STATUS_REPARSE; 470 } 471 else 472 { 473 Status = STATUS_NOT_IMPLEMENTED; 474 ExFreePoolWithTag(ReparseData, TAG_NTFS); 475 } 476 } 477 478 Irp->IoStatus.Information = ((Status == STATUS_REPARSE) ? ReparseData->ReparseTag : 0); 479 480 NtfsCloseFile(DeviceExt, FileObject); 481 return Status; 482 } 483 484 if (RequestedDisposition == FILE_OVERWRITE || 485 RequestedDisposition == FILE_OVERWRITE_IF || 486 RequestedDisposition == FILE_SUPERSEDE) 487 { 488 PFILE_RECORD_HEADER fileRecord = NULL; 489 PNTFS_ATTR_CONTEXT dataContext = NULL; 490 ULONG DataAttributeOffset; 491 LARGE_INTEGER Zero; 492 Zero.QuadPart = 0; 493 494 if (!NtfsGlobalData->EnableWriteSupport) 495 { 496 DPRINT1("NTFS write-support is EXPERIMENTAL and is disabled by default!\n"); 497 NtfsCloseFile(DeviceExt, FileObject); 498 return STATUS_ACCESS_DENIED; 499 } 500 501 // TODO: check for appropriate access 502 503 ExAcquireResourceExclusiveLite(&(Fcb->MainResource), TRUE); 504 505 fileRecord = ExAllocateFromNPagedLookasideList(&Fcb->Vcb->FileRecLookasideList); 506 if (fileRecord) 507 { 508 509 Status = ReadFileRecord(Fcb->Vcb, 510 Fcb->MFTIndex, 511 fileRecord); 512 if (!NT_SUCCESS(Status)) 513 goto DoneOverwriting; 514 515 // find the data attribute and set it's length to 0 (TODO: Handle Alternate Data Streams) 516 Status = FindAttribute(Fcb->Vcb, fileRecord, AttributeData, L"", 0, &dataContext, &DataAttributeOffset); 517 if (!NT_SUCCESS(Status)) 518 goto DoneOverwriting; 519 520 Status = SetAttributeDataLength(FileObject, Fcb, dataContext, DataAttributeOffset, fileRecord, &Zero); 521 } 522 else 523 { 524 Status = STATUS_NO_MEMORY; 525 } 526 527 DoneOverwriting: 528 if (fileRecord) 529 ExFreeToNPagedLookasideList(&Fcb->Vcb->FileRecLookasideList, fileRecord); 530 if (dataContext) 531 ReleaseAttributeContext(dataContext); 532 533 ExReleaseResourceLite(&(Fcb->MainResource)); 534 535 if (!NT_SUCCESS(Status)) 536 { 537 NtfsCloseFile(DeviceExt, FileObject); 538 return Status; 539 } 540 541 if (RequestedDisposition == FILE_SUPERSEDE) 542 { 543 Irp->IoStatus.Information = FILE_SUPERSEDED; 544 } 545 else 546 { 547 Irp->IoStatus.Information = FILE_OVERWRITTEN; 548 } 549 } 550 } 551 else 552 { 553 /* HUGLY HACK: Can't create new files yet... */ 554 if (RequestedDisposition == FILE_CREATE || 555 RequestedDisposition == FILE_OPEN_IF || 556 RequestedDisposition == FILE_OVERWRITE_IF || 557 RequestedDisposition == FILE_SUPERSEDE) 558 { 559 if (!NtfsGlobalData->EnableWriteSupport) 560 { 561 DPRINT1("NTFS write-support is EXPERIMENTAL and is disabled by default!\n"); 562 NtfsCloseFile(DeviceExt, FileObject); 563 return STATUS_ACCESS_DENIED; 564 } 565 566 // Was the user trying to create a directory? 567 if (RequestedOptions & FILE_DIRECTORY_FILE) 568 { 569 // Create the directory on disk 570 Status = NtfsCreateDirectory(DeviceExt, 571 FileObject, 572 BooleanFlagOn(Stack->Flags, SL_CASE_SENSITIVE), 573 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)); 574 } 575 else 576 { 577 // Create the file record on disk 578 Status = NtfsCreateFileRecord(DeviceExt, 579 FileObject, 580 BooleanFlagOn(Stack->Flags, SL_CASE_SENSITIVE), 581 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)); 582 } 583 584 if (!NT_SUCCESS(Status)) 585 { 586 DPRINT1("ERROR: Couldn't create file record!\n"); 587 return Status; 588 } 589 590 // Before we open the file/directory we just created, we need to change the disposition (upper 8 bits of ULONG) 591 // from create to open, since we already created the file 592 Stack->Parameters.Create.Options = (ULONG)FILE_OPEN << 24 | RequestedOptions; 593 594 // Now we should be able to open the file using NtfsCreateFile() 595 Status = NtfsCreateFile(DeviceObject, IrpContext); 596 if (NT_SUCCESS(Status)) 597 { 598 // We need to change Irp->IoStatus.Information to reflect creation 599 Irp->IoStatus.Information = FILE_CREATED; 600 } 601 return Status; 602 } 603 } 604 605 if (NT_SUCCESS(Status)) 606 { 607 Fcb->OpenHandleCount++; 608 DeviceExt->OpenHandleCount++; 609 } 610 611 /* 612 * If the directory containing the file to open doesn't exist then 613 * fail immediately 614 */ 615 Irp->IoStatus.Information = (NT_SUCCESS(Status)) ? FILE_OPENED : 0; 616 617 return Status; 618 } 619 620 621 NTSTATUS 622 NtfsCreate(PNTFS_IRP_CONTEXT IrpContext) 623 { 624 PDEVICE_EXTENSION DeviceExt; 625 NTSTATUS Status; 626 PDEVICE_OBJECT DeviceObject; 627 628 DeviceObject = IrpContext->DeviceObject; 629 if (DeviceObject == NtfsGlobalData->DeviceObject) 630 { 631 /* DeviceObject represents FileSystem instead of logical volume */ 632 DPRINT("Opening file system\n"); 633 IrpContext->Irp->IoStatus.Information = FILE_OPENED; 634 return STATUS_SUCCESS; 635 } 636 637 DeviceExt = DeviceObject->DeviceExtension; 638 639 if (!(IrpContext->Flags & IRPCONTEXT_CANWAIT)) 640 { 641 return NtfsMarkIrpContextForQueue(IrpContext); 642 } 643 644 ExAcquireResourceExclusiveLite(&DeviceExt->DirResource, 645 TRUE); 646 Status = NtfsCreateFile(DeviceObject, 647 IrpContext); 648 ExReleaseResourceLite(&DeviceExt->DirResource); 649 650 return Status; 651 } 652 653 /** 654 * @name NtfsCreateDirectory() 655 * @implemented 656 * 657 * Creates a file record for a new directory and saves it to the MFT. Adds the filename attribute of the 658 * created directory to the parent directory's index. 659 * 660 * @param DeviceExt 661 * Points to the target disk's DEVICE_EXTENSION 662 * 663 * @param FileObject 664 * Pointer to a FILE_OBJECT describing the directory to be created 665 * 666 * @param CaseSensitive 667 * Boolean indicating if the function should operate in case-sensitive mode. This will be TRUE 668 * if an application created the folder with the FILE_FLAG_POSIX_SEMANTICS flag. 669 * 670 * @param CanWait 671 * Boolean indicating if the function is allowed to wait for exclusive access to the master file table. 672 * This will only be relevant if the MFT doesn't have any free file records and needs to be enlarged. 673 * 674 * @return 675 * STATUS_SUCCESS on success. 676 * STATUS_INSUFFICIENT_RESOURCES if unable to allocate memory for the file record. 677 * STATUS_CANT_WAIT if CanWait was FALSE and the function needed to resize the MFT but 678 * couldn't get immediate, exclusive access to it. 679 */ 680 NTSTATUS 681 NtfsCreateDirectory(PDEVICE_EXTENSION DeviceExt, 682 PFILE_OBJECT FileObject, 683 BOOLEAN CaseSensitive, 684 BOOLEAN CanWait) 685 { 686 687 NTSTATUS Status = STATUS_SUCCESS; 688 PFILE_RECORD_HEADER FileRecord; 689 PNTFS_ATTR_RECORD NextAttribute; 690 PFILENAME_ATTRIBUTE FilenameAttribute; 691 ULONGLONG ParentMftIndex; 692 ULONGLONG FileMftIndex; 693 PB_TREE Tree; 694 PINDEX_ROOT_ATTRIBUTE NewIndexRoot; 695 ULONG MaxIndexRootSize; 696 ULONG RootLength; 697 698 DPRINT("NtfsCreateFileRecord(%p, %p, %s, %s)\n", 699 DeviceExt, 700 FileObject, 701 CaseSensitive ? "TRUE" : "FALSE", 702 CanWait ? "TRUE" : "FALSE"); 703 704 // Start with an empty file record 705 FileRecord = NtfsCreateEmptyFileRecord(DeviceExt); 706 if (!FileRecord) 707 { 708 DPRINT1("ERROR: Unable to allocate memory for file record!\n"); 709 return STATUS_INSUFFICIENT_RESOURCES; 710 } 711 712 // Set the directory flag 713 FileRecord->Flags |= FRH_DIRECTORY; 714 715 // find where the first attribute will be added 716 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset); 717 718 // add first attribute, $STANDARD_INFORMATION 719 AddStandardInformation(FileRecord, NextAttribute); 720 721 // advance NextAttribute pointer to the next attribute 722 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length); 723 724 // Add the $FILE_NAME attribute 725 AddFileName(FileRecord, NextAttribute, DeviceExt, FileObject, CaseSensitive, &ParentMftIndex); 726 727 // save a pointer to the filename attribute 728 FilenameAttribute = (PFILENAME_ATTRIBUTE)((ULONG_PTR)NextAttribute + NextAttribute->Resident.ValueOffset); 729 730 // advance NextAttribute pointer to the next attribute 731 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length); 732 733 // Create an empty b-tree to represent our new index 734 Status = CreateEmptyBTree(&Tree); 735 if (!NT_SUCCESS(Status)) 736 { 737 DPRINT1("ERROR: Failed to create empty B-Tree!\n"); 738 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord); 739 return Status; 740 } 741 742 // Calculate maximum size of index root 743 MaxIndexRootSize = DeviceExt->NtfsInfo.BytesPerFileRecord 744 - ((ULONG_PTR)NextAttribute - (ULONG_PTR)FileRecord) 745 - sizeof(ULONG) * 2; 746 747 // Create a new index record from the tree 748 Status = CreateIndexRootFromBTree(DeviceExt, 749 Tree, 750 MaxIndexRootSize, 751 &NewIndexRoot, 752 &RootLength); 753 if (!NT_SUCCESS(Status)) 754 { 755 DPRINT1("ERROR: Unable to create empty index root!\n"); 756 DestroyBTree(Tree); 757 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord); 758 return Status; 759 } 760 761 // We're done with the B-Tree 762 DestroyBTree(Tree); 763 764 // add the $INDEX_ROOT attribute 765 Status = AddIndexRoot(DeviceExt, FileRecord, NextAttribute, NewIndexRoot, RootLength, L"$I30", 4); 766 if (!NT_SUCCESS(Status)) 767 { 768 DPRINT1("ERROR: Failed to add index root to new file record!\n"); 769 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS); 770 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord); 771 return Status; 772 } 773 774 775 #ifndef NDEBUG 776 NtfsDumpFileRecord(DeviceExt, FileRecord); 777 #endif 778 779 // Now that we've built the file record in memory, we need to store it in the MFT. 780 Status = AddNewMftEntry(FileRecord, DeviceExt, &FileMftIndex, CanWait); 781 if (NT_SUCCESS(Status)) 782 { 783 // The highest 2 bytes should be the sequence number, unless the parent happens to be root 784 if (FileMftIndex == NTFS_FILE_ROOT) 785 FileMftIndex = FileMftIndex + ((ULONGLONG)NTFS_FILE_ROOT << 48); 786 else 787 FileMftIndex = FileMftIndex + ((ULONGLONG)FileRecord->SequenceNumber << 48); 788 789 DPRINT1("New File Reference: 0x%016I64x\n", FileMftIndex); 790 791 // Add the filename attribute to the filename-index of the parent directory 792 Status = NtfsAddFilenameToDirectory(DeviceExt, 793 ParentMftIndex, 794 FileMftIndex, 795 FilenameAttribute, 796 CaseSensitive); 797 } 798 799 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS); 800 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord); 801 802 return Status; 803 } 804 805 /** 806 * @name NtfsCreateEmptyFileRecord 807 * @implemented 808 * 809 * Creates a new, empty file record, with no attributes. 810 * 811 * @param DeviceExt 812 * Pointer to the DEVICE_EXTENSION of the target volume the file record will be stored on. 813 * 814 * @return 815 * A pointer to the newly-created FILE_RECORD_HEADER if the function succeeds, NULL otherwise. 816 */ 817 PFILE_RECORD_HEADER 818 NtfsCreateEmptyFileRecord(PDEVICE_EXTENSION DeviceExt) 819 { 820 PFILE_RECORD_HEADER FileRecord; 821 PNTFS_ATTR_RECORD NextAttribute; 822 823 DPRINT("NtfsCreateEmptyFileRecord(%p)\n", DeviceExt); 824 825 // allocate memory for file record 826 FileRecord = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList); 827 if (!FileRecord) 828 { 829 DPRINT1("ERROR: Unable to allocate memory for file record!\n"); 830 return NULL; 831 } 832 833 RtlZeroMemory(FileRecord, DeviceExt->NtfsInfo.BytesPerFileRecord); 834 835 FileRecord->Ntfs.Type = NRH_FILE_TYPE; 836 837 // calculate USA offset and count 838 FileRecord->Ntfs.UsaOffset = FIELD_OFFSET(FILE_RECORD_HEADER, MFTRecordNumber) + sizeof(ULONG); 839 840 // size of USA (in ULONG's) will be 1 (for USA number) + 1 for every sector the file record uses 841 FileRecord->BytesAllocated = DeviceExt->NtfsInfo.BytesPerFileRecord; 842 FileRecord->Ntfs.UsaCount = (FileRecord->BytesAllocated / DeviceExt->NtfsInfo.BytesPerSector) + 1; 843 844 // setup other file record fields 845 FileRecord->SequenceNumber = 1; 846 FileRecord->AttributeOffset = FileRecord->Ntfs.UsaOffset + (2 * FileRecord->Ntfs.UsaCount); 847 FileRecord->AttributeOffset = ALIGN_UP_BY(FileRecord->AttributeOffset, ATTR_RECORD_ALIGNMENT); 848 FileRecord->Flags = FRH_IN_USE; 849 FileRecord->BytesInUse = FileRecord->AttributeOffset + sizeof(ULONG) * 2; 850 851 // find where the first attribute will be added 852 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset); 853 854 // mark the (temporary) end of the file-record 855 NextAttribute->Type = AttributeEnd; 856 NextAttribute->Length = FILE_RECORD_END; 857 858 return FileRecord; 859 } 860 861 862 /** 863 * @name NtfsCreateFileRecord() 864 * @implemented 865 * 866 * Creates a file record and saves it to the MFT. Adds the filename attribute of the 867 * created file to the parent directory's index. 868 * 869 * @param DeviceExt 870 * Points to the target disk's DEVICE_EXTENSION 871 * 872 * @param FileObject 873 * Pointer to a FILE_OBJECT describing the file to be created 874 * 875 * @param CanWait 876 * Boolean indicating if the function is allowed to wait for exclusive access to the master file table. 877 * This will only be relevant if the MFT doesn't have any free file records and needs to be enlarged. 878 * 879 * @return 880 * STATUS_SUCCESS on success. 881 * STATUS_INSUFFICIENT_RESOURCES if unable to allocate memory for the file record. 882 * STATUS_CANT_WAIT if CanWait was FALSE and the function needed to resize the MFT but 883 * couldn't get immediate, exclusive access to it. 884 */ 885 NTSTATUS 886 NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt, 887 PFILE_OBJECT FileObject, 888 BOOLEAN CaseSensitive, 889 BOOLEAN CanWait) 890 { 891 NTSTATUS Status = STATUS_SUCCESS; 892 PFILE_RECORD_HEADER FileRecord; 893 PNTFS_ATTR_RECORD NextAttribute; 894 PFILENAME_ATTRIBUTE FilenameAttribute; 895 ULONGLONG ParentMftIndex; 896 ULONGLONG FileMftIndex; 897 898 DPRINT("NtfsCreateFileRecord(%p, %p, %s, %s)\n", 899 DeviceExt, 900 FileObject, 901 CaseSensitive ? "TRUE" : "FALSE", 902 CanWait ? "TRUE" : "FALSE"); 903 904 // allocate memory for file record 905 FileRecord = NtfsCreateEmptyFileRecord(DeviceExt); 906 if (!FileRecord) 907 { 908 DPRINT1("ERROR: Unable to allocate memory for file record!\n"); 909 return STATUS_INSUFFICIENT_RESOURCES; 910 } 911 912 // find where the first attribute will be added 913 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset); 914 915 // add first attribute, $STANDARD_INFORMATION 916 AddStandardInformation(FileRecord, NextAttribute); 917 918 // advance NextAttribute pointer to the next attribute 919 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length); 920 921 // Add the $FILE_NAME attribute 922 AddFileName(FileRecord, NextAttribute, DeviceExt, FileObject, CaseSensitive, &ParentMftIndex); 923 924 // save a pointer to the filename attribute 925 FilenameAttribute = (PFILENAME_ATTRIBUTE)((ULONG_PTR)NextAttribute + NextAttribute->Resident.ValueOffset); 926 927 // advance NextAttribute pointer to the next attribute 928 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length); 929 930 // add the $DATA attribute 931 AddData(FileRecord, NextAttribute); 932 933 #ifndef NDEBUG 934 // dump file record in memory (for debugging) 935 NtfsDumpFileRecord(DeviceExt, FileRecord); 936 #endif 937 938 // Now that we've built the file record in memory, we need to store it in the MFT. 939 Status = AddNewMftEntry(FileRecord, DeviceExt, &FileMftIndex, CanWait); 940 if (NT_SUCCESS(Status)) 941 { 942 // The highest 2 bytes should be the sequence number, unless the parent happens to be root 943 if (FileMftIndex == NTFS_FILE_ROOT) 944 FileMftIndex = FileMftIndex + ((ULONGLONG)NTFS_FILE_ROOT << 48); 945 else 946 FileMftIndex = FileMftIndex + ((ULONGLONG)FileRecord->SequenceNumber << 48); 947 948 DPRINT1("New File Reference: 0x%016I64x\n", FileMftIndex); 949 950 // Add the filename attribute to the filename-index of the parent directory 951 Status = NtfsAddFilenameToDirectory(DeviceExt, 952 ParentMftIndex, 953 FileMftIndex, 954 FilenameAttribute, 955 CaseSensitive); 956 } 957 958 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord); 959 960 return Status; 961 } 962 963 /* EOF */ 964