1c2c66affSColin Finck /*
2c2c66affSColin Finck * ReactOS kernel
3c2c66affSColin Finck * Copyright (C) 2002, 2014 ReactOS Team
4c2c66affSColin Finck *
5c2c66affSColin Finck * This program is free software; you can redistribute it and/or modify
6c2c66affSColin Finck * it under the terms of the GNU General Public License as published by
7c2c66affSColin Finck * the Free Software Foundation; either version 2 of the License, or
8c2c66affSColin Finck * (at your option) any later version.
9c2c66affSColin Finck *
10c2c66affSColin Finck * This program is distributed in the hope that it will be useful,
11c2c66affSColin Finck * but WITHOUT ANY WARRANTY; without even the implied warranty of
12c2c66affSColin Finck * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13c2c66affSColin Finck * GNU General Public License for more details.
14c2c66affSColin Finck *
15c2c66affSColin Finck * You should have received a copy of the GNU General Public License
16c2c66affSColin Finck * along with this program; if not, write to the Free Software
17c2c66affSColin Finck * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
18c2c66affSColin Finck *
19c2c66affSColin Finck * COPYRIGHT: See COPYING in the top level directory
20c2c66affSColin Finck * PROJECT: ReactOS kernel
21c2c66affSColin Finck * FILE: drivers/filesystem/ntfs/mft.c
22c2c66affSColin Finck * PURPOSE: NTFS filesystem driver
23c2c66affSColin Finck * PROGRAMMERS: Eric Kohl
24c2c66affSColin Finck * Valentin Verkhovsky
25c2c66affSColin Finck * Pierre Schweitzer (pierre@reactos.org)
26c2c66affSColin Finck * Hervé Poussineau (hpoussin@reactos.org)
2758a13831STrevor Thompson * Trevor Thompson
28c2c66affSColin Finck */
29c2c66affSColin Finck
30c2c66affSColin Finck /* INCLUDES *****************************************************************/
31c2c66affSColin Finck
32c2c66affSColin Finck #include "ntfs.h"
335e7c1184STrevor Thompson #include <ntintsafe.h>
34c2c66affSColin Finck
35c2c66affSColin Finck #define NDEBUG
36c2c66affSColin Finck #include <debug.h>
37c2c66affSColin Finck
38c2c66affSColin Finck /* FUNCTIONS ****************************************************************/
39c2c66affSColin Finck
40c2c66affSColin Finck PNTFS_ATTR_CONTEXT
PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord)41c2c66affSColin Finck PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord)
42c2c66affSColin Finck {
43c2c66affSColin Finck PNTFS_ATTR_CONTEXT Context;
44c2c66affSColin Finck
453ddf44ffSPierre Schweitzer Context = ExAllocateFromNPagedLookasideList(&NtfsGlobalData->AttrCtxtLookasideList);
464dfcd1d5STrevor Thompson if(!Context)
474dfcd1d5STrevor Thompson {
484dfcd1d5STrevor Thompson DPRINT1("Error: Unable to allocate memory for context!\n");
494dfcd1d5STrevor Thompson return NULL;
504dfcd1d5STrevor Thompson }
514dfcd1d5STrevor Thompson
524dfcd1d5STrevor Thompson // Allocate memory for a copy of the attribute
534dfcd1d5STrevor Thompson Context->pRecord = ExAllocatePoolWithTag(NonPagedPool, AttrRecord->Length, TAG_NTFS);
544dfcd1d5STrevor Thompson if(!Context->pRecord)
554dfcd1d5STrevor Thompson {
564dfcd1d5STrevor Thompson DPRINT1("Error: Unable to allocate memory for attribute record!\n");
573ddf44ffSPierre Schweitzer ExFreeToNPagedLookasideList(&NtfsGlobalData->AttrCtxtLookasideList, Context);
584dfcd1d5STrevor Thompson return NULL;
594dfcd1d5STrevor Thompson }
604dfcd1d5STrevor Thompson
614dfcd1d5STrevor Thompson // Copy the attribute
624dfcd1d5STrevor Thompson RtlCopyMemory(Context->pRecord, AttrRecord, AttrRecord->Length);
634dfcd1d5STrevor Thompson
64c2c66affSColin Finck if (AttrRecord->IsNonResident)
65c2c66affSColin Finck {
66c2c66affSColin Finck LONGLONG DataRunOffset;
67c2c66affSColin Finck ULONGLONG DataRunLength;
6852b9f467STrevor Thompson ULONGLONG NextVBN = 0;
694dfcd1d5STrevor Thompson PUCHAR DataRun = (PUCHAR)((ULONG_PTR)Context->pRecord + Context->pRecord->NonResident.MappingPairsOffset);
70c2c66affSColin Finck
7152b9f467STrevor Thompson Context->CacheRun = DataRun;
72c2c66affSColin Finck Context->CacheRunOffset = 0;
73c2c66affSColin Finck Context->CacheRun = DecodeRun(Context->CacheRun, &DataRunOffset, &DataRunLength);
74c2c66affSColin Finck Context->CacheRunLength = DataRunLength;
75c2c66affSColin Finck if (DataRunOffset != -1)
76c2c66affSColin Finck {
77c2c66affSColin Finck /* Normal run. */
78c2c66affSColin Finck Context->CacheRunStartLCN =
79c2c66affSColin Finck Context->CacheRunLastLCN = DataRunOffset;
80c2c66affSColin Finck }
81c2c66affSColin Finck else
82c2c66affSColin Finck {
83c2c66affSColin Finck /* Sparse run. */
84c2c66affSColin Finck Context->CacheRunStartLCN = -1;
85c2c66affSColin Finck Context->CacheRunLastLCN = 0;
86c2c66affSColin Finck }
87c2c66affSColin Finck Context->CacheRunCurrentOffset = 0;
8852b9f467STrevor Thompson
8952b9f467STrevor Thompson // Convert the data runs to a map control block
9052b9f467STrevor Thompson if (!NT_SUCCESS(ConvertDataRunsToLargeMCB(DataRun, &Context->DataRunsMCB, &NextVBN)))
9152b9f467STrevor Thompson {
9252b9f467STrevor Thompson DPRINT1("Unable to convert data runs to MCB!\n");
934dfcd1d5STrevor Thompson ExFreePoolWithTag(Context->pRecord, TAG_NTFS);
943ddf44ffSPierre Schweitzer ExFreeToNPagedLookasideList(&NtfsGlobalData->AttrCtxtLookasideList, Context);
9552b9f467STrevor Thompson return NULL;
9652b9f467STrevor Thompson }
97c2c66affSColin Finck }
98c2c66affSColin Finck
99c2c66affSColin Finck return Context;
100c2c66affSColin Finck }
101c2c66affSColin Finck
102c2c66affSColin Finck
103c2c66affSColin Finck VOID
ReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context)104c2c66affSColin Finck ReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context)
105c2c66affSColin Finck {
106849fa7fbSPierre Schweitzer if (Context->pRecord)
107849fa7fbSPierre Schweitzer {
1084dfcd1d5STrevor Thompson if (Context->pRecord->IsNonResident)
10952b9f467STrevor Thompson {
11052b9f467STrevor Thompson FsRtlUninitializeLargeMcb(&Context->DataRunsMCB);
11152b9f467STrevor Thompson }
11252b9f467STrevor Thompson
1134dfcd1d5STrevor Thompson ExFreePoolWithTag(Context->pRecord, TAG_NTFS);
114849fa7fbSPierre Schweitzer }
1154dfcd1d5STrevor Thompson
1163ddf44ffSPierre Schweitzer ExFreeToNPagedLookasideList(&NtfsGlobalData->AttrCtxtLookasideList, Context);
117c2c66affSColin Finck }
118c2c66affSColin Finck
119c2c66affSColin Finck
120ba33b9faSTrevor Thompson /**
121ba33b9faSTrevor Thompson * @name FindAttribute
122ba33b9faSTrevor Thompson * @implemented
123ba33b9faSTrevor Thompson *
124ba33b9faSTrevor Thompson * Searches a file record for an attribute matching the given type and name.
125ba33b9faSTrevor Thompson *
126ba33b9faSTrevor Thompson * @param Offset
127ba33b9faSTrevor Thompson * Optional pointer to a ULONG that will receive the offset of the found attribute
128ba33b9faSTrevor Thompson * from the beginning of the record. Can be set to NULL.
129ba33b9faSTrevor Thompson */
130c2c66affSColin Finck NTSTATUS
FindAttribute(PDEVICE_EXTENSION Vcb,PFILE_RECORD_HEADER MftRecord,ULONG Type,PCWSTR Name,ULONG NameLength,PNTFS_ATTR_CONTEXT * AttrCtx,PULONG Offset)131c2c66affSColin Finck FindAttribute(PDEVICE_EXTENSION Vcb,
132c2c66affSColin Finck PFILE_RECORD_HEADER MftRecord,
133c2c66affSColin Finck ULONG Type,
134c2c66affSColin Finck PCWSTR Name,
135c2c66affSColin Finck ULONG NameLength,
136ba33b9faSTrevor Thompson PNTFS_ATTR_CONTEXT * AttrCtx,
137ba33b9faSTrevor Thompson PULONG Offset)
138c2c66affSColin Finck {
139c2c66affSColin Finck BOOLEAN Found;
140c2c66affSColin Finck NTSTATUS Status;
141c2c66affSColin Finck FIND_ATTR_CONTXT Context;
142c2c66affSColin Finck PNTFS_ATTR_RECORD Attribute;
143*4d3df0daSSylvain Deverre PNTFS_ATTRIBUTE_LIST_ITEM AttrListItem;
144c2c66affSColin Finck
14538c947b7STrevor Thompson DPRINT("FindAttribute(%p, %p, 0x%x, %S, %lu, %p, %p)\n", Vcb, MftRecord, Type, Name, NameLength, AttrCtx, Offset);
146c2c66affSColin Finck
147c2c66affSColin Finck Found = FALSE;
148c2c66affSColin Finck Status = FindFirstAttribute(&Context, Vcb, MftRecord, FALSE, &Attribute);
149c2c66affSColin Finck while (NT_SUCCESS(Status))
150c2c66affSColin Finck {
151c2c66affSColin Finck if (Attribute->Type == Type && Attribute->NameLength == NameLength)
152c2c66affSColin Finck {
153c2c66affSColin Finck if (NameLength != 0)
154c2c66affSColin Finck {
155c2c66affSColin Finck PWCHAR AttrName;
156c2c66affSColin Finck
157c2c66affSColin Finck AttrName = (PWCHAR)((PCHAR)Attribute + Attribute->NameOffset);
158c2c66affSColin Finck DPRINT("%.*S, %.*S\n", Attribute->NameLength, AttrName, NameLength, Name);
15968a48b27STrevor Thompson if (RtlCompareMemory(AttrName, Name, NameLength * sizeof(WCHAR)) == (NameLength * sizeof(WCHAR)))
160c2c66affSColin Finck {
161c2c66affSColin Finck Found = TRUE;
162c2c66affSColin Finck }
163c2c66affSColin Finck }
164c2c66affSColin Finck else
165c2c66affSColin Finck {
166c2c66affSColin Finck Found = TRUE;
167c2c66affSColin Finck }
168c2c66affSColin Finck
169c2c66affSColin Finck if (Found)
170c2c66affSColin Finck {
171c2c66affSColin Finck /* Found it, fill up the context and return. */
172c2c66affSColin Finck DPRINT("Found context\n");
173c2c66affSColin Finck *AttrCtx = PrepareAttributeContext(Attribute);
174ba33b9faSTrevor Thompson
175760cdfb5STrevor Thompson (*AttrCtx)->FileMFTIndex = MftRecord->MFTRecordNumber;
176760cdfb5STrevor Thompson
177ba33b9faSTrevor Thompson if (Offset != NULL)
178ba33b9faSTrevor Thompson *Offset = Context.Offset;
179ba33b9faSTrevor Thompson
180c2c66affSColin Finck FindCloseAttribute(&Context);
181c2c66affSColin Finck return STATUS_SUCCESS;
182c2c66affSColin Finck }
183c2c66affSColin Finck }
184c2c66affSColin Finck
185c2c66affSColin Finck Status = FindNextAttribute(&Context, &Attribute);
186c2c66affSColin Finck }
187c2c66affSColin Finck
188*4d3df0daSSylvain Deverre /* No attribute found, check if it is referenced in another file record */
189*4d3df0daSSylvain Deverre Status = FindFirstAttributeListItem(&Context, &AttrListItem);
190*4d3df0daSSylvain Deverre while (NT_SUCCESS(Status))
191*4d3df0daSSylvain Deverre {
192*4d3df0daSSylvain Deverre if (AttrListItem->Type == Type && AttrListItem->NameLength == NameLength)
193*4d3df0daSSylvain Deverre {
194*4d3df0daSSylvain Deverre if (NameLength != 0)
195*4d3df0daSSylvain Deverre {
196*4d3df0daSSylvain Deverre PWCHAR AttrName;
197*4d3df0daSSylvain Deverre
198*4d3df0daSSylvain Deverre AttrName = (PWCHAR)((PCHAR)AttrListItem + AttrListItem->NameOffset);
199*4d3df0daSSylvain Deverre DPRINT("%.*S, %.*S\n", AttrListItem->NameLength, AttrName, NameLength, Name);
200*4d3df0daSSylvain Deverre if (RtlCompareMemory(AttrName, Name, NameLength * sizeof(WCHAR)) == (NameLength * sizeof(WCHAR)))
201*4d3df0daSSylvain Deverre {
202*4d3df0daSSylvain Deverre Found = TRUE;
203*4d3df0daSSylvain Deverre }
204*4d3df0daSSylvain Deverre }
205*4d3df0daSSylvain Deverre else
206*4d3df0daSSylvain Deverre {
207*4d3df0daSSylvain Deverre Found = TRUE;
208*4d3df0daSSylvain Deverre }
209*4d3df0daSSylvain Deverre
210*4d3df0daSSylvain Deverre if (Found == TRUE)
211*4d3df0daSSylvain Deverre {
212*4d3df0daSSylvain Deverre /* Get the MFT Index of attribute */
213*4d3df0daSSylvain Deverre ULONGLONG MftIndex;
214*4d3df0daSSylvain Deverre PFILE_RECORD_HEADER RemoteHdr;
215*4d3df0daSSylvain Deverre
216*4d3df0daSSylvain Deverre MftIndex = AttrListItem->MFTIndex & NTFS_MFT_MASK;
217*4d3df0daSSylvain Deverre RemoteHdr = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
218*4d3df0daSSylvain Deverre
219*4d3df0daSSylvain Deverre if (RemoteHdr == NULL)
220*4d3df0daSSylvain Deverre {
221*4d3df0daSSylvain Deverre FindCloseAttribute(&Context);
222*4d3df0daSSylvain Deverre return STATUS_INSUFFICIENT_RESOURCES;
223*4d3df0daSSylvain Deverre }
224*4d3df0daSSylvain Deverre
225*4d3df0daSSylvain Deverre /* Check we are not reading ourselves */
226*4d3df0daSSylvain Deverre if (MftRecord->MFTRecordNumber == MftIndex)
227*4d3df0daSSylvain Deverre {
228*4d3df0daSSylvain Deverre DPRINT1("Attribute list references missing attribute to this file entry !");
229*4d3df0daSSylvain Deverre ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, RemoteHdr);
230*4d3df0daSSylvain Deverre FindCloseAttribute(&Context);
231*4d3df0daSSylvain Deverre return STATUS_OBJECT_NAME_NOT_FOUND;
232*4d3df0daSSylvain Deverre }
233*4d3df0daSSylvain Deverre /* Read the new file record */
234*4d3df0daSSylvain Deverre ReadFileRecord(Vcb, MftIndex, RemoteHdr);
235*4d3df0daSSylvain Deverre Status = FindAttribute(Vcb, RemoteHdr, Type, Name, NameLength, AttrCtx, Offset);
236*4d3df0daSSylvain Deverre ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, RemoteHdr);
237*4d3df0daSSylvain Deverre FindCloseAttribute(&Context);
238*4d3df0daSSylvain Deverre return Status;
239*4d3df0daSSylvain Deverre }
240*4d3df0daSSylvain Deverre }
241*4d3df0daSSylvain Deverre Status = FindNextAttributeListItem(&Context, &AttrListItem);
242*4d3df0daSSylvain Deverre }
243c2c66affSColin Finck FindCloseAttribute(&Context);
244c2c66affSColin Finck return STATUS_OBJECT_NAME_NOT_FOUND;
245c2c66affSColin Finck }
246c2c66affSColin Finck
247c2c66affSColin Finck
24877fc65dcSTrevor Thompson ULONGLONG
AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord)249c2c66affSColin Finck AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord)
250c2c66affSColin Finck {
251c2c66affSColin Finck if (AttrRecord->IsNonResident)
252c2c66affSColin Finck return AttrRecord->NonResident.AllocatedSize;
253c2c66affSColin Finck else
254f2e47474STrevor Thompson return ALIGN_UP_BY(AttrRecord->Resident.ValueLength, ATTR_RECORD_ALIGNMENT);
255c2c66affSColin Finck }
256c2c66affSColin Finck
257c2c66affSColin Finck
258c2c66affSColin Finck ULONGLONG
AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord)259c2c66affSColin Finck AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord)
260c2c66affSColin Finck {
261c2c66affSColin Finck if (AttrRecord->IsNonResident)
262c2c66affSColin Finck return AttrRecord->NonResident.DataSize;
263c2c66affSColin Finck else
264c2c66affSColin Finck return AttrRecord->Resident.ValueLength;
265c2c66affSColin Finck }
266c2c66affSColin Finck
2679ab86116STrevor Thompson /**
2689ab86116STrevor Thompson * @name IncreaseMftSize
2699ab86116STrevor Thompson * @implemented
2709ab86116STrevor Thompson *
2719ab86116STrevor Thompson * Increases the size of the master file table on a volume, increasing the space available for file records.
2729ab86116STrevor Thompson *
2739ab86116STrevor Thompson * @param Vcb
2749ab86116STrevor Thompson * Pointer to the VCB (DEVICE_EXTENSION) of the target volume.
2759ab86116STrevor Thompson *
27698ddf610STrevor Thompson *
27798ddf610STrevor Thompson * @param CanWait
27898ddf610STrevor Thompson * Boolean indicating if the function is allowed to wait for exclusive access to the master file table.
27998ddf610STrevor Thompson * This will only be relevant if the MFT doesn't have any free file records and needs to be enlarged.
28098ddf610STrevor Thompson *
2819ab86116STrevor Thompson * @return
2829ab86116STrevor Thompson * STATUS_SUCCESS on success.
2839ab86116STrevor Thompson * STATUS_INSUFFICIENT_RESOURCES if an allocation fails.
2849ab86116STrevor Thompson * STATUS_INVALID_PARAMETER if there was an error reading the Mft's bitmap.
28598ddf610STrevor Thompson * STATUS_CANT_WAIT if CanWait was FALSE and the function could not get immediate, exclusive access to the MFT.
2869ab86116STrevor Thompson *
2879ab86116STrevor Thompson * @remarks
2885e7c1184STrevor Thompson * Increases the size of the Master File Table by 64 records. Bitmap entries for the new records are cleared,
2899ab86116STrevor Thompson * and the bitmap is also enlarged if needed. Mimicking Windows' behavior when enlarging the mft is still TODO.
2909ab86116STrevor Thompson * This function will wait for exlusive access to the volume fcb.
2919ab86116STrevor Thompson */
2929ab86116STrevor Thompson NTSTATUS
IncreaseMftSize(PDEVICE_EXTENSION Vcb,BOOLEAN CanWait)29398ddf610STrevor Thompson IncreaseMftSize(PDEVICE_EXTENSION Vcb, BOOLEAN CanWait)
2949ab86116STrevor Thompson {
2959ab86116STrevor Thompson PNTFS_ATTR_CONTEXT BitmapContext;
2969ab86116STrevor Thompson LARGE_INTEGER BitmapSize;
2979ab86116STrevor Thompson LARGE_INTEGER DataSize;
2989ab86116STrevor Thompson LONGLONG BitmapSizeDifference;
2995e7c1184STrevor Thompson ULONG NewRecords = ATTR_RECORD_ALIGNMENT * 8; // Allocate one new record for every bit of every byte we'll be adding to the bitmap
3005e7c1184STrevor Thompson ULONG DataSizeDifference = Vcb->NtfsInfo.BytesPerFileRecord * NewRecords;
3019ab86116STrevor Thompson ULONG BitmapOffset;
3029ab86116STrevor Thompson PUCHAR BitmapBuffer;
3039ab86116STrevor Thompson ULONGLONG BitmapBytes;
3049ab86116STrevor Thompson ULONGLONG NewBitmapSize;
3055e7c1184STrevor Thompson ULONGLONG FirstNewMftIndex;
3069ab86116STrevor Thompson ULONG BytesRead;
3079ab86116STrevor Thompson ULONG LengthWritten;
3085e7c1184STrevor Thompson PFILE_RECORD_HEADER BlankFileRecord;
3095e7c1184STrevor Thompson ULONG i;
3109ab86116STrevor Thompson NTSTATUS Status;
3119ab86116STrevor Thompson
31298ddf610STrevor Thompson DPRINT1("IncreaseMftSize(%p, %s)\n", Vcb, CanWait ? "TRUE" : "FALSE");
3139ab86116STrevor Thompson
3149ab86116STrevor Thompson // We need exclusive access to the mft while we change its size
31598ddf610STrevor Thompson if (!ExAcquireResourceExclusiveLite(&(Vcb->DirResource), CanWait))
3169ab86116STrevor Thompson {
3179ab86116STrevor Thompson return STATUS_CANT_WAIT;
3189ab86116STrevor Thompson }
3199ab86116STrevor Thompson
3205e7c1184STrevor Thompson // Create a blank file record that will be used later
3215e7c1184STrevor Thompson BlankFileRecord = NtfsCreateEmptyFileRecord(Vcb);
3225e7c1184STrevor Thompson if (!BlankFileRecord)
3235e7c1184STrevor Thompson {
3245e7c1184STrevor Thompson DPRINT1("Error: Unable to create empty file record!\n");
3255e7c1184STrevor Thompson return STATUS_INSUFFICIENT_RESOURCES;
3265e7c1184STrevor Thompson }
3275e7c1184STrevor Thompson
3285e7c1184STrevor Thompson // Clear the flags (file record is not in use)
3295e7c1184STrevor Thompson BlankFileRecord->Flags = 0;
3305e7c1184STrevor Thompson
3319ab86116STrevor Thompson // Find the bitmap attribute of master file table
3325e7c1184STrevor Thompson Status = FindAttribute(Vcb, Vcb->MasterFileTable, AttributeBitmap, L"", 0, &BitmapContext, NULL);
3339ab86116STrevor Thompson if (!NT_SUCCESS(Status))
3349ab86116STrevor Thompson {
3359ab86116STrevor Thompson DPRINT1("ERROR: Couldn't find $BITMAP attribute of Mft!\n");
336216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
3379ab86116STrevor Thompson ExReleaseResourceLite(&(Vcb->DirResource));
3389ab86116STrevor Thompson return Status;
3399ab86116STrevor Thompson }
3409ab86116STrevor Thompson
3419ab86116STrevor Thompson // Get size of Bitmap Attribute
3424dfcd1d5STrevor Thompson BitmapSize.QuadPart = AttributeDataLength(BitmapContext->pRecord);
3439ab86116STrevor Thompson
3449ab86116STrevor Thompson // Calculate the new mft size
3454dfcd1d5STrevor Thompson DataSize.QuadPart = AttributeDataLength(Vcb->MFTContext->pRecord) + DataSizeDifference;
3469ab86116STrevor Thompson
3475e7c1184STrevor Thompson // Find the index of the first Mft entry that will be created
3485e7c1184STrevor Thompson FirstNewMftIndex = AttributeDataLength(Vcb->MFTContext->pRecord) / Vcb->NtfsInfo.BytesPerFileRecord;
3495e7c1184STrevor Thompson
3509ab86116STrevor Thompson // Determine how many bytes will make up the bitmap
3519ab86116STrevor Thompson BitmapBytes = DataSize.QuadPart / Vcb->NtfsInfo.BytesPerFileRecord / 8;
3525e7c1184STrevor Thompson if ((DataSize.QuadPart / Vcb->NtfsInfo.BytesPerFileRecord) % 8 != 0)
3535e7c1184STrevor Thompson BitmapBytes++;
3545e7c1184STrevor Thompson
3555e7c1184STrevor Thompson // Windows will always keep the number of bytes in a bitmap as a multiple of 8, so no bytes are wasted on slack
3565e7c1184STrevor Thompson BitmapBytes = ALIGN_UP_BY(BitmapBytes, ATTR_RECORD_ALIGNMENT);
3579ab86116STrevor Thompson
3589ab86116STrevor Thompson // Determine how much we need to adjust the bitmap size (it's possible we don't)
3599ab86116STrevor Thompson BitmapSizeDifference = BitmapBytes - BitmapSize.QuadPart;
3609ab86116STrevor Thompson NewBitmapSize = max(BitmapSize.QuadPart + BitmapSizeDifference, BitmapSize.QuadPart);
3619ab86116STrevor Thompson
3629ab86116STrevor Thompson // Allocate memory for the bitmap
3639ab86116STrevor Thompson BitmapBuffer = ExAllocatePoolWithTag(NonPagedPool, NewBitmapSize, TAG_NTFS);
3649ab86116STrevor Thompson if (!BitmapBuffer)
3659ab86116STrevor Thompson {
3669ab86116STrevor Thompson DPRINT1("ERROR: Unable to allocate memory for bitmap attribute!\n");
367216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
3689ab86116STrevor Thompson ExReleaseResourceLite(&(Vcb->DirResource));
3699ab86116STrevor Thompson ReleaseAttributeContext(BitmapContext);
3709ab86116STrevor Thompson return STATUS_INSUFFICIENT_RESOURCES;
3719ab86116STrevor Thompson }
3729ab86116STrevor Thompson
3739ab86116STrevor Thompson // Zero the bytes we'll be adding
37468a48b27STrevor Thompson RtlZeroMemory(BitmapBuffer, NewBitmapSize);
3759ab86116STrevor Thompson
3769ab86116STrevor Thompson // Read the bitmap attribute
3779ab86116STrevor Thompson BytesRead = ReadAttribute(Vcb,
3789ab86116STrevor Thompson BitmapContext,
3799ab86116STrevor Thompson 0,
3809ab86116STrevor Thompson (PCHAR)BitmapBuffer,
3819ab86116STrevor Thompson BitmapSize.LowPart);
3829ab86116STrevor Thompson if (BytesRead != BitmapSize.LowPart)
3839ab86116STrevor Thompson {
3849ab86116STrevor Thompson DPRINT1("ERROR: Bytes read != Bitmap size!\n");
385216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
3869ab86116STrevor Thompson ExReleaseResourceLite(&(Vcb->DirResource));
3879ab86116STrevor Thompson ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
3889ab86116STrevor Thompson ReleaseAttributeContext(BitmapContext);
3899ab86116STrevor Thompson return STATUS_INVALID_PARAMETER;
3909ab86116STrevor Thompson }
3919ab86116STrevor Thompson
3929ab86116STrevor Thompson // Increase the mft size
3939ab86116STrevor Thompson Status = SetNonResidentAttributeDataLength(Vcb, Vcb->MFTContext, Vcb->MftDataOffset, Vcb->MasterFileTable, &DataSize);
3949ab86116STrevor Thompson if (!NT_SUCCESS(Status))
3959ab86116STrevor Thompson {
3969ab86116STrevor Thompson DPRINT1("ERROR: Failed to set size of $MFT data attribute!\n");
397216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
3989ab86116STrevor Thompson ExReleaseResourceLite(&(Vcb->DirResource));
3999ab86116STrevor Thompson ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
4009ab86116STrevor Thompson ReleaseAttributeContext(BitmapContext);
4019ab86116STrevor Thompson return Status;
4029ab86116STrevor Thompson }
4039ab86116STrevor Thompson
4045e7c1184STrevor Thompson // We'll need to find the bitmap again, because its offset will have changed after resizing the data attribute
4055e7c1184STrevor Thompson ReleaseAttributeContext(BitmapContext);
4065e7c1184STrevor Thompson Status = FindAttribute(Vcb, Vcb->MasterFileTable, AttributeBitmap, L"", 0, &BitmapContext, &BitmapOffset);
4075e7c1184STrevor Thompson if (!NT_SUCCESS(Status))
4085e7c1184STrevor Thompson {
4095e7c1184STrevor Thompson DPRINT1("ERROR: Couldn't find $BITMAP attribute of Mft!\n");
410216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
4115e7c1184STrevor Thompson ExReleaseResourceLite(&(Vcb->DirResource));
4125e7c1184STrevor Thompson return Status;
4135e7c1184STrevor Thompson }
4145e7c1184STrevor Thompson
4159ab86116STrevor Thompson // If the bitmap grew
4169ab86116STrevor Thompson if (BitmapSizeDifference > 0)
4179ab86116STrevor Thompson {
4189ab86116STrevor Thompson // Set the new bitmap size
4195e7c1184STrevor Thompson BitmapSize.QuadPart = NewBitmapSize;
4204dfcd1d5STrevor Thompson if (BitmapContext->pRecord->IsNonResident)
4219ab86116STrevor Thompson Status = SetNonResidentAttributeDataLength(Vcb, BitmapContext, BitmapOffset, Vcb->MasterFileTable, &BitmapSize);
4229ab86116STrevor Thompson else
4239ab86116STrevor Thompson Status = SetResidentAttributeDataLength(Vcb, BitmapContext, BitmapOffset, Vcb->MasterFileTable, &BitmapSize);
4249ab86116STrevor Thompson
4259ab86116STrevor Thompson if (!NT_SUCCESS(Status))
4269ab86116STrevor Thompson {
4279ab86116STrevor Thompson DPRINT1("ERROR: Failed to set size of bitmap attribute!\n");
428216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
4299ab86116STrevor Thompson ExReleaseResourceLite(&(Vcb->DirResource));
4309ab86116STrevor Thompson ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
4319ab86116STrevor Thompson ReleaseAttributeContext(BitmapContext);
4329ab86116STrevor Thompson return Status;
4339ab86116STrevor Thompson }
4349ab86116STrevor Thompson }
4359ab86116STrevor Thompson
4365e7c1184STrevor Thompson NtfsDumpFileAttributes(Vcb, Vcb->MasterFileTable);
4379ab86116STrevor Thompson
4389ab86116STrevor Thompson // Update the file record with the new attribute sizes
4399ab86116STrevor Thompson Status = UpdateFileRecord(Vcb, Vcb->VolumeFcb->MFTIndex, Vcb->MasterFileTable);
4409ab86116STrevor Thompson if (!NT_SUCCESS(Status))
4419ab86116STrevor Thompson {
4429ab86116STrevor Thompson DPRINT1("ERROR: Failed to update $MFT file record!\n");
443216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
4449ab86116STrevor Thompson ExReleaseResourceLite(&(Vcb->DirResource));
4459ab86116STrevor Thompson ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
4469ab86116STrevor Thompson ReleaseAttributeContext(BitmapContext);
4479ab86116STrevor Thompson return Status;
4489ab86116STrevor Thompson }
4499ab86116STrevor Thompson
4509ab86116STrevor Thompson // Write out the new bitmap
4515e7c1184STrevor Thompson Status = WriteAttribute(Vcb, BitmapContext, 0, BitmapBuffer, NewBitmapSize, &LengthWritten, Vcb->MasterFileTable);
4529ab86116STrevor Thompson if (!NT_SUCCESS(Status))
4539ab86116STrevor Thompson {
454216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
4559ab86116STrevor Thompson ExReleaseResourceLite(&(Vcb->DirResource));
4569ab86116STrevor Thompson ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
4579ab86116STrevor Thompson ReleaseAttributeContext(BitmapContext);
4589ab86116STrevor Thompson DPRINT1("ERROR: Couldn't write to bitmap attribute of $MFT!\n");
45968a48b27STrevor Thompson return Status;
4609ab86116STrevor Thompson }
4619ab86116STrevor Thompson
4625e7c1184STrevor Thompson // Create blank records for the new file record entries.
4635e7c1184STrevor Thompson for (i = 0; i < NewRecords; i++)
4645e7c1184STrevor Thompson {
4655e7c1184STrevor Thompson Status = UpdateFileRecord(Vcb, FirstNewMftIndex + i, BlankFileRecord);
4665e7c1184STrevor Thompson if (!NT_SUCCESS(Status))
4675e7c1184STrevor Thompson {
4685e7c1184STrevor Thompson DPRINT1("ERROR: Failed to write blank file record!\n");
469216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
4705e7c1184STrevor Thompson ExReleaseResourceLite(&(Vcb->DirResource));
4715e7c1184STrevor Thompson ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
4725e7c1184STrevor Thompson ReleaseAttributeContext(BitmapContext);
4735e7c1184STrevor Thompson return Status;
4745e7c1184STrevor Thompson }
4755e7c1184STrevor Thompson }
4765e7c1184STrevor Thompson
4775e7c1184STrevor Thompson // Update the mft mirror
4785e7c1184STrevor Thompson Status = UpdateMftMirror(Vcb);
4795e7c1184STrevor Thompson
4809ab86116STrevor Thompson // Cleanup
481216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, BlankFileRecord);
4829ab86116STrevor Thompson ExReleaseResourceLite(&(Vcb->DirResource));
4839ab86116STrevor Thompson ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
4849ab86116STrevor Thompson ReleaseAttributeContext(BitmapContext);
4859ab86116STrevor Thompson
4865e7c1184STrevor Thompson return Status;
4879ab86116STrevor Thompson }
4889ab86116STrevor Thompson
489d484d91eSTrevor Thompson /**
490d484d91eSTrevor Thompson * @name MoveAttributes
491d484d91eSTrevor Thompson * @implemented
492d484d91eSTrevor Thompson *
493d484d91eSTrevor Thompson * Moves a block of attributes to a new location in the file Record. The attribute at FirstAttributeToMove
494d484d91eSTrevor Thompson * and every attribute after that will be moved to MoveTo.
495d484d91eSTrevor Thompson *
496d484d91eSTrevor Thompson * @param DeviceExt
497d484d91eSTrevor Thompson * Pointer to the DEVICE_EXTENSION (VCB) of the target volume.
498d484d91eSTrevor Thompson *
499d484d91eSTrevor Thompson * @param FirstAttributeToMove
500d484d91eSTrevor Thompson * Pointer to the first NTFS_ATTR_RECORD that needs to be moved. This pointer must reside within a file record.
501d484d91eSTrevor Thompson *
502d484d91eSTrevor Thompson * @param FirstAttributeOffset
503d484d91eSTrevor Thompson * Offset of FirstAttributeToMove relative to the beginning of the file record.
504d484d91eSTrevor Thompson *
505d484d91eSTrevor Thompson * @param MoveTo
506d484d91eSTrevor Thompson * ULONG_PTR with the memory location that will be the new location of the first attribute being moved.
507d484d91eSTrevor Thompson *
508d484d91eSTrevor Thompson * @return
509d484d91eSTrevor Thompson * The new location of the final attribute (i.e. AttributeEnd marker).
510d484d91eSTrevor Thompson */
511d484d91eSTrevor Thompson PNTFS_ATTR_RECORD
MoveAttributes(PDEVICE_EXTENSION DeviceExt,PNTFS_ATTR_RECORD FirstAttributeToMove,ULONG FirstAttributeOffset,ULONG_PTR MoveTo)512d484d91eSTrevor Thompson MoveAttributes(PDEVICE_EXTENSION DeviceExt,
513d484d91eSTrevor Thompson PNTFS_ATTR_RECORD FirstAttributeToMove,
514d484d91eSTrevor Thompson ULONG FirstAttributeOffset,
515d484d91eSTrevor Thompson ULONG_PTR MoveTo)
516d484d91eSTrevor Thompson {
517d484d91eSTrevor Thompson // Get the size of all attributes after this one
518d484d91eSTrevor Thompson ULONG MemBlockSize = 0;
519d484d91eSTrevor Thompson PNTFS_ATTR_RECORD CurrentAttribute = FirstAttributeToMove;
520d484d91eSTrevor Thompson ULONG CurrentOffset = FirstAttributeOffset;
521d484d91eSTrevor Thompson PNTFS_ATTR_RECORD FinalAttribute;
522d484d91eSTrevor Thompson
523d484d91eSTrevor Thompson while (CurrentAttribute->Type != AttributeEnd && CurrentOffset < DeviceExt->NtfsInfo.BytesPerFileRecord)
524d484d91eSTrevor Thompson {
525d484d91eSTrevor Thompson CurrentOffset += CurrentAttribute->Length;
526d484d91eSTrevor Thompson MemBlockSize += CurrentAttribute->Length;
527d484d91eSTrevor Thompson CurrentAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)CurrentAttribute + CurrentAttribute->Length);
528d484d91eSTrevor Thompson }
529d484d91eSTrevor Thompson
530d484d91eSTrevor Thompson FinalAttribute = (PNTFS_ATTR_RECORD)(MoveTo + MemBlockSize);
531d484d91eSTrevor Thompson MemBlockSize += sizeof(ULONG) * 2; // Add the AttributeEnd and file record end
532d484d91eSTrevor Thompson
533d484d91eSTrevor Thompson ASSERT(MemBlockSize % ATTR_RECORD_ALIGNMENT == 0);
534d484d91eSTrevor Thompson
535d484d91eSTrevor Thompson // Move the attributes after this one
536d484d91eSTrevor Thompson RtlMoveMemory((PCHAR)MoveTo, FirstAttributeToMove, MemBlockSize);
537d484d91eSTrevor Thompson
538d484d91eSTrevor Thompson return FinalAttribute;
539d484d91eSTrevor Thompson }
540d484d91eSTrevor Thompson
5414dfcd1d5STrevor Thompson NTSTATUS
InternalSetResidentAttributeLength(PDEVICE_EXTENSION DeviceExt,PNTFS_ATTR_CONTEXT AttrContext,PFILE_RECORD_HEADER FileRecord,ULONG AttrOffset,ULONG DataSize)542d484d91eSTrevor Thompson InternalSetResidentAttributeLength(PDEVICE_EXTENSION DeviceExt,
543d484d91eSTrevor Thompson PNTFS_ATTR_CONTEXT AttrContext,
544760cdfb5STrevor Thompson PFILE_RECORD_HEADER FileRecord,
545760cdfb5STrevor Thompson ULONG AttrOffset,
546760cdfb5STrevor Thompson ULONG DataSize)
547760cdfb5STrevor Thompson {
548c08d37d1STrevor Thompson PNTFS_ATTR_RECORD Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
549d484d91eSTrevor Thompson PNTFS_ATTR_RECORD NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)Destination + Destination->Length);
550d484d91eSTrevor Thompson PNTFS_ATTR_RECORD FinalAttribute;
551d484d91eSTrevor Thompson ULONG OldAttributeLength = Destination->Length;
552760cdfb5STrevor Thompson ULONG NextAttributeOffset;
553760cdfb5STrevor Thompson
554d484d91eSTrevor Thompson DPRINT1("InternalSetResidentAttributeLength( %p, %p, %p, %lu, %lu )\n", DeviceExt, AttrContext, FileRecord, AttrOffset, DataSize);
555760cdfb5STrevor Thompson
5564dfcd1d5STrevor Thompson ASSERT(!AttrContext->pRecord->IsNonResident);
55768a48b27STrevor Thompson
5584dfcd1d5STrevor Thompson // Update ValueLength Field
559c08d37d1STrevor Thompson Destination->Resident.ValueLength = DataSize;
560760cdfb5STrevor Thompson
5614dfcd1d5STrevor Thompson // Calculate the record length and end marker offset
5624dfcd1d5STrevor Thompson Destination->Length = ALIGN_UP_BY(DataSize + AttrContext->pRecord->Resident.ValueOffset, ATTR_RECORD_ALIGNMENT);
5634dfcd1d5STrevor Thompson NextAttributeOffset = AttrOffset + Destination->Length;
5644dfcd1d5STrevor Thompson
565d484d91eSTrevor Thompson // Ensure NextAttributeOffset is aligned to an 8-byte boundary
566d484d91eSTrevor Thompson ASSERT(NextAttributeOffset % ATTR_RECORD_ALIGNMENT == 0);
5674dfcd1d5STrevor Thompson
568d484d91eSTrevor Thompson // Will the new attribute be larger than the old one?
569d484d91eSTrevor Thompson if (Destination->Length > OldAttributeLength)
570d484d91eSTrevor Thompson {
5714dfcd1d5STrevor Thompson // Free the old copy of the attribute in the context, as it will be the wrong length
5724dfcd1d5STrevor Thompson ExFreePoolWithTag(AttrContext->pRecord, TAG_NTFS);
5734dfcd1d5STrevor Thompson
574d484d91eSTrevor Thompson // Create a new copy of the attribute record for the context
5754dfcd1d5STrevor Thompson AttrContext->pRecord = ExAllocatePoolWithTag(NonPagedPool, Destination->Length, TAG_NTFS);
5764dfcd1d5STrevor Thompson if (!AttrContext->pRecord)
5774dfcd1d5STrevor Thompson {
5784dfcd1d5STrevor Thompson DPRINT1("Unable to allocate memory for attribute!\n");
5794dfcd1d5STrevor Thompson return STATUS_INSUFFICIENT_RESOURCES;
5804dfcd1d5STrevor Thompson }
581d484d91eSTrevor Thompson RtlZeroMemory((PVOID)((ULONG_PTR)AttrContext->pRecord + OldAttributeLength), Destination->Length - OldAttributeLength);
582d484d91eSTrevor Thompson RtlCopyMemory(AttrContext->pRecord, Destination, OldAttributeLength);
583d484d91eSTrevor Thompson }
584760cdfb5STrevor Thompson
585d484d91eSTrevor Thompson // Are there attributes after this one that need to be moved?
586d484d91eSTrevor Thompson if (NextAttribute->Type != AttributeEnd)
587d484d91eSTrevor Thompson {
588d484d91eSTrevor Thompson // Move the attributes after this one
589d484d91eSTrevor Thompson FinalAttribute = MoveAttributes(DeviceExt, NextAttribute, NextAttributeOffset, (ULONG_PTR)Destination + Destination->Length);
590d484d91eSTrevor Thompson }
591d484d91eSTrevor Thompson else
592d484d91eSTrevor Thompson {
593d484d91eSTrevor Thompson // advance to the final "attribute," adjust for the changed length of the attribute we're resizing
594d484d91eSTrevor Thompson FinalAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute - OldAttributeLength + Destination->Length);
595d484d91eSTrevor Thompson }
596760cdfb5STrevor Thompson
597d484d91eSTrevor Thompson // Update pRecord's length
598d484d91eSTrevor Thompson AttrContext->pRecord->Length = Destination->Length;
599d484d91eSTrevor Thompson AttrContext->pRecord->Resident.ValueLength = DataSize;
600d484d91eSTrevor Thompson
601d484d91eSTrevor Thompson // set the file record end
602d484d91eSTrevor Thompson SetFileRecordEnd(FileRecord, FinalAttribute, FILE_RECORD_END);
603d484d91eSTrevor Thompson
604d484d91eSTrevor Thompson //NtfsDumpFileRecord(DeviceExt, FileRecord);
6054dfcd1d5STrevor Thompson
6064dfcd1d5STrevor Thompson return STATUS_SUCCESS;
607760cdfb5STrevor Thompson }
608c2c66affSColin Finck
609afe40eb0STrevor Thompson /**
610afe40eb0STrevor Thompson * @parameter FileRecord
611afe40eb0STrevor Thompson * Pointer to a file record. Must be a full record at least
612afe40eb0STrevor Thompson * Fcb->Vcb->NtfsInfo.BytesPerFileRecord bytes large, not just the header.
613afe40eb0STrevor Thompson */
614ba33b9faSTrevor Thompson NTSTATUS
SetAttributeDataLength(PFILE_OBJECT FileObject,PNTFS_FCB Fcb,PNTFS_ATTR_CONTEXT AttrContext,ULONG AttrOffset,PFILE_RECORD_HEADER FileRecord,PLARGE_INTEGER DataSize)615ba33b9faSTrevor Thompson SetAttributeDataLength(PFILE_OBJECT FileObject,
616ba33b9faSTrevor Thompson PNTFS_FCB Fcb,
617ba33b9faSTrevor Thompson PNTFS_ATTR_CONTEXT AttrContext,
618ba33b9faSTrevor Thompson ULONG AttrOffset,
619ba33b9faSTrevor Thompson PFILE_RECORD_HEADER FileRecord,
620ba33b9faSTrevor Thompson PLARGE_INTEGER DataSize)
621ba33b9faSTrevor Thompson {
622760cdfb5STrevor Thompson NTSTATUS Status = STATUS_SUCCESS;
623760cdfb5STrevor Thompson
624f2e47474STrevor Thompson DPRINT1("SetAttributeDataLength(%p, %p, %p, %lu, %p, %I64u)\n",
62538c947b7STrevor Thompson FileObject,
62638c947b7STrevor Thompson Fcb,
62738c947b7STrevor Thompson AttrContext,
62838c947b7STrevor Thompson AttrOffset,
62938c947b7STrevor Thompson FileRecord,
63038c947b7STrevor Thompson DataSize->QuadPart);
63138c947b7STrevor Thompson
632760cdfb5STrevor Thompson // are we truncating the file?
6334dfcd1d5STrevor Thompson if (DataSize->QuadPart < AttributeDataLength(AttrContext->pRecord))
634760cdfb5STrevor Thompson {
6357eb1264fSTrevor Thompson if (!MmCanFileBeTruncated(FileObject->SectionObjectPointer, DataSize))
636760cdfb5STrevor Thompson {
637760cdfb5STrevor Thompson DPRINT1("Can't truncate a memory-mapped file!\n");
638760cdfb5STrevor Thompson return STATUS_USER_MAPPED_FILE;
639760cdfb5STrevor Thompson }
640760cdfb5STrevor Thompson }
641760cdfb5STrevor Thompson
6424dfcd1d5STrevor Thompson if (AttrContext->pRecord->IsNonResident)
643ba33b9faSTrevor Thompson {
6441417f286STrevor Thompson Status = SetNonResidentAttributeDataLength(Fcb->Vcb,
6451417f286STrevor Thompson AttrContext,
6461417f286STrevor Thompson AttrOffset,
6471417f286STrevor Thompson FileRecord,
6481417f286STrevor Thompson DataSize);
649760cdfb5STrevor Thompson }
650760cdfb5STrevor Thompson else
651760cdfb5STrevor Thompson {
652760cdfb5STrevor Thompson // resident attribute
6531417f286STrevor Thompson Status = SetResidentAttributeDataLength(Fcb->Vcb,
6541417f286STrevor Thompson AttrContext,
6551417f286STrevor Thompson AttrOffset,
6561417f286STrevor Thompson FileRecord,
6571417f286STrevor Thompson DataSize);
658c25b9747STrevor Thompson }
659c25b9747STrevor Thompson
660c25b9747STrevor Thompson if (!NT_SUCCESS(Status))
661c25b9747STrevor Thompson {
6621417f286STrevor Thompson DPRINT1("ERROR: Failed to set size of attribute!\n");
663c25b9747STrevor Thompson return Status;
664c25b9747STrevor Thompson }
665ba33b9faSTrevor Thompson
666ba33b9faSTrevor Thompson //NtfsDumpFileAttributes(Fcb->Vcb, FileRecord);
667ba33b9faSTrevor Thompson
668ba33b9faSTrevor Thompson // write the updated file record back to disk
669760cdfb5STrevor Thompson Status = UpdateFileRecord(Fcb->Vcb, Fcb->MFTIndex, FileRecord);
670ba33b9faSTrevor Thompson
671760cdfb5STrevor Thompson if (NT_SUCCESS(Status))
672ba33b9faSTrevor Thompson {
6734dfcd1d5STrevor Thompson if (AttrContext->pRecord->IsNonResident)
6744dfcd1d5STrevor Thompson Fcb->RFCB.AllocationSize.QuadPart = AttrContext->pRecord->NonResident.AllocatedSize;
6751417f286STrevor Thompson else
6761417f286STrevor Thompson Fcb->RFCB.AllocationSize = *DataSize;
677760cdfb5STrevor Thompson Fcb->RFCB.FileSize = *DataSize;
678760cdfb5STrevor Thompson Fcb->RFCB.ValidDataLength = *DataSize;
679760cdfb5STrevor Thompson CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&Fcb->RFCB.AllocationSize);
680ba33b9faSTrevor Thompson }
681ba33b9faSTrevor Thompson
682ba33b9faSTrevor Thompson return STATUS_SUCCESS;
683ba33b9faSTrevor Thompson }
684ba33b9faSTrevor Thompson
6850409b316STrevor Thompson /**
6860409b316STrevor Thompson * @name SetFileRecordEnd
6870409b316STrevor Thompson * @implemented
6880409b316STrevor Thompson *
6890409b316STrevor Thompson * This small function sets a new endpoint for the file record. It set's the final
6900409b316STrevor Thompson * AttrEnd->Type to AttributeEnd and recalculates the bytes used by the file record.
6910409b316STrevor Thompson *
6920409b316STrevor Thompson * @param FileRecord
6930409b316STrevor Thompson * Pointer to the file record whose endpoint (length) will be set.
6940409b316STrevor Thompson *
6950409b316STrevor Thompson * @param AttrEnd
6960409b316STrevor Thompson * Pointer to section of memory that will receive the AttributeEnd marker. This must point
6970409b316STrevor Thompson * to memory allocated for the FileRecord. Must be aligned to an 8-byte boundary (relative to FileRecord).
6980409b316STrevor Thompson *
6990409b316STrevor Thompson * @param EndMarker
7000409b316STrevor Thompson * This value will be written after AttributeEnd but isn't critical at all. When Windows resizes
7010409b316STrevor Thompson * a file record, it preserves the final ULONG that previously ended the record, even though this
7020409b316STrevor Thompson * value is (to my knowledge) never used. We emulate this behavior.
7030409b316STrevor Thompson *
7040409b316STrevor Thompson */
7050409b316STrevor Thompson VOID
SetFileRecordEnd(PFILE_RECORD_HEADER FileRecord,PNTFS_ATTR_RECORD AttrEnd,ULONG EndMarker)7060409b316STrevor Thompson SetFileRecordEnd(PFILE_RECORD_HEADER FileRecord,
7070409b316STrevor Thompson PNTFS_ATTR_RECORD AttrEnd,
7080409b316STrevor Thompson ULONG EndMarker)
7090409b316STrevor Thompson {
710d484d91eSTrevor Thompson // Ensure AttrEnd is aligned on an 8-byte boundary, relative to FileRecord
711d484d91eSTrevor Thompson ASSERT(((ULONG_PTR)AttrEnd - (ULONG_PTR)FileRecord) % ATTR_RECORD_ALIGNMENT == 0);
712d484d91eSTrevor Thompson
7130409b316STrevor Thompson // mark the end of attributes
7140409b316STrevor Thompson AttrEnd->Type = AttributeEnd;
7150409b316STrevor Thompson
7160409b316STrevor Thompson // Restore the "file-record-end marker." The value is never checked but this behavior is consistent with Win2k3.
7170409b316STrevor Thompson AttrEnd->Length = EndMarker;
7180409b316STrevor Thompson
7190409b316STrevor Thompson // recalculate bytes in use
7200409b316STrevor Thompson FileRecord->BytesInUse = (ULONG_PTR)AttrEnd - (ULONG_PTR)FileRecord + sizeof(ULONG) * 2;
7210409b316STrevor Thompson }
7220409b316STrevor Thompson
7231417f286STrevor Thompson /**
7241417f286STrevor Thompson * @name SetNonResidentAttributeDataLength
7251417f286STrevor Thompson * @implemented
7261417f286STrevor Thompson *
7271417f286STrevor Thompson * Called by SetAttributeDataLength() to set the size of a non-resident attribute. Doesn't update the file record.
7281417f286STrevor Thompson *
7291417f286STrevor Thompson * @param Vcb
7301417f286STrevor Thompson * Pointer to a DEVICE_EXTENSION describing the target disk.
7311417f286STrevor Thompson *
7321417f286STrevor Thompson * @param AttrContext
7331417f286STrevor Thompson * PNTFS_ATTR_CONTEXT describing the location of the attribute whose size is being set.
7341417f286STrevor Thompson *
7351417f286STrevor Thompson * @param AttrOffset
7361417f286STrevor Thompson * Offset, from the beginning of the record, of the attribute being sized.
7371417f286STrevor Thompson *
7381417f286STrevor Thompson * @param FileRecord
7391417f286STrevor Thompson * Pointer to a file record containing the attribute to be resized. Must be a complete file record,
7401417f286STrevor Thompson * not just the header.
7411417f286STrevor Thompson *
7421417f286STrevor Thompson * @param DataSize
7431417f286STrevor Thompson * Pointer to a LARGE_INTEGER describing the new size of the attribute's data.
7441417f286STrevor Thompson *
7451417f286STrevor Thompson * @return
7461417f286STrevor Thompson * STATUS_SUCCESS on success;
7471417f286STrevor Thompson * STATUS_INSUFFICIENT_RESOURCES if an allocation fails.
7481417f286STrevor Thompson * STATUS_INVALID_PARAMETER if we can't find the last cluster in the data run.
7491417f286STrevor Thompson *
7501417f286STrevor Thompson * @remarks
7519ab86116STrevor Thompson * Called by SetAttributeDataLength() and IncreaseMftSize(). Use SetAttributeDataLength() unless you have a good
7521417f286STrevor Thompson * reason to use this. Doesn't update the file record on disk. Doesn't inform the cache controller of changes with
7531417f286STrevor Thompson * any associated files. Synchronization is the callers responsibility.
7541417f286STrevor Thompson */
7551417f286STrevor Thompson NTSTATUS
SetNonResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,PNTFS_ATTR_CONTEXT AttrContext,ULONG AttrOffset,PFILE_RECORD_HEADER FileRecord,PLARGE_INTEGER DataSize)7561417f286STrevor Thompson SetNonResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
7571417f286STrevor Thompson PNTFS_ATTR_CONTEXT AttrContext,
7581417f286STrevor Thompson ULONG AttrOffset,
7591417f286STrevor Thompson PFILE_RECORD_HEADER FileRecord,
7601417f286STrevor Thompson PLARGE_INTEGER DataSize)
7611417f286STrevor Thompson {
7621417f286STrevor Thompson NTSTATUS Status = STATUS_SUCCESS;
7631417f286STrevor Thompson ULONG BytesPerCluster = Vcb->NtfsInfo.BytesPerCluster;
7641417f286STrevor Thompson ULONGLONG AllocationSize = ROUND_UP(DataSize->QuadPart, BytesPerCluster);
7651417f286STrevor Thompson PNTFS_ATTR_RECORD DestinationAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
7664dfcd1d5STrevor Thompson ULONG ExistingClusters = AttrContext->pRecord->NonResident.AllocatedSize / BytesPerCluster;
7671417f286STrevor Thompson
7684dfcd1d5STrevor Thompson ASSERT(AttrContext->pRecord->IsNonResident);
7691417f286STrevor Thompson
7701417f286STrevor Thompson // do we need to increase the allocation size?
7714dfcd1d5STrevor Thompson if (AttrContext->pRecord->NonResident.AllocatedSize < AllocationSize)
7721417f286STrevor Thompson {
7731417f286STrevor Thompson ULONG ClustersNeeded = (AllocationSize / BytesPerCluster) - ExistingClusters;
7741417f286STrevor Thompson LARGE_INTEGER LastClusterInDataRun;
7751417f286STrevor Thompson ULONG NextAssignedCluster;
7761417f286STrevor Thompson ULONG AssignedClusters;
7771417f286STrevor Thompson
7781417f286STrevor Thompson if (ExistingClusters == 0)
7791417f286STrevor Thompson {
7801417f286STrevor Thompson LastClusterInDataRun.QuadPart = 0;
7811417f286STrevor Thompson }
7821417f286STrevor Thompson else
7831417f286STrevor Thompson {
7841417f286STrevor Thompson if (!FsRtlLookupLargeMcbEntry(&AttrContext->DataRunsMCB,
7854dfcd1d5STrevor Thompson (LONGLONG)AttrContext->pRecord->NonResident.HighestVCN,
7861417f286STrevor Thompson (PLONGLONG)&LastClusterInDataRun.QuadPart,
7871417f286STrevor Thompson NULL,
7881417f286STrevor Thompson NULL,
7891417f286STrevor Thompson NULL,
7901417f286STrevor Thompson NULL))
7911417f286STrevor Thompson {
7921417f286STrevor Thompson DPRINT1("Error looking up final large MCB entry!\n");
7931417f286STrevor Thompson
7941417f286STrevor Thompson // Most likely, HighestVCN went above the largest mapping
7954dfcd1d5STrevor Thompson DPRINT1("Highest VCN of record: %I64u\n", AttrContext->pRecord->NonResident.HighestVCN);
7961417f286STrevor Thompson return STATUS_INVALID_PARAMETER;
7971417f286STrevor Thompson }
7981417f286STrevor Thompson }
7991417f286STrevor Thompson
8001417f286STrevor Thompson DPRINT("LastClusterInDataRun: %I64u\n", LastClusterInDataRun.QuadPart);
8014dfcd1d5STrevor Thompson DPRINT("Highest VCN of record: %I64u\n", AttrContext->pRecord->NonResident.HighestVCN);
8021417f286STrevor Thompson
8031417f286STrevor Thompson while (ClustersNeeded > 0)
8041417f286STrevor Thompson {
8051417f286STrevor Thompson Status = NtfsAllocateClusters(Vcb,
8061417f286STrevor Thompson LastClusterInDataRun.LowPart + 1,
8071417f286STrevor Thompson ClustersNeeded,
8081417f286STrevor Thompson &NextAssignedCluster,
8091417f286STrevor Thompson &AssignedClusters);
8101417f286STrevor Thompson
8111417f286STrevor Thompson if (!NT_SUCCESS(Status))
8121417f286STrevor Thompson {
8131417f286STrevor Thompson DPRINT1("Error: Unable to allocate requested clusters!\n");
8141417f286STrevor Thompson return Status;
8151417f286STrevor Thompson }
8161417f286STrevor Thompson
8171417f286STrevor Thompson // now we need to add the clusters we allocated to the data run
8181417f286STrevor Thompson Status = AddRun(Vcb, AttrContext, AttrOffset, FileRecord, NextAssignedCluster, AssignedClusters);
8191417f286STrevor Thompson if (!NT_SUCCESS(Status))
8201417f286STrevor Thompson {
8211417f286STrevor Thompson DPRINT1("Error: Unable to add data run!\n");
8221417f286STrevor Thompson return Status;
8231417f286STrevor Thompson }
8241417f286STrevor Thompson
8251417f286STrevor Thompson ClustersNeeded -= AssignedClusters;
8261417f286STrevor Thompson LastClusterInDataRun.LowPart = NextAssignedCluster + AssignedClusters - 1;
8271417f286STrevor Thompson }
8281417f286STrevor Thompson }
8294dfcd1d5STrevor Thompson else if (AttrContext->pRecord->NonResident.AllocatedSize > AllocationSize)
8301417f286STrevor Thompson {
8311417f286STrevor Thompson // shrink allocation size
8321417f286STrevor Thompson ULONG ClustersToFree = ExistingClusters - (AllocationSize / BytesPerCluster);
8331417f286STrevor Thompson Status = FreeClusters(Vcb, AttrContext, AttrOffset, FileRecord, ClustersToFree);
8341417f286STrevor Thompson }
8351417f286STrevor Thompson
8361417f286STrevor Thompson // TODO: is the file compressed, encrypted, or sparse?
8371417f286STrevor Thompson
8384dfcd1d5STrevor Thompson AttrContext->pRecord->NonResident.AllocatedSize = AllocationSize;
8394dfcd1d5STrevor Thompson AttrContext->pRecord->NonResident.DataSize = DataSize->QuadPart;
8404dfcd1d5STrevor Thompson AttrContext->pRecord->NonResident.InitializedSize = DataSize->QuadPart;
8411417f286STrevor Thompson
8421417f286STrevor Thompson DestinationAttribute->NonResident.AllocatedSize = AllocationSize;
8431417f286STrevor Thompson DestinationAttribute->NonResident.DataSize = DataSize->QuadPart;
8441417f286STrevor Thompson DestinationAttribute->NonResident.InitializedSize = DataSize->QuadPart;
8451417f286STrevor Thompson
84652c30fdfSTrevor Thompson // HighestVCN seems to be set incorrectly somewhere. Apply a hack-fix to reset it.
84752c30fdfSTrevor Thompson // HACKHACK FIXME: Fix for sparse files; this math won't work in that case.
84852c30fdfSTrevor Thompson AttrContext->pRecord->NonResident.HighestVCN = ((ULONGLONG)AllocationSize / Vcb->NtfsInfo.BytesPerCluster) - 1;
84952c30fdfSTrevor Thompson DestinationAttribute->NonResident.HighestVCN = AttrContext->pRecord->NonResident.HighestVCN;
85052c30fdfSTrevor Thompson
8511417f286STrevor Thompson DPRINT("Allocated Size: %I64u\n", DestinationAttribute->NonResident.AllocatedSize);
8521417f286STrevor Thompson
8531417f286STrevor Thompson return Status;
8541417f286STrevor Thompson }
8551417f286STrevor Thompson
8561417f286STrevor Thompson /**
8571417f286STrevor Thompson * @name SetResidentAttributeDataLength
8581417f286STrevor Thompson * @implemented
8591417f286STrevor Thompson *
8601417f286STrevor Thompson * Called by SetAttributeDataLength() to set the size of a non-resident attribute. Doesn't update the file record.
8611417f286STrevor Thompson *
8621417f286STrevor Thompson * @param Vcb
8631417f286STrevor Thompson * Pointer to a DEVICE_EXTENSION describing the target disk.
8641417f286STrevor Thompson *
8651417f286STrevor Thompson * @param AttrContext
8661417f286STrevor Thompson * PNTFS_ATTR_CONTEXT describing the location of the attribute whose size is being set.
8671417f286STrevor Thompson *
8681417f286STrevor Thompson * @param AttrOffset
8691417f286STrevor Thompson * Offset, from the beginning of the record, of the attribute being sized.
8701417f286STrevor Thompson *
8711417f286STrevor Thompson * @param FileRecord
8721417f286STrevor Thompson * Pointer to a file record containing the attribute to be resized. Must be a complete file record,
8731417f286STrevor Thompson * not just the header.
8741417f286STrevor Thompson *
8751417f286STrevor Thompson * @param DataSize
8761417f286STrevor Thompson * Pointer to a LARGE_INTEGER describing the new size of the attribute's data.
8771417f286STrevor Thompson *
8781417f286STrevor Thompson * @return
8791417f286STrevor Thompson * STATUS_SUCCESS on success;
8801417f286STrevor Thompson * STATUS_INSUFFICIENT_RESOURCES if an allocation fails.
8811417f286STrevor Thompson * STATUS_INVALID_PARAMETER if AttrContext describes a non-resident attribute.
8821417f286STrevor Thompson * STATUS_NOT_IMPLEMENTED if requested to decrease the size of an attribute that isn't the
8831417f286STrevor Thompson * last attribute listed in the file record.
8841417f286STrevor Thompson *
8851417f286STrevor Thompson * @remarks
8869ab86116STrevor Thompson * Called by SetAttributeDataLength() and IncreaseMftSize(). Use SetAttributeDataLength() unless you have a good
8871417f286STrevor Thompson * reason to use this. Doesn't update the file record on disk. Doesn't inform the cache controller of changes with
8881417f286STrevor Thompson * any associated files. Synchronization is the callers responsibility.
8891417f286STrevor Thompson */
8901417f286STrevor Thompson NTSTATUS
SetResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,PNTFS_ATTR_CONTEXT AttrContext,ULONG AttrOffset,PFILE_RECORD_HEADER FileRecord,PLARGE_INTEGER DataSize)8911417f286STrevor Thompson SetResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
8921417f286STrevor Thompson PNTFS_ATTR_CONTEXT AttrContext,
8931417f286STrevor Thompson ULONG AttrOffset,
8941417f286STrevor Thompson PFILE_RECORD_HEADER FileRecord,
8951417f286STrevor Thompson PLARGE_INTEGER DataSize)
8961417f286STrevor Thompson {
8971417f286STrevor Thompson NTSTATUS Status;
8981417f286STrevor Thompson
8991417f286STrevor Thompson // find the next attribute
9004dfcd1d5STrevor Thompson ULONG NextAttributeOffset = AttrOffset + AttrContext->pRecord->Length;
9011417f286STrevor Thompson PNTFS_ATTR_RECORD NextAttribute = (PNTFS_ATTR_RECORD)((PCHAR)FileRecord + NextAttributeOffset);
9021417f286STrevor Thompson
9034dfcd1d5STrevor Thompson ASSERT(!AttrContext->pRecord->IsNonResident);
9041417f286STrevor Thompson
9051417f286STrevor Thompson //NtfsDumpFileAttributes(Vcb, FileRecord);
9061417f286STrevor Thompson
9071417f286STrevor Thompson // Do we need to increase the data length?
9084dfcd1d5STrevor Thompson if (DataSize->QuadPart > AttrContext->pRecord->Resident.ValueLength)
9091417f286STrevor Thompson {
9101417f286STrevor Thompson // There's usually padding at the end of a record. Do we need to extend past it?
9114dfcd1d5STrevor Thompson ULONG MaxValueLength = AttrContext->pRecord->Length - AttrContext->pRecord->Resident.ValueOffset;
9121417f286STrevor Thompson if (MaxValueLength < DataSize->LowPart)
9131417f286STrevor Thompson {
9141417f286STrevor Thompson // If this is the last attribute, we could move the end marker to the very end of the file record
9151417f286STrevor Thompson MaxValueLength += Vcb->NtfsInfo.BytesPerFileRecord - NextAttributeOffset - (sizeof(ULONG) * 2);
9161417f286STrevor Thompson
9171417f286STrevor Thompson if (MaxValueLength < DataSize->LowPart || NextAttribute->Type != AttributeEnd)
9181417f286STrevor Thompson {
9191417f286STrevor Thompson // convert attribute to non-resident
9201417f286STrevor Thompson PNTFS_ATTR_RECORD Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
9214dfcd1d5STrevor Thompson PNTFS_ATTR_RECORD NewRecord;
9221417f286STrevor Thompson LARGE_INTEGER AttribDataSize;
9231417f286STrevor Thompson PVOID AttribData;
9244dfcd1d5STrevor Thompson ULONG NewRecordLength;
9251417f286STrevor Thompson ULONG EndAttributeOffset;
9261417f286STrevor Thompson ULONG LengthWritten;
9271417f286STrevor Thompson
9281417f286STrevor Thompson DPRINT1("Converting attribute to non-resident.\n");
9291417f286STrevor Thompson
9304dfcd1d5STrevor Thompson AttribDataSize.QuadPart = AttrContext->pRecord->Resident.ValueLength;
9311417f286STrevor Thompson
9321417f286STrevor Thompson // Is there existing data we need to back-up?
9331417f286STrevor Thompson if (AttribDataSize.QuadPart > 0)
9341417f286STrevor Thompson {
9351417f286STrevor Thompson AttribData = ExAllocatePoolWithTag(NonPagedPool, AttribDataSize.QuadPart, TAG_NTFS);
9361417f286STrevor Thompson if (AttribData == NULL)
9371417f286STrevor Thompson {
9381417f286STrevor Thompson DPRINT1("ERROR: Couldn't allocate memory for attribute data. Can't migrate to non-resident!\n");
9391417f286STrevor Thompson return STATUS_INSUFFICIENT_RESOURCES;
9401417f286STrevor Thompson }
9411417f286STrevor Thompson
9421417f286STrevor Thompson // read data to temp buffer
9431417f286STrevor Thompson Status = ReadAttribute(Vcb, AttrContext, 0, AttribData, AttribDataSize.QuadPart);
9441417f286STrevor Thompson if (!NT_SUCCESS(Status))
9451417f286STrevor Thompson {
9461417f286STrevor Thompson DPRINT1("ERROR: Unable to read attribute before migrating!\n");
9471417f286STrevor Thompson ExFreePoolWithTag(AttribData, TAG_NTFS);
9481417f286STrevor Thompson return Status;
9491417f286STrevor Thompson }
9501417f286STrevor Thompson }
9511417f286STrevor Thompson
9521417f286STrevor Thompson // Start by turning this attribute into a 0-length, non-resident attribute, then enlarge it.
9531417f286STrevor Thompson
9544dfcd1d5STrevor Thompson // The size of a 0-length, non-resident attribute will be 0x41 + the size of the attribute name, aligned to an 8-byte boundary
9554dfcd1d5STrevor Thompson NewRecordLength = ALIGN_UP_BY(0x41 + (AttrContext->pRecord->NameLength * sizeof(WCHAR)), ATTR_RECORD_ALIGNMENT);
9561417f286STrevor Thompson
9574dfcd1d5STrevor Thompson // Create a new attribute record that will store the 0-length, non-resident attribute
9584dfcd1d5STrevor Thompson NewRecord = ExAllocatePoolWithTag(NonPagedPool, NewRecordLength, TAG_NTFS);
9594dfcd1d5STrevor Thompson
9604dfcd1d5STrevor Thompson // Zero out the NonResident structure
9614dfcd1d5STrevor Thompson RtlZeroMemory(NewRecord, NewRecordLength);
9624dfcd1d5STrevor Thompson
9634dfcd1d5STrevor Thompson // Copy the data that's common to both non-resident and resident attributes
9644dfcd1d5STrevor Thompson RtlCopyMemory(NewRecord, AttrContext->pRecord, FIELD_OFFSET(NTFS_ATTR_RECORD, Resident.ValueLength));
9654dfcd1d5STrevor Thompson
9664dfcd1d5STrevor Thompson // if there's a name
9674dfcd1d5STrevor Thompson if (AttrContext->pRecord->NameLength != 0)
9684dfcd1d5STrevor Thompson {
9694dfcd1d5STrevor Thompson // copy the name
9704dfcd1d5STrevor Thompson // An attribute name will be located at offset 0x18 for a resident attribute, 0x40 for non-resident
9714dfcd1d5STrevor Thompson RtlCopyMemory((PCHAR)((ULONG_PTR)NewRecord + 0x40),
9724dfcd1d5STrevor Thompson (PCHAR)((ULONG_PTR)AttrContext->pRecord + 0x18),
9734dfcd1d5STrevor Thompson AttrContext->pRecord->NameLength * sizeof(WCHAR));
9744dfcd1d5STrevor Thompson }
9754dfcd1d5STrevor Thompson
9764dfcd1d5STrevor Thompson // update the mapping pairs offset, which will be 0x40 (size of a non-resident header) + length in bytes of the name
9774dfcd1d5STrevor Thompson NewRecord->NonResident.MappingPairsOffset = 0x40 + (AttrContext->pRecord->NameLength * sizeof(WCHAR));
9781417f286STrevor Thompson
9791417f286STrevor Thompson // update the end of the file record
9801417f286STrevor Thompson // calculate position of end markers (1 byte for empty data run)
9814dfcd1d5STrevor Thompson EndAttributeOffset = AttrOffset + NewRecord->NonResident.MappingPairsOffset + 1;
9825ab24a5aSTrevor Thompson EndAttributeOffset = ALIGN_UP_BY(EndAttributeOffset, ATTR_RECORD_ALIGNMENT);
9831417f286STrevor Thompson
9841417f286STrevor Thompson // Update the length
9854dfcd1d5STrevor Thompson NewRecord->Length = EndAttributeOffset - AttrOffset;
9864dfcd1d5STrevor Thompson
9874dfcd1d5STrevor Thompson ASSERT(NewRecord->Length == NewRecordLength);
9884dfcd1d5STrevor Thompson
9894dfcd1d5STrevor Thompson // Copy the new attribute record into the file record
9904dfcd1d5STrevor Thompson RtlCopyMemory(Destination, NewRecord, NewRecord->Length);
9911417f286STrevor Thompson
9921417f286STrevor Thompson // Update the file record end
9931417f286STrevor Thompson SetFileRecordEnd(FileRecord,
9941417f286STrevor Thompson (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + EndAttributeOffset),
9951417f286STrevor Thompson FILE_RECORD_END);
9961417f286STrevor Thompson
99768a48b27STrevor Thompson // Initialize the MCB, potentially catch an exception
99868a48b27STrevor Thompson _SEH2_TRY
99968a48b27STrevor Thompson {
100068a48b27STrevor Thompson FsRtlInitializeLargeMcb(&AttrContext->DataRunsMCB, NonPagedPool);
100168a48b27STrevor Thompson }
100268a48b27STrevor Thompson _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
100368a48b27STrevor Thompson {
100468a48b27STrevor Thompson DPRINT1("Unable to create LargeMcb!\n");
1005935fcd1bSTrevor Thompson if (AttribDataSize.QuadPart > 0)
100668a48b27STrevor Thompson ExFreePoolWithTag(AttribData, TAG_NTFS);
10072d3d7415SPierre Schweitzer ExFreePoolWithTag(NewRecord, TAG_NTFS);
100868a48b27STrevor Thompson _SEH2_YIELD(return _SEH2_GetExceptionCode());
100968a48b27STrevor Thompson } _SEH2_END;
101068a48b27STrevor Thompson
101168a48b27STrevor Thompson // Mark the attribute as non-resident (we wait until after we know the LargeMcb was initialized)
10124dfcd1d5STrevor Thompson NewRecord->IsNonResident = Destination->IsNonResident = 1;
101368a48b27STrevor Thompson
101468a48b27STrevor Thompson // Update file record on disk
10151417f286STrevor Thompson Status = UpdateFileRecord(Vcb, AttrContext->FileMFTIndex, FileRecord);
10161417f286STrevor Thompson if (!NT_SUCCESS(Status))
10171417f286STrevor Thompson {
10181417f286STrevor Thompson DPRINT1("ERROR: Couldn't update file record to continue migration!\n");
10191417f286STrevor Thompson if (AttribDataSize.QuadPart > 0)
10201417f286STrevor Thompson ExFreePoolWithTag(AttribData, TAG_NTFS);
10212d3d7415SPierre Schweitzer ExFreePoolWithTag(NewRecord, TAG_NTFS);
10221417f286STrevor Thompson return Status;
10231417f286STrevor Thompson }
10241417f286STrevor Thompson
10254dfcd1d5STrevor Thompson // Now we need to free the old copy of the attribute record in the context and replace it with the new one
10264dfcd1d5STrevor Thompson ExFreePoolWithTag(AttrContext->pRecord, TAG_NTFS);
10274dfcd1d5STrevor Thompson AttrContext->pRecord = NewRecord;
10284dfcd1d5STrevor Thompson
10291417f286STrevor Thompson // Now we can treat the attribute as non-resident and enlarge it normally
10301417f286STrevor Thompson Status = SetNonResidentAttributeDataLength(Vcb, AttrContext, AttrOffset, FileRecord, DataSize);
10311417f286STrevor Thompson if (!NT_SUCCESS(Status))
10321417f286STrevor Thompson {
10331417f286STrevor Thompson DPRINT1("ERROR: Unable to migrate resident attribute!\n");
10341417f286STrevor Thompson if (AttribDataSize.QuadPart > 0)
10351417f286STrevor Thompson ExFreePoolWithTag(AttribData, TAG_NTFS);
10361417f286STrevor Thompson return Status;
10371417f286STrevor Thompson }
10381417f286STrevor Thompson
10391417f286STrevor Thompson // restore the back-up attribute, if we made one
10401417f286STrevor Thompson if (AttribDataSize.QuadPart > 0)
10411417f286STrevor Thompson {
1042d484d91eSTrevor Thompson Status = WriteAttribute(Vcb, AttrContext, 0, AttribData, AttribDataSize.QuadPart, &LengthWritten, FileRecord);
10431417f286STrevor Thompson if (!NT_SUCCESS(Status))
10441417f286STrevor Thompson {
10451417f286STrevor Thompson DPRINT1("ERROR: Unable to write attribute data to non-resident clusters during migration!\n");
10461417f286STrevor Thompson // TODO: Reverse migration so no data is lost
10471417f286STrevor Thompson ExFreePoolWithTag(AttribData, TAG_NTFS);
10481417f286STrevor Thompson return Status;
10491417f286STrevor Thompson }
10501417f286STrevor Thompson
10511417f286STrevor Thompson ExFreePoolWithTag(AttribData, TAG_NTFS);
10521417f286STrevor Thompson }
10531417f286STrevor Thompson }
10541417f286STrevor Thompson }
10551417f286STrevor Thompson }
10561417f286STrevor Thompson
10571417f286STrevor Thompson // set the new length of the resident attribute (if we didn't migrate it)
10584dfcd1d5STrevor Thompson if (!AttrContext->pRecord->IsNonResident)
1059d484d91eSTrevor Thompson return InternalSetResidentAttributeLength(Vcb, AttrContext, FileRecord, AttrOffset, DataSize->LowPart);
10601417f286STrevor Thompson
10611417f286STrevor Thompson return STATUS_SUCCESS;
10621417f286STrevor Thompson }
10631417f286STrevor Thompson
1064c2c66affSColin Finck ULONG
ReadAttribute(PDEVICE_EXTENSION Vcb,PNTFS_ATTR_CONTEXT Context,ULONGLONG Offset,PCHAR Buffer,ULONG Length)1065c2c66affSColin Finck ReadAttribute(PDEVICE_EXTENSION Vcb,
1066c2c66affSColin Finck PNTFS_ATTR_CONTEXT Context,
1067c2c66affSColin Finck ULONGLONG Offset,
1068c2c66affSColin Finck PCHAR Buffer,
1069c2c66affSColin Finck ULONG Length)
1070c2c66affSColin Finck {
1071c2c66affSColin Finck ULONGLONG LastLCN;
1072c2c66affSColin Finck PUCHAR DataRun;
1073c2c66affSColin Finck LONGLONG DataRunOffset;
1074c2c66affSColin Finck ULONGLONG DataRunLength;
1075c2c66affSColin Finck LONGLONG DataRunStartLCN;
1076c2c66affSColin Finck ULONGLONG CurrentOffset;
1077c2c66affSColin Finck ULONG ReadLength;
1078c2c66affSColin Finck ULONG AlreadyRead;
1079c2c66affSColin Finck NTSTATUS Status;
1080c2c66affSColin Finck
108152b9f467STrevor Thompson //TEMPTEMP
108252b9f467STrevor Thompson PUCHAR TempBuffer;
108352b9f467STrevor Thompson
10844dfcd1d5STrevor Thompson if (!Context->pRecord->IsNonResident)
1085c2c66affSColin Finck {
10864dfcd1d5STrevor Thompson // We need to truncate Offset to a ULONG for pointer arithmetic
10874dfcd1d5STrevor Thompson // The check below should ensure that Offset is well within the range of 32 bits
10884dfcd1d5STrevor Thompson ULONG LittleOffset = (ULONG)Offset;
10894dfcd1d5STrevor Thompson
10904dfcd1d5STrevor Thompson // Ensure that offset isn't beyond the end of the attribute
10914dfcd1d5STrevor Thompson if (Offset > Context->pRecord->Resident.ValueLength)
1092c2c66affSColin Finck return 0;
10934dfcd1d5STrevor Thompson if (Offset + Length > Context->pRecord->Resident.ValueLength)
10944dfcd1d5STrevor Thompson Length = (ULONG)(Context->pRecord->Resident.ValueLength - Offset);
10954dfcd1d5STrevor Thompson
10964dfcd1d5STrevor Thompson RtlCopyMemory(Buffer, (PVOID)((ULONG_PTR)Context->pRecord + Context->pRecord->Resident.ValueOffset + LittleOffset), Length);
1097c2c66affSColin Finck return Length;
1098c2c66affSColin Finck }
1099c2c66affSColin Finck
1100c2c66affSColin Finck /*
1101c2c66affSColin Finck * Non-resident attribute
1102c2c66affSColin Finck */
1103c2c66affSColin Finck
1104c2c66affSColin Finck /*
1105c2c66affSColin Finck * I. Find the corresponding start data run.
1106c2c66affSColin Finck */
1107c2c66affSColin Finck
1108c2c66affSColin Finck AlreadyRead = 0;
1109c2c66affSColin Finck
1110c2c66affSColin Finck // FIXME: Cache seems to be non-working. Disable it for now
1111c2c66affSColin Finck //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
1112c2c66affSColin Finck if (0)
1113c2c66affSColin Finck {
1114c2c66affSColin Finck DataRun = Context->CacheRun;
1115c2c66affSColin Finck LastLCN = Context->CacheRunLastLCN;
1116c2c66affSColin Finck DataRunStartLCN = Context->CacheRunStartLCN;
1117c2c66affSColin Finck DataRunLength = Context->CacheRunLength;
1118c2c66affSColin Finck CurrentOffset = Context->CacheRunCurrentOffset;
1119c2c66affSColin Finck }
1120c2c66affSColin Finck else
1121c2c66affSColin Finck {
112252b9f467STrevor Thompson //TEMPTEMP
112352b9f467STrevor Thompson ULONG UsedBufferSize;
112452b9f467STrevor Thompson TempBuffer = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
1125391056baSPierre Schweitzer if (TempBuffer == NULL)
1126391056baSPierre Schweitzer {
1127391056baSPierre Schweitzer return STATUS_INSUFFICIENT_RESOURCES;
1128391056baSPierre Schweitzer }
112952b9f467STrevor Thompson
1130c2c66affSColin Finck LastLCN = 0;
1131c2c66affSColin Finck CurrentOffset = 0;
1132c2c66affSColin Finck
113352b9f467STrevor Thompson // This will be rewritten in the next iteration to just use the DataRuns MCB directly
113452b9f467STrevor Thompson ConvertLargeMCBToDataRuns(&Context->DataRunsMCB,
113552b9f467STrevor Thompson TempBuffer,
113652b9f467STrevor Thompson Vcb->NtfsInfo.BytesPerFileRecord,
113752b9f467STrevor Thompson &UsedBufferSize);
113852b9f467STrevor Thompson
113952b9f467STrevor Thompson DataRun = TempBuffer;
114052b9f467STrevor Thompson
1141c2c66affSColin Finck while (1)
1142c2c66affSColin Finck {
1143c2c66affSColin Finck DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
1144c2c66affSColin Finck if (DataRunOffset != -1)
1145c2c66affSColin Finck {
1146c2c66affSColin Finck /* Normal data run. */
1147c2c66affSColin Finck DataRunStartLCN = LastLCN + DataRunOffset;
1148c2c66affSColin Finck LastLCN = DataRunStartLCN;
1149c2c66affSColin Finck }
1150c2c66affSColin Finck else
1151c2c66affSColin Finck {
1152c2c66affSColin Finck /* Sparse data run. */
1153c2c66affSColin Finck DataRunStartLCN = -1;
1154c2c66affSColin Finck }
1155c2c66affSColin Finck
1156c2c66affSColin Finck if (Offset >= CurrentOffset &&
1157c2c66affSColin Finck Offset < CurrentOffset + (DataRunLength * Vcb->NtfsInfo.BytesPerCluster))
1158c2c66affSColin Finck {
1159c2c66affSColin Finck break;
1160c2c66affSColin Finck }
1161c2c66affSColin Finck
1162c2c66affSColin Finck if (*DataRun == 0)
1163c2c66affSColin Finck {
1164391056baSPierre Schweitzer ExFreePoolWithTag(TempBuffer, TAG_NTFS);
1165c2c66affSColin Finck return AlreadyRead;
1166c2c66affSColin Finck }
1167c2c66affSColin Finck
1168c2c66affSColin Finck CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
1169c2c66affSColin Finck }
1170c2c66affSColin Finck }
1171c2c66affSColin Finck
1172c2c66affSColin Finck /*
1173c2c66affSColin Finck * II. Go through the run list and read the data
1174c2c66affSColin Finck */
1175c2c66affSColin Finck
1176c2c66affSColin Finck ReadLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset), Length);
1177c2c66affSColin Finck if (DataRunStartLCN == -1)
1178c2c66affSColin Finck {
1179c2c66affSColin Finck RtlZeroMemory(Buffer, ReadLength);
1180c2c66affSColin Finck Status = STATUS_SUCCESS;
1181c2c66affSColin Finck }
1182c2c66affSColin Finck else
1183c2c66affSColin Finck {
1184c2c66affSColin Finck Status = NtfsReadDisk(Vcb->StorageDevice,
1185c2c66affSColin Finck DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster + Offset - CurrentOffset,
1186c2c66affSColin Finck ReadLength,
1187c2c66affSColin Finck Vcb->NtfsInfo.BytesPerSector,
1188c2c66affSColin Finck (PVOID)Buffer,
1189c2c66affSColin Finck FALSE);
1190c2c66affSColin Finck }
1191c2c66affSColin Finck if (NT_SUCCESS(Status))
1192c2c66affSColin Finck {
1193c2c66affSColin Finck Length -= ReadLength;
1194c2c66affSColin Finck Buffer += ReadLength;
1195c2c66affSColin Finck AlreadyRead += ReadLength;
1196c2c66affSColin Finck
1197c2c66affSColin Finck if (ReadLength == DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset))
1198c2c66affSColin Finck {
1199c2c66affSColin Finck CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
1200c2c66affSColin Finck DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
1201c2c66affSColin Finck if (DataRunOffset != (ULONGLONG)-1)
1202c2c66affSColin Finck {
1203c2c66affSColin Finck DataRunStartLCN = LastLCN + DataRunOffset;
1204c2c66affSColin Finck LastLCN = DataRunStartLCN;
1205c2c66affSColin Finck }
1206c2c66affSColin Finck else
1207c2c66affSColin Finck DataRunStartLCN = -1;
1208c2c66affSColin Finck }
1209c2c66affSColin Finck
1210c2c66affSColin Finck while (Length > 0)
1211c2c66affSColin Finck {
1212c2c66affSColin Finck ReadLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster, Length);
1213c2c66affSColin Finck if (DataRunStartLCN == -1)
1214c2c66affSColin Finck RtlZeroMemory(Buffer, ReadLength);
1215c2c66affSColin Finck else
1216c2c66affSColin Finck {
1217c2c66affSColin Finck Status = NtfsReadDisk(Vcb->StorageDevice,
1218c2c66affSColin Finck DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster,
1219c2c66affSColin Finck ReadLength,
1220c2c66affSColin Finck Vcb->NtfsInfo.BytesPerSector,
1221c2c66affSColin Finck (PVOID)Buffer,
1222c2c66affSColin Finck FALSE);
1223c2c66affSColin Finck if (!NT_SUCCESS(Status))
1224c2c66affSColin Finck break;
1225c2c66affSColin Finck }
1226c2c66affSColin Finck
1227c2c66affSColin Finck Length -= ReadLength;
1228c2c66affSColin Finck Buffer += ReadLength;
1229c2c66affSColin Finck AlreadyRead += ReadLength;
1230c2c66affSColin Finck
1231c2c66affSColin Finck /* We finished this request, but there still data in this data run. */
1232c2c66affSColin Finck if (Length == 0 && ReadLength != DataRunLength * Vcb->NtfsInfo.BytesPerCluster)
1233c2c66affSColin Finck break;
1234c2c66affSColin Finck
1235c2c66affSColin Finck /*
1236c2c66affSColin Finck * Go to next run in the list.
1237c2c66affSColin Finck */
1238c2c66affSColin Finck
1239c2c66affSColin Finck if (*DataRun == 0)
1240c2c66affSColin Finck break;
1241c2c66affSColin Finck CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
1242c2c66affSColin Finck DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
1243c2c66affSColin Finck if (DataRunOffset != -1)
1244c2c66affSColin Finck {
1245c2c66affSColin Finck /* Normal data run. */
1246c2c66affSColin Finck DataRunStartLCN = LastLCN + DataRunOffset;
1247c2c66affSColin Finck LastLCN = DataRunStartLCN;
1248c2c66affSColin Finck }
1249c2c66affSColin Finck else
1250c2c66affSColin Finck {
1251c2c66affSColin Finck /* Sparse data run. */
1252c2c66affSColin Finck DataRunStartLCN = -1;
1253c2c66affSColin Finck }
1254c2c66affSColin Finck } /* while */
1255c2c66affSColin Finck
1256c2c66affSColin Finck } /* if Disk */
1257c2c66affSColin Finck
125852b9f467STrevor Thompson // TEMPTEMP
12594dfcd1d5STrevor Thompson if (Context->pRecord->IsNonResident)
126052b9f467STrevor Thompson ExFreePoolWithTag(TempBuffer, TAG_NTFS);
126152b9f467STrevor Thompson
1262c2c66affSColin Finck Context->CacheRun = DataRun;
1263c2c66affSColin Finck Context->CacheRunOffset = Offset + AlreadyRead;
1264c2c66affSColin Finck Context->CacheRunStartLCN = DataRunStartLCN;
1265c2c66affSColin Finck Context->CacheRunLength = DataRunLength;
1266c2c66affSColin Finck Context->CacheRunLastLCN = LastLCN;
1267c2c66affSColin Finck Context->CacheRunCurrentOffset = CurrentOffset;
1268c2c66affSColin Finck
1269c2c66affSColin Finck return AlreadyRead;
1270c2c66affSColin Finck }
1271c2c66affSColin Finck
1272c2c66affSColin Finck
127358a13831STrevor Thompson /**
127458a13831STrevor Thompson * @name WriteAttribute
127558a13831STrevor Thompson * @implemented
127658a13831STrevor Thompson *
127758a13831STrevor Thompson * Writes an NTFS attribute to the disk. It presently borrows a lot of code from ReadAttribute(),
127858a13831STrevor Thompson * and it still needs more documentation / cleaning up.
127958a13831STrevor Thompson *
128058a13831STrevor Thompson * @param Vcb
128158a13831STrevor Thompson * Volume Control Block indicating which volume to write the attribute to
128258a13831STrevor Thompson *
128358a13831STrevor Thompson * @param Context
128458a13831STrevor Thompson * Pointer to an NTFS_ATTR_CONTEXT that has information about the attribute
128558a13831STrevor Thompson *
128658a13831STrevor Thompson * @param Offset
128758a13831STrevor Thompson * Offset, in bytes, from the beginning of the attribute indicating where to start
128858a13831STrevor Thompson * writing data
128958a13831STrevor Thompson *
129058a13831STrevor Thompson * @param Buffer
129158a13831STrevor Thompson * The data that's being written to the device
129258a13831STrevor Thompson *
129358a13831STrevor Thompson * @param Length
129458a13831STrevor Thompson * How much data will be written, in bytes
129558a13831STrevor Thompson *
129658a13831STrevor Thompson * @param RealLengthWritten
129758a13831STrevor Thompson * Pointer to a ULONG which will receive how much data was written, in bytes
129858a13831STrevor Thompson *
1299d484d91eSTrevor Thompson * @param FileRecord
1300d484d91eSTrevor Thompson * Optional pointer to a FILE_RECORD_HEADER that contains a copy of the file record
1301d484d91eSTrevor Thompson * being written to. Can be NULL, in which case the file record will be read from disk.
1302d484d91eSTrevor Thompson * If not-null, WriteAttribute() will skip reading from disk, and FileRecord
1303d484d91eSTrevor Thompson * will be updated with the newly-written attribute before the function returns.
1304d484d91eSTrevor Thompson *
130558a13831STrevor Thompson * @return
130658a13831STrevor Thompson * STATUS_SUCCESS if successful, an error code otherwise. STATUS_NOT_IMPLEMENTED if
130758a13831STrevor Thompson * writing to a sparse file.
130858a13831STrevor Thompson *
130958a13831STrevor Thompson * @remarks Note that in this context the word "attribute" isn't referring read-only, hidden,
131058a13831STrevor Thompson * etc. - the file's data is actually stored in an attribute in NTFS parlance.
131158a13831STrevor Thompson *
131258a13831STrevor Thompson */
131358a13831STrevor Thompson
131458a13831STrevor Thompson NTSTATUS
WriteAttribute(PDEVICE_EXTENSION Vcb,PNTFS_ATTR_CONTEXT Context,ULONGLONG Offset,const PUCHAR Buffer,ULONG Length,PULONG RealLengthWritten,PFILE_RECORD_HEADER FileRecord)131558a13831STrevor Thompson WriteAttribute(PDEVICE_EXTENSION Vcb,
131658a13831STrevor Thompson PNTFS_ATTR_CONTEXT Context,
131758a13831STrevor Thompson ULONGLONG Offset,
131858a13831STrevor Thompson const PUCHAR Buffer,
131958a13831STrevor Thompson ULONG Length,
1320d484d91eSTrevor Thompson PULONG RealLengthWritten,
1321d484d91eSTrevor Thompson PFILE_RECORD_HEADER FileRecord)
132258a13831STrevor Thompson {
132358a13831STrevor Thompson ULONGLONG LastLCN;
132458a13831STrevor Thompson PUCHAR DataRun;
132558a13831STrevor Thompson LONGLONG DataRunOffset;
132658a13831STrevor Thompson ULONGLONG DataRunLength;
132758a13831STrevor Thompson LONGLONG DataRunStartLCN;
132858a13831STrevor Thompson ULONGLONG CurrentOffset;
132958a13831STrevor Thompson ULONG WriteLength;
133058a13831STrevor Thompson NTSTATUS Status;
133158a13831STrevor Thompson PUCHAR SourceBuffer = Buffer;
133258a13831STrevor Thompson LONGLONG StartingOffset;
1333d484d91eSTrevor Thompson BOOLEAN FileRecordAllocated = FALSE;
133458a13831STrevor Thompson
133552b9f467STrevor Thompson //TEMPTEMP
133652b9f467STrevor Thompson PUCHAR TempBuffer;
133752b9f467STrevor Thompson
133852b9f467STrevor Thompson
1339d484d91eSTrevor Thompson DPRINT("WriteAttribute(%p, %p, %I64u, %p, %lu, %p, %p)\n", Vcb, Context, Offset, Buffer, Length, RealLengthWritten, FileRecord);
134058a13831STrevor Thompson
1341760cdfb5STrevor Thompson *RealLengthWritten = 0;
1342760cdfb5STrevor Thompson
134358a13831STrevor Thompson // is this a resident attribute?
13444dfcd1d5STrevor Thompson if (!Context->pRecord->IsNonResident)
134558a13831STrevor Thompson {
1346760cdfb5STrevor Thompson ULONG AttributeOffset;
1347760cdfb5STrevor Thompson PNTFS_ATTR_CONTEXT FoundContext;
1348d484d91eSTrevor Thompson PNTFS_ATTR_RECORD Destination;
1349d484d91eSTrevor Thompson
1350d484d91eSTrevor Thompson // Ensure requested data is within the bounds of the attribute
1351d484d91eSTrevor Thompson ASSERT(Offset + Length <= Context->pRecord->Resident.ValueLength);
135258a13831STrevor Thompson
13534dfcd1d5STrevor Thompson if (Offset + Length > Context->pRecord->Resident.ValueLength)
1354760cdfb5STrevor Thompson {
1355760cdfb5STrevor Thompson DPRINT1("DRIVER ERROR: Attribute is too small!\n");
1356760cdfb5STrevor Thompson return STATUS_INVALID_PARAMETER;
1357760cdfb5STrevor Thompson }
135858a13831STrevor Thompson
1359d484d91eSTrevor Thompson // Do we need to read the file record?
1360d484d91eSTrevor Thompson if (FileRecord == NULL)
1361d484d91eSTrevor Thompson {
1362216a2caeSPierre Schweitzer FileRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
1363760cdfb5STrevor Thompson if (!FileRecord)
1364760cdfb5STrevor Thompson {
1365760cdfb5STrevor Thompson DPRINT1("Error: Couldn't allocate file record!\n");
1366760cdfb5STrevor Thompson return STATUS_NO_MEMORY;
1367760cdfb5STrevor Thompson }
1368760cdfb5STrevor Thompson
1369d484d91eSTrevor Thompson FileRecordAllocated = TRUE;
1370d484d91eSTrevor Thompson
1371760cdfb5STrevor Thompson // read the file record
1372760cdfb5STrevor Thompson ReadFileRecord(Vcb, Context->FileMFTIndex, FileRecord);
1373d484d91eSTrevor Thompson }
1374760cdfb5STrevor Thompson
1375760cdfb5STrevor Thompson // find where to write the attribute data to
1376760cdfb5STrevor Thompson Status = FindAttribute(Vcb, FileRecord,
13774dfcd1d5STrevor Thompson Context->pRecord->Type,
13784dfcd1d5STrevor Thompson (PCWSTR)((ULONG_PTR)Context->pRecord + Context->pRecord->NameOffset),
13794dfcd1d5STrevor Thompson Context->pRecord->NameLength,
1380760cdfb5STrevor Thompson &FoundContext,
1381760cdfb5STrevor Thompson &AttributeOffset);
1382760cdfb5STrevor Thompson
1383760cdfb5STrevor Thompson if (!NT_SUCCESS(Status))
1384760cdfb5STrevor Thompson {
1385760cdfb5STrevor Thompson DPRINT1("ERROR: Couldn't find matching attribute!\n");
1386d484d91eSTrevor Thompson if(FileRecordAllocated)
1387216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord);
1388760cdfb5STrevor Thompson return Status;
1389760cdfb5STrevor Thompson }
1390760cdfb5STrevor Thompson
1391d484d91eSTrevor Thompson Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttributeOffset);
1392760cdfb5STrevor Thompson
1393d484d91eSTrevor Thompson DPRINT("Offset: %I64u, AttributeOffset: %u, ValueOffset: %u\n", Offset, AttributeOffset, Context->pRecord->Resident.ValueLength);
1394d484d91eSTrevor Thompson
1395d484d91eSTrevor Thompson // Will we be writing past the end of the allocated file record?
1396d484d91eSTrevor Thompson if (Offset + Length + AttributeOffset + Context->pRecord->Resident.ValueOffset > Vcb->NtfsInfo.BytesPerFileRecord)
1397760cdfb5STrevor Thompson {
1398760cdfb5STrevor Thompson DPRINT1("DRIVER ERROR: Data being written extends past end of file record!\n");
1399760cdfb5STrevor Thompson ReleaseAttributeContext(FoundContext);
1400d484d91eSTrevor Thompson if (FileRecordAllocated)
1401216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord);
1402760cdfb5STrevor Thompson return STATUS_INVALID_PARAMETER;
1403760cdfb5STrevor Thompson }
1404760cdfb5STrevor Thompson
1405d484d91eSTrevor Thompson // copy the data being written into the file record. We cast Offset to ULONG, which is safe because it's range has been verified.
1406d484d91eSTrevor Thompson RtlCopyMemory((PCHAR)((ULONG_PTR)Destination + Context->pRecord->Resident.ValueOffset + (ULONG)Offset), Buffer, Length);
1407760cdfb5STrevor Thompson
1408760cdfb5STrevor Thompson Status = UpdateFileRecord(Vcb, Context->FileMFTIndex, FileRecord);
1409760cdfb5STrevor Thompson
1410d484d91eSTrevor Thompson // Update the context's copy of the resident attribute
1411d484d91eSTrevor Thompson ASSERT(Context->pRecord->Length == Destination->Length);
1412d484d91eSTrevor Thompson RtlCopyMemory((PVOID)Context->pRecord, Destination, Context->pRecord->Length);
1413d484d91eSTrevor Thompson
1414760cdfb5STrevor Thompson ReleaseAttributeContext(FoundContext);
1415d484d91eSTrevor Thompson if (FileRecordAllocated)
1416216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, FileRecord);
1417760cdfb5STrevor Thompson
1418760cdfb5STrevor Thompson if (NT_SUCCESS(Status))
1419760cdfb5STrevor Thompson *RealLengthWritten = Length;
1420760cdfb5STrevor Thompson
1421760cdfb5STrevor Thompson return Status;
142258a13831STrevor Thompson }
142358a13831STrevor Thompson
142458a13831STrevor Thompson // This is a non-resident attribute.
142558a13831STrevor Thompson
142658a13831STrevor Thompson // I. Find the corresponding start data run.
142758a13831STrevor Thompson
142858a13831STrevor Thompson // FIXME: Cache seems to be non-working. Disable it for now
142958a13831STrevor Thompson //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
143058a13831STrevor Thompson /*if (0)
143158a13831STrevor Thompson {
143258a13831STrevor Thompson DataRun = Context->CacheRun;
143358a13831STrevor Thompson LastLCN = Context->CacheRunLastLCN;
143458a13831STrevor Thompson DataRunStartLCN = Context->CacheRunStartLCN;
143558a13831STrevor Thompson DataRunLength = Context->CacheRunLength;
143658a13831STrevor Thompson CurrentOffset = Context->CacheRunCurrentOffset;
143758a13831STrevor Thompson }
143858a13831STrevor Thompson else*/
143958a13831STrevor Thompson {
144052b9f467STrevor Thompson ULONG UsedBufferSize;
144158a13831STrevor Thompson LastLCN = 0;
144258a13831STrevor Thompson CurrentOffset = 0;
144358a13831STrevor Thompson
144452b9f467STrevor Thompson // This will be rewritten in the next iteration to just use the DataRuns MCB directly
144552b9f467STrevor Thompson TempBuffer = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
1446391056baSPierre Schweitzer if (TempBuffer == NULL)
1447391056baSPierre Schweitzer {
1448391056baSPierre Schweitzer return STATUS_INSUFFICIENT_RESOURCES;
1449391056baSPierre Schweitzer }
145052b9f467STrevor Thompson
145152b9f467STrevor Thompson ConvertLargeMCBToDataRuns(&Context->DataRunsMCB,
145252b9f467STrevor Thompson TempBuffer,
145352b9f467STrevor Thompson Vcb->NtfsInfo.BytesPerFileRecord,
145452b9f467STrevor Thompson &UsedBufferSize);
145552b9f467STrevor Thompson
145652b9f467STrevor Thompson DataRun = TempBuffer;
145752b9f467STrevor Thompson
145858a13831STrevor Thompson while (1)
145958a13831STrevor Thompson {
146058a13831STrevor Thompson DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
146158a13831STrevor Thompson if (DataRunOffset != -1)
146258a13831STrevor Thompson {
146358a13831STrevor Thompson // Normal data run.
146458a13831STrevor Thompson // DPRINT1("Writing to normal data run, LastLCN %I64u DataRunOffset %I64d\n", LastLCN, DataRunOffset);
146558a13831STrevor Thompson DataRunStartLCN = LastLCN + DataRunOffset;
146658a13831STrevor Thompson LastLCN = DataRunStartLCN;
146758a13831STrevor Thompson }
146858a13831STrevor Thompson else
146958a13831STrevor Thompson {
147058a13831STrevor Thompson // Sparse data run. We can't support writing to sparse files yet
147158a13831STrevor Thompson // (it may require increasing the allocation size).
147258a13831STrevor Thompson DataRunStartLCN = -1;
147358a13831STrevor Thompson DPRINT1("FIXME: Writing to sparse files is not supported yet!\n");
1474391056baSPierre Schweitzer Status = STATUS_NOT_IMPLEMENTED;
1475391056baSPierre Schweitzer goto Cleanup;
147658a13831STrevor Thompson }
147758a13831STrevor Thompson
147858a13831STrevor Thompson // Have we reached the data run we're trying to write to?
147958a13831STrevor Thompson if (Offset >= CurrentOffset &&
148058a13831STrevor Thompson Offset < CurrentOffset + (DataRunLength * Vcb->NtfsInfo.BytesPerCluster))
148158a13831STrevor Thompson {
148258a13831STrevor Thompson break;
148358a13831STrevor Thompson }
148458a13831STrevor Thompson
148558a13831STrevor Thompson if (*DataRun == 0)
148658a13831STrevor Thompson {
148758a13831STrevor Thompson // We reached the last assigned cluster
148858a13831STrevor Thompson // TODO: assign new clusters to the end of the file.
14890409b316STrevor Thompson // (Presently, this code will rarely be reached, the write will usually have already failed by now)
14900409b316STrevor Thompson // [We can reach here by creating a new file record when the MFT isn't large enough]
14910409b316STrevor Thompson DPRINT1("FIXME: Master File Table needs to be enlarged.\n");
1492391056baSPierre Schweitzer Status = STATUS_END_OF_FILE;
1493391056baSPierre Schweitzer goto Cleanup;
149458a13831STrevor Thompson }
149558a13831STrevor Thompson
149658a13831STrevor Thompson CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
149758a13831STrevor Thompson }
149858a13831STrevor Thompson }
149958a13831STrevor Thompson
150058a13831STrevor Thompson // II. Go through the run list and write the data
150158a13831STrevor Thompson
150258a13831STrevor Thompson /* REVIEWME -- As adapted from NtfsReadAttribute():
150358a13831STrevor Thompson We seem to be making a special case for the first applicable data run, but I'm not sure why.
150458a13831STrevor Thompson Does it have something to do with (not) caching? Is this strategy equally applicable to writing? */
150558a13831STrevor Thompson
150658a13831STrevor Thompson WriteLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset), Length);
150758a13831STrevor Thompson
150858a13831STrevor Thompson StartingOffset = DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster + Offset - CurrentOffset;
150958a13831STrevor Thompson
151058a13831STrevor Thompson // Write the data to the disk
151158a13831STrevor Thompson Status = NtfsWriteDisk(Vcb->StorageDevice,
151258a13831STrevor Thompson StartingOffset,
151358a13831STrevor Thompson WriteLength,
151458a13831STrevor Thompson Vcb->NtfsInfo.BytesPerSector,
151558a13831STrevor Thompson (PVOID)SourceBuffer);
151658a13831STrevor Thompson
151758a13831STrevor Thompson // Did the write fail?
151858a13831STrevor Thompson if (!NT_SUCCESS(Status))
151958a13831STrevor Thompson {
152058a13831STrevor Thompson Context->CacheRun = DataRun;
152158a13831STrevor Thompson Context->CacheRunOffset = Offset;
152258a13831STrevor Thompson Context->CacheRunStartLCN = DataRunStartLCN;
152358a13831STrevor Thompson Context->CacheRunLength = DataRunLength;
152458a13831STrevor Thompson Context->CacheRunLastLCN = LastLCN;
152558a13831STrevor Thompson Context->CacheRunCurrentOffset = CurrentOffset;
152658a13831STrevor Thompson
1527391056baSPierre Schweitzer goto Cleanup;
152858a13831STrevor Thompson }
152958a13831STrevor Thompson
153058a13831STrevor Thompson Length -= WriteLength;
153158a13831STrevor Thompson SourceBuffer += WriteLength;
153258a13831STrevor Thompson *RealLengthWritten += WriteLength;
153358a13831STrevor Thompson
153458a13831STrevor Thompson // Did we write to the end of the data run?
153558a13831STrevor Thompson if (WriteLength == DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset))
153658a13831STrevor Thompson {
153758a13831STrevor Thompson // Advance to the next data run
153858a13831STrevor Thompson CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
153958a13831STrevor Thompson DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
154058a13831STrevor Thompson
154158a13831STrevor Thompson if (DataRunOffset != (ULONGLONG)-1)
154258a13831STrevor Thompson {
154358a13831STrevor Thompson DataRunStartLCN = LastLCN + DataRunOffset;
154458a13831STrevor Thompson LastLCN = DataRunStartLCN;
154558a13831STrevor Thompson }
154658a13831STrevor Thompson else
154758a13831STrevor Thompson DataRunStartLCN = -1;
154858a13831STrevor Thompson }
154958a13831STrevor Thompson
155058a13831STrevor Thompson // Do we have more data to write?
155158a13831STrevor Thompson while (Length > 0)
155258a13831STrevor Thompson {
155358a13831STrevor Thompson // Make sure we don't write past the end of the current data run
155458a13831STrevor Thompson WriteLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster, Length);
155558a13831STrevor Thompson
155658a13831STrevor Thompson // Are we dealing with a sparse data run?
155758a13831STrevor Thompson if (DataRunStartLCN == -1)
155858a13831STrevor Thompson {
155958a13831STrevor Thompson DPRINT1("FIXME: Don't know how to write to sparse files yet! (DataRunStartLCN == -1)\n");
1560391056baSPierre Schweitzer Status = STATUS_NOT_IMPLEMENTED;
1561391056baSPierre Schweitzer goto Cleanup;
156258a13831STrevor Thompson }
156358a13831STrevor Thompson else
156458a13831STrevor Thompson {
156558a13831STrevor Thompson // write the data to the disk
156658a13831STrevor Thompson Status = NtfsWriteDisk(Vcb->StorageDevice,
156758a13831STrevor Thompson DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster,
156858a13831STrevor Thompson WriteLength,
156958a13831STrevor Thompson Vcb->NtfsInfo.BytesPerSector,
157058a13831STrevor Thompson (PVOID)SourceBuffer);
157158a13831STrevor Thompson if (!NT_SUCCESS(Status))
157258a13831STrevor Thompson break;
157358a13831STrevor Thompson }
157458a13831STrevor Thompson
157558a13831STrevor Thompson Length -= WriteLength;
157658a13831STrevor Thompson SourceBuffer += WriteLength;
1577ea6b9622STrevor Thompson *RealLengthWritten += WriteLength;
157858a13831STrevor Thompson
157958a13831STrevor Thompson // We finished this request, but there's still data in this data run.
158058a13831STrevor Thompson if (Length == 0 && WriteLength != DataRunLength * Vcb->NtfsInfo.BytesPerCluster)
158158a13831STrevor Thompson break;
158258a13831STrevor Thompson
158358a13831STrevor Thompson // Go to next run in the list.
158458a13831STrevor Thompson
158558a13831STrevor Thompson if (*DataRun == 0)
158658a13831STrevor Thompson {
158758a13831STrevor Thompson // that was the last run
158858a13831STrevor Thompson if (Length > 0)
158958a13831STrevor Thompson {
159058a13831STrevor Thompson // Failed sanity check.
159158a13831STrevor Thompson DPRINT1("Encountered EOF before expected!\n");
1592391056baSPierre Schweitzer Status = STATUS_END_OF_FILE;
1593391056baSPierre Schweitzer goto Cleanup;
159458a13831STrevor Thompson }
159558a13831STrevor Thompson
159658a13831STrevor Thompson break;
159758a13831STrevor Thompson }
159858a13831STrevor Thompson
159958a13831STrevor Thompson // Advance to the next data run
160058a13831STrevor Thompson CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
160158a13831STrevor Thompson DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
160258a13831STrevor Thompson if (DataRunOffset != -1)
160358a13831STrevor Thompson {
160458a13831STrevor Thompson // Normal data run.
160558a13831STrevor Thompson DataRunStartLCN = LastLCN + DataRunOffset;
160658a13831STrevor Thompson LastLCN = DataRunStartLCN;
160758a13831STrevor Thompson }
160858a13831STrevor Thompson else
160958a13831STrevor Thompson {
161058a13831STrevor Thompson // Sparse data run.
161158a13831STrevor Thompson DataRunStartLCN = -1;
161258a13831STrevor Thompson }
161358a13831STrevor Thompson } // end while (Length > 0) [more data to write]
161458a13831STrevor Thompson
161558a13831STrevor Thompson Context->CacheRun = DataRun;
161658a13831STrevor Thompson Context->CacheRunOffset = Offset + *RealLengthWritten;
161758a13831STrevor Thompson Context->CacheRunStartLCN = DataRunStartLCN;
161858a13831STrevor Thompson Context->CacheRunLength = DataRunLength;
161958a13831STrevor Thompson Context->CacheRunLastLCN = LastLCN;
162058a13831STrevor Thompson Context->CacheRunCurrentOffset = CurrentOffset;
162158a13831STrevor Thompson
1622391056baSPierre Schweitzer Cleanup:
1623391056baSPierre Schweitzer // TEMPTEMP
1624391056baSPierre Schweitzer if (Context->pRecord->IsNonResident)
1625391056baSPierre Schweitzer ExFreePoolWithTag(TempBuffer, TAG_NTFS);
1626391056baSPierre Schweitzer
162758a13831STrevor Thompson return Status;
162858a13831STrevor Thompson }
162958a13831STrevor Thompson
1630c2c66affSColin Finck NTSTATUS
ReadFileRecord(PDEVICE_EXTENSION Vcb,ULONGLONG index,PFILE_RECORD_HEADER file)1631c2c66affSColin Finck ReadFileRecord(PDEVICE_EXTENSION Vcb,
1632c2c66affSColin Finck ULONGLONG index,
1633c2c66affSColin Finck PFILE_RECORD_HEADER file)
1634c2c66affSColin Finck {
1635c2c66affSColin Finck ULONGLONG BytesRead;
1636c2c66affSColin Finck
1637c2c66affSColin Finck DPRINT("ReadFileRecord(%p, %I64x, %p)\n", Vcb, index, file);
1638c2c66affSColin Finck
1639c2c66affSColin Finck BytesRead = ReadAttribute(Vcb, Vcb->MFTContext, index * Vcb->NtfsInfo.BytesPerFileRecord, (PCHAR)file, Vcb->NtfsInfo.BytesPerFileRecord);
1640c2c66affSColin Finck if (BytesRead != Vcb->NtfsInfo.BytesPerFileRecord)
1641c2c66affSColin Finck {
164238c947b7STrevor Thompson DPRINT1("ReadFileRecord failed: %I64u read, %lu expected\n", BytesRead, Vcb->NtfsInfo.BytesPerFileRecord);
1643c2c66affSColin Finck return STATUS_PARTIAL_COPY;
1644c2c66affSColin Finck }
1645c2c66affSColin Finck
1646c2c66affSColin Finck /* Apply update sequence array fixups. */
1647760cdfb5STrevor Thompson DPRINT("Sequence number: %u\n", file->SequenceNumber);
1648c2c66affSColin Finck return FixupUpdateSequenceArray(Vcb, &file->Ntfs);
1649c2c66affSColin Finck }
1650c2c66affSColin Finck
1651a7a2c0d7STrevor Thompson
1652a7a2c0d7STrevor Thompson /**
1653a7a2c0d7STrevor Thompson * Searches a file's parent directory (given the parent's index in the mft)
1654a7a2c0d7STrevor Thompson * for the given file. Upon finding an index entry for that file, updates
1655a7a2c0d7STrevor Thompson * Data Size and Allocated Size values in the $FILE_NAME attribute of that entry.
1656a7a2c0d7STrevor Thompson *
1657a7a2c0d7STrevor Thompson * (Most of this code was copied from NtfsFindMftRecord)
1658a7a2c0d7STrevor Thompson */
1659a7a2c0d7STrevor Thompson NTSTATUS
UpdateFileNameRecord(PDEVICE_EXTENSION Vcb,ULONGLONG ParentMFTIndex,PUNICODE_STRING FileName,BOOLEAN DirSearch,ULONGLONG NewDataSize,ULONGLONG NewAllocationSize,BOOLEAN CaseSensitive)1660a7a2c0d7STrevor Thompson UpdateFileNameRecord(PDEVICE_EXTENSION Vcb,
1661a7a2c0d7STrevor Thompson ULONGLONG ParentMFTIndex,
1662a7a2c0d7STrevor Thompson PUNICODE_STRING FileName,
1663a7a2c0d7STrevor Thompson BOOLEAN DirSearch,
1664a7a2c0d7STrevor Thompson ULONGLONG NewDataSize,
1665032be029STrevor Thompson ULONGLONG NewAllocationSize,
1666032be029STrevor Thompson BOOLEAN CaseSensitive)
1667a7a2c0d7STrevor Thompson {
1668a7a2c0d7STrevor Thompson PFILE_RECORD_HEADER MftRecord;
1669a7a2c0d7STrevor Thompson PNTFS_ATTR_CONTEXT IndexRootCtx;
1670a7a2c0d7STrevor Thompson PINDEX_ROOT_ATTRIBUTE IndexRoot;
1671a7a2c0d7STrevor Thompson PCHAR IndexRecord;
1672a7a2c0d7STrevor Thompson PINDEX_ENTRY_ATTRIBUTE IndexEntry, IndexEntryEnd;
1673a7a2c0d7STrevor Thompson NTSTATUS Status;
1674a7a2c0d7STrevor Thompson ULONG CurrentEntry = 0;
1675a7a2c0d7STrevor Thompson
167638c947b7STrevor Thompson DPRINT("UpdateFileNameRecord(%p, %I64d, %wZ, %s, %I64u, %I64u, %s)\n",
1677032be029STrevor Thompson Vcb,
1678032be029STrevor Thompson ParentMFTIndex,
1679032be029STrevor Thompson FileName,
168038c947b7STrevor Thompson DirSearch ? "TRUE" : "FALSE",
1681032be029STrevor Thompson NewDataSize,
1682032be029STrevor Thompson NewAllocationSize,
1683032be029STrevor Thompson CaseSensitive ? "TRUE" : "FALSE");
1684a7a2c0d7STrevor Thompson
1685216a2caeSPierre Schweitzer MftRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
1686a7a2c0d7STrevor Thompson if (MftRecord == NULL)
1687a7a2c0d7STrevor Thompson {
1688a7a2c0d7STrevor Thompson return STATUS_INSUFFICIENT_RESOURCES;
1689a7a2c0d7STrevor Thompson }
1690a7a2c0d7STrevor Thompson
1691a7a2c0d7STrevor Thompson Status = ReadFileRecord(Vcb, ParentMFTIndex, MftRecord);
1692a7a2c0d7STrevor Thompson if (!NT_SUCCESS(Status))
1693a7a2c0d7STrevor Thompson {
1694216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
1695a7a2c0d7STrevor Thompson return Status;
1696a7a2c0d7STrevor Thompson }
1697a7a2c0d7STrevor Thompson
1698a7a2c0d7STrevor Thompson ASSERT(MftRecord->Ntfs.Type == NRH_FILE_TYPE);
1699a7a2c0d7STrevor Thompson Status = FindAttribute(Vcb, MftRecord, AttributeIndexRoot, L"$I30", 4, &IndexRootCtx, NULL);
1700a7a2c0d7STrevor Thompson if (!NT_SUCCESS(Status))
1701a7a2c0d7STrevor Thompson {
1702216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
1703a7a2c0d7STrevor Thompson return Status;
1704a7a2c0d7STrevor Thompson }
1705a7a2c0d7STrevor Thompson
1706a7a2c0d7STrevor Thompson IndexRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerIndexRecord, TAG_NTFS);
1707a7a2c0d7STrevor Thompson if (IndexRecord == NULL)
1708a7a2c0d7STrevor Thompson {
1709a7a2c0d7STrevor Thompson ReleaseAttributeContext(IndexRootCtx);
1710216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
1711a7a2c0d7STrevor Thompson return STATUS_INSUFFICIENT_RESOURCES;
1712a7a2c0d7STrevor Thompson }
1713a7a2c0d7STrevor Thompson
17144dfcd1d5STrevor Thompson Status = ReadAttribute(Vcb, IndexRootCtx, 0, IndexRecord, AttributeDataLength(IndexRootCtx->pRecord));
1715f5b7f90fSTrevor Thompson if (!NT_SUCCESS(Status))
1716f5b7f90fSTrevor Thompson {
1717f5b7f90fSTrevor Thompson DPRINT1("ERROR: Failed to read Index Root!\n");
1718f5b7f90fSTrevor Thompson ExFreePoolWithTag(IndexRecord, TAG_NTFS);
1719f5b7f90fSTrevor Thompson ReleaseAttributeContext(IndexRootCtx);
1720216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
1721c7bba39aSPierre Schweitzer return Status;
1722f5b7f90fSTrevor Thompson }
1723f5b7f90fSTrevor Thompson
1724a7a2c0d7STrevor Thompson IndexRoot = (PINDEX_ROOT_ATTRIBUTE)IndexRecord;
1725a7a2c0d7STrevor Thompson IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)&IndexRoot->Header + IndexRoot->Header.FirstEntryOffset);
1726a7a2c0d7STrevor Thompson // Index root is always resident.
1727a7a2c0d7STrevor Thompson IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)(IndexRecord + IndexRoot->Header.TotalSizeOfEntries);
1728a7a2c0d7STrevor Thompson
1729a7a2c0d7STrevor Thompson DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb->NtfsInfo.BytesPerIndexRecord, IndexRoot->SizeOfEntry);
1730a7a2c0d7STrevor Thompson
1731a7a2c0d7STrevor Thompson Status = UpdateIndexEntryFileNameSize(Vcb,
1732a7a2c0d7STrevor Thompson MftRecord,
1733a7a2c0d7STrevor Thompson IndexRecord,
1734a7a2c0d7STrevor Thompson IndexRoot->SizeOfEntry,
1735a7a2c0d7STrevor Thompson IndexEntry,
1736a7a2c0d7STrevor Thompson IndexEntryEnd,
1737a7a2c0d7STrevor Thompson FileName,
1738a7a2c0d7STrevor Thompson &CurrentEntry,
1739a7a2c0d7STrevor Thompson &CurrentEntry,
1740a7a2c0d7STrevor Thompson DirSearch,
1741a7a2c0d7STrevor Thompson NewDataSize,
1742032be029STrevor Thompson NewAllocationSize,
1743032be029STrevor Thompson CaseSensitive);
1744a7a2c0d7STrevor Thompson
1745f5b7f90fSTrevor Thompson if (Status == STATUS_PENDING)
1746f5b7f90fSTrevor Thompson {
1747f5b7f90fSTrevor Thompson // we need to write the index root attribute back to disk
1748f5b7f90fSTrevor Thompson ULONG LengthWritten;
1749d484d91eSTrevor Thompson Status = WriteAttribute(Vcb, IndexRootCtx, 0, (PUCHAR)IndexRecord, AttributeDataLength(IndexRootCtx->pRecord), &LengthWritten, MftRecord);
1750f5b7f90fSTrevor Thompson if (!NT_SUCCESS(Status))
1751f5b7f90fSTrevor Thompson {
1752f5b7f90fSTrevor Thompson DPRINT1("ERROR: Couldn't update Index Root!\n");
1753f5b7f90fSTrevor Thompson }
1754f5b7f90fSTrevor Thompson
1755f5b7f90fSTrevor Thompson }
1756f5b7f90fSTrevor Thompson
1757a7a2c0d7STrevor Thompson ReleaseAttributeContext(IndexRootCtx);
1758a7a2c0d7STrevor Thompson ExFreePoolWithTag(IndexRecord, TAG_NTFS);
1759216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
1760a7a2c0d7STrevor Thompson
1761a7a2c0d7STrevor Thompson return Status;
1762a7a2c0d7STrevor Thompson }
1763a7a2c0d7STrevor Thompson
1764a7a2c0d7STrevor Thompson /**
1765a7a2c0d7STrevor Thompson * Recursively searches directory index and applies the size update to the $FILE_NAME attribute of the
1766a7a2c0d7STrevor Thompson * proper index entry.
1767a7a2c0d7STrevor Thompson * (Heavily based on BrowseIndexEntries)
1768a7a2c0d7STrevor Thompson */
1769a7a2c0d7STrevor Thompson NTSTATUS
UpdateIndexEntryFileNameSize(PDEVICE_EXTENSION Vcb,PFILE_RECORD_HEADER MftRecord,PCHAR IndexRecord,ULONG IndexBlockSize,PINDEX_ENTRY_ATTRIBUTE FirstEntry,PINDEX_ENTRY_ATTRIBUTE LastEntry,PUNICODE_STRING FileName,PULONG StartEntry,PULONG CurrentEntry,BOOLEAN DirSearch,ULONGLONG NewDataSize,ULONGLONG NewAllocatedSize,BOOLEAN CaseSensitive)1770a7a2c0d7STrevor Thompson UpdateIndexEntryFileNameSize(PDEVICE_EXTENSION Vcb,
1771a7a2c0d7STrevor Thompson PFILE_RECORD_HEADER MftRecord,
1772a7a2c0d7STrevor Thompson PCHAR IndexRecord,
1773a7a2c0d7STrevor Thompson ULONG IndexBlockSize,
1774a7a2c0d7STrevor Thompson PINDEX_ENTRY_ATTRIBUTE FirstEntry,
1775a7a2c0d7STrevor Thompson PINDEX_ENTRY_ATTRIBUTE LastEntry,
1776a7a2c0d7STrevor Thompson PUNICODE_STRING FileName,
1777a7a2c0d7STrevor Thompson PULONG StartEntry,
1778a7a2c0d7STrevor Thompson PULONG CurrentEntry,
1779a7a2c0d7STrevor Thompson BOOLEAN DirSearch,
1780a7a2c0d7STrevor Thompson ULONGLONG NewDataSize,
1781032be029STrevor Thompson ULONGLONG NewAllocatedSize,
1782032be029STrevor Thompson BOOLEAN CaseSensitive)
1783a7a2c0d7STrevor Thompson {
1784a7a2c0d7STrevor Thompson NTSTATUS Status;
1785a7a2c0d7STrevor Thompson ULONG RecordOffset;
1786a7a2c0d7STrevor Thompson PINDEX_ENTRY_ATTRIBUTE IndexEntry;
1787a7a2c0d7STrevor Thompson PNTFS_ATTR_CONTEXT IndexAllocationCtx;
1788a7a2c0d7STrevor Thompson ULONGLONG IndexAllocationSize;
1789a7a2c0d7STrevor Thompson PINDEX_BUFFER IndexBuffer;
1790a7a2c0d7STrevor Thompson
179138c947b7STrevor Thompson DPRINT("UpdateIndexEntrySize(%p, %p, %p, %lu, %p, %p, %wZ, %lu, %lu, %s, %I64u, %I64u, %s)\n",
179238c947b7STrevor Thompson Vcb,
179338c947b7STrevor Thompson MftRecord,
179438c947b7STrevor Thompson IndexRecord,
179538c947b7STrevor Thompson IndexBlockSize,
179638c947b7STrevor Thompson FirstEntry,
179738c947b7STrevor Thompson LastEntry,
179838c947b7STrevor Thompson FileName,
179938c947b7STrevor Thompson *StartEntry,
180038c947b7STrevor Thompson *CurrentEntry,
180138c947b7STrevor Thompson DirSearch ? "TRUE" : "FALSE",
180238c947b7STrevor Thompson NewDataSize,
180338c947b7STrevor Thompson NewAllocatedSize,
180438c947b7STrevor Thompson CaseSensitive ? "TRUE" : "FALSE");
1805a7a2c0d7STrevor Thompson
1806a7a2c0d7STrevor Thompson // find the index entry responsible for the file we're trying to update
1807a7a2c0d7STrevor Thompson IndexEntry = FirstEntry;
1808a7a2c0d7STrevor Thompson while (IndexEntry < LastEntry &&
1809a7a2c0d7STrevor Thompson !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
1810a7a2c0d7STrevor Thompson {
1811935fcd1bSTrevor Thompson if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) > NTFS_FILE_FIRST_USER_FILE &&
1812a7a2c0d7STrevor Thompson *CurrentEntry >= *StartEntry &&
1813a7a2c0d7STrevor Thompson IndexEntry->FileName.NameType != NTFS_FILE_NAME_DOS &&
1814032be029STrevor Thompson CompareFileName(FileName, IndexEntry, DirSearch, CaseSensitive))
1815a7a2c0d7STrevor Thompson {
1816a7a2c0d7STrevor Thompson *StartEntry = *CurrentEntry;
1817a7a2c0d7STrevor Thompson IndexEntry->FileName.DataSize = NewDataSize;
1818a7a2c0d7STrevor Thompson IndexEntry->FileName.AllocatedSize = NewAllocatedSize;
1819a7a2c0d7STrevor Thompson // indicate that the caller will still need to write the structure to the disk
1820a7a2c0d7STrevor Thompson return STATUS_PENDING;
1821a7a2c0d7STrevor Thompson }
1822a7a2c0d7STrevor Thompson
1823a7a2c0d7STrevor Thompson (*CurrentEntry) += 1;
1824a7a2c0d7STrevor Thompson ASSERT(IndexEntry->Length >= sizeof(INDEX_ENTRY_ATTRIBUTE));
1825a7a2c0d7STrevor Thompson IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
1826a7a2c0d7STrevor Thompson }
1827a7a2c0d7STrevor Thompson
1828a7a2c0d7STrevor Thompson /* If we're already browsing a subnode */
1829a7a2c0d7STrevor Thompson if (IndexRecord == NULL)
1830a7a2c0d7STrevor Thompson {
1831a7a2c0d7STrevor Thompson return STATUS_OBJECT_PATH_NOT_FOUND;
1832a7a2c0d7STrevor Thompson }
1833a7a2c0d7STrevor Thompson
1834a7a2c0d7STrevor Thompson /* If there's no subnode */
1835a7a2c0d7STrevor Thompson if (!(IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE))
1836a7a2c0d7STrevor Thompson {
1837a7a2c0d7STrevor Thompson return STATUS_OBJECT_PATH_NOT_FOUND;
1838a7a2c0d7STrevor Thompson }
1839a7a2c0d7STrevor Thompson
1840a7a2c0d7STrevor Thompson Status = FindAttribute(Vcb, MftRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationCtx, NULL);
1841a7a2c0d7STrevor Thompson if (!NT_SUCCESS(Status))
1842a7a2c0d7STrevor Thompson {
1843a7a2c0d7STrevor Thompson DPRINT("Corrupted filesystem!\n");
1844a7a2c0d7STrevor Thompson return Status;
1845a7a2c0d7STrevor Thompson }
1846a7a2c0d7STrevor Thompson
18474dfcd1d5STrevor Thompson IndexAllocationSize = AttributeDataLength(IndexAllocationCtx->pRecord);
1848a7a2c0d7STrevor Thompson Status = STATUS_OBJECT_PATH_NOT_FOUND;
1849a7a2c0d7STrevor Thompson for (RecordOffset = 0; RecordOffset < IndexAllocationSize; RecordOffset += IndexBlockSize)
1850a7a2c0d7STrevor Thompson {
1851a7a2c0d7STrevor Thompson ReadAttribute(Vcb, IndexAllocationCtx, RecordOffset, IndexRecord, IndexBlockSize);
1852a7a2c0d7STrevor Thompson Status = FixupUpdateSequenceArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs);
1853a7a2c0d7STrevor Thompson if (!NT_SUCCESS(Status))
1854a7a2c0d7STrevor Thompson {
1855a7a2c0d7STrevor Thompson break;
1856a7a2c0d7STrevor Thompson }
1857a7a2c0d7STrevor Thompson
1858a7a2c0d7STrevor Thompson IndexBuffer = (PINDEX_BUFFER)IndexRecord;
1859a7a2c0d7STrevor Thompson ASSERT(IndexBuffer->Ntfs.Type == NRH_INDX_TYPE);
1860a7a2c0d7STrevor Thompson ASSERT(IndexBuffer->Header.AllocatedSize + FIELD_OFFSET(INDEX_BUFFER, Header) == IndexBlockSize);
1861a7a2c0d7STrevor Thompson FirstEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.FirstEntryOffset);
1862a7a2c0d7STrevor Thompson LastEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.TotalSizeOfEntries);
1863a7a2c0d7STrevor Thompson ASSERT(LastEntry <= (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexBuffer + IndexBlockSize));
1864a7a2c0d7STrevor Thompson
1865032be029STrevor Thompson Status = UpdateIndexEntryFileNameSize(NULL,
1866032be029STrevor Thompson NULL,
1867032be029STrevor Thompson NULL,
1868032be029STrevor Thompson 0,
1869032be029STrevor Thompson FirstEntry,
1870032be029STrevor Thompson LastEntry,
1871032be029STrevor Thompson FileName,
1872032be029STrevor Thompson StartEntry,
1873032be029STrevor Thompson CurrentEntry,
1874032be029STrevor Thompson DirSearch,
1875032be029STrevor Thompson NewDataSize,
1876032be029STrevor Thompson NewAllocatedSize,
1877032be029STrevor Thompson CaseSensitive);
1878a7a2c0d7STrevor Thompson if (Status == STATUS_PENDING)
1879a7a2c0d7STrevor Thompson {
1880a7a2c0d7STrevor Thompson // write the index record back to disk
1881a7a2c0d7STrevor Thompson ULONG Written;
1882a7a2c0d7STrevor Thompson
1883a7a2c0d7STrevor Thompson // first we need to update the fixup values for the index block
1884a7a2c0d7STrevor Thompson Status = AddFixupArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs);
1885a7a2c0d7STrevor Thompson if (!NT_SUCCESS(Status))
1886a7a2c0d7STrevor Thompson {
1887a7a2c0d7STrevor Thompson DPRINT1("Error: Failed to update fixup sequence array!\n");
1888a7a2c0d7STrevor Thompson break;
1889a7a2c0d7STrevor Thompson }
1890a7a2c0d7STrevor Thompson
1891d484d91eSTrevor Thompson Status = WriteAttribute(Vcb, IndexAllocationCtx, RecordOffset, (const PUCHAR)IndexRecord, IndexBlockSize, &Written, MftRecord);
1892a7a2c0d7STrevor Thompson if (!NT_SUCCESS(Status))
1893a7a2c0d7STrevor Thompson {
1894a7a2c0d7STrevor Thompson DPRINT1("ERROR Performing write!\n");
1895a7a2c0d7STrevor Thompson break;
1896a7a2c0d7STrevor Thompson }
1897a7a2c0d7STrevor Thompson
1898a7a2c0d7STrevor Thompson Status = STATUS_SUCCESS;
1899a7a2c0d7STrevor Thompson break;
1900a7a2c0d7STrevor Thompson }
1901a7a2c0d7STrevor Thompson if (NT_SUCCESS(Status))
1902a7a2c0d7STrevor Thompson {
1903a7a2c0d7STrevor Thompson break;
1904a7a2c0d7STrevor Thompson }
1905a7a2c0d7STrevor Thompson }
1906a7a2c0d7STrevor Thompson
1907a7a2c0d7STrevor Thompson ReleaseAttributeContext(IndexAllocationCtx);
1908a7a2c0d7STrevor Thompson return Status;
1909a7a2c0d7STrevor Thompson }
1910a7a2c0d7STrevor Thompson
1911ba33b9faSTrevor Thompson /**
19120409b316STrevor Thompson * @name UpdateFileRecord
1913ba33b9faSTrevor Thompson * @implemented
19140409b316STrevor Thompson *
1915ba33b9faSTrevor Thompson * Writes a file record to the master file table, at a given index.
19160409b316STrevor Thompson *
19170409b316STrevor Thompson * @param Vcb
19180409b316STrevor Thompson * Pointer to the DEVICE_EXTENSION of the target drive being written to.
19190409b316STrevor Thompson *
19200409b316STrevor Thompson * @param MftIndex
19210409b316STrevor Thompson * Target index in the master file table to store the file record.
19220409b316STrevor Thompson *
19230409b316STrevor Thompson * @param FileRecord
19240409b316STrevor Thompson * Pointer to the complete file record which will be written to the master file table.
19250409b316STrevor Thompson *
19260409b316STrevor Thompson * @return
19270409b316STrevor Thompson * STATUS_SUCCESSFUL on success. An error passed from WriteAttribute() otherwise.
19280409b316STrevor Thompson *
1929ba33b9faSTrevor Thompson */
1930ba33b9faSTrevor Thompson NTSTATUS
UpdateFileRecord(PDEVICE_EXTENSION Vcb,ULONGLONG MftIndex,PFILE_RECORD_HEADER FileRecord)1931ba33b9faSTrevor Thompson UpdateFileRecord(PDEVICE_EXTENSION Vcb,
19320409b316STrevor Thompson ULONGLONG MftIndex,
19330409b316STrevor Thompson PFILE_RECORD_HEADER FileRecord)
1934ba33b9faSTrevor Thompson {
1935ba33b9faSTrevor Thompson ULONG BytesWritten;
1936ba33b9faSTrevor Thompson NTSTATUS Status = STATUS_SUCCESS;
1937ba33b9faSTrevor Thompson
19380409b316STrevor Thompson DPRINT("UpdateFileRecord(%p, 0x%I64x, %p)\n", Vcb, MftIndex, FileRecord);
1939ba33b9faSTrevor Thompson
1940ba33b9faSTrevor Thompson // Add the fixup array to prepare the data for writing to disk
19410409b316STrevor Thompson AddFixupArray(Vcb, &FileRecord->Ntfs);
1942ba33b9faSTrevor Thompson
1943ba33b9faSTrevor Thompson // write the file record to the master file table
1944d484d91eSTrevor Thompson Status = WriteAttribute(Vcb,
1945d484d91eSTrevor Thompson Vcb->MFTContext,
1946d484d91eSTrevor Thompson MftIndex * Vcb->NtfsInfo.BytesPerFileRecord,
1947d484d91eSTrevor Thompson (const PUCHAR)FileRecord,
1948d484d91eSTrevor Thompson Vcb->NtfsInfo.BytesPerFileRecord,
1949d484d91eSTrevor Thompson &BytesWritten,
1950d484d91eSTrevor Thompson FileRecord);
1951ba33b9faSTrevor Thompson
1952ba33b9faSTrevor Thompson if (!NT_SUCCESS(Status))
1953ba33b9faSTrevor Thompson {
19546ab30720STrevor Thompson DPRINT1("UpdateFileRecord failed: %lu written, %lu expected\n", BytesWritten, Vcb->NtfsInfo.BytesPerFileRecord);
1955ba33b9faSTrevor Thompson }
1956ba33b9faSTrevor Thompson
1957268a139eSTrevor Thompson // remove the fixup array (so the file record pointer can still be used)
19580409b316STrevor Thompson FixupUpdateSequenceArray(Vcb, &FileRecord->Ntfs);
1959268a139eSTrevor Thompson
1960ba33b9faSTrevor Thompson return Status;
1961ba33b9faSTrevor Thompson }
1962ba33b9faSTrevor Thompson
1963c2c66affSColin Finck
1964c2c66affSColin Finck NTSTATUS
FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb,PNTFS_RECORD_HEADER Record)1965c2c66affSColin Finck FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb,
1966c2c66affSColin Finck PNTFS_RECORD_HEADER Record)
1967c2c66affSColin Finck {
1968c2c66affSColin Finck USHORT *USA;
1969c2c66affSColin Finck USHORT USANumber;
1970c2c66affSColin Finck USHORT USACount;
1971c2c66affSColin Finck USHORT *Block;
1972c2c66affSColin Finck
1973c2c66affSColin Finck USA = (USHORT*)((PCHAR)Record + Record->UsaOffset);
1974c2c66affSColin Finck USANumber = *(USA++);
1975c2c66affSColin Finck USACount = Record->UsaCount - 1; /* Exclude the USA Number. */
1976c2c66affSColin Finck Block = (USHORT*)((PCHAR)Record + Vcb->NtfsInfo.BytesPerSector - 2);
1977c2c66affSColin Finck
1978ba33b9faSTrevor Thompson DPRINT("FixupUpdateSequenceArray(%p, %p)\nUSANumber: %u\tUSACount: %u\n", Vcb, Record, USANumber, USACount);
1979ba33b9faSTrevor Thompson
1980c2c66affSColin Finck while (USACount)
1981c2c66affSColin Finck {
1982c2c66affSColin Finck if (*Block != USANumber)
1983c2c66affSColin Finck {
1984c2c66affSColin Finck DPRINT1("Mismatch with USA: %u read, %u expected\n" , *Block, USANumber);
1985c2c66affSColin Finck return STATUS_UNSUCCESSFUL;
1986c2c66affSColin Finck }
1987c2c66affSColin Finck *Block = *(USA++);
1988c2c66affSColin Finck Block = (USHORT*)((PCHAR)Block + Vcb->NtfsInfo.BytesPerSector);
1989c2c66affSColin Finck USACount--;
1990c2c66affSColin Finck }
1991c2c66affSColin Finck
1992c2c66affSColin Finck return STATUS_SUCCESS;
1993c2c66affSColin Finck }
1994c2c66affSColin Finck
19950409b316STrevor Thompson /**
19960409b316STrevor Thompson * @name AddNewMftEntry
19970409b316STrevor Thompson * @implemented
19980409b316STrevor Thompson *
19990409b316STrevor Thompson * Adds a file record to the master file table of a given device.
20000409b316STrevor Thompson *
20010409b316STrevor Thompson * @param FileRecord
20020409b316STrevor Thompson * Pointer to a complete file record which will be saved to disk.
20030409b316STrevor Thompson *
20040409b316STrevor Thompson * @param DeviceExt
20050409b316STrevor Thompson * Pointer to the DEVICE_EXTENSION of the target drive.
20060409b316STrevor Thompson *
2007e0048b13STrevor Thompson * @param DestinationIndex
2008e0048b13STrevor Thompson * Pointer to a ULONGLONG which will receive the MFT index where the file record was stored.
2009e0048b13STrevor Thompson *
201098ddf610STrevor Thompson * @param CanWait
201198ddf610STrevor Thompson * Boolean indicating if the function is allowed to wait for exclusive access to the master file table.
201298ddf610STrevor Thompson * This will only be relevant if the MFT doesn't have any free file records and needs to be enlarged.
201398ddf610STrevor Thompson *
20140409b316STrevor Thompson * @return
20150409b316STrevor Thompson * STATUS_SUCCESS on success.
20160409b316STrevor Thompson * STATUS_OBJECT_NAME_NOT_FOUND if we can't find the MFT's $Bitmap or if we weren't able
20170409b316STrevor Thompson * to read the attribute.
20180409b316STrevor Thompson * STATUS_INSUFFICIENT_RESOURCES if we can't allocate enough memory for a copy of $Bitmap.
201998ddf610STrevor Thompson * STATUS_CANT_WAIT if CanWait was FALSE and the function could not get immediate, exclusive access to the MFT.
20200409b316STrevor Thompson */
20210409b316STrevor Thompson NTSTATUS
AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,PDEVICE_EXTENSION DeviceExt,PULONGLONG DestinationIndex,BOOLEAN CanWait)20220409b316STrevor Thompson AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
2023e0048b13STrevor Thompson PDEVICE_EXTENSION DeviceExt,
202498ddf610STrevor Thompson PULONGLONG DestinationIndex,
202598ddf610STrevor Thompson BOOLEAN CanWait)
20260409b316STrevor Thompson {
20270409b316STrevor Thompson NTSTATUS Status = STATUS_SUCCESS;
20280409b316STrevor Thompson ULONGLONG MftIndex;
20290409b316STrevor Thompson RTL_BITMAP Bitmap;
20300409b316STrevor Thompson ULONGLONG BitmapDataSize;
20310409b316STrevor Thompson ULONGLONG AttrBytesRead;
203268a48b27STrevor Thompson PUCHAR BitmapData;
2033b033f00fSTrevor Thompson PUCHAR BitmapBuffer;
20340409b316STrevor Thompson ULONG LengthWritten;
20359ab86116STrevor Thompson PNTFS_ATTR_CONTEXT BitmapContext;
20369ab86116STrevor Thompson LARGE_INTEGER BitmapBits;
20379ab86116STrevor Thompson UCHAR SystemReservedBits;
20389ab86116STrevor Thompson
203998ddf610STrevor Thompson DPRINT1("AddNewMftEntry(%p, %p, %p, %s)\n", FileRecord, DeviceExt, DestinationIndex, CanWait ? "TRUE" : "FALSE");
20400409b316STrevor Thompson
20410409b316STrevor Thompson // First, we have to read the mft's $Bitmap attribute
2042b033f00fSTrevor Thompson
2043b033f00fSTrevor Thompson // Find the attribute
20440409b316STrevor Thompson Status = FindAttribute(DeviceExt, DeviceExt->MasterFileTable, AttributeBitmap, L"", 0, &BitmapContext, NULL);
20450409b316STrevor Thompson if (!NT_SUCCESS(Status))
20460409b316STrevor Thompson {
20470409b316STrevor Thompson DPRINT1("ERROR: Couldn't find $Bitmap attribute of master file table!\n");
20480409b316STrevor Thompson return Status;
20490409b316STrevor Thompson }
20500409b316STrevor Thompson
2051b033f00fSTrevor Thompson // Get size of bitmap
20524dfcd1d5STrevor Thompson BitmapDataSize = AttributeDataLength(BitmapContext->pRecord);
2053b033f00fSTrevor Thompson
2054b033f00fSTrevor Thompson // RtlInitializeBitmap wants a ULONG-aligned pointer, and wants the memory passed to it to be a ULONG-multiple
2055b033f00fSTrevor Thompson // Allocate a buffer for the $Bitmap attribute plus enough to ensure we can get a ULONG-aligned pointer
2056b033f00fSTrevor Thompson BitmapBuffer = ExAllocatePoolWithTag(NonPagedPool, BitmapDataSize + sizeof(ULONG), TAG_NTFS);
2057b033f00fSTrevor Thompson if (!BitmapBuffer)
20580409b316STrevor Thompson {
20590409b316STrevor Thompson ReleaseAttributeContext(BitmapContext);
20600409b316STrevor Thompson return STATUS_INSUFFICIENT_RESOURCES;
20610409b316STrevor Thompson }
20621ac7128dSTrevor Thompson RtlZeroMemory(BitmapBuffer, BitmapDataSize + sizeof(ULONG));
20630409b316STrevor Thompson
2064b033f00fSTrevor Thompson // Get a ULONG-aligned pointer for the bitmap itself
2065b033f00fSTrevor Thompson BitmapData = (PUCHAR)ALIGN_UP_BY((ULONG_PTR)BitmapBuffer, sizeof(ULONG));
2066b033f00fSTrevor Thompson
20670409b316STrevor Thompson // read $Bitmap attribute
2068935fcd1bSTrevor Thompson AttrBytesRead = ReadAttribute(DeviceExt, BitmapContext, 0, (PCHAR)BitmapData, BitmapDataSize);
20690409b316STrevor Thompson
2070b033f00fSTrevor Thompson if (AttrBytesRead != BitmapDataSize)
20710409b316STrevor Thompson {
20720409b316STrevor Thompson DPRINT1("ERROR: Unable to read $Bitmap attribute of master file table!\n");
2073b033f00fSTrevor Thompson ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
20740409b316STrevor Thompson ReleaseAttributeContext(BitmapContext);
20750409b316STrevor Thompson return STATUS_OBJECT_NAME_NOT_FOUND;
20760409b316STrevor Thompson }
20770409b316STrevor Thompson
207868a48b27STrevor Thompson // We need to backup the bits for records 0x10 - 0x17 (3rd byte of bitmap) and mark these records
207968a48b27STrevor Thompson // as in-use so we don't assign files to those indices. They're reserved for the system (e.g. ChkDsk).
208068a48b27STrevor Thompson SystemReservedBits = BitmapData[2];
208168a48b27STrevor Thompson BitmapData[2] = 0xff;
20829ab86116STrevor Thompson
20839ab86116STrevor Thompson // Calculate bit count
20844dfcd1d5STrevor Thompson BitmapBits.QuadPart = AttributeDataLength(DeviceExt->MFTContext->pRecord) /
20859ab86116STrevor Thompson DeviceExt->NtfsInfo.BytesPerFileRecord;
20869ab86116STrevor Thompson if (BitmapBits.HighPart != 0)
20879ab86116STrevor Thompson {
208868a48b27STrevor Thompson DPRINT1("\tFIXME: bitmap sizes beyond 32bits are not yet supported! (Your NTFS volume is too large)\n");
2089b033f00fSTrevor Thompson NtfsGlobalData->EnableWriteSupport = FALSE;
2090b033f00fSTrevor Thompson ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
209168a48b27STrevor Thompson ReleaseAttributeContext(BitmapContext);
209268a48b27STrevor Thompson return STATUS_NOT_IMPLEMENTED;
20939ab86116STrevor Thompson }
20949ab86116STrevor Thompson
20950409b316STrevor Thompson // convert buffer into bitmap
20969ab86116STrevor Thompson RtlInitializeBitMap(&Bitmap, (PULONG)BitmapData, BitmapBits.LowPart);
20970409b316STrevor Thompson
20980409b316STrevor Thompson // set next available bit, preferrably after 23rd bit
20990409b316STrevor Thompson MftIndex = RtlFindClearBitsAndSet(&Bitmap, 1, 24);
21000409b316STrevor Thompson if ((LONG)MftIndex == -1)
21010409b316STrevor Thompson {
21029ab86116STrevor Thompson DPRINT1("Couldn't find free space in MFT for file record, increasing MFT size.\n");
21030409b316STrevor Thompson
2104b033f00fSTrevor Thompson ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
21050409b316STrevor Thompson ReleaseAttributeContext(BitmapContext);
21060409b316STrevor Thompson
21079ab86116STrevor Thompson // Couldn't find a free record in the MFT, add some blank records and try again
210898ddf610STrevor Thompson Status = IncreaseMftSize(DeviceExt, CanWait);
21099ab86116STrevor Thompson if (!NT_SUCCESS(Status))
21109ab86116STrevor Thompson {
21119ab86116STrevor Thompson DPRINT1("ERROR: Couldn't find space in MFT for file or increase MFT size!\n");
21129ab86116STrevor Thompson return Status;
21139ab86116STrevor Thompson }
21149ab86116STrevor Thompson
211598ddf610STrevor Thompson return AddNewMftEntry(FileRecord, DeviceExt, DestinationIndex, CanWait);
21160409b316STrevor Thompson }
21170409b316STrevor Thompson
21180409b316STrevor Thompson DPRINT1("Creating file record at MFT index: %I64u\n", MftIndex);
21190409b316STrevor Thompson
21200409b316STrevor Thompson // update file record with index
21210409b316STrevor Thompson FileRecord->MFTRecordNumber = MftIndex;
21220409b316STrevor Thompson
21230409b316STrevor Thompson // [BitmapData should have been updated via RtlFindClearBitsAndSet()]
21240409b316STrevor Thompson
21259ab86116STrevor Thompson // Restore the system reserved bits
212668a48b27STrevor Thompson BitmapData[2] = SystemReservedBits;
21279ab86116STrevor Thompson
21280409b316STrevor Thompson // write the bitmap back to the MFT's $Bitmap attribute
2129d484d91eSTrevor Thompson Status = WriteAttribute(DeviceExt, BitmapContext, 0, BitmapData, BitmapDataSize, &LengthWritten, FileRecord);
21300409b316STrevor Thompson if (!NT_SUCCESS(Status))
21310409b316STrevor Thompson {
21320409b316STrevor Thompson DPRINT1("ERROR encountered when writing $Bitmap attribute!\n");
2133b033f00fSTrevor Thompson ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
21340409b316STrevor Thompson ReleaseAttributeContext(BitmapContext);
21350409b316STrevor Thompson return Status;
21360409b316STrevor Thompson }
21370409b316STrevor Thompson
21380409b316STrevor Thompson // update the file record (write it to disk)
21390409b316STrevor Thompson Status = UpdateFileRecord(DeviceExt, MftIndex, FileRecord);
21400409b316STrevor Thompson
21410409b316STrevor Thompson if (!NT_SUCCESS(Status))
21420409b316STrevor Thompson {
21430409b316STrevor Thompson DPRINT1("ERROR: Unable to write file record!\n");
2144b033f00fSTrevor Thompson ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
21450409b316STrevor Thompson ReleaseAttributeContext(BitmapContext);
21460409b316STrevor Thompson return Status;
21470409b316STrevor Thompson }
21480409b316STrevor Thompson
2149e0048b13STrevor Thompson *DestinationIndex = MftIndex;
2150e0048b13STrevor Thompson
2151b033f00fSTrevor Thompson ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
21520409b316STrevor Thompson ReleaseAttributeContext(BitmapContext);
21530409b316STrevor Thompson
21540409b316STrevor Thompson return Status;
21550409b316STrevor Thompson }
21560409b316STrevor Thompson
2157935fcd1bSTrevor Thompson /**
2158935fcd1bSTrevor Thompson * @name NtfsAddFilenameToDirectory
2159935fcd1bSTrevor Thompson * @implemented
2160935fcd1bSTrevor Thompson *
2161935fcd1bSTrevor Thompson * Adds a $FILE_NAME attribute to a given directory index.
2162935fcd1bSTrevor Thompson *
2163935fcd1bSTrevor Thompson * @param DeviceExt
2164935fcd1bSTrevor Thompson * Points to the target disk's DEVICE_EXTENSION.
2165935fcd1bSTrevor Thompson *
2166935fcd1bSTrevor Thompson * @param DirectoryMftIndex
2167935fcd1bSTrevor Thompson * Mft index of the parent directory which will receive the file.
2168935fcd1bSTrevor Thompson *
2169935fcd1bSTrevor Thompson * @param FileReferenceNumber
2170935fcd1bSTrevor Thompson * File reference of the file to be added to the directory. This is a combination of the
2171935fcd1bSTrevor Thompson * Mft index and sequence number.
2172935fcd1bSTrevor Thompson *
2173935fcd1bSTrevor Thompson * @param FilenameAttribute
2174935fcd1bSTrevor Thompson * Pointer to the FILENAME_ATTRIBUTE of the file being added to the directory.
2175935fcd1bSTrevor Thompson *
2176935fcd1bSTrevor Thompson * @param CaseSensitive
2177935fcd1bSTrevor Thompson * Boolean indicating if the function should operate in case-sensitive mode. This will be TRUE
2178935fcd1bSTrevor Thompson * if an application created the file with the FILE_FLAG_POSIX_SEMANTICS flag.
2179935fcd1bSTrevor Thompson *
2180935fcd1bSTrevor Thompson * @return
2181935fcd1bSTrevor Thompson * STATUS_SUCCESS on success.
2182935fcd1bSTrevor Thompson * STATUS_INSUFFICIENT_RESOURCES if an allocation fails.
2183935fcd1bSTrevor Thompson * STATUS_NOT_IMPLEMENTED if target address isn't at the end of the given file record.
2184935fcd1bSTrevor Thompson *
2185935fcd1bSTrevor Thompson * @remarks
2186935fcd1bSTrevor Thompson * WIP - Can only support a few files in a directory.
2187935fcd1bSTrevor Thompson * One FILENAME_ATTRIBUTE is added to the directory's index for each link to that file. So, each
2188935fcd1bSTrevor Thompson * file which contains one FILENAME_ATTRIBUTE for a long name and another for the 8.3 name, will
2189935fcd1bSTrevor Thompson * get both attributes added to its parent directory.
2190935fcd1bSTrevor Thompson */
2191935fcd1bSTrevor Thompson NTSTATUS
NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt,ULONGLONG DirectoryMftIndex,ULONGLONG FileReferenceNumber,PFILENAME_ATTRIBUTE FilenameAttribute,BOOLEAN CaseSensitive)2192935fcd1bSTrevor Thompson NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt,
2193935fcd1bSTrevor Thompson ULONGLONG DirectoryMftIndex,
2194935fcd1bSTrevor Thompson ULONGLONG FileReferenceNumber,
2195935fcd1bSTrevor Thompson PFILENAME_ATTRIBUTE FilenameAttribute,
2196935fcd1bSTrevor Thompson BOOLEAN CaseSensitive)
2197935fcd1bSTrevor Thompson {
2198935fcd1bSTrevor Thompson NTSTATUS Status = STATUS_SUCCESS;
2199935fcd1bSTrevor Thompson PFILE_RECORD_HEADER ParentFileRecord;
2200935fcd1bSTrevor Thompson PNTFS_ATTR_CONTEXT IndexRootContext;
2201935fcd1bSTrevor Thompson PINDEX_ROOT_ATTRIBUTE I30IndexRoot;
2202935fcd1bSTrevor Thompson ULONG IndexRootOffset;
2203935fcd1bSTrevor Thompson ULONGLONG I30IndexRootLength;
2204935fcd1bSTrevor Thompson ULONG LengthWritten;
2205935fcd1bSTrevor Thompson PINDEX_ROOT_ATTRIBUTE NewIndexRoot;
2206935fcd1bSTrevor Thompson ULONG AttributeLength;
220788a7c3d1STrevor Thompson PNTFS_ATTR_RECORD NextAttribute;
2208935fcd1bSTrevor Thompson PB_TREE NewTree;
2209935fcd1bSTrevor Thompson ULONG BtreeIndexLength;
221088a7c3d1STrevor Thompson ULONG MaxIndexRootSize;
221152c30fdfSTrevor Thompson PB_TREE_KEY NewLeftKey;
221252c30fdfSTrevor Thompson PB_TREE_FILENAME_NODE NewRightHandNode;
2213a40ba448STrevor Thompson LARGE_INTEGER MinIndexRootSize;
2214a40ba448STrevor Thompson ULONG NewMaxIndexRootSize;
2215a40ba448STrevor Thompson ULONG NodeSize;
2216935fcd1bSTrevor Thompson
2217935fcd1bSTrevor Thompson // Allocate memory for the parent directory
2218216a2caeSPierre Schweitzer ParentFileRecord = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList);
2219935fcd1bSTrevor Thompson if (!ParentFileRecord)
2220935fcd1bSTrevor Thompson {
2221935fcd1bSTrevor Thompson DPRINT1("ERROR: Couldn't allocate memory for file record!\n");
2222935fcd1bSTrevor Thompson return STATUS_INSUFFICIENT_RESOURCES;
2223935fcd1bSTrevor Thompson }
2224935fcd1bSTrevor Thompson
2225935fcd1bSTrevor Thompson // Open the parent directory
2226935fcd1bSTrevor Thompson Status = ReadFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
2227935fcd1bSTrevor Thompson if (!NT_SUCCESS(Status))
2228935fcd1bSTrevor Thompson {
2229216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2230935fcd1bSTrevor Thompson DPRINT1("ERROR: Couldn't read parent directory with index %I64u\n",
2231935fcd1bSTrevor Thompson DirectoryMftIndex);
2232935fcd1bSTrevor Thompson return Status;
2233935fcd1bSTrevor Thompson }
2234935fcd1bSTrevor Thompson
223552c30fdfSTrevor Thompson #ifndef NDEBUG
2236935fcd1bSTrevor Thompson DPRINT1("Dumping old parent file record:\n");
2237935fcd1bSTrevor Thompson NtfsDumpFileRecord(DeviceExt, ParentFileRecord);
223852c30fdfSTrevor Thompson #endif
2239935fcd1bSTrevor Thompson
2240935fcd1bSTrevor Thompson // Find the index root attribute for the directory
2241935fcd1bSTrevor Thompson Status = FindAttribute(DeviceExt,
2242935fcd1bSTrevor Thompson ParentFileRecord,
2243935fcd1bSTrevor Thompson AttributeIndexRoot,
2244935fcd1bSTrevor Thompson L"$I30",
2245935fcd1bSTrevor Thompson 4,
2246935fcd1bSTrevor Thompson &IndexRootContext,
2247935fcd1bSTrevor Thompson &IndexRootOffset);
2248935fcd1bSTrevor Thompson if (!NT_SUCCESS(Status))
2249935fcd1bSTrevor Thompson {
2250935fcd1bSTrevor Thompson DPRINT1("ERROR: Couldn't find $I30 $INDEX_ROOT attribute for parent directory with MFT #: %I64u!\n",
2251935fcd1bSTrevor Thompson DirectoryMftIndex);
2252216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2253935fcd1bSTrevor Thompson return Status;
2254935fcd1bSTrevor Thompson }
2255935fcd1bSTrevor Thompson
2256935fcd1bSTrevor Thompson // Find the maximum index size given what the file record can hold
225788a7c3d1STrevor Thompson // First, find the max index size assuming index root is the last attribute
225888a7c3d1STrevor Thompson MaxIndexRootSize = DeviceExt->NtfsInfo.BytesPerFileRecord // Start with the size of a file record
225988a7c3d1STrevor Thompson - IndexRootOffset // Subtract the length of everything that comes before index root
226088a7c3d1STrevor Thompson - IndexRootContext->pRecord->Resident.ValueOffset // Subtract the length of the attribute header for index root
226152c30fdfSTrevor Thompson - sizeof(INDEX_ROOT_ATTRIBUTE) // Subtract the length of the index root header
226288a7c3d1STrevor Thompson - (sizeof(ULONG) * 2); // Subtract the length of the file record end marker and padding
226388a7c3d1STrevor Thompson
226488a7c3d1STrevor Thompson // Are there attributes after this one?
226588a7c3d1STrevor Thompson NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)ParentFileRecord + IndexRootOffset + IndexRootContext->pRecord->Length);
226688a7c3d1STrevor Thompson if (NextAttribute->Type != AttributeEnd)
226788a7c3d1STrevor Thompson {
226888a7c3d1STrevor Thompson // Find the length of all attributes after this one, not counting the end marker
226988a7c3d1STrevor Thompson ULONG LengthOfAttributes = 0;
227088a7c3d1STrevor Thompson PNTFS_ATTR_RECORD CurrentAttribute = NextAttribute;
227188a7c3d1STrevor Thompson while (CurrentAttribute->Type != AttributeEnd)
227288a7c3d1STrevor Thompson {
227388a7c3d1STrevor Thompson LengthOfAttributes += CurrentAttribute->Length;
227488a7c3d1STrevor Thompson CurrentAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)CurrentAttribute + CurrentAttribute->Length);
227588a7c3d1STrevor Thompson }
227688a7c3d1STrevor Thompson
227788a7c3d1STrevor Thompson // Leave room for the existing attributes
227888a7c3d1STrevor Thompson MaxIndexRootSize -= LengthOfAttributes;
227988a7c3d1STrevor Thompson }
2280935fcd1bSTrevor Thompson
2281935fcd1bSTrevor Thompson // Allocate memory for the index root data
22824dfcd1d5STrevor Thompson I30IndexRootLength = AttributeDataLength(IndexRootContext->pRecord);
2283935fcd1bSTrevor Thompson I30IndexRoot = ExAllocatePoolWithTag(NonPagedPool, I30IndexRootLength, TAG_NTFS);
2284935fcd1bSTrevor Thompson if (!I30IndexRoot)
2285935fcd1bSTrevor Thompson {
2286935fcd1bSTrevor Thompson DPRINT1("ERROR: Couldn't allocate memory for index root attribute!\n");
2287935fcd1bSTrevor Thompson ReleaseAttributeContext(IndexRootContext);
2288216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2289935fcd1bSTrevor Thompson return STATUS_INSUFFICIENT_RESOURCES;
2290935fcd1bSTrevor Thompson }
2291935fcd1bSTrevor Thompson
2292935fcd1bSTrevor Thompson // Read the Index Root
2293935fcd1bSTrevor Thompson Status = ReadAttribute(DeviceExt, IndexRootContext, 0, (PCHAR)I30IndexRoot, I30IndexRootLength);
2294935fcd1bSTrevor Thompson if (!NT_SUCCESS(Status))
2295935fcd1bSTrevor Thompson {
2296935fcd1bSTrevor Thompson DPRINT1("ERROR: Couln't read index root attribute for Mft index #%I64u\n", DirectoryMftIndex);
2297935fcd1bSTrevor Thompson ReleaseAttributeContext(IndexRootContext);
2298935fcd1bSTrevor Thompson ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2299216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2300935fcd1bSTrevor Thompson return Status;
2301935fcd1bSTrevor Thompson }
2302935fcd1bSTrevor Thompson
2303935fcd1bSTrevor Thompson // Convert the index to a B*Tree
230492d1f92aSTrevor Thompson Status = CreateBTreeFromIndex(DeviceExt,
230592d1f92aSTrevor Thompson ParentFileRecord,
230692d1f92aSTrevor Thompson IndexRootContext,
230792d1f92aSTrevor Thompson I30IndexRoot,
230892d1f92aSTrevor Thompson &NewTree);
2309935fcd1bSTrevor Thompson if (!NT_SUCCESS(Status))
2310935fcd1bSTrevor Thompson {
2311935fcd1bSTrevor Thompson DPRINT1("ERROR: Failed to create B-Tree from Index!\n");
2312935fcd1bSTrevor Thompson ReleaseAttributeContext(IndexRootContext);
2313935fcd1bSTrevor Thompson ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2314216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2315935fcd1bSTrevor Thompson return Status;
2316935fcd1bSTrevor Thompson }
2317935fcd1bSTrevor Thompson
231852c30fdfSTrevor Thompson #ifndef NDEBUG
2319935fcd1bSTrevor Thompson DumpBTree(NewTree);
232052c30fdfSTrevor Thompson #endif
2321935fcd1bSTrevor Thompson
2322935fcd1bSTrevor Thompson // Insert the key for the file we're adding
232352c30fdfSTrevor Thompson Status = NtfsInsertKey(NewTree,
232452c30fdfSTrevor Thompson FileReferenceNumber,
232552c30fdfSTrevor Thompson FilenameAttribute,
232652c30fdfSTrevor Thompson NewTree->RootNode,
232752c30fdfSTrevor Thompson CaseSensitive,
232852c30fdfSTrevor Thompson MaxIndexRootSize,
232952c30fdfSTrevor Thompson I30IndexRoot->SizeOfEntry,
233052c30fdfSTrevor Thompson &NewLeftKey,
233152c30fdfSTrevor Thompson &NewRightHandNode);
2332935fcd1bSTrevor Thompson if (!NT_SUCCESS(Status))
2333935fcd1bSTrevor Thompson {
2334935fcd1bSTrevor Thompson DPRINT1("ERROR: Failed to insert key into B-Tree!\n");
2335935fcd1bSTrevor Thompson DestroyBTree(NewTree);
2336935fcd1bSTrevor Thompson ReleaseAttributeContext(IndexRootContext);
2337935fcd1bSTrevor Thompson ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2338216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2339935fcd1bSTrevor Thompson return Status;
2340935fcd1bSTrevor Thompson }
2341935fcd1bSTrevor Thompson
234252c30fdfSTrevor Thompson #ifndef NDEBUG
2343935fcd1bSTrevor Thompson DumpBTree(NewTree);
234452c30fdfSTrevor Thompson #endif
2345935fcd1bSTrevor Thompson
234652c30fdfSTrevor Thompson // The root node can't be split
234752c30fdfSTrevor Thompson ASSERT(NewLeftKey == NULL);
234852c30fdfSTrevor Thompson ASSERT(NewRightHandNode == NULL);
234952c30fdfSTrevor Thompson
235052c30fdfSTrevor Thompson // Convert B*Tree back to Index
235152c30fdfSTrevor Thompson
235252c30fdfSTrevor Thompson // Updating the index allocation can change the size available for the index root,
235352c30fdfSTrevor Thompson // And if the index root is demoted, the index allocation will need to be updated again,
235452c30fdfSTrevor Thompson // which may change the size available for index root... etc.
235552c30fdfSTrevor Thompson // My solution is to decrease index root to the size it would be if it was demoted,
235652c30fdfSTrevor Thompson // then UpdateIndexAllocation will have an accurate representation of the maximum space
235752c30fdfSTrevor Thompson // it can use in the file record. There's still a chance that the act of allocating an
235852c30fdfSTrevor Thompson // index node after demoting the index root will increase the size of the file record beyond
235952c30fdfSTrevor Thompson // it's limit, but if that happens, an attribute-list will most definitely be needed.
236052c30fdfSTrevor Thompson // This a bit hacky, but it seems to be functional.
236152c30fdfSTrevor Thompson
236252c30fdfSTrevor Thompson // Calculate the minimum size of the index root attribute, considering one dummy key and one VCN
236352c30fdfSTrevor Thompson MinIndexRootSize.QuadPart = sizeof(INDEX_ROOT_ATTRIBUTE) // size of the index root headers
236452c30fdfSTrevor Thompson + 0x18; // Size of dummy key with a VCN for a subnode
236552c30fdfSTrevor Thompson ASSERT(MinIndexRootSize.QuadPart % ATTR_RECORD_ALIGNMENT == 0);
236652c30fdfSTrevor Thompson
236752c30fdfSTrevor Thompson // Temporarily shrink the index root to it's minimal size
236852c30fdfSTrevor Thompson AttributeLength = MinIndexRootSize.LowPart;
236952c30fdfSTrevor Thompson AttributeLength += sizeof(INDEX_ROOT_ATTRIBUTE);
237052c30fdfSTrevor Thompson
237152c30fdfSTrevor Thompson
237252c30fdfSTrevor Thompson // FIXME: IndexRoot will probably be invalid until we're finished. If we fail before we finish, the directory will probably be toast.
237352c30fdfSTrevor Thompson // The potential for catastrophic data-loss exists!!! :)
237452c30fdfSTrevor Thompson
237552c30fdfSTrevor Thompson // Update the length of the attribute in the file record of the parent directory
237652c30fdfSTrevor Thompson Status = InternalSetResidentAttributeLength(DeviceExt,
237752c30fdfSTrevor Thompson IndexRootContext,
237852c30fdfSTrevor Thompson ParentFileRecord,
237952c30fdfSTrevor Thompson IndexRootOffset,
238052c30fdfSTrevor Thompson AttributeLength);
238152c30fdfSTrevor Thompson if (!NT_SUCCESS(Status))
238252c30fdfSTrevor Thompson {
238352c30fdfSTrevor Thompson DPRINT1("ERROR: Unable to set length of index root!\n");
238452c30fdfSTrevor Thompson DestroyBTree(NewTree);
238552c30fdfSTrevor Thompson ReleaseAttributeContext(IndexRootContext);
238652c30fdfSTrevor Thompson ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2387216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
238852c30fdfSTrevor Thompson return Status;
238952c30fdfSTrevor Thompson }
239052c30fdfSTrevor Thompson
239152c30fdfSTrevor Thompson // Update the index allocation
239292d1f92aSTrevor Thompson Status = UpdateIndexAllocation(DeviceExt, NewTree, I30IndexRoot->SizeOfEntry, ParentFileRecord);
239392d1f92aSTrevor Thompson if (!NT_SUCCESS(Status))
239492d1f92aSTrevor Thompson {
239592d1f92aSTrevor Thompson DPRINT1("ERROR: Failed to update index allocation from B-Tree!\n");
239692d1f92aSTrevor Thompson DestroyBTree(NewTree);
239792d1f92aSTrevor Thompson ReleaseAttributeContext(IndexRootContext);
239892d1f92aSTrevor Thompson ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2399216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
240092d1f92aSTrevor Thompson return Status;
240192d1f92aSTrevor Thompson }
240292d1f92aSTrevor Thompson
240352c30fdfSTrevor Thompson #ifndef NDEBUG
240452c30fdfSTrevor Thompson DPRINT1("Index Allocation updated\n");
240552c30fdfSTrevor Thompson DumpBTree(NewTree);
240652c30fdfSTrevor Thompson #endif
240752c30fdfSTrevor Thompson
240852c30fdfSTrevor Thompson // Find the maximum index root size given what the file record can hold
240952c30fdfSTrevor Thompson // First, find the max index size assuming index root is the last attribute
2410a40ba448STrevor Thompson NewMaxIndexRootSize =
241152c30fdfSTrevor Thompson DeviceExt->NtfsInfo.BytesPerFileRecord // Start with the size of a file record
241252c30fdfSTrevor Thompson - IndexRootOffset // Subtract the length of everything that comes before index root
241352c30fdfSTrevor Thompson - IndexRootContext->pRecord->Resident.ValueOffset // Subtract the length of the attribute header for index root
241452c30fdfSTrevor Thompson - sizeof(INDEX_ROOT_ATTRIBUTE) // Subtract the length of the index root header
241552c30fdfSTrevor Thompson - (sizeof(ULONG) * 2); // Subtract the length of the file record end marker and padding
241652c30fdfSTrevor Thompson
241752c30fdfSTrevor Thompson // Are there attributes after this one?
241852c30fdfSTrevor Thompson NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)ParentFileRecord + IndexRootOffset + IndexRootContext->pRecord->Length);
241952c30fdfSTrevor Thompson if (NextAttribute->Type != AttributeEnd)
242052c30fdfSTrevor Thompson {
242152c30fdfSTrevor Thompson // Find the length of all attributes after this one, not counting the end marker
242252c30fdfSTrevor Thompson ULONG LengthOfAttributes = 0;
242352c30fdfSTrevor Thompson PNTFS_ATTR_RECORD CurrentAttribute = NextAttribute;
242452c30fdfSTrevor Thompson while (CurrentAttribute->Type != AttributeEnd)
242552c30fdfSTrevor Thompson {
242652c30fdfSTrevor Thompson LengthOfAttributes += CurrentAttribute->Length;
242752c30fdfSTrevor Thompson CurrentAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)CurrentAttribute + CurrentAttribute->Length);
242852c30fdfSTrevor Thompson }
242952c30fdfSTrevor Thompson
243052c30fdfSTrevor Thompson // Leave room for the existing attributes
243152c30fdfSTrevor Thompson NewMaxIndexRootSize -= LengthOfAttributes;
243252c30fdfSTrevor Thompson }
243352c30fdfSTrevor Thompson
243452c30fdfSTrevor Thompson // The index allocation and index bitmap may have grown, leaving less room for the index root,
243552c30fdfSTrevor Thompson // so now we need to double-check that index root isn't too large
2436a40ba448STrevor Thompson NodeSize = GetSizeOfIndexEntries(NewTree->RootNode);
243752c30fdfSTrevor Thompson if (NodeSize > NewMaxIndexRootSize)
243852c30fdfSTrevor Thompson {
243952c30fdfSTrevor Thompson DPRINT1("Demoting index root.\nNodeSize: 0x%lx\nNewMaxIndexRootSize: 0x%lx\n", NodeSize, NewMaxIndexRootSize);
244052c30fdfSTrevor Thompson
244152c30fdfSTrevor Thompson Status = DemoteBTreeRoot(NewTree);
244252c30fdfSTrevor Thompson if (!NT_SUCCESS(Status))
244352c30fdfSTrevor Thompson {
244452c30fdfSTrevor Thompson DPRINT1("ERROR: Failed to demote index root!\n");
244552c30fdfSTrevor Thompson DestroyBTree(NewTree);
244652c30fdfSTrevor Thompson ReleaseAttributeContext(IndexRootContext);
244752c30fdfSTrevor Thompson ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2448216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
244952c30fdfSTrevor Thompson return Status;
245052c30fdfSTrevor Thompson }
245152c30fdfSTrevor Thompson
245252c30fdfSTrevor Thompson // We need to update the index allocation once more
245352c30fdfSTrevor Thompson Status = UpdateIndexAllocation(DeviceExt, NewTree, I30IndexRoot->SizeOfEntry, ParentFileRecord);
245452c30fdfSTrevor Thompson if (!NT_SUCCESS(Status))
245552c30fdfSTrevor Thompson {
245652c30fdfSTrevor Thompson DPRINT1("ERROR: Failed to update index allocation from B-Tree!\n");
245752c30fdfSTrevor Thompson DestroyBTree(NewTree);
245852c30fdfSTrevor Thompson ReleaseAttributeContext(IndexRootContext);
245952c30fdfSTrevor Thompson ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2460216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
246152c30fdfSTrevor Thompson return Status;
246252c30fdfSTrevor Thompson }
246352c30fdfSTrevor Thompson
246452c30fdfSTrevor Thompson // re-recalculate max size of index root
246552c30fdfSTrevor Thompson NewMaxIndexRootSize =
246652c30fdfSTrevor Thompson // Find the maximum index size given what the file record can hold
246752c30fdfSTrevor Thompson // First, find the max index size assuming index root is the last attribute
246852c30fdfSTrevor Thompson DeviceExt->NtfsInfo.BytesPerFileRecord // Start with the size of a file record
246952c30fdfSTrevor Thompson - IndexRootOffset // Subtract the length of everything that comes before index root
247052c30fdfSTrevor Thompson - IndexRootContext->pRecord->Resident.ValueOffset // Subtract the length of the attribute header for index root
247152c30fdfSTrevor Thompson - sizeof(INDEX_ROOT_ATTRIBUTE) // Subtract the length of the index root header
247252c30fdfSTrevor Thompson - (sizeof(ULONG) * 2); // Subtract the length of the file record end marker and padding
247352c30fdfSTrevor Thompson
247452c30fdfSTrevor Thompson // Are there attributes after this one?
247552c30fdfSTrevor Thompson NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)ParentFileRecord + IndexRootOffset + IndexRootContext->pRecord->Length);
247652c30fdfSTrevor Thompson if (NextAttribute->Type != AttributeEnd)
247752c30fdfSTrevor Thompson {
247852c30fdfSTrevor Thompson // Find the length of all attributes after this one, not counting the end marker
247952c30fdfSTrevor Thompson ULONG LengthOfAttributes = 0;
248052c30fdfSTrevor Thompson PNTFS_ATTR_RECORD CurrentAttribute = NextAttribute;
248152c30fdfSTrevor Thompson while (CurrentAttribute->Type != AttributeEnd)
248252c30fdfSTrevor Thompson {
248352c30fdfSTrevor Thompson LengthOfAttributes += CurrentAttribute->Length;
248452c30fdfSTrevor Thompson CurrentAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)CurrentAttribute + CurrentAttribute->Length);
248552c30fdfSTrevor Thompson }
248652c30fdfSTrevor Thompson
248752c30fdfSTrevor Thompson // Leave room for the existing attributes
248852c30fdfSTrevor Thompson NewMaxIndexRootSize -= LengthOfAttributes;
248952c30fdfSTrevor Thompson }
249052c30fdfSTrevor Thompson
249152c30fdfSTrevor Thompson
249252c30fdfSTrevor Thompson }
249352c30fdfSTrevor Thompson
249492d1f92aSTrevor Thompson // Create the Index Root from the B*Tree
249552c30fdfSTrevor Thompson Status = CreateIndexRootFromBTree(DeviceExt, NewTree, NewMaxIndexRootSize, &NewIndexRoot, &BtreeIndexLength);
2496935fcd1bSTrevor Thompson if (!NT_SUCCESS(Status))
2497935fcd1bSTrevor Thompson {
2498935fcd1bSTrevor Thompson DPRINT1("ERROR: Failed to create Index root from B-Tree!\n");
2499935fcd1bSTrevor Thompson DestroyBTree(NewTree);
2500935fcd1bSTrevor Thompson ReleaseAttributeContext(IndexRootContext);
2501935fcd1bSTrevor Thompson ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2502216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2503935fcd1bSTrevor Thompson return Status;
2504935fcd1bSTrevor Thompson }
2505935fcd1bSTrevor Thompson
2506935fcd1bSTrevor Thompson // We're done with the B-Tree now
2507935fcd1bSTrevor Thompson DestroyBTree(NewTree);
2508935fcd1bSTrevor Thompson
2509935fcd1bSTrevor Thompson // Write back the new index root attribute to the parent directory file record
2510935fcd1bSTrevor Thompson
2511935fcd1bSTrevor Thompson // First, we need to resize the attribute.
2512935fcd1bSTrevor Thompson // CreateIndexRootFromBTree() should have verified that the index root fits within MaxIndexSize.
251352c30fdfSTrevor Thompson // We can't set the size as we normally would, because $INDEX_ROOT must always be resident.
2514935fcd1bSTrevor Thompson AttributeLength = NewIndexRoot->Header.AllocatedSize + FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header);
25155579428bSTrevor Thompson
25164dfcd1d5STrevor Thompson if (AttributeLength != IndexRootContext->pRecord->Resident.ValueLength)
25175579428bSTrevor Thompson {
2518935fcd1bSTrevor Thompson // Update the length of the attribute in the file record of the parent directory
2519d484d91eSTrevor Thompson Status = InternalSetResidentAttributeLength(DeviceExt,
2520d484d91eSTrevor Thompson IndexRootContext,
2521935fcd1bSTrevor Thompson ParentFileRecord,
2522935fcd1bSTrevor Thompson IndexRootOffset,
2523935fcd1bSTrevor Thompson AttributeLength);
25244dfcd1d5STrevor Thompson if (!NT_SUCCESS(Status))
25254dfcd1d5STrevor Thompson {
25264dfcd1d5STrevor Thompson ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
25274dfcd1d5STrevor Thompson ReleaseAttributeContext(IndexRootContext);
25284dfcd1d5STrevor Thompson ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2529216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
25304dfcd1d5STrevor Thompson DPRINT1("ERROR: Unable to set resident attribute length!\n");
25314dfcd1d5STrevor Thompson return Status;
25324dfcd1d5STrevor Thompson }
25334dfcd1d5STrevor Thompson
25345579428bSTrevor Thompson }
2535935fcd1bSTrevor Thompson
2536935fcd1bSTrevor Thompson NT_ASSERT(ParentFileRecord->BytesInUse <= DeviceExt->NtfsInfo.BytesPerFileRecord);
2537935fcd1bSTrevor Thompson
2538935fcd1bSTrevor Thompson Status = UpdateFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
2539935fcd1bSTrevor Thompson if (!NT_SUCCESS(Status))
2540935fcd1bSTrevor Thompson {
2541935fcd1bSTrevor Thompson DPRINT1("ERROR: Failed to update file record of directory with index: %llx\n", DirectoryMftIndex);
2542216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2543935fcd1bSTrevor Thompson ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
2544935fcd1bSTrevor Thompson ReleaseAttributeContext(IndexRootContext);
2545935fcd1bSTrevor Thompson ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2546935fcd1bSTrevor Thompson return Status;
2547935fcd1bSTrevor Thompson }
2548935fcd1bSTrevor Thompson
2549935fcd1bSTrevor Thompson // Write the new index root to disk
2550935fcd1bSTrevor Thompson Status = WriteAttribute(DeviceExt,
2551935fcd1bSTrevor Thompson IndexRootContext,
2552935fcd1bSTrevor Thompson 0,
2553935fcd1bSTrevor Thompson (PUCHAR)NewIndexRoot,
2554935fcd1bSTrevor Thompson AttributeLength,
2555d484d91eSTrevor Thompson &LengthWritten,
2556d484d91eSTrevor Thompson ParentFileRecord);
2557b033f00fSTrevor Thompson if (!NT_SUCCESS(Status) || LengthWritten != AttributeLength)
2558935fcd1bSTrevor Thompson {
2559935fcd1bSTrevor Thompson DPRINT1("ERROR: Unable to write new index root attribute to parent directory!\n");
2560935fcd1bSTrevor Thompson ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
2561935fcd1bSTrevor Thompson ReleaseAttributeContext(IndexRootContext);
2562935fcd1bSTrevor Thompson ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2563216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2564935fcd1bSTrevor Thompson return Status;
2565935fcd1bSTrevor Thompson }
2566935fcd1bSTrevor Thompson
2567935fcd1bSTrevor Thompson // re-read the parent file record, so we can dump it
2568935fcd1bSTrevor Thompson Status = ReadFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
2569935fcd1bSTrevor Thompson if (!NT_SUCCESS(Status))
2570935fcd1bSTrevor Thompson {
2571935fcd1bSTrevor Thompson DPRINT1("ERROR: Couldn't read parent directory after messing with it!\n");
2572935fcd1bSTrevor Thompson }
2573935fcd1bSTrevor Thompson else
2574935fcd1bSTrevor Thompson {
257552c30fdfSTrevor Thompson #ifndef NDEBUG
257652c30fdfSTrevor Thompson DPRINT1("Dumping new B-Tree:\n");
257752c30fdfSTrevor Thompson
257852c30fdfSTrevor Thompson Status = CreateBTreeFromIndex(DeviceExt, ParentFileRecord, IndexRootContext, NewIndexRoot, &NewTree);
257952c30fdfSTrevor Thompson if (!NT_SUCCESS(Status))
258052c30fdfSTrevor Thompson {
258152c30fdfSTrevor Thompson DPRINT1("ERROR: Couldn't re-create b-tree\n");
258252c30fdfSTrevor Thompson return Status;
258352c30fdfSTrevor Thompson }
258452c30fdfSTrevor Thompson
258552c30fdfSTrevor Thompson DumpBTree(NewTree);
258652c30fdfSTrevor Thompson
258752c30fdfSTrevor Thompson DestroyBTree(NewTree);
258852c30fdfSTrevor Thompson
2589935fcd1bSTrevor Thompson NtfsDumpFileRecord(DeviceExt, ParentFileRecord);
259052c30fdfSTrevor Thompson #endif
2591935fcd1bSTrevor Thompson }
2592935fcd1bSTrevor Thompson
2593935fcd1bSTrevor Thompson // Cleanup
2594935fcd1bSTrevor Thompson ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
2595935fcd1bSTrevor Thompson ReleaseAttributeContext(IndexRootContext);
2596935fcd1bSTrevor Thompson ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2597216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, ParentFileRecord);
2598935fcd1bSTrevor Thompson
2599935fcd1bSTrevor Thompson return Status;
2600935fcd1bSTrevor Thompson }
2601935fcd1bSTrevor Thompson
2602ba33b9faSTrevor Thompson NTSTATUS
AddFixupArray(PDEVICE_EXTENSION Vcb,PNTFS_RECORD_HEADER Record)2603ba33b9faSTrevor Thompson AddFixupArray(PDEVICE_EXTENSION Vcb,
26044f8133f4STrevor Thompson PNTFS_RECORD_HEADER Record)
2605ba33b9faSTrevor Thompson {
2606ba33b9faSTrevor Thompson USHORT *pShortToFixUp;
260768a48b27STrevor Thompson ULONG ArrayEntryCount = Record->UsaCount - 1;
260868a48b27STrevor Thompson ULONG Offset = Vcb->NtfsInfo.BytesPerSector - 2;
260968a48b27STrevor Thompson ULONG i;
2610ba33b9faSTrevor Thompson
26114f8133f4STrevor Thompson PFIXUP_ARRAY fixupArray = (PFIXUP_ARRAY)((UCHAR*)Record + Record->UsaOffset);
2612ba33b9faSTrevor Thompson
2613ba33b9faSTrevor Thompson DPRINT("AddFixupArray(%p, %p)\n fixupArray->USN: %u, ArrayEntryCount: %u\n", Vcb, Record, fixupArray->USN, ArrayEntryCount);
2614ba33b9faSTrevor Thompson
2615ba33b9faSTrevor Thompson fixupArray->USN++;
2616ba33b9faSTrevor Thompson
2617ba33b9faSTrevor Thompson for (i = 0; i < ArrayEntryCount; i++)
2618ba33b9faSTrevor Thompson {
2619ba33b9faSTrevor Thompson DPRINT("USN: %u\tOffset: %u\n", fixupArray->USN, Offset);
2620ba33b9faSTrevor Thompson
26214f8133f4STrevor Thompson pShortToFixUp = (USHORT*)((PCHAR)Record + Offset);
2622ba33b9faSTrevor Thompson fixupArray->Array[i] = *pShortToFixUp;
2623ba33b9faSTrevor Thompson *pShortToFixUp = fixupArray->USN;
2624ba33b9faSTrevor Thompson Offset += Vcb->NtfsInfo.BytesPerSector;
2625ba33b9faSTrevor Thompson }
2626ba33b9faSTrevor Thompson
2627ba33b9faSTrevor Thompson return STATUS_SUCCESS;
2628ba33b9faSTrevor Thompson }
2629c2c66affSColin Finck
2630c2c66affSColin Finck NTSTATUS
ReadLCN(PDEVICE_EXTENSION Vcb,ULONGLONG lcn,ULONG count,PVOID buffer)2631c2c66affSColin Finck ReadLCN(PDEVICE_EXTENSION Vcb,
2632c2c66affSColin Finck ULONGLONG lcn,
2633c2c66affSColin Finck ULONG count,
2634c2c66affSColin Finck PVOID buffer)
2635c2c66affSColin Finck {
2636c2c66affSColin Finck LARGE_INTEGER DiskSector;
2637c2c66affSColin Finck
2638c2c66affSColin Finck DiskSector.QuadPart = lcn;
2639c2c66affSColin Finck
2640c2c66affSColin Finck return NtfsReadSectors(Vcb->StorageDevice,
2641c2c66affSColin Finck DiskSector.u.LowPart * Vcb->NtfsInfo.SectorsPerCluster,
2642c2c66affSColin Finck count * Vcb->NtfsInfo.SectorsPerCluster,
2643c2c66affSColin Finck Vcb->NtfsInfo.BytesPerSector,
2644c2c66affSColin Finck buffer,
2645c2c66affSColin Finck FALSE);
2646c2c66affSColin Finck }
2647c2c66affSColin Finck
2648c2c66affSColin Finck
2649c2c66affSColin Finck BOOLEAN
CompareFileName(PUNICODE_STRING FileName,PINDEX_ENTRY_ATTRIBUTE IndexEntry,BOOLEAN DirSearch,BOOLEAN CaseSensitive)2650c2c66affSColin Finck CompareFileName(PUNICODE_STRING FileName,
2651c2c66affSColin Finck PINDEX_ENTRY_ATTRIBUTE IndexEntry,
2652032be029STrevor Thompson BOOLEAN DirSearch,
2653032be029STrevor Thompson BOOLEAN CaseSensitive)
2654c2c66affSColin Finck {
2655c2c66affSColin Finck BOOLEAN Ret, Alloc = FALSE;
2656c2c66affSColin Finck UNICODE_STRING EntryName;
2657c2c66affSColin Finck
2658c2c66affSColin Finck EntryName.Buffer = IndexEntry->FileName.Name;
2659c2c66affSColin Finck EntryName.Length =
2660c2c66affSColin Finck EntryName.MaximumLength = IndexEntry->FileName.NameLength * sizeof(WCHAR);
2661c2c66affSColin Finck
2662c2c66affSColin Finck if (DirSearch)
2663c2c66affSColin Finck {
2664c2c66affSColin Finck UNICODE_STRING IntFileName;
2665032be029STrevor Thompson if (!CaseSensitive)
2666c2c66affSColin Finck {
2667c2c66affSColin Finck NT_VERIFY(NT_SUCCESS(RtlUpcaseUnicodeString(&IntFileName, FileName, TRUE)));
2668c2c66affSColin Finck Alloc = TRUE;
2669c2c66affSColin Finck }
2670c2c66affSColin Finck else
2671c2c66affSColin Finck {
2672c2c66affSColin Finck IntFileName = *FileName;
2673c2c66affSColin Finck }
2674c2c66affSColin Finck
2675032be029STrevor Thompson Ret = FsRtlIsNameInExpression(&IntFileName, &EntryName, !CaseSensitive, NULL);
2676c2c66affSColin Finck
2677c2c66affSColin Finck if (Alloc)
2678c2c66affSColin Finck {
2679c2c66affSColin Finck RtlFreeUnicodeString(&IntFileName);
2680c2c66affSColin Finck }
2681c2c66affSColin Finck
2682c2c66affSColin Finck return Ret;
2683c2c66affSColin Finck }
2684c2c66affSColin Finck else
2685c2c66affSColin Finck {
2686032be029STrevor Thompson return (RtlCompareUnicodeString(FileName, &EntryName, !CaseSensitive) == 0);
2687c2c66affSColin Finck }
2688c2c66affSColin Finck }
2689c2c66affSColin Finck
26905e7c1184STrevor Thompson /**
26915e7c1184STrevor Thompson * @name UpdateMftMirror
26925e7c1184STrevor Thompson * @implemented
26935e7c1184STrevor Thompson *
26945e7c1184STrevor Thompson * Backs-up the first ~4 master file table entries to the $MFTMirr file.
26955e7c1184STrevor Thompson *
26965e7c1184STrevor Thompson * @param Vcb
26975e7c1184STrevor Thompson * Pointer to an NTFS_VCB for the volume whose Mft mirror is being updated.
26985e7c1184STrevor Thompson *
2699a40ba448STrevor Thompson * @return
27005e7c1184STrevor Thompson
27015e7c1184STrevor Thompson * STATUS_SUCCESS on success.
27025e7c1184STrevor Thompson * STATUS_INSUFFICIENT_RESOURCES if an allocation failed.
27035e7c1184STrevor Thompson * STATUS_UNSUCCESSFUL if we couldn't read the master file table.
27045e7c1184STrevor Thompson *
27055e7c1184STrevor Thompson * @remarks
27065e7c1184STrevor Thompson * NTFS maintains up-to-date copies of the first several mft entries in the $MFTMirr file. Usually, the first 4 file
27075e7c1184STrevor Thompson * records from the mft are stored. The exact number of entries is determined by the size of $MFTMirr's $DATA.
27085e7c1184STrevor Thompson * If $MFTMirr is not up-to-date, chkdsk will reject every change it can find prior to when $MFTMirr was last updated.
27095e7c1184STrevor Thompson * Therefore, it's recommended to call this function if the volume changes considerably. For instance, IncreaseMftSize()
27105e7c1184STrevor Thompson * relies on this function to keep chkdsk from deleting the mft entries it creates. Note that under most instances, creating
27115e7c1184STrevor Thompson * or deleting a file will not affect the first ~four mft entries, and so will not require updating the mft mirror.
27125e7c1184STrevor Thompson */
27135e7c1184STrevor Thompson NTSTATUS
UpdateMftMirror(PNTFS_VCB Vcb)27145e7c1184STrevor Thompson UpdateMftMirror(PNTFS_VCB Vcb)
27155e7c1184STrevor Thompson {
27165e7c1184STrevor Thompson PFILE_RECORD_HEADER MirrorFileRecord;
27175e7c1184STrevor Thompson PNTFS_ATTR_CONTEXT MirrDataContext;
27185e7c1184STrevor Thompson PNTFS_ATTR_CONTEXT MftDataContext;
27195e7c1184STrevor Thompson PCHAR DataBuffer;
27205e7c1184STrevor Thompson ULONGLONG DataLength;
27215e7c1184STrevor Thompson NTSTATUS Status;
27225e7c1184STrevor Thompson ULONG BytesRead;
27235e7c1184STrevor Thompson ULONG LengthWritten;
27245e7c1184STrevor Thompson
27255e7c1184STrevor Thompson // Allocate memory for the Mft mirror file record
2726216a2caeSPierre Schweitzer MirrorFileRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
27275e7c1184STrevor Thompson if (!MirrorFileRecord)
27285e7c1184STrevor Thompson {
27295e7c1184STrevor Thompson DPRINT1("Error: Failed to allocate memory for $MFTMirr!\n");
27305e7c1184STrevor Thompson return STATUS_INSUFFICIENT_RESOURCES;
27315e7c1184STrevor Thompson }
27325e7c1184STrevor Thompson
27335e7c1184STrevor Thompson // Read the Mft Mirror file record
27345e7c1184STrevor Thompson Status = ReadFileRecord(Vcb, NTFS_FILE_MFTMIRR, MirrorFileRecord);
27355e7c1184STrevor Thompson if (!NT_SUCCESS(Status))
27365e7c1184STrevor Thompson {
27375e7c1184STrevor Thompson DPRINT1("ERROR: Failed to read $MFTMirr!\n");
2738216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MirrorFileRecord);
27395e7c1184STrevor Thompson return Status;
27405e7c1184STrevor Thompson }
27415e7c1184STrevor Thompson
27425e7c1184STrevor Thompson // Find the $DATA attribute of $MFTMirr
27435e7c1184STrevor Thompson Status = FindAttribute(Vcb, MirrorFileRecord, AttributeData, L"", 0, &MirrDataContext, NULL);
27445e7c1184STrevor Thompson if (!NT_SUCCESS(Status))
27455e7c1184STrevor Thompson {
27465e7c1184STrevor Thompson DPRINT1("ERROR: Couldn't find $DATA attribute!\n");
2747216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MirrorFileRecord);
27485e7c1184STrevor Thompson return Status;
27495e7c1184STrevor Thompson }
27505e7c1184STrevor Thompson
27515e7c1184STrevor Thompson // Find the $DATA attribute of $MFT
27525e7c1184STrevor Thompson Status = FindAttribute(Vcb, Vcb->MasterFileTable, AttributeData, L"", 0, &MftDataContext, NULL);
27535e7c1184STrevor Thompson if (!NT_SUCCESS(Status))
27545e7c1184STrevor Thompson {
27555e7c1184STrevor Thompson DPRINT1("ERROR: Couldn't find $DATA attribute!\n");
27565e7c1184STrevor Thompson ReleaseAttributeContext(MirrDataContext);
2757216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MirrorFileRecord);
27585e7c1184STrevor Thompson return Status;
27595e7c1184STrevor Thompson }
27605e7c1184STrevor Thompson
27615e7c1184STrevor Thompson // Get the size of the mirror's $DATA attribute
27625e7c1184STrevor Thompson DataLength = AttributeDataLength(MirrDataContext->pRecord);
27635e7c1184STrevor Thompson
27645e7c1184STrevor Thompson ASSERT(DataLength % Vcb->NtfsInfo.BytesPerFileRecord == 0);
27655e7c1184STrevor Thompson
27665e7c1184STrevor Thompson // Create buffer for the mirror's $DATA attribute
27675e7c1184STrevor Thompson DataBuffer = ExAllocatePoolWithTag(NonPagedPool, DataLength, TAG_NTFS);
27685e7c1184STrevor Thompson if (!DataBuffer)
27695e7c1184STrevor Thompson {
27705e7c1184STrevor Thompson DPRINT1("Error: Couldn't allocate memory for $DATA buffer!\n");
27715e7c1184STrevor Thompson ReleaseAttributeContext(MftDataContext);
27725e7c1184STrevor Thompson ReleaseAttributeContext(MirrDataContext);
2773216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MirrorFileRecord);
27745e7c1184STrevor Thompson return STATUS_INSUFFICIENT_RESOURCES;
27755e7c1184STrevor Thompson }
27765e7c1184STrevor Thompson
27775e7c1184STrevor Thompson ASSERT(DataLength < ULONG_MAX);
27785e7c1184STrevor Thompson
27795e7c1184STrevor Thompson // Back up the first several entries of the Mft's $DATA Attribute
27805e7c1184STrevor Thompson BytesRead = ReadAttribute(Vcb, MftDataContext, 0, DataBuffer, (ULONG)DataLength);
27815e7c1184STrevor Thompson if (BytesRead != (ULONG)DataLength)
27825e7c1184STrevor Thompson {
27835e7c1184STrevor Thompson DPRINT1("Error: Failed to read $DATA for $MFTMirr!\n");
27845e7c1184STrevor Thompson ReleaseAttributeContext(MftDataContext);
27855e7c1184STrevor Thompson ReleaseAttributeContext(MirrDataContext);
27865e7c1184STrevor Thompson ExFreePoolWithTag(DataBuffer, TAG_NTFS);
2787216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MirrorFileRecord);
27885e7c1184STrevor Thompson return STATUS_UNSUCCESSFUL;
27895e7c1184STrevor Thompson }
27905e7c1184STrevor Thompson
27915e7c1184STrevor Thompson // Write the mirror's $DATA attribute
27925e7c1184STrevor Thompson Status = WriteAttribute(Vcb,
27935e7c1184STrevor Thompson MirrDataContext,
27945e7c1184STrevor Thompson 0,
27955e7c1184STrevor Thompson (PUCHAR)DataBuffer,
27965e7c1184STrevor Thompson DataLength,
27975e7c1184STrevor Thompson &LengthWritten,
27985e7c1184STrevor Thompson MirrorFileRecord);
27995e7c1184STrevor Thompson if (!NT_SUCCESS(Status))
28005e7c1184STrevor Thompson {
28015e7c1184STrevor Thompson DPRINT1("ERROR: Failed to write $DATA attribute of $MFTMirr!\n");
28025e7c1184STrevor Thompson }
28035e7c1184STrevor Thompson
28045e7c1184STrevor Thompson // Cleanup
28055e7c1184STrevor Thompson ReleaseAttributeContext(MftDataContext);
28065e7c1184STrevor Thompson ReleaseAttributeContext(MirrDataContext);
28075e7c1184STrevor Thompson ExFreePoolWithTag(DataBuffer, TAG_NTFS);
2808216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MirrorFileRecord);
28095e7c1184STrevor Thompson
28105e7c1184STrevor Thompson return Status;
28115e7c1184STrevor Thompson }
28125e7c1184STrevor Thompson
2813c2c66affSColin Finck #if 0
2814c2c66affSColin Finck static
2815c2c66affSColin Finck VOID
2816c2c66affSColin Finck DumpIndexEntry(PINDEX_ENTRY_ATTRIBUTE IndexEntry)
2817c2c66affSColin Finck {
2818c2c66affSColin Finck DPRINT1("Entry: %p\n", IndexEntry);
2819c2c66affSColin Finck DPRINT1("\tData.Directory.IndexedFile: %I64x\n", IndexEntry->Data.Directory.IndexedFile);
2820c2c66affSColin Finck DPRINT1("\tLength: %u\n", IndexEntry->Length);
2821c2c66affSColin Finck DPRINT1("\tKeyLength: %u\n", IndexEntry->KeyLength);
2822c2c66affSColin Finck DPRINT1("\tFlags: %x\n", IndexEntry->Flags);
2823c2c66affSColin Finck DPRINT1("\tReserved: %x\n", IndexEntry->Reserved);
2824c2c66affSColin Finck DPRINT1("\t\tDirectoryFileReferenceNumber: %I64x\n", IndexEntry->FileName.DirectoryFileReferenceNumber);
2825c2c66affSColin Finck DPRINT1("\t\tCreationTime: %I64u\n", IndexEntry->FileName.CreationTime);
2826c2c66affSColin Finck DPRINT1("\t\tChangeTime: %I64u\n", IndexEntry->FileName.ChangeTime);
2827c2c66affSColin Finck DPRINT1("\t\tLastWriteTime: %I64u\n", IndexEntry->FileName.LastWriteTime);
2828c2c66affSColin Finck DPRINT1("\t\tLastAccessTime: %I64u\n", IndexEntry->FileName.LastAccessTime);
2829c2c66affSColin Finck DPRINT1("\t\tAllocatedSize: %I64u\n", IndexEntry->FileName.AllocatedSize);
2830c2c66affSColin Finck DPRINT1("\t\tDataSize: %I64u\n", IndexEntry->FileName.DataSize);
2831c2c66affSColin Finck DPRINT1("\t\tFileAttributes: %x\n", IndexEntry->FileName.FileAttributes);
2832c2c66affSColin Finck DPRINT1("\t\tNameLength: %u\n", IndexEntry->FileName.NameLength);
2833c2c66affSColin Finck DPRINT1("\t\tNameType: %x\n", IndexEntry->FileName.NameType);
2834c2c66affSColin Finck DPRINT1("\t\tName: %.*S\n", IndexEntry->FileName.NameLength, IndexEntry->FileName.Name);
2835c2c66affSColin Finck }
2836c2c66affSColin Finck #endif
2837c2c66affSColin Finck
2838c2c66affSColin Finck NTSTATUS
BrowseSubNodeIndexEntries(PNTFS_VCB Vcb,PFILE_RECORD_HEADER MftRecord,ULONG IndexBlockSize,PUNICODE_STRING FileName,PNTFS_ATTR_CONTEXT IndexAllocationContext,PRTL_BITMAP Bitmap,ULONGLONG VCN,PULONG StartEntry,PULONG CurrentEntry,BOOLEAN DirSearch,BOOLEAN CaseSensitive,ULONGLONG * OutMFTIndex)28391ac7128dSTrevor Thompson BrowseSubNodeIndexEntries(PNTFS_VCB Vcb,
28401ac7128dSTrevor Thompson PFILE_RECORD_HEADER MftRecord,
28411ac7128dSTrevor Thompson ULONG IndexBlockSize,
28421ac7128dSTrevor Thompson PUNICODE_STRING FileName,
28431ac7128dSTrevor Thompson PNTFS_ATTR_CONTEXT IndexAllocationContext,
28441ac7128dSTrevor Thompson PRTL_BITMAP Bitmap,
28451ac7128dSTrevor Thompson ULONGLONG VCN,
28461ac7128dSTrevor Thompson PULONG StartEntry,
28471ac7128dSTrevor Thompson PULONG CurrentEntry,
28481ac7128dSTrevor Thompson BOOLEAN DirSearch,
28491ac7128dSTrevor Thompson BOOLEAN CaseSensitive,
28501ac7128dSTrevor Thompson ULONGLONG *OutMFTIndex)
28511ac7128dSTrevor Thompson {
28521ac7128dSTrevor Thompson PINDEX_BUFFER IndexRecord;
28531ac7128dSTrevor Thompson ULONGLONG Offset;
28541ac7128dSTrevor Thompson ULONG BytesRead;
28551ac7128dSTrevor Thompson PINDEX_ENTRY_ATTRIBUTE FirstEntry;
28561ac7128dSTrevor Thompson PINDEX_ENTRY_ATTRIBUTE LastEntry;
28571ac7128dSTrevor Thompson PINDEX_ENTRY_ATTRIBUTE IndexEntry;
28581ac7128dSTrevor Thompson ULONG NodeNumber;
28591ac7128dSTrevor Thompson NTSTATUS Status;
28601ac7128dSTrevor Thompson
28611ac7128dSTrevor Thompson DPRINT("BrowseSubNodeIndexEntries(%p, %p, %lu, %wZ, %p, %p, %I64d, %lu, %lu, %s, %s, %p)\n",
28621ac7128dSTrevor Thompson Vcb,
28631ac7128dSTrevor Thompson MftRecord,
28641ac7128dSTrevor Thompson IndexBlockSize,
28651ac7128dSTrevor Thompson FileName,
28661ac7128dSTrevor Thompson IndexAllocationContext,
28671ac7128dSTrevor Thompson Bitmap,
28681ac7128dSTrevor Thompson VCN,
28691ac7128dSTrevor Thompson *StartEntry,
28701ac7128dSTrevor Thompson *CurrentEntry,
28711ac7128dSTrevor Thompson "FALSE",
28721ac7128dSTrevor Thompson DirSearch ? "TRUE" : "FALSE",
28731ac7128dSTrevor Thompson CaseSensitive ? "TRUE" : "FALSE",
28741ac7128dSTrevor Thompson OutMFTIndex);
28751ac7128dSTrevor Thompson
28761ac7128dSTrevor Thompson // Calculate node number as VCN / Clusters per index record
28771ac7128dSTrevor Thompson NodeNumber = VCN / (Vcb->NtfsInfo.BytesPerIndexRecord / Vcb->NtfsInfo.BytesPerCluster);
28781ac7128dSTrevor Thompson
28791ac7128dSTrevor Thompson // Is the bit for this node clear in the bitmap?
28801ac7128dSTrevor Thompson if (!RtlCheckBit(Bitmap, NodeNumber))
28811ac7128dSTrevor Thompson {
2882f4d29a74SPierre Schweitzer DPRINT1("File system corruption detected, node with VCN %I64u is marked as deleted.\n", VCN);
28831ac7128dSTrevor Thompson return STATUS_DATA_ERROR;
28841ac7128dSTrevor Thompson }
28851ac7128dSTrevor Thompson
28861ac7128dSTrevor Thompson // Allocate memory for the index record
28871ac7128dSTrevor Thompson IndexRecord = ExAllocatePoolWithTag(NonPagedPool, IndexBlockSize, TAG_NTFS);
28881ac7128dSTrevor Thompson if (!IndexRecord)
28891ac7128dSTrevor Thompson {
28901ac7128dSTrevor Thompson DPRINT1("Unable to allocate memory for index record!\n");
28911ac7128dSTrevor Thompson return STATUS_INSUFFICIENT_RESOURCES;
28921ac7128dSTrevor Thompson }
28931ac7128dSTrevor Thompson
28941ac7128dSTrevor Thompson // Calculate offset of index record
28951ac7128dSTrevor Thompson Offset = VCN * Vcb->NtfsInfo.BytesPerCluster;
28961ac7128dSTrevor Thompson
28971ac7128dSTrevor Thompson // Read the index record
28981ac7128dSTrevor Thompson BytesRead = ReadAttribute(Vcb, IndexAllocationContext, Offset, (PCHAR)IndexRecord, IndexBlockSize);
28991ac7128dSTrevor Thompson if (BytesRead != IndexBlockSize)
29001ac7128dSTrevor Thompson {
29011ac7128dSTrevor Thompson DPRINT1("Unable to read index record!\n");
29021ac7128dSTrevor Thompson ExFreePoolWithTag(IndexRecord, TAG_NTFS);
29031ac7128dSTrevor Thompson return STATUS_UNSUCCESSFUL;
29041ac7128dSTrevor Thompson }
29051ac7128dSTrevor Thompson
29061ac7128dSTrevor Thompson // Assert that we're dealing with an index record here
29071ac7128dSTrevor Thompson ASSERT(IndexRecord->Ntfs.Type == NRH_INDX_TYPE);
29081ac7128dSTrevor Thompson
29091ac7128dSTrevor Thompson // Apply the fixup array to the index record
29101ac7128dSTrevor Thompson Status = FixupUpdateSequenceArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs);
29111ac7128dSTrevor Thompson if (!NT_SUCCESS(Status))
29121ac7128dSTrevor Thompson {
29131ac7128dSTrevor Thompson ExFreePoolWithTag(IndexRecord, TAG_NTFS);
29141ac7128dSTrevor Thompson DPRINT1("Failed to apply fixup array!\n");
29151ac7128dSTrevor Thompson return Status;
29161ac7128dSTrevor Thompson }
29171ac7128dSTrevor Thompson
29181ac7128dSTrevor Thompson ASSERT(IndexRecord->Header.AllocatedSize + FIELD_OFFSET(INDEX_BUFFER, Header) == IndexBlockSize);
29191ac7128dSTrevor Thompson FirstEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexRecord->Header + IndexRecord->Header.FirstEntryOffset);
29201ac7128dSTrevor Thompson LastEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexRecord->Header + IndexRecord->Header.TotalSizeOfEntries);
29211ac7128dSTrevor Thompson ASSERT(LastEntry <= (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexRecord + IndexBlockSize));
29221ac7128dSTrevor Thompson
29231ac7128dSTrevor Thompson // Loop through all Index Entries of index, starting with FirstEntry
29241ac7128dSTrevor Thompson IndexEntry = FirstEntry;
29251ac7128dSTrevor Thompson while (IndexEntry <= LastEntry)
29261ac7128dSTrevor Thompson {
29271ac7128dSTrevor Thompson // Does IndexEntry have a sub-node?
29281ac7128dSTrevor Thompson if (IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
29291ac7128dSTrevor Thompson {
29301ac7128dSTrevor Thompson if (!(IndexRecord->Header.Flags & INDEX_NODE_LARGE) || !IndexAllocationContext)
29311ac7128dSTrevor Thompson {
29321ac7128dSTrevor Thompson DPRINT1("Filesystem corruption detected!\n");
29331ac7128dSTrevor Thompson }
29341ac7128dSTrevor Thompson else
29351ac7128dSTrevor Thompson {
29361ac7128dSTrevor Thompson Status = BrowseSubNodeIndexEntries(Vcb,
29371ac7128dSTrevor Thompson MftRecord,
29381ac7128dSTrevor Thompson IndexBlockSize,
29391ac7128dSTrevor Thompson FileName,
29401ac7128dSTrevor Thompson IndexAllocationContext,
29411ac7128dSTrevor Thompson Bitmap,
29421ac7128dSTrevor Thompson GetIndexEntryVCN(IndexEntry),
29431ac7128dSTrevor Thompson StartEntry,
29441ac7128dSTrevor Thompson CurrentEntry,
29451ac7128dSTrevor Thompson DirSearch,
29461ac7128dSTrevor Thompson CaseSensitive,
29471ac7128dSTrevor Thompson OutMFTIndex);
29481ac7128dSTrevor Thompson if (NT_SUCCESS(Status))
29491ac7128dSTrevor Thompson {
29501ac7128dSTrevor Thompson ExFreePoolWithTag(IndexRecord, TAG_NTFS);
29511ac7128dSTrevor Thompson return Status;
29521ac7128dSTrevor Thompson }
29531ac7128dSTrevor Thompson }
29541ac7128dSTrevor Thompson }
29551ac7128dSTrevor Thompson
29561ac7128dSTrevor Thompson // Are we done?
29571ac7128dSTrevor Thompson if (IndexEntry->Flags & NTFS_INDEX_ENTRY_END)
29581ac7128dSTrevor Thompson break;
29591ac7128dSTrevor Thompson
29601ac7128dSTrevor Thompson // If we've found a file whose index is greater than or equal to StartEntry that matches the search criteria
29611ac7128dSTrevor Thompson if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) >= NTFS_FILE_FIRST_USER_FILE &&
29621ac7128dSTrevor Thompson *CurrentEntry >= *StartEntry &&
29631ac7128dSTrevor Thompson IndexEntry->FileName.NameType != NTFS_FILE_NAME_DOS &&
29641ac7128dSTrevor Thompson CompareFileName(FileName, IndexEntry, DirSearch, CaseSensitive))
29651ac7128dSTrevor Thompson {
29661ac7128dSTrevor Thompson *StartEntry = *CurrentEntry;
29671ac7128dSTrevor Thompson *OutMFTIndex = (IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK);
29681ac7128dSTrevor Thompson ExFreePoolWithTag(IndexRecord, TAG_NTFS);
29691ac7128dSTrevor Thompson return STATUS_SUCCESS;
29701ac7128dSTrevor Thompson }
29711ac7128dSTrevor Thompson
29721ac7128dSTrevor Thompson // Advance to the next index entry
29731ac7128dSTrevor Thompson (*CurrentEntry) += 1;
29741ac7128dSTrevor Thompson ASSERT(IndexEntry->Length >= sizeof(INDEX_ENTRY_ATTRIBUTE));
29751ac7128dSTrevor Thompson IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
29761ac7128dSTrevor Thompson }
29771ac7128dSTrevor Thompson
29781ac7128dSTrevor Thompson ExFreePoolWithTag(IndexRecord, TAG_NTFS);
29791ac7128dSTrevor Thompson
29801ac7128dSTrevor Thompson return STATUS_OBJECT_PATH_NOT_FOUND;
29811ac7128dSTrevor Thompson }
29821ac7128dSTrevor Thompson
29831ac7128dSTrevor Thompson NTSTATUS
BrowseIndexEntries(PDEVICE_EXTENSION Vcb,PFILE_RECORD_HEADER MftRecord,PINDEX_ROOT_ATTRIBUTE IndexRecord,ULONG IndexBlockSize,PINDEX_ENTRY_ATTRIBUTE FirstEntry,PINDEX_ENTRY_ATTRIBUTE LastEntry,PUNICODE_STRING FileName,PULONG StartEntry,PULONG CurrentEntry,BOOLEAN DirSearch,BOOLEAN CaseSensitive,ULONGLONG * OutMFTIndex)2984c2c66affSColin Finck BrowseIndexEntries(PDEVICE_EXTENSION Vcb,
2985c2c66affSColin Finck PFILE_RECORD_HEADER MftRecord,
29861ac7128dSTrevor Thompson PINDEX_ROOT_ATTRIBUTE IndexRecord,
2987c2c66affSColin Finck ULONG IndexBlockSize,
2988c2c66affSColin Finck PINDEX_ENTRY_ATTRIBUTE FirstEntry,
2989c2c66affSColin Finck PINDEX_ENTRY_ATTRIBUTE LastEntry,
2990c2c66affSColin Finck PUNICODE_STRING FileName,
2991c2c66affSColin Finck PULONG StartEntry,
2992c2c66affSColin Finck PULONG CurrentEntry,
2993c2c66affSColin Finck BOOLEAN DirSearch,
2994032be029STrevor Thompson BOOLEAN CaseSensitive,
2995c2c66affSColin Finck ULONGLONG *OutMFTIndex)
2996c2c66affSColin Finck {
2997c2c66affSColin Finck NTSTATUS Status;
2998c2c66affSColin Finck PINDEX_ENTRY_ATTRIBUTE IndexEntry;
29991ac7128dSTrevor Thompson PNTFS_ATTR_CONTEXT IndexAllocationContext;
30001ac7128dSTrevor Thompson PNTFS_ATTR_CONTEXT BitmapContext;
30011ac7128dSTrevor Thompson PCHAR *BitmapMem;
30021ac7128dSTrevor Thompson ULONG *BitmapPtr;
30031ac7128dSTrevor Thompson RTL_BITMAP Bitmap;
3004c2c66affSColin Finck
300538c947b7STrevor Thompson DPRINT("BrowseIndexEntries(%p, %p, %p, %lu, %p, %p, %wZ, %lu, %lu, %s, %s, %p)\n",
3006032be029STrevor Thompson Vcb,
3007032be029STrevor Thompson MftRecord,
3008032be029STrevor Thompson IndexRecord,
3009032be029STrevor Thompson IndexBlockSize,
3010032be029STrevor Thompson FirstEntry,
3011032be029STrevor Thompson LastEntry,
3012032be029STrevor Thompson FileName,
3013032be029STrevor Thompson *StartEntry,
3014032be029STrevor Thompson *CurrentEntry,
3015032be029STrevor Thompson DirSearch ? "TRUE" : "FALSE",
3016032be029STrevor Thompson CaseSensitive ? "TRUE" : "FALSE",
3017032be029STrevor Thompson OutMFTIndex);
3018c2c66affSColin Finck
30191ac7128dSTrevor Thompson // Find the $I30 index allocation, if there is one
30201ac7128dSTrevor Thompson Status = FindAttribute(Vcb, MftRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationContext, NULL);
30211ac7128dSTrevor Thompson if (NT_SUCCESS(Status))
3022c2c66affSColin Finck {
30231ac7128dSTrevor Thompson ULONGLONG BitmapLength;
30241ac7128dSTrevor Thompson // Find the bitmap attribute for the index
30251ac7128dSTrevor Thompson Status = FindAttribute(Vcb, MftRecord, AttributeBitmap, L"$I30", 4, &BitmapContext, NULL);
3026c2c66affSColin Finck if (!NT_SUCCESS(Status))
3027c2c66affSColin Finck {
30281ac7128dSTrevor Thompson DPRINT1("Potential file system corruption detected!\n");
30291ac7128dSTrevor Thompson ReleaseAttributeContext(IndexAllocationContext);
3030c2c66affSColin Finck return Status;
3031c2c66affSColin Finck }
3032c2c66affSColin Finck
30331ac7128dSTrevor Thompson // Get the length of the bitmap attribute
30341ac7128dSTrevor Thompson BitmapLength = AttributeDataLength(BitmapContext->pRecord);
30351ac7128dSTrevor Thompson
30361ac7128dSTrevor Thompson // Allocate memory for the bitmap, including some padding; RtlInitializeBitmap() wants a pointer
30371ac7128dSTrevor Thompson // that's ULONG-aligned, and it wants the size of the memory allocated for it to be a ULONG-multiple.
30381ac7128dSTrevor Thompson BitmapMem = ExAllocatePoolWithTag(NonPagedPool, BitmapLength + sizeof(ULONG), TAG_NTFS);
30391ac7128dSTrevor Thompson if (!BitmapMem)
3040c2c66affSColin Finck {
30411ac7128dSTrevor Thompson DPRINT1("Error: failed to allocate bitmap!");
30421ac7128dSTrevor Thompson ReleaseAttributeContext(BitmapContext);
30431ac7128dSTrevor Thompson ReleaseAttributeContext(IndexAllocationContext);
30441ac7128dSTrevor Thompson return STATUS_INSUFFICIENT_RESOURCES;
3045c2c66affSColin Finck }
3046c2c66affSColin Finck
30471ac7128dSTrevor Thompson RtlZeroMemory(BitmapMem, BitmapLength + sizeof(ULONG));
3048c2c66affSColin Finck
30491ac7128dSTrevor Thompson // RtlInitializeBitmap() wants a pointer that's ULONG-aligned.
30501ac7128dSTrevor Thompson BitmapPtr = (PULONG)ALIGN_UP_BY((ULONG_PTR)BitmapMem, sizeof(ULONG));
30511ac7128dSTrevor Thompson
30521ac7128dSTrevor Thompson // Read the existing bitmap data
30531ac7128dSTrevor Thompson Status = ReadAttribute(Vcb, BitmapContext, 0, (PCHAR)BitmapPtr, BitmapLength);
30541ac7128dSTrevor Thompson if (!NT_SUCCESS(Status))
30551ac7128dSTrevor Thompson {
30561ac7128dSTrevor Thompson DPRINT1("ERROR: Failed to read bitmap attribute!\n");
30571ac7128dSTrevor Thompson ExFreePoolWithTag(BitmapMem, TAG_NTFS);
30581ac7128dSTrevor Thompson ReleaseAttributeContext(BitmapContext);
30591ac7128dSTrevor Thompson ReleaseAttributeContext(IndexAllocationContext);
30601ac7128dSTrevor Thompson return Status;
30611ac7128dSTrevor Thompson }
30621ac7128dSTrevor Thompson
30631ac7128dSTrevor Thompson // Initialize bitmap
30641ac7128dSTrevor Thompson RtlInitializeBitMap(&Bitmap, BitmapPtr, BitmapLength * 8);
30651ac7128dSTrevor Thompson }
30661ac7128dSTrevor Thompson else
30671ac7128dSTrevor Thompson {
30681ac7128dSTrevor Thompson // Couldn't find an index allocation
30691ac7128dSTrevor Thompson IndexAllocationContext = NULL;
30701ac7128dSTrevor Thompson }
30711ac7128dSTrevor Thompson
30721ac7128dSTrevor Thompson
30731ac7128dSTrevor Thompson // Loop through all Index Entries of index, starting with FirstEntry
30741ac7128dSTrevor Thompson IndexEntry = FirstEntry;
30751ac7128dSTrevor Thompson while (IndexEntry <= LastEntry)
30761ac7128dSTrevor Thompson {
30771ac7128dSTrevor Thompson // Does IndexEntry have a sub-node?
30781ac7128dSTrevor Thompson if (IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
30791ac7128dSTrevor Thompson {
30801ac7128dSTrevor Thompson if (!(IndexRecord->Header.Flags & INDEX_ROOT_LARGE) || !IndexAllocationContext)
30811ac7128dSTrevor Thompson {
30821ac7128dSTrevor Thompson DPRINT1("Filesystem corruption detected!\n");
30831ac7128dSTrevor Thompson }
30841ac7128dSTrevor Thompson else
30851ac7128dSTrevor Thompson {
30861ac7128dSTrevor Thompson Status = BrowseSubNodeIndexEntries(Vcb,
30871ac7128dSTrevor Thompson MftRecord,
30881ac7128dSTrevor Thompson IndexBlockSize,
3089032be029STrevor Thompson FileName,
30901ac7128dSTrevor Thompson IndexAllocationContext,
30911ac7128dSTrevor Thompson &Bitmap,
30921ac7128dSTrevor Thompson GetIndexEntryVCN(IndexEntry),
3093032be029STrevor Thompson StartEntry,
3094032be029STrevor Thompson CurrentEntry,
3095032be029STrevor Thompson DirSearch,
3096032be029STrevor Thompson CaseSensitive,
3097032be029STrevor Thompson OutMFTIndex);
3098c2c66affSColin Finck if (NT_SUCCESS(Status))
3099c2c66affSColin Finck {
31001ac7128dSTrevor Thompson ExFreePoolWithTag(BitmapMem, TAG_NTFS);
31011ac7128dSTrevor Thompson ReleaseAttributeContext(BitmapContext);
31021ac7128dSTrevor Thompson ReleaseAttributeContext(IndexAllocationContext);
31031ac7128dSTrevor Thompson return Status;
31041ac7128dSTrevor Thompson }
3105c2c66affSColin Finck }
3106c2c66affSColin Finck }
3107c2c66affSColin Finck
31081ac7128dSTrevor Thompson // Are we done?
31091ac7128dSTrevor Thompson if (IndexEntry->Flags & NTFS_INDEX_ENTRY_END)
31101ac7128dSTrevor Thompson break;
31111ac7128dSTrevor Thompson
31121ac7128dSTrevor Thompson // If we've found a file whose index is greater than or equal to StartEntry that matches the search criteria
31131ac7128dSTrevor Thompson if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) >= NTFS_FILE_FIRST_USER_FILE &&
31141ac7128dSTrevor Thompson *CurrentEntry >= *StartEntry &&
31151ac7128dSTrevor Thompson IndexEntry->FileName.NameType != NTFS_FILE_NAME_DOS &&
31161ac7128dSTrevor Thompson CompareFileName(FileName, IndexEntry, DirSearch, CaseSensitive))
31171ac7128dSTrevor Thompson {
31181ac7128dSTrevor Thompson *StartEntry = *CurrentEntry;
31191ac7128dSTrevor Thompson *OutMFTIndex = (IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK);
31201ac7128dSTrevor Thompson if (IndexAllocationContext)
31211ac7128dSTrevor Thompson {
31221ac7128dSTrevor Thompson ExFreePoolWithTag(BitmapMem, TAG_NTFS);
31231ac7128dSTrevor Thompson ReleaseAttributeContext(BitmapContext);
31241ac7128dSTrevor Thompson ReleaseAttributeContext(IndexAllocationContext);
31251ac7128dSTrevor Thompson }
31261ac7128dSTrevor Thompson return STATUS_SUCCESS;
31271ac7128dSTrevor Thompson }
31281ac7128dSTrevor Thompson
31291ac7128dSTrevor Thompson // Advance to the next index entry
31301ac7128dSTrevor Thompson (*CurrentEntry) += 1;
31311ac7128dSTrevor Thompson ASSERT(IndexEntry->Length >= sizeof(INDEX_ENTRY_ATTRIBUTE));
31321ac7128dSTrevor Thompson IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
31331ac7128dSTrevor Thompson }
31341ac7128dSTrevor Thompson
31351ac7128dSTrevor Thompson if (IndexAllocationContext)
31361ac7128dSTrevor Thompson {
31371ac7128dSTrevor Thompson ExFreePoolWithTag(BitmapMem, TAG_NTFS);
31381ac7128dSTrevor Thompson ReleaseAttributeContext(BitmapContext);
31391ac7128dSTrevor Thompson ReleaseAttributeContext(IndexAllocationContext);
31401ac7128dSTrevor Thompson }
31411ac7128dSTrevor Thompson
31421ac7128dSTrevor Thompson return STATUS_OBJECT_PATH_NOT_FOUND;
3143c2c66affSColin Finck }
3144c2c66affSColin Finck
3145c2c66affSColin Finck NTSTATUS
NtfsFindMftRecord(PDEVICE_EXTENSION Vcb,ULONGLONG MFTIndex,PUNICODE_STRING FileName,PULONG FirstEntry,BOOLEAN DirSearch,BOOLEAN CaseSensitive,ULONGLONG * OutMFTIndex)3146c2c66affSColin Finck NtfsFindMftRecord(PDEVICE_EXTENSION Vcb,
3147c2c66affSColin Finck ULONGLONG MFTIndex,
3148c2c66affSColin Finck PUNICODE_STRING FileName,
3149c2c66affSColin Finck PULONG FirstEntry,
3150c2c66affSColin Finck BOOLEAN DirSearch,
315138c947b7STrevor Thompson BOOLEAN CaseSensitive,
315238c947b7STrevor Thompson ULONGLONG *OutMFTIndex)
3153c2c66affSColin Finck {
3154c2c66affSColin Finck PFILE_RECORD_HEADER MftRecord;
3155c2c66affSColin Finck PNTFS_ATTR_CONTEXT IndexRootCtx;
3156c2c66affSColin Finck PINDEX_ROOT_ATTRIBUTE IndexRoot;
3157c2c66affSColin Finck PCHAR IndexRecord;
3158c2c66affSColin Finck PINDEX_ENTRY_ATTRIBUTE IndexEntry, IndexEntryEnd;
3159c2c66affSColin Finck NTSTATUS Status;
3160c2c66affSColin Finck ULONG CurrentEntry = 0;
3161c2c66affSColin Finck
316238c947b7STrevor Thompson DPRINT("NtfsFindMftRecord(%p, %I64d, %wZ, %lu, %s, %s, %p)\n",
316338c947b7STrevor Thompson Vcb,
316438c947b7STrevor Thompson MFTIndex,
316538c947b7STrevor Thompson FileName,
316638c947b7STrevor Thompson *FirstEntry,
316738c947b7STrevor Thompson DirSearch ? "TRUE" : "FALSE",
316838c947b7STrevor Thompson CaseSensitive ? "TRUE" : "FALSE",
316938c947b7STrevor Thompson OutMFTIndex);
3170c2c66affSColin Finck
3171216a2caeSPierre Schweitzer MftRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
3172c2c66affSColin Finck if (MftRecord == NULL)
3173c2c66affSColin Finck {
3174c2c66affSColin Finck return STATUS_INSUFFICIENT_RESOURCES;
3175c2c66affSColin Finck }
3176c2c66affSColin Finck
3177c2c66affSColin Finck Status = ReadFileRecord(Vcb, MFTIndex, MftRecord);
3178c2c66affSColin Finck if (!NT_SUCCESS(Status))
3179c2c66affSColin Finck {
3180216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
3181c2c66affSColin Finck return Status;
3182c2c66affSColin Finck }
3183c2c66affSColin Finck
3184c2c66affSColin Finck ASSERT(MftRecord->Ntfs.Type == NRH_FILE_TYPE);
3185ba33b9faSTrevor Thompson Status = FindAttribute(Vcb, MftRecord, AttributeIndexRoot, L"$I30", 4, &IndexRootCtx, NULL);
3186c2c66affSColin Finck if (!NT_SUCCESS(Status))
3187c2c66affSColin Finck {
3188216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
3189c2c66affSColin Finck return Status;
3190c2c66affSColin Finck }
3191c2c66affSColin Finck
3192c2c66affSColin Finck IndexRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerIndexRecord, TAG_NTFS);
3193c2c66affSColin Finck if (IndexRecord == NULL)
3194c2c66affSColin Finck {
3195c2c66affSColin Finck ReleaseAttributeContext(IndexRootCtx);
3196216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
3197c2c66affSColin Finck return STATUS_INSUFFICIENT_RESOURCES;
3198c2c66affSColin Finck }
3199c2c66affSColin Finck
3200c2c66affSColin Finck ReadAttribute(Vcb, IndexRootCtx, 0, IndexRecord, Vcb->NtfsInfo.BytesPerIndexRecord);
3201c2c66affSColin Finck IndexRoot = (PINDEX_ROOT_ATTRIBUTE)IndexRecord;
3202c2c66affSColin Finck IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)&IndexRoot->Header + IndexRoot->Header.FirstEntryOffset);
3203c2c66affSColin Finck /* Index root is always resident. */
3204c2c66affSColin Finck IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)(IndexRecord + IndexRoot->Header.TotalSizeOfEntries);
3205c2c66affSColin Finck ReleaseAttributeContext(IndexRootCtx);
3206c2c66affSColin Finck
3207c2c66affSColin Finck DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb->NtfsInfo.BytesPerIndexRecord, IndexRoot->SizeOfEntry);
3208c2c66affSColin Finck
3209032be029STrevor Thompson Status = BrowseIndexEntries(Vcb,
3210032be029STrevor Thompson MftRecord,
32111ac7128dSTrevor Thompson (PINDEX_ROOT_ATTRIBUTE)IndexRecord,
3212032be029STrevor Thompson IndexRoot->SizeOfEntry,
3213032be029STrevor Thompson IndexEntry,
3214032be029STrevor Thompson IndexEntryEnd,
3215032be029STrevor Thompson FileName,
3216032be029STrevor Thompson FirstEntry,
3217032be029STrevor Thompson &CurrentEntry,
3218032be029STrevor Thompson DirSearch,
3219032be029STrevor Thompson CaseSensitive,
3220032be029STrevor Thompson OutMFTIndex);
3221c2c66affSColin Finck
3222c2c66affSColin Finck ExFreePoolWithTag(IndexRecord, TAG_NTFS);
3223216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, MftRecord);
3224c2c66affSColin Finck
3225c2c66affSColin Finck return Status;
3226c2c66affSColin Finck }
3227c2c66affSColin Finck
3228c2c66affSColin Finck NTSTATUS
NtfsLookupFileAt(PDEVICE_EXTENSION Vcb,PUNICODE_STRING PathName,BOOLEAN CaseSensitive,PFILE_RECORD_HEADER * FileRecord,PULONGLONG MFTIndex,ULONGLONG CurrentMFTIndex)3229c2c66affSColin Finck NtfsLookupFileAt(PDEVICE_EXTENSION Vcb,
3230c2c66affSColin Finck PUNICODE_STRING PathName,
3231948e9190STrevor Thompson BOOLEAN CaseSensitive,
3232c2c66affSColin Finck PFILE_RECORD_HEADER *FileRecord,
3233c2c66affSColin Finck PULONGLONG MFTIndex,
3234948e9190STrevor Thompson ULONGLONG CurrentMFTIndex)
3235c2c66affSColin Finck {
3236c2c66affSColin Finck UNICODE_STRING Current, Remaining;
3237c2c66affSColin Finck NTSTATUS Status;
3238c2c66affSColin Finck ULONG FirstEntry = 0;
3239c2c66affSColin Finck
3240948e9190STrevor Thompson DPRINT("NtfsLookupFileAt(%p, %wZ, %s, %p, %p, %I64x)\n",
3241948e9190STrevor Thompson Vcb,
3242948e9190STrevor Thompson PathName,
3243948e9190STrevor Thompson CaseSensitive ? "TRUE" : "FALSE",
3244948e9190STrevor Thompson FileRecord,
3245948e9190STrevor Thompson MFTIndex,
3246948e9190STrevor Thompson CurrentMFTIndex);
3247c2c66affSColin Finck
3248c2c66affSColin Finck FsRtlDissectName(*PathName, &Current, &Remaining);
3249c2c66affSColin Finck
3250c2c66affSColin Finck while (Current.Length != 0)
3251c2c66affSColin Finck {
3252c2c66affSColin Finck DPRINT("Current: %wZ\n", &Current);
3253c2c66affSColin Finck
325438c947b7STrevor Thompson Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, &Current, &FirstEntry, FALSE, CaseSensitive, &CurrentMFTIndex);
3255c2c66affSColin Finck if (!NT_SUCCESS(Status))
3256c2c66affSColin Finck {
3257c2c66affSColin Finck return Status;
3258c2c66affSColin Finck }
3259c2c66affSColin Finck
3260c2c66affSColin Finck if (Remaining.Length == 0)
3261c2c66affSColin Finck break;
3262c2c66affSColin Finck
3263c2c66affSColin Finck FsRtlDissectName(Current, &Current, &Remaining);
3264c2c66affSColin Finck }
3265c2c66affSColin Finck
3266216a2caeSPierre Schweitzer *FileRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
3267c2c66affSColin Finck if (*FileRecord == NULL)
3268c2c66affSColin Finck {
3269c2c66affSColin Finck DPRINT("NtfsLookupFileAt: Can't allocate MFT record\n");
3270c2c66affSColin Finck return STATUS_INSUFFICIENT_RESOURCES;
3271c2c66affSColin Finck }
3272c2c66affSColin Finck
3273c2c66affSColin Finck Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord);
3274c2c66affSColin Finck if (!NT_SUCCESS(Status))
3275c2c66affSColin Finck {
3276c2c66affSColin Finck DPRINT("NtfsLookupFileAt: Can't read MFT record\n");
3277216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, *FileRecord);
3278c2c66affSColin Finck return Status;
3279c2c66affSColin Finck }
3280c2c66affSColin Finck
3281c2c66affSColin Finck *MFTIndex = CurrentMFTIndex;
3282c2c66affSColin Finck
3283c2c66affSColin Finck return STATUS_SUCCESS;
3284c2c66affSColin Finck }
3285c2c66affSColin Finck
3286c2c66affSColin Finck NTSTATUS
NtfsLookupFile(PDEVICE_EXTENSION Vcb,PUNICODE_STRING PathName,BOOLEAN CaseSensitive,PFILE_RECORD_HEADER * FileRecord,PULONGLONG MFTIndex)3287c2c66affSColin Finck NtfsLookupFile(PDEVICE_EXTENSION Vcb,
3288c2c66affSColin Finck PUNICODE_STRING PathName,
3289948e9190STrevor Thompson BOOLEAN CaseSensitive,
3290c2c66affSColin Finck PFILE_RECORD_HEADER *FileRecord,
3291948e9190STrevor Thompson PULONGLONG MFTIndex)
3292c2c66affSColin Finck {
3293948e9190STrevor Thompson return NtfsLookupFileAt(Vcb, PathName, CaseSensitive, FileRecord, MFTIndex, NTFS_FILE_ROOT);
3294c2c66affSColin Finck }
3295c2c66affSColin Finck
32961ac7128dSTrevor Thompson void
NtfsDumpData(ULONG_PTR Buffer,ULONG Length)32971ac7128dSTrevor Thompson NtfsDumpData(ULONG_PTR Buffer, ULONG Length)
32981ac7128dSTrevor Thompson {
32991ac7128dSTrevor Thompson ULONG i, j;
33001ac7128dSTrevor Thompson
33011ac7128dSTrevor Thompson // dump binary data, 8 bytes at a time
33021ac7128dSTrevor Thompson for (i = 0; i < Length; i += 8)
33031ac7128dSTrevor Thompson {
33041ac7128dSTrevor Thompson // display current offset, in hex
33051ac7128dSTrevor Thompson DbgPrint("\t%03x\t", i);
33061ac7128dSTrevor Thompson
33071ac7128dSTrevor Thompson // display hex value of each of the next 8 bytes
33081ac7128dSTrevor Thompson for (j = 0; j < 8; j++)
33091ac7128dSTrevor Thompson DbgPrint("%02x ", *(PUCHAR)(Buffer + i + j));
33101ac7128dSTrevor Thompson DbgPrint("\n");
33111ac7128dSTrevor Thompson }
33121ac7128dSTrevor Thompson }
33131ac7128dSTrevor Thompson
33147643831dSTrevor Thompson /**
33157643831dSTrevor Thompson * @name NtfsDumpFileRecord
33167643831dSTrevor Thompson * @implemented
33177643831dSTrevor Thompson *
33187643831dSTrevor Thompson * Provides diagnostic information about a file record. Prints a hex dump
33197643831dSTrevor Thompson * of the entire record (based on the size reported by FileRecord->ByesInUse),
33207643831dSTrevor Thompson * then prints a dump of each attribute.
33217643831dSTrevor Thompson *
33227643831dSTrevor Thompson * @param Vcb
33237643831dSTrevor Thompson * Pointer to a DEVICE_EXTENSION describing the volume.
33247643831dSTrevor Thompson *
33257643831dSTrevor Thompson * @param FileRecord
33267643831dSTrevor Thompson * Pointer to the file record to be analyzed.
33277643831dSTrevor Thompson *
33287643831dSTrevor Thompson * @remarks
33297643831dSTrevor Thompson * FileRecord must be a complete file record at least FileRecord->BytesAllocated
33307643831dSTrevor Thompson * in size, and not just the header.
33317643831dSTrevor Thompson *
33327643831dSTrevor Thompson */
33337643831dSTrevor Thompson VOID
NtfsDumpFileRecord(PDEVICE_EXTENSION Vcb,PFILE_RECORD_HEADER FileRecord)33347643831dSTrevor Thompson NtfsDumpFileRecord(PDEVICE_EXTENSION Vcb,
33357643831dSTrevor Thompson PFILE_RECORD_HEADER FileRecord)
33367643831dSTrevor Thompson {
33377643831dSTrevor Thompson ULONG i, j;
33387643831dSTrevor Thompson
33397643831dSTrevor Thompson // dump binary data, 8 bytes at a time
33407643831dSTrevor Thompson for (i = 0; i < FileRecord->BytesInUse; i += 8)
33417643831dSTrevor Thompson {
33427643831dSTrevor Thompson // display current offset, in hex
33437643831dSTrevor Thompson DbgPrint("\t%03x\t", i);
33447643831dSTrevor Thompson
33457643831dSTrevor Thompson // display hex value of each of the next 8 bytes
33467643831dSTrevor Thompson for (j = 0; j < 8; j++)
33477643831dSTrevor Thompson DbgPrint("%02x ", *(PUCHAR)((ULONG_PTR)FileRecord + i + j));
33487643831dSTrevor Thompson DbgPrint("\n");
33497643831dSTrevor Thompson }
33507643831dSTrevor Thompson
33517643831dSTrevor Thompson NtfsDumpFileAttributes(Vcb, FileRecord);
33527643831dSTrevor Thompson }
33537643831dSTrevor Thompson
3354c2c66affSColin Finck NTSTATUS
NtfsFindFileAt(PDEVICE_EXTENSION Vcb,PUNICODE_STRING SearchPattern,PULONG FirstEntry,PFILE_RECORD_HEADER * FileRecord,PULONGLONG MFTIndex,ULONGLONG CurrentMFTIndex,BOOLEAN CaseSensitive)3355c2c66affSColin Finck NtfsFindFileAt(PDEVICE_EXTENSION Vcb,
3356c2c66affSColin Finck PUNICODE_STRING SearchPattern,
3357c2c66affSColin Finck PULONG FirstEntry,
3358c2c66affSColin Finck PFILE_RECORD_HEADER *FileRecord,
3359c2c66affSColin Finck PULONGLONG MFTIndex,
3360032be029STrevor Thompson ULONGLONG CurrentMFTIndex,
3361032be029STrevor Thompson BOOLEAN CaseSensitive)
3362c2c66affSColin Finck {
3363c2c66affSColin Finck NTSTATUS Status;
3364c2c66affSColin Finck
336538c947b7STrevor Thompson DPRINT("NtfsFindFileAt(%p, %wZ, %lu, %p, %p, %I64x, %s)\n",
3366032be029STrevor Thompson Vcb,
3367032be029STrevor Thompson SearchPattern,
3368032be029STrevor Thompson *FirstEntry,
3369032be029STrevor Thompson FileRecord,
3370032be029STrevor Thompson MFTIndex,
3371032be029STrevor Thompson CurrentMFTIndex,
3372032be029STrevor Thompson (CaseSensitive ? "TRUE" : "FALSE"));
3373c2c66affSColin Finck
337438c947b7STrevor Thompson Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, SearchPattern, FirstEntry, TRUE, CaseSensitive, &CurrentMFTIndex);
3375c2c66affSColin Finck if (!NT_SUCCESS(Status))
3376c2c66affSColin Finck {
3377c2c66affSColin Finck DPRINT("NtfsFindFileAt: NtfsFindMftRecord() failed with status 0x%08lx\n", Status);
3378c2c66affSColin Finck return Status;
3379c2c66affSColin Finck }
3380c2c66affSColin Finck
3381216a2caeSPierre Schweitzer *FileRecord = ExAllocateFromNPagedLookasideList(&Vcb->FileRecLookasideList);
3382c2c66affSColin Finck if (*FileRecord == NULL)
3383c2c66affSColin Finck {
3384c2c66affSColin Finck DPRINT("NtfsFindFileAt: Can't allocate MFT record\n");
3385c2c66affSColin Finck return STATUS_INSUFFICIENT_RESOURCES;
3386c2c66affSColin Finck }
3387c2c66affSColin Finck
3388c2c66affSColin Finck Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord);
3389c2c66affSColin Finck if (!NT_SUCCESS(Status))
3390c2c66affSColin Finck {
3391c2c66affSColin Finck DPRINT("NtfsFindFileAt: Can't read MFT record\n");
3392216a2caeSPierre Schweitzer ExFreeToNPagedLookasideList(&Vcb->FileRecLookasideList, *FileRecord);
3393c2c66affSColin Finck return Status;
3394c2c66affSColin Finck }
3395c2c66affSColin Finck
3396c2c66affSColin Finck *MFTIndex = CurrentMFTIndex;
3397c2c66affSColin Finck
3398c2c66affSColin Finck return STATUS_SUCCESS;
3399c2c66affSColin Finck }
3400c2c66affSColin Finck
3401c2c66affSColin Finck /* EOF */
3402