/* * ReactOS kernel * Copyright (C) 2002, 2014 ReactOS Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: drivers/filesystem/ntfs/create.c * PURPOSE: NTFS filesystem driver * PROGRAMMERS: Eric Kohl * Pierre Schweitzer (pierre@reactos.org) */ /* INCLUDES *****************************************************************/ #include "ntfs.h" #define NDEBUG #include static PCWSTR MftIdToName[] = { L"$MFT", L"$MFTMirr", L"$LogFile", L"$Volume", L"AttrDef", L".", L"$Bitmap", L"$Boot", L"$BadClus", L"$Quota", L"$UpCase", L"$Extended", }; /* FUNCTIONS ****************************************************************/ static NTSTATUS NtfsMakeAbsoluteFilename(PFILE_OBJECT pFileObject, PWSTR pRelativeFileName, PWSTR *pAbsoluteFilename) { PWSTR rcName; PNTFS_FCB Fcb; DPRINT("try related for %S\n", pRelativeFileName); Fcb = pFileObject->FsContext; ASSERT(Fcb); if (Fcb->Flags & FCB_IS_VOLUME) { /* This is likely to be an opening by ID, return ourselves */ if (pRelativeFileName[0] == L'\\') { *pAbsoluteFilename = NULL; return STATUS_SUCCESS; } return STATUS_INVALID_PARAMETER; } /* verify related object is a directory and target name don't start with \. */ if (NtfsFCBIsDirectory(Fcb) == FALSE || pRelativeFileName[0] == L'\\') { return STATUS_INVALID_PARAMETER; } /* construct absolute path name */ ASSERT(wcslen (Fcb->PathName) + 1 + wcslen (pRelativeFileName) + 1 <= MAX_PATH); rcName = ExAllocatePoolWithTag(NonPagedPool, MAX_PATH * sizeof(WCHAR), TAG_NTFS); if (!rcName) { return STATUS_INSUFFICIENT_RESOURCES; } wcscpy(rcName, Fcb->PathName); if (!NtfsFCBIsRoot(Fcb)) wcscat (rcName, L"\\"); wcscat (rcName, pRelativeFileName); *pAbsoluteFilename = rcName; return STATUS_SUCCESS; } static NTSTATUS NtfsMoonWalkID(PDEVICE_EXTENSION DeviceExt, ULONGLONG Id, PUNICODE_STRING OutPath) { NTSTATUS Status; PFILE_RECORD_HEADER MftRecord; PFILENAME_ATTRIBUTE FileName; WCHAR FullPath[MAX_PATH]; ULONG WritePosition = MAX_PATH - 1; DPRINT("NtfsMoonWalkID(%p, %I64x, %p)\n", DeviceExt, Id, OutPath); RtlZeroMemory(FullPath, sizeof(FullPath)); MftRecord = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList); if (MftRecord == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } while (TRUE) { Status = ReadFileRecord(DeviceExt, Id, MftRecord); if (!NT_SUCCESS(Status)) break; ASSERT(MftRecord->Ntfs.Type == NRH_FILE_TYPE); if (!(MftRecord->Flags & FRH_IN_USE)) { Status = STATUS_OBJECT_PATH_NOT_FOUND; break; } FileName = GetBestFileNameFromRecord(DeviceExt, MftRecord); if (FileName == NULL) { DPRINT1("$FILE_NAME attribute not found for %I64x\n", Id); Status = STATUS_OBJECT_PATH_NOT_FOUND; break; } WritePosition -= FileName->NameLength; ASSERT(WritePosition < MAX_PATH); RtlCopyMemory(FullPath + WritePosition, FileName->Name, FileName->NameLength * sizeof(WCHAR)); WritePosition -= 1; ASSERT(WritePosition < MAX_PATH); FullPath[WritePosition] = L'\\'; Id = FileName->DirectoryFileReferenceNumber & NTFS_MFT_MASK; if (Id == NTFS_FILE_ROOT) break; } ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, MftRecord); if (!NT_SUCCESS(Status)) return Status; OutPath->Length = (MAX_PATH - WritePosition - 1) * sizeof(WCHAR); OutPath->MaximumLength = (MAX_PATH - WritePosition) * sizeof(WCHAR); OutPath->Buffer = ExAllocatePoolWithTag(NonPagedPool, OutPath->MaximumLength, TAG_NTFS); if (OutPath->Buffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } RtlCopyMemory(OutPath->Buffer, FullPath + WritePosition, OutPath->MaximumLength); return Status; } static NTSTATUS NtfsOpenFileById(PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject, ULONGLONG MftId, PNTFS_FCB * FoundFCB) { NTSTATUS Status; PNTFS_FCB FCB; PFILE_RECORD_HEADER MftRecord; DPRINT("NtfsOpenFileById(%p, %p, %I64x, %p)\n", DeviceExt, FileObject, MftId, FoundFCB); ASSERT(MftId < NTFS_FILE_FIRST_USER_FILE); if (MftId > 0xb) /* No entries are used yet beyond this */ { return STATUS_OBJECT_NAME_NOT_FOUND; } MftRecord = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList); if (MftRecord == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } Status = ReadFileRecord(DeviceExt, MftId, MftRecord); if (!NT_SUCCESS(Status)) { ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, MftRecord); return Status; } if (!(MftRecord->Flags & FRH_IN_USE)) { ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, MftRecord); return STATUS_OBJECT_PATH_NOT_FOUND; } FCB = NtfsGrabFCBFromTable(DeviceExt, MftIdToName[MftId]); if (FCB == NULL) { UNICODE_STRING Name; RtlInitUnicodeString(&Name, MftIdToName[MftId]); Status = NtfsMakeFCBFromDirEntry(DeviceExt, NULL, &Name, NULL, MftRecord, MftId, &FCB); if (!NT_SUCCESS(Status)) { ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, MftRecord); return Status; } } ASSERT(FCB != NULL); ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, MftRecord); Status = NtfsAttachFCBToFileObject(DeviceExt, FCB, FileObject); *FoundFCB = FCB; return Status; } /* * FUNCTION: Opens a file */ static NTSTATUS NtfsOpenFile(PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject, PWSTR FileName, BOOLEAN CaseSensitive, PNTFS_FCB * FoundFCB) { PNTFS_FCB ParentFcb; PNTFS_FCB Fcb; NTSTATUS Status; PWSTR AbsFileName = NULL; DPRINT("NtfsOpenFile(%p, %p, %S, %s, %p)\n", DeviceExt, FileObject, FileName, CaseSensitive ? "TRUE" : "FALSE", FoundFCB); *FoundFCB = NULL; if (FileObject->RelatedFileObject) { DPRINT("Converting relative filename to absolute filename\n"); Status = NtfsMakeAbsoluteFilename(FileObject->RelatedFileObject, FileName, &AbsFileName); if (AbsFileName) FileName = AbsFileName; if (!NT_SUCCESS(Status)) { return Status; } } //FIXME: Get canonical path name (remove .'s, ..'s and extra separators) DPRINT("PathName to open: %S\n", FileName); /* try first to find an existing FCB in memory */ DPRINT("Checking for existing FCB in memory\n"); Fcb = NtfsGrabFCBFromTable(DeviceExt, FileName); if (Fcb == NULL) { DPRINT("No existing FCB found, making a new one if file exists.\n"); Status = NtfsGetFCBForFile(DeviceExt, &ParentFcb, &Fcb, FileName, CaseSensitive); if (ParentFcb != NULL) { NtfsReleaseFCB(DeviceExt, ParentFcb); } if (!NT_SUCCESS(Status)) { DPRINT("Could not make a new FCB, status: %x\n", Status); if (AbsFileName) ExFreePoolWithTag(AbsFileName, TAG_NTFS); return Status; } } DPRINT("Attaching FCB to fileObject\n"); Status = NtfsAttachFCBToFileObject(DeviceExt, Fcb, FileObject); if (AbsFileName) ExFreePool(AbsFileName); *FoundFCB = Fcb; return Status; } /* * FUNCTION: Opens a file */ static NTSTATUS NtfsCreateFile(PDEVICE_OBJECT DeviceObject, PNTFS_IRP_CONTEXT IrpContext) { PDEVICE_EXTENSION DeviceExt; PIO_STACK_LOCATION Stack; PFILE_OBJECT FileObject; ULONG RequestedDisposition; ULONG RequestedOptions; PNTFS_FCB Fcb = NULL; // PWSTR FileName; NTSTATUS Status; UNICODE_STRING FullPath; PIRP Irp = IrpContext->Irp; DPRINT("NtfsCreateFile(%p, %p) called\n", DeviceObject, IrpContext); DeviceExt = DeviceObject->DeviceExtension; ASSERT(DeviceExt); Stack = IoGetCurrentIrpStackLocation(Irp); ASSERT(Stack); RequestedDisposition = ((Stack->Parameters.Create.Options >> 24) & 0xff); RequestedOptions = Stack->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS; // PagingFileCreate = (Stack->Flags & SL_OPEN_PAGING_FILE) ? TRUE : FALSE; if (RequestedOptions & FILE_DIRECTORY_FILE && RequestedDisposition == FILE_SUPERSEDE) { return STATUS_INVALID_PARAMETER; } /* Deny create if the volume is locked */ if (DeviceExt->Flags & VCB_VOLUME_LOCKED) { return STATUS_ACCESS_DENIED; } FileObject = Stack->FileObject; if ((RequestedOptions & FILE_OPEN_BY_FILE_ID) == FILE_OPEN_BY_FILE_ID) { ULONGLONG MFTId; if (FileObject->FileName.Length != sizeof(ULONGLONG)) return STATUS_INVALID_PARAMETER; MFTId = (*(PULONGLONG)FileObject->FileName.Buffer) & NTFS_MFT_MASK; if (MFTId < NTFS_FILE_FIRST_USER_FILE) { Status = NtfsOpenFileById(DeviceExt, FileObject, MFTId, &Fcb); } else { Status = NtfsMoonWalkID(DeviceExt, MFTId, &FullPath); } if (!NT_SUCCESS(Status)) { return Status; } DPRINT1("Open by ID: %I64x -> %wZ\n", (*(PULONGLONG)FileObject->FileName.Buffer) & NTFS_MFT_MASK, &FullPath); } /* This a open operation for the volume itself */ if (FileObject->FileName.Length == 0 && (FileObject->RelatedFileObject == NULL || FileObject->RelatedFileObject->FsContext2 != NULL)) { if (RequestedDisposition != FILE_OPEN && RequestedDisposition != FILE_OPEN_IF) { return STATUS_ACCESS_DENIED; } if (RequestedOptions & FILE_DIRECTORY_FILE) { return STATUS_NOT_A_DIRECTORY; } NtfsAttachFCBToFileObject(DeviceExt, DeviceExt->VolumeFcb, FileObject); DeviceExt->VolumeFcb->RefCount++; Irp->IoStatus.Information = FILE_OPENED; return STATUS_SUCCESS; } if (Fcb == NULL) { Status = NtfsOpenFile(DeviceExt, FileObject, ((RequestedOptions & FILE_OPEN_BY_FILE_ID) ? FullPath.Buffer : FileObject->FileName.Buffer), BooleanFlagOn(Stack->Flags, SL_CASE_SENSITIVE), &Fcb); if (RequestedOptions & FILE_OPEN_BY_FILE_ID) { ExFreePoolWithTag(FullPath.Buffer, TAG_NTFS); } } if (NT_SUCCESS(Status)) { if (RequestedDisposition == FILE_CREATE) { Irp->IoStatus.Information = FILE_EXISTS; NtfsCloseFile(DeviceExt, FileObject); return STATUS_OBJECT_NAME_COLLISION; } if (RequestedOptions & FILE_NON_DIRECTORY_FILE && NtfsFCBIsDirectory(Fcb)) { NtfsCloseFile(DeviceExt, FileObject); return STATUS_FILE_IS_A_DIRECTORY; } if (RequestedOptions & FILE_DIRECTORY_FILE && !NtfsFCBIsDirectory(Fcb)) { NtfsCloseFile(DeviceExt, FileObject); return STATUS_NOT_A_DIRECTORY; } /* * If it is a reparse point & FILE_OPEN_REPARSE_POINT, then allow opening it * as a normal file. * Otherwise, attempt to read reparse data and hand them to the Io manager * with status reparse to force a reparse. */ if (NtfsFCBIsReparsePoint(Fcb) && ((RequestedOptions & FILE_OPEN_REPARSE_POINT) != FILE_OPEN_REPARSE_POINT)) { PREPARSE_DATA_BUFFER ReparseData = NULL; Status = NtfsReadFCBAttribute(DeviceExt, Fcb, AttributeReparsePoint, L"", 0, (PVOID *)&Irp->Tail.Overlay.AuxiliaryBuffer); if (NT_SUCCESS(Status)) { ReparseData = (PREPARSE_DATA_BUFFER)Irp->Tail.Overlay.AuxiliaryBuffer; if (ReparseData->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) { Status = STATUS_REPARSE; } else { Status = STATUS_NOT_IMPLEMENTED; ExFreePoolWithTag(ReparseData, TAG_NTFS); } } Irp->IoStatus.Information = ((Status == STATUS_REPARSE) ? ReparseData->ReparseTag : 0); NtfsCloseFile(DeviceExt, FileObject); return Status; } if (RequestedDisposition == FILE_OVERWRITE || RequestedDisposition == FILE_OVERWRITE_IF || RequestedDisposition == FILE_SUPERSEDE) { PFILE_RECORD_HEADER fileRecord = NULL; PNTFS_ATTR_CONTEXT dataContext = NULL; ULONG DataAttributeOffset; LARGE_INTEGER Zero; Zero.QuadPart = 0; if (!NtfsGlobalData->EnableWriteSupport) { DPRINT1("NTFS write-support is EXPERIMENTAL and is disabled by default!\n"); NtfsCloseFile(DeviceExt, FileObject); return STATUS_ACCESS_DENIED; } // TODO: check for appropriate access ExAcquireResourceExclusiveLite(&(Fcb->MainResource), TRUE); fileRecord = ExAllocateFromNPagedLookasideList(&Fcb->Vcb->FileRecLookasideList); if (fileRecord) { Status = ReadFileRecord(Fcb->Vcb, Fcb->MFTIndex, fileRecord); if (!NT_SUCCESS(Status)) goto DoneOverwriting; // find the data attribute and set it's length to 0 (TODO: Handle Alternate Data Streams) Status = FindAttribute(Fcb->Vcb, fileRecord, AttributeData, L"", 0, &dataContext, &DataAttributeOffset); if (!NT_SUCCESS(Status)) goto DoneOverwriting; Status = SetAttributeDataLength(FileObject, Fcb, dataContext, DataAttributeOffset, fileRecord, &Zero); } else { Status = STATUS_NO_MEMORY; } DoneOverwriting: if (fileRecord) ExFreeToNPagedLookasideList(&Fcb->Vcb->FileRecLookasideList, fileRecord); if (dataContext) ReleaseAttributeContext(dataContext); ExReleaseResourceLite(&(Fcb->MainResource)); if (!NT_SUCCESS(Status)) { NtfsCloseFile(DeviceExt, FileObject); return Status; } if (RequestedDisposition == FILE_SUPERSEDE) { Irp->IoStatus.Information = FILE_SUPERSEDED; } else { Irp->IoStatus.Information = FILE_OVERWRITTEN; } } } else { /* HUGLY HACK: Can't create new files yet... */ if (RequestedDisposition == FILE_CREATE || RequestedDisposition == FILE_OPEN_IF || RequestedDisposition == FILE_OVERWRITE_IF || RequestedDisposition == FILE_SUPERSEDE) { if (!NtfsGlobalData->EnableWriteSupport) { DPRINT1("NTFS write-support is EXPERIMENTAL and is disabled by default!\n"); NtfsCloseFile(DeviceExt, FileObject); return STATUS_ACCESS_DENIED; } // Was the user trying to create a directory? if (RequestedOptions & FILE_DIRECTORY_FILE) { // Create the directory on disk Status = NtfsCreateDirectory(DeviceExt, FileObject, BooleanFlagOn(Stack->Flags, SL_CASE_SENSITIVE), BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)); } else { // Create the file record on disk Status = NtfsCreateFileRecord(DeviceExt, FileObject, BooleanFlagOn(Stack->Flags, SL_CASE_SENSITIVE), BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)); } if (!NT_SUCCESS(Status)) { DPRINT1("ERROR: Couldn't create file record!\n"); return Status; } // Before we open the file/directory we just created, we need to change the disposition (upper 8 bits of ULONG) // from create to open, since we already created the file Stack->Parameters.Create.Options = (ULONG)FILE_OPEN << 24 | RequestedOptions; // Now we should be able to open the file using NtfsCreateFile() Status = NtfsCreateFile(DeviceObject, IrpContext); if (NT_SUCCESS(Status)) { // We need to change Irp->IoStatus.Information to reflect creation Irp->IoStatus.Information = FILE_CREATED; } return Status; } } if (NT_SUCCESS(Status)) { Fcb->OpenHandleCount++; DeviceExt->OpenHandleCount++; } /* * If the directory containing the file to open doesn't exist then * fail immediately */ Irp->IoStatus.Information = (NT_SUCCESS(Status)) ? FILE_OPENED : 0; return Status; } NTSTATUS NtfsCreate(PNTFS_IRP_CONTEXT IrpContext) { PDEVICE_EXTENSION DeviceExt; NTSTATUS Status; PDEVICE_OBJECT DeviceObject; DeviceObject = IrpContext->DeviceObject; if (DeviceObject == NtfsGlobalData->DeviceObject) { /* DeviceObject represents FileSystem instead of logical volume */ DPRINT("Opening file system\n"); IrpContext->Irp->IoStatus.Information = FILE_OPENED; return STATUS_SUCCESS; } DeviceExt = DeviceObject->DeviceExtension; if (!(IrpContext->Flags & IRPCONTEXT_CANWAIT)) { return NtfsMarkIrpContextForQueue(IrpContext); } ExAcquireResourceExclusiveLite(&DeviceExt->DirResource, TRUE); Status = NtfsCreateFile(DeviceObject, IrpContext); ExReleaseResourceLite(&DeviceExt->DirResource); return Status; } /** * @name NtfsCreateDirectory() * @implemented * * Creates a file record for a new directory and saves it to the MFT. Adds the filename attribute of the * created directory to the parent directory's index. * * @param DeviceExt * Points to the target disk's DEVICE_EXTENSION * * @param FileObject * Pointer to a FILE_OBJECT describing the directory to be created * * @param CaseSensitive * Boolean indicating if the function should operate in case-sensitive mode. This will be TRUE * if an application created the folder with the FILE_FLAG_POSIX_SEMANTICS flag. * * @param CanWait * Boolean indicating if the function is allowed to wait for exclusive access to the master file table. * This will only be relevant if the MFT doesn't have any free file records and needs to be enlarged. * * @return * STATUS_SUCCESS on success. * STATUS_INSUFFICIENT_RESOURCES if unable to allocate memory for the file record. * STATUS_CANT_WAIT if CanWait was FALSE and the function needed to resize the MFT but * couldn't get immediate, exclusive access to it. */ NTSTATUS NtfsCreateDirectory(PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject, BOOLEAN CaseSensitive, BOOLEAN CanWait) { NTSTATUS Status = STATUS_SUCCESS; PFILE_RECORD_HEADER FileRecord; PNTFS_ATTR_RECORD NextAttribute; PFILENAME_ATTRIBUTE FilenameAttribute; ULONGLONG ParentMftIndex; ULONGLONG FileMftIndex; PB_TREE Tree; PINDEX_ROOT_ATTRIBUTE NewIndexRoot; ULONG MaxIndexRootSize; ULONG RootLength; DPRINT("NtfsCreateFileRecord(%p, %p, %s, %s)\n", DeviceExt, FileObject, CaseSensitive ? "TRUE" : "FALSE", CanWait ? "TRUE" : "FALSE"); // Start with an empty file record FileRecord = NtfsCreateEmptyFileRecord(DeviceExt); if (!FileRecord) { DPRINT1("ERROR: Unable to allocate memory for file record!\n"); return STATUS_INSUFFICIENT_RESOURCES; } // Set the directory flag FileRecord->Flags |= FRH_DIRECTORY; // find where the first attribute will be added NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset); // add first attribute, $STANDARD_INFORMATION AddStandardInformation(FileRecord, NextAttribute); // advance NextAttribute pointer to the next attribute NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length); // Add the $FILE_NAME attribute AddFileName(FileRecord, NextAttribute, DeviceExt, FileObject, CaseSensitive, &ParentMftIndex); // save a pointer to the filename attribute FilenameAttribute = (PFILENAME_ATTRIBUTE)((ULONG_PTR)NextAttribute + NextAttribute->Resident.ValueOffset); // advance NextAttribute pointer to the next attribute NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length); // Create an empty b-tree to represent our new index Status = CreateEmptyBTree(&Tree); if (!NT_SUCCESS(Status)) { DPRINT1("ERROR: Failed to create empty B-Tree!\n"); ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord); return Status; } // Calculate maximum size of index root MaxIndexRootSize = DeviceExt->NtfsInfo.BytesPerFileRecord - ((ULONG_PTR)NextAttribute - (ULONG_PTR)FileRecord) - sizeof(ULONG) * 2; // Create a new index record from the tree Status = CreateIndexRootFromBTree(DeviceExt, Tree, MaxIndexRootSize, &NewIndexRoot, &RootLength); if (!NT_SUCCESS(Status)) { DPRINT1("ERROR: Unable to create empty index root!\n"); DestroyBTree(Tree); ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord); return Status; } // We're done with the B-Tree DestroyBTree(Tree); // add the $INDEX_ROOT attribute Status = AddIndexRoot(DeviceExt, FileRecord, NextAttribute, NewIndexRoot, RootLength, L"$I30", 4); if (!NT_SUCCESS(Status)) { DPRINT1("ERROR: Failed to add index root to new file record!\n"); ExFreePoolWithTag(NewIndexRoot, TAG_NTFS); ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord); return Status; } #ifndef NDEBUG NtfsDumpFileRecord(DeviceExt, FileRecord); #endif // Now that we've built the file record in memory, we need to store it in the MFT. Status = AddNewMftEntry(FileRecord, DeviceExt, &FileMftIndex, CanWait); if (NT_SUCCESS(Status)) { // The highest 2 bytes should be the sequence number, unless the parent happens to be root if (FileMftIndex == NTFS_FILE_ROOT) FileMftIndex = FileMftIndex + ((ULONGLONG)NTFS_FILE_ROOT << 48); else FileMftIndex = FileMftIndex + ((ULONGLONG)FileRecord->SequenceNumber << 48); DPRINT1("New File Reference: 0x%016I64x\n", FileMftIndex); // Add the filename attribute to the filename-index of the parent directory Status = NtfsAddFilenameToDirectory(DeviceExt, ParentMftIndex, FileMftIndex, FilenameAttribute, CaseSensitive); } ExFreePoolWithTag(NewIndexRoot, TAG_NTFS); ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord); return Status; } /** * @name NtfsCreateEmptyFileRecord * @implemented * * Creates a new, empty file record, with no attributes. * * @param DeviceExt * Pointer to the DEVICE_EXTENSION of the target volume the file record will be stored on. * * @return * A pointer to the newly-created FILE_RECORD_HEADER if the function succeeds, NULL otherwise. */ PFILE_RECORD_HEADER NtfsCreateEmptyFileRecord(PDEVICE_EXTENSION DeviceExt) { PFILE_RECORD_HEADER FileRecord; PNTFS_ATTR_RECORD NextAttribute; DPRINT("NtfsCreateEmptyFileRecord(%p)\n", DeviceExt); // allocate memory for file record FileRecord = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList); if (!FileRecord) { DPRINT1("ERROR: Unable to allocate memory for file record!\n"); return NULL; } RtlZeroMemory(FileRecord, DeviceExt->NtfsInfo.BytesPerFileRecord); FileRecord->Ntfs.Type = NRH_FILE_TYPE; // calculate USA offset and count FileRecord->Ntfs.UsaOffset = FIELD_OFFSET(FILE_RECORD_HEADER, MFTRecordNumber) + sizeof(ULONG); // size of USA (in ULONG's) will be 1 (for USA number) + 1 for every sector the file record uses FileRecord->BytesAllocated = DeviceExt->NtfsInfo.BytesPerFileRecord; FileRecord->Ntfs.UsaCount = (FileRecord->BytesAllocated / DeviceExt->NtfsInfo.BytesPerSector) + 1; // setup other file record fields FileRecord->SequenceNumber = 1; FileRecord->AttributeOffset = FileRecord->Ntfs.UsaOffset + (2 * FileRecord->Ntfs.UsaCount); FileRecord->AttributeOffset = ALIGN_UP_BY(FileRecord->AttributeOffset, ATTR_RECORD_ALIGNMENT); FileRecord->Flags = FRH_IN_USE; FileRecord->BytesInUse = FileRecord->AttributeOffset + sizeof(ULONG) * 2; // find where the first attribute will be added NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset); // mark the (temporary) end of the file-record NextAttribute->Type = AttributeEnd; NextAttribute->Length = FILE_RECORD_END; return FileRecord; } /** * @name NtfsCreateFileRecord() * @implemented * * Creates a file record and saves it to the MFT. Adds the filename attribute of the * created file to the parent directory's index. * * @param DeviceExt * Points to the target disk's DEVICE_EXTENSION * * @param FileObject * Pointer to a FILE_OBJECT describing the file to be created * * @param CanWait * Boolean indicating if the function is allowed to wait for exclusive access to the master file table. * This will only be relevant if the MFT doesn't have any free file records and needs to be enlarged. * * @return * STATUS_SUCCESS on success. * STATUS_INSUFFICIENT_RESOURCES if unable to allocate memory for the file record. * STATUS_CANT_WAIT if CanWait was FALSE and the function needed to resize the MFT but * couldn't get immediate, exclusive access to it. */ NTSTATUS NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject, BOOLEAN CaseSensitive, BOOLEAN CanWait) { NTSTATUS Status = STATUS_SUCCESS; PFILE_RECORD_HEADER FileRecord; PNTFS_ATTR_RECORD NextAttribute; PFILENAME_ATTRIBUTE FilenameAttribute; ULONGLONG ParentMftIndex; ULONGLONG FileMftIndex; DPRINT("NtfsCreateFileRecord(%p, %p, %s, %s)\n", DeviceExt, FileObject, CaseSensitive ? "TRUE" : "FALSE", CanWait ? "TRUE" : "FALSE"); // allocate memory for file record FileRecord = NtfsCreateEmptyFileRecord(DeviceExt); if (!FileRecord) { DPRINT1("ERROR: Unable to allocate memory for file record!\n"); return STATUS_INSUFFICIENT_RESOURCES; } // find where the first attribute will be added NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset); // add first attribute, $STANDARD_INFORMATION AddStandardInformation(FileRecord, NextAttribute); // advance NextAttribute pointer to the next attribute NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length); // Add the $FILE_NAME attribute AddFileName(FileRecord, NextAttribute, DeviceExt, FileObject, CaseSensitive, &ParentMftIndex); // save a pointer to the filename attribute FilenameAttribute = (PFILENAME_ATTRIBUTE)((ULONG_PTR)NextAttribute + NextAttribute->Resident.ValueOffset); // advance NextAttribute pointer to the next attribute NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length); // add the $DATA attribute AddData(FileRecord, NextAttribute); #ifndef NDEBUG // dump file record in memory (for debugging) NtfsDumpFileRecord(DeviceExt, FileRecord); #endif // Now that we've built the file record in memory, we need to store it in the MFT. Status = AddNewMftEntry(FileRecord, DeviceExt, &FileMftIndex, CanWait); if (NT_SUCCESS(Status)) { // The highest 2 bytes should be the sequence number, unless the parent happens to be root if (FileMftIndex == NTFS_FILE_ROOT) FileMftIndex = FileMftIndex + ((ULONGLONG)NTFS_FILE_ROOT << 48); else FileMftIndex = FileMftIndex + ((ULONGLONG)FileRecord->SequenceNumber << 48); DPRINT1("New File Reference: 0x%016I64x\n", FileMftIndex); // Add the filename attribute to the filename-index of the parent directory Status = NtfsAddFilenameToDirectory(DeviceExt, ParentMftIndex, FileMftIndex, FilenameAttribute, CaseSensitive); } ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord); return Status; } /* EOF */