Something went wrong. Try again.
Reactos
Something went wrong. Try again.
32 kB · 963 lines
C
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964/* * 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 <debug.h>
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 ****************************************************************/
staticNTSTATUSNtfsMakeAbsoluteFilename(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;}
staticNTSTATUSNtfsMoonWalkID(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;}
staticNTSTATUSNtfsOpenFileById(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 */staticNTSTATUSNtfsOpenFile(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 */staticNTSTATUSNtfsCreateFile(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;}
NTSTATUSNtfsCreate(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.*/NTSTATUSNtfsCreateDirectory(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_HEADERNtfsCreateEmptyFileRecord(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.*/NTSTATUSNtfsCreateFileRecord(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 */