xref: /reactos/drivers/filesystems/ntfs/mft.c (revision 52c30fdf)
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
41c2c66affSColin Finck PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord)
42c2c66affSColin Finck {
43c2c66affSColin Finck     PNTFS_ATTR_CONTEXT Context;
44c2c66affSColin Finck 
45c2c66affSColin Finck     Context = ExAllocatePoolWithTag(NonPagedPool,
464dfcd1d5STrevor Thompson                                     sizeof(NTFS_ATTR_CONTEXT),
47c2c66affSColin Finck                                     TAG_NTFS);
484dfcd1d5STrevor Thompson     if(!Context)
494dfcd1d5STrevor Thompson     {
504dfcd1d5STrevor Thompson         DPRINT1("Error: Unable to allocate memory for context!\n");
514dfcd1d5STrevor Thompson         return NULL;
524dfcd1d5STrevor Thompson     }
534dfcd1d5STrevor Thompson 
544dfcd1d5STrevor Thompson     // Allocate memory for a copy of the attribute
554dfcd1d5STrevor Thompson     Context->pRecord = ExAllocatePoolWithTag(NonPagedPool, AttrRecord->Length, TAG_NTFS);
564dfcd1d5STrevor Thompson     if(!Context->pRecord)
574dfcd1d5STrevor Thompson     {
584dfcd1d5STrevor Thompson         DPRINT1("Error: Unable to allocate memory for attribute record!\n");
594dfcd1d5STrevor Thompson         ExFreePoolWithTag(Context, TAG_NTFS);
604dfcd1d5STrevor Thompson         return NULL;
614dfcd1d5STrevor Thompson     }
624dfcd1d5STrevor Thompson 
634dfcd1d5STrevor Thompson     // Copy the attribute
644dfcd1d5STrevor Thompson     RtlCopyMemory(Context->pRecord, AttrRecord, AttrRecord->Length);
654dfcd1d5STrevor Thompson 
66c2c66affSColin Finck     if (AttrRecord->IsNonResident)
67c2c66affSColin Finck     {
68c2c66affSColin Finck         LONGLONG DataRunOffset;
69c2c66affSColin Finck         ULONGLONG DataRunLength;
7052b9f467STrevor Thompson         ULONGLONG NextVBN = 0;
714dfcd1d5STrevor Thompson         PUCHAR DataRun = (PUCHAR)((ULONG_PTR)Context->pRecord + Context->pRecord->NonResident.MappingPairsOffset);
72c2c66affSColin Finck 
7352b9f467STrevor Thompson         Context->CacheRun = DataRun;
74c2c66affSColin Finck         Context->CacheRunOffset = 0;
75c2c66affSColin Finck         Context->CacheRun = DecodeRun(Context->CacheRun, &DataRunOffset, &DataRunLength);
76c2c66affSColin Finck         Context->CacheRunLength = DataRunLength;
77c2c66affSColin Finck         if (DataRunOffset != -1)
78c2c66affSColin Finck         {
79c2c66affSColin Finck             /* Normal run. */
80c2c66affSColin Finck             Context->CacheRunStartLCN =
81c2c66affSColin Finck             Context->CacheRunLastLCN = DataRunOffset;
82c2c66affSColin Finck         }
83c2c66affSColin Finck         else
84c2c66affSColin Finck         {
85c2c66affSColin Finck             /* Sparse run. */
86c2c66affSColin Finck             Context->CacheRunStartLCN = -1;
87c2c66affSColin Finck             Context->CacheRunLastLCN = 0;
88c2c66affSColin Finck         }
89c2c66affSColin Finck         Context->CacheRunCurrentOffset = 0;
9052b9f467STrevor Thompson 
9152b9f467STrevor Thompson         // Convert the data runs to a map control block
9252b9f467STrevor Thompson         if (!NT_SUCCESS(ConvertDataRunsToLargeMCB(DataRun, &Context->DataRunsMCB, &NextVBN)))
9352b9f467STrevor Thompson         {
9452b9f467STrevor Thompson             DPRINT1("Unable to convert data runs to MCB!\n");
954dfcd1d5STrevor Thompson             ExFreePoolWithTag(Context->pRecord, TAG_NTFS);
9652b9f467STrevor Thompson             ExFreePoolWithTag(Context, TAG_NTFS);
9752b9f467STrevor Thompson             return NULL;
9852b9f467STrevor Thompson         }
99c2c66affSColin Finck     }
100c2c66affSColin Finck 
101c2c66affSColin Finck     return Context;
102c2c66affSColin Finck }
103c2c66affSColin Finck 
104c2c66affSColin Finck 
105c2c66affSColin Finck VOID
106c2c66affSColin Finck ReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context)
107c2c66affSColin Finck {
1084dfcd1d5STrevor Thompson     if (Context->pRecord->IsNonResident)
10952b9f467STrevor Thompson     {
11052b9f467STrevor Thompson         FsRtlUninitializeLargeMcb(&Context->DataRunsMCB);
11152b9f467STrevor Thompson     }
11252b9f467STrevor Thompson 
1134dfcd1d5STrevor Thompson     if(Context->pRecord)
1144dfcd1d5STrevor Thompson         ExFreePoolWithTag(Context->pRecord, TAG_NTFS);
1154dfcd1d5STrevor Thompson 
116c2c66affSColin Finck     ExFreePoolWithTag(Context, TAG_NTFS);
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
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;
143c2c66affSColin Finck 
14438c947b7STrevor Thompson     DPRINT("FindAttribute(%p, %p, 0x%x, %S, %lu, %p, %p)\n", Vcb, MftRecord, Type, Name, NameLength, AttrCtx, Offset);
145c2c66affSColin Finck 
146c2c66affSColin Finck     Found = FALSE;
147c2c66affSColin Finck     Status = FindFirstAttribute(&Context, Vcb, MftRecord, FALSE, &Attribute);
148c2c66affSColin Finck     while (NT_SUCCESS(Status))
149c2c66affSColin Finck     {
150c2c66affSColin Finck         if (Attribute->Type == Type && Attribute->NameLength == NameLength)
151c2c66affSColin Finck         {
152c2c66affSColin Finck             if (NameLength != 0)
153c2c66affSColin Finck             {
154c2c66affSColin Finck                 PWCHAR AttrName;
155c2c66affSColin Finck 
156c2c66affSColin Finck                 AttrName = (PWCHAR)((PCHAR)Attribute + Attribute->NameOffset);
157c2c66affSColin Finck                 DPRINT("%.*S, %.*S\n", Attribute->NameLength, AttrName, NameLength, Name);
15868a48b27STrevor Thompson                 if (RtlCompareMemory(AttrName, Name, NameLength * sizeof(WCHAR)) == (NameLength  * sizeof(WCHAR)))
159c2c66affSColin Finck                 {
160c2c66affSColin Finck                     Found = TRUE;
161c2c66affSColin Finck                 }
162c2c66affSColin Finck             }
163c2c66affSColin Finck             else
164c2c66affSColin Finck             {
165c2c66affSColin Finck                 Found = TRUE;
166c2c66affSColin Finck             }
167c2c66affSColin Finck 
168c2c66affSColin Finck             if (Found)
169c2c66affSColin Finck             {
170c2c66affSColin Finck                 /* Found it, fill up the context and return. */
171c2c66affSColin Finck                 DPRINT("Found context\n");
172c2c66affSColin Finck                 *AttrCtx = PrepareAttributeContext(Attribute);
173ba33b9faSTrevor Thompson 
174760cdfb5STrevor Thompson                 (*AttrCtx)->FileMFTIndex = MftRecord->MFTRecordNumber;
175760cdfb5STrevor Thompson 
176ba33b9faSTrevor Thompson                 if (Offset != NULL)
177ba33b9faSTrevor Thompson                     *Offset = Context.Offset;
178ba33b9faSTrevor Thompson 
179c2c66affSColin Finck                 FindCloseAttribute(&Context);
180c2c66affSColin Finck                 return STATUS_SUCCESS;
181c2c66affSColin Finck             }
182c2c66affSColin Finck         }
183c2c66affSColin Finck 
184c2c66affSColin Finck         Status = FindNextAttribute(&Context, &Attribute);
185c2c66affSColin Finck     }
186c2c66affSColin Finck 
187c2c66affSColin Finck     FindCloseAttribute(&Context);
188c2c66affSColin Finck     return STATUS_OBJECT_NAME_NOT_FOUND;
189c2c66affSColin Finck }
190c2c66affSColin Finck 
191c2c66affSColin Finck 
19277fc65dcSTrevor Thompson ULONGLONG
193c2c66affSColin Finck AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord)
194c2c66affSColin Finck {
195c2c66affSColin Finck     if (AttrRecord->IsNonResident)
196c2c66affSColin Finck         return AttrRecord->NonResident.AllocatedSize;
197c2c66affSColin Finck     else
198f2e47474STrevor Thompson         return ALIGN_UP_BY(AttrRecord->Resident.ValueLength, ATTR_RECORD_ALIGNMENT);
199c2c66affSColin Finck }
200c2c66affSColin Finck 
201c2c66affSColin Finck 
202c2c66affSColin Finck ULONGLONG
203c2c66affSColin Finck AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord)
204c2c66affSColin Finck {
205c2c66affSColin Finck     if (AttrRecord->IsNonResident)
206c2c66affSColin Finck         return AttrRecord->NonResident.DataSize;
207c2c66affSColin Finck     else
208c2c66affSColin Finck         return AttrRecord->Resident.ValueLength;
209c2c66affSColin Finck }
210c2c66affSColin Finck 
2119ab86116STrevor Thompson /**
2129ab86116STrevor Thompson * @name IncreaseMftSize
2139ab86116STrevor Thompson * @implemented
2149ab86116STrevor Thompson *
2159ab86116STrevor Thompson * Increases the size of the master file table on a volume, increasing the space available for file records.
2169ab86116STrevor Thompson *
2179ab86116STrevor Thompson * @param Vcb
2189ab86116STrevor Thompson * Pointer to the VCB (DEVICE_EXTENSION) of the target volume.
2199ab86116STrevor Thompson *
22098ddf610STrevor Thompson *
22198ddf610STrevor Thompson * @param CanWait
22298ddf610STrevor Thompson * Boolean indicating if the function is allowed to wait for exclusive access to the master file table.
22398ddf610STrevor Thompson * This will only be relevant if the MFT doesn't have any free file records and needs to be enlarged.
22498ddf610STrevor Thompson *
2259ab86116STrevor Thompson * @return
2269ab86116STrevor Thompson * STATUS_SUCCESS on success.
2279ab86116STrevor Thompson * STATUS_INSUFFICIENT_RESOURCES if an allocation fails.
2289ab86116STrevor Thompson * STATUS_INVALID_PARAMETER if there was an error reading the Mft's bitmap.
22998ddf610STrevor Thompson * STATUS_CANT_WAIT if CanWait was FALSE and the function could not get immediate, exclusive access to the MFT.
2309ab86116STrevor Thompson *
2319ab86116STrevor Thompson * @remarks
2325e7c1184STrevor Thompson * Increases the size of the Master File Table by 64 records. Bitmap entries for the new records are cleared,
2339ab86116STrevor Thompson * and the bitmap is also enlarged if needed. Mimicking Windows' behavior when enlarging the mft is still TODO.
2349ab86116STrevor Thompson * This function will wait for exlusive access to the volume fcb.
2359ab86116STrevor Thompson */
2369ab86116STrevor Thompson NTSTATUS
23798ddf610STrevor Thompson IncreaseMftSize(PDEVICE_EXTENSION Vcb, BOOLEAN CanWait)
2389ab86116STrevor Thompson {
2399ab86116STrevor Thompson     PNTFS_ATTR_CONTEXT BitmapContext;
2409ab86116STrevor Thompson     LARGE_INTEGER BitmapSize;
2419ab86116STrevor Thompson     LARGE_INTEGER DataSize;
2429ab86116STrevor Thompson     LONGLONG BitmapSizeDifference;
2435e7c1184STrevor Thompson     ULONG NewRecords = ATTR_RECORD_ALIGNMENT * 8;  // Allocate one new record for every bit of every byte we'll be adding to the bitmap
2445e7c1184STrevor Thompson     ULONG DataSizeDifference = Vcb->NtfsInfo.BytesPerFileRecord * NewRecords;
2459ab86116STrevor Thompson     ULONG BitmapOffset;
2469ab86116STrevor Thompson     PUCHAR BitmapBuffer;
2479ab86116STrevor Thompson     ULONGLONG BitmapBytes;
2489ab86116STrevor Thompson     ULONGLONG NewBitmapSize;
2495e7c1184STrevor Thompson     ULONGLONG FirstNewMftIndex;
2509ab86116STrevor Thompson     ULONG BytesRead;
2519ab86116STrevor Thompson     ULONG LengthWritten;
2525e7c1184STrevor Thompson     PFILE_RECORD_HEADER BlankFileRecord;
2535e7c1184STrevor Thompson     ULONG i;
2549ab86116STrevor Thompson     NTSTATUS Status;
2559ab86116STrevor Thompson 
25698ddf610STrevor Thompson     DPRINT1("IncreaseMftSize(%p, %s)\n", Vcb, CanWait ? "TRUE" : "FALSE");
2579ab86116STrevor Thompson 
2589ab86116STrevor Thompson     // We need exclusive access to the mft while we change its size
25998ddf610STrevor Thompson     if (!ExAcquireResourceExclusiveLite(&(Vcb->DirResource), CanWait))
2609ab86116STrevor Thompson     {
2619ab86116STrevor Thompson         return STATUS_CANT_WAIT;
2629ab86116STrevor Thompson     }
2639ab86116STrevor Thompson 
2645e7c1184STrevor Thompson     // Create a blank file record that will be used later
2655e7c1184STrevor Thompson     BlankFileRecord = NtfsCreateEmptyFileRecord(Vcb);
2665e7c1184STrevor Thompson     if (!BlankFileRecord)
2675e7c1184STrevor Thompson     {
2685e7c1184STrevor Thompson         DPRINT1("Error: Unable to create empty file record!\n");
2695e7c1184STrevor Thompson         return STATUS_INSUFFICIENT_RESOURCES;
2705e7c1184STrevor Thompson     }
2715e7c1184STrevor Thompson 
2725e7c1184STrevor Thompson     // Clear the flags (file record is not in use)
2735e7c1184STrevor Thompson     BlankFileRecord->Flags = 0;
2745e7c1184STrevor Thompson 
2759ab86116STrevor Thompson     // Find the bitmap attribute of master file table
2765e7c1184STrevor Thompson     Status = FindAttribute(Vcb, Vcb->MasterFileTable, AttributeBitmap, L"", 0, &BitmapContext, NULL);
2779ab86116STrevor Thompson     if (!NT_SUCCESS(Status))
2789ab86116STrevor Thompson     {
2799ab86116STrevor Thompson         DPRINT1("ERROR: Couldn't find $BITMAP attribute of Mft!\n");
2805e7c1184STrevor Thompson         ExFreePoolWithTag(BlankFileRecord, TAG_NTFS);
2819ab86116STrevor Thompson         ExReleaseResourceLite(&(Vcb->DirResource));
2829ab86116STrevor Thompson         return Status;
2839ab86116STrevor Thompson     }
2849ab86116STrevor Thompson 
2859ab86116STrevor Thompson     // Get size of Bitmap Attribute
2864dfcd1d5STrevor Thompson     BitmapSize.QuadPart = AttributeDataLength(BitmapContext->pRecord);
2879ab86116STrevor Thompson 
2889ab86116STrevor Thompson     // Calculate the new mft size
2894dfcd1d5STrevor Thompson     DataSize.QuadPart = AttributeDataLength(Vcb->MFTContext->pRecord) + DataSizeDifference;
2909ab86116STrevor Thompson 
2915e7c1184STrevor Thompson     // Find the index of the first Mft entry that will be created
2925e7c1184STrevor Thompson     FirstNewMftIndex = AttributeDataLength(Vcb->MFTContext->pRecord) / Vcb->NtfsInfo.BytesPerFileRecord;
2935e7c1184STrevor Thompson 
2949ab86116STrevor Thompson     // Determine how many bytes will make up the bitmap
2959ab86116STrevor Thompson     BitmapBytes = DataSize.QuadPart / Vcb->NtfsInfo.BytesPerFileRecord / 8;
2965e7c1184STrevor Thompson     if ((DataSize.QuadPart / Vcb->NtfsInfo.BytesPerFileRecord) % 8 != 0)
2975e7c1184STrevor Thompson         BitmapBytes++;
2985e7c1184STrevor Thompson 
2995e7c1184STrevor Thompson     // Windows will always keep the number of bytes in a bitmap as a multiple of 8, so no bytes are wasted on slack
3005e7c1184STrevor Thompson     BitmapBytes = ALIGN_UP_BY(BitmapBytes, ATTR_RECORD_ALIGNMENT);
3019ab86116STrevor Thompson 
3029ab86116STrevor Thompson     // Determine how much we need to adjust the bitmap size (it's possible we don't)
3039ab86116STrevor Thompson     BitmapSizeDifference = BitmapBytes - BitmapSize.QuadPart;
3049ab86116STrevor Thompson     NewBitmapSize = max(BitmapSize.QuadPart + BitmapSizeDifference, BitmapSize.QuadPart);
3059ab86116STrevor Thompson 
3069ab86116STrevor Thompson     // Allocate memory for the bitmap
3079ab86116STrevor Thompson     BitmapBuffer = ExAllocatePoolWithTag(NonPagedPool, NewBitmapSize, TAG_NTFS);
3089ab86116STrevor Thompson     if (!BitmapBuffer)
3099ab86116STrevor Thompson     {
3109ab86116STrevor Thompson         DPRINT1("ERROR: Unable to allocate memory for bitmap attribute!\n");
3115e7c1184STrevor Thompson         ExFreePoolWithTag(BlankFileRecord, TAG_NTFS);
3129ab86116STrevor Thompson         ExReleaseResourceLite(&(Vcb->DirResource));
3139ab86116STrevor Thompson         ReleaseAttributeContext(BitmapContext);
3149ab86116STrevor Thompson         return STATUS_INSUFFICIENT_RESOURCES;
3159ab86116STrevor Thompson     }
3169ab86116STrevor Thompson 
3179ab86116STrevor Thompson     // Zero the bytes we'll be adding
31868a48b27STrevor Thompson     RtlZeroMemory(BitmapBuffer, NewBitmapSize);
3199ab86116STrevor Thompson 
3209ab86116STrevor Thompson     // Read the bitmap attribute
3219ab86116STrevor Thompson     BytesRead = ReadAttribute(Vcb,
3229ab86116STrevor Thompson                               BitmapContext,
3239ab86116STrevor Thompson                               0,
3249ab86116STrevor Thompson                               (PCHAR)BitmapBuffer,
3259ab86116STrevor Thompson                               BitmapSize.LowPart);
3269ab86116STrevor Thompson     if (BytesRead != BitmapSize.LowPart)
3279ab86116STrevor Thompson     {
3289ab86116STrevor Thompson         DPRINT1("ERROR: Bytes read != Bitmap size!\n");
3295e7c1184STrevor Thompson         ExFreePoolWithTag(BlankFileRecord, TAG_NTFS);
3309ab86116STrevor Thompson         ExReleaseResourceLite(&(Vcb->DirResource));
3319ab86116STrevor Thompson         ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
3329ab86116STrevor Thompson         ReleaseAttributeContext(BitmapContext);
3339ab86116STrevor Thompson         return STATUS_INVALID_PARAMETER;
3349ab86116STrevor Thompson     }
3359ab86116STrevor Thompson 
3369ab86116STrevor Thompson     // Increase the mft size
3379ab86116STrevor Thompson     Status = SetNonResidentAttributeDataLength(Vcb, Vcb->MFTContext, Vcb->MftDataOffset, Vcb->MasterFileTable, &DataSize);
3389ab86116STrevor Thompson     if (!NT_SUCCESS(Status))
3399ab86116STrevor Thompson     {
3409ab86116STrevor Thompson         DPRINT1("ERROR: Failed to set size of $MFT data attribute!\n");
3415e7c1184STrevor Thompson         ExFreePoolWithTag(BlankFileRecord, TAG_NTFS);
3429ab86116STrevor Thompson         ExReleaseResourceLite(&(Vcb->DirResource));
3439ab86116STrevor Thompson         ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
3449ab86116STrevor Thompson         ReleaseAttributeContext(BitmapContext);
3459ab86116STrevor Thompson         return Status;
3469ab86116STrevor Thompson     }
3479ab86116STrevor Thompson 
3485e7c1184STrevor Thompson     // We'll need to find the bitmap again, because its offset will have changed after resizing the data attribute
3495e7c1184STrevor Thompson     ReleaseAttributeContext(BitmapContext);
3505e7c1184STrevor Thompson     Status = FindAttribute(Vcb, Vcb->MasterFileTable, AttributeBitmap, L"", 0, &BitmapContext, &BitmapOffset);
3515e7c1184STrevor Thompson     if (!NT_SUCCESS(Status))
3525e7c1184STrevor Thompson     {
3535e7c1184STrevor Thompson         DPRINT1("ERROR: Couldn't find $BITMAP attribute of Mft!\n");
3545e7c1184STrevor Thompson         ExFreePoolWithTag(BlankFileRecord, TAG_NTFS);
3555e7c1184STrevor Thompson         ExReleaseResourceLite(&(Vcb->DirResource));
3565e7c1184STrevor Thompson         return Status;
3575e7c1184STrevor Thompson     }
3585e7c1184STrevor Thompson 
3599ab86116STrevor Thompson     // If the bitmap grew
3609ab86116STrevor Thompson     if (BitmapSizeDifference > 0)
3619ab86116STrevor Thompson     {
3629ab86116STrevor Thompson         // Set the new bitmap size
3635e7c1184STrevor Thompson         BitmapSize.QuadPart = NewBitmapSize;
3644dfcd1d5STrevor Thompson         if (BitmapContext->pRecord->IsNonResident)
3659ab86116STrevor Thompson             Status = SetNonResidentAttributeDataLength(Vcb, BitmapContext, BitmapOffset, Vcb->MasterFileTable, &BitmapSize);
3669ab86116STrevor Thompson         else
3679ab86116STrevor Thompson             Status = SetResidentAttributeDataLength(Vcb, BitmapContext, BitmapOffset, Vcb->MasterFileTable, &BitmapSize);
3689ab86116STrevor Thompson 
3699ab86116STrevor Thompson         if (!NT_SUCCESS(Status))
3709ab86116STrevor Thompson         {
3719ab86116STrevor Thompson             DPRINT1("ERROR: Failed to set size of bitmap attribute!\n");
3725e7c1184STrevor Thompson             ExFreePoolWithTag(BlankFileRecord, TAG_NTFS);
3739ab86116STrevor Thompson             ExReleaseResourceLite(&(Vcb->DirResource));
3749ab86116STrevor Thompson             ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
3759ab86116STrevor Thompson             ReleaseAttributeContext(BitmapContext);
3769ab86116STrevor Thompson             return Status;
3779ab86116STrevor Thompson         }
3789ab86116STrevor Thompson     }
3799ab86116STrevor Thompson 
3805e7c1184STrevor Thompson     NtfsDumpFileAttributes(Vcb, Vcb->MasterFileTable);
3819ab86116STrevor Thompson 
3829ab86116STrevor Thompson     // Update the file record with the new attribute sizes
3839ab86116STrevor Thompson     Status = UpdateFileRecord(Vcb, Vcb->VolumeFcb->MFTIndex, Vcb->MasterFileTable);
3849ab86116STrevor Thompson     if (!NT_SUCCESS(Status))
3859ab86116STrevor Thompson     {
3869ab86116STrevor Thompson         DPRINT1("ERROR: Failed to update $MFT file record!\n");
3875e7c1184STrevor Thompson         ExFreePoolWithTag(BlankFileRecord, TAG_NTFS);
3889ab86116STrevor Thompson         ExReleaseResourceLite(&(Vcb->DirResource));
3899ab86116STrevor Thompson         ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
3909ab86116STrevor Thompson         ReleaseAttributeContext(BitmapContext);
3919ab86116STrevor Thompson         return Status;
3929ab86116STrevor Thompson     }
3939ab86116STrevor Thompson 
3949ab86116STrevor Thompson     // Write out the new bitmap
3955e7c1184STrevor Thompson     Status = WriteAttribute(Vcb, BitmapContext, 0, BitmapBuffer, NewBitmapSize, &LengthWritten, Vcb->MasterFileTable);
3969ab86116STrevor Thompson     if (!NT_SUCCESS(Status))
3979ab86116STrevor Thompson     {
3985e7c1184STrevor Thompson         ExFreePoolWithTag(BlankFileRecord, TAG_NTFS);
3999ab86116STrevor Thompson         ExReleaseResourceLite(&(Vcb->DirResource));
4009ab86116STrevor Thompson         ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
4019ab86116STrevor Thompson         ReleaseAttributeContext(BitmapContext);
4029ab86116STrevor Thompson         DPRINT1("ERROR: Couldn't write to bitmap attribute of $MFT!\n");
40368a48b27STrevor Thompson         return Status;
4049ab86116STrevor Thompson     }
4059ab86116STrevor Thompson 
4065e7c1184STrevor Thompson     // Create blank records for the new file record entries.
4075e7c1184STrevor Thompson     for (i = 0; i < NewRecords; i++)
4085e7c1184STrevor Thompson     {
4095e7c1184STrevor Thompson         Status = UpdateFileRecord(Vcb, FirstNewMftIndex + i, BlankFileRecord);
4105e7c1184STrevor Thompson         if (!NT_SUCCESS(Status))
4115e7c1184STrevor Thompson         {
4125e7c1184STrevor Thompson             DPRINT1("ERROR: Failed to write blank file record!\n");
4135e7c1184STrevor Thompson             ExFreePoolWithTag(BlankFileRecord, TAG_NTFS);
4145e7c1184STrevor Thompson             ExReleaseResourceLite(&(Vcb->DirResource));
4155e7c1184STrevor Thompson             ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
4165e7c1184STrevor Thompson             ReleaseAttributeContext(BitmapContext);
4175e7c1184STrevor Thompson             return Status;
4185e7c1184STrevor Thompson         }
4195e7c1184STrevor Thompson     }
4205e7c1184STrevor Thompson 
4215e7c1184STrevor Thompson     // Update the mft mirror
4225e7c1184STrevor Thompson     Status = UpdateMftMirror(Vcb);
4235e7c1184STrevor Thompson 
4249ab86116STrevor Thompson     // Cleanup
4255e7c1184STrevor Thompson     ExFreePoolWithTag(BlankFileRecord, TAG_NTFS);
4269ab86116STrevor Thompson     ExReleaseResourceLite(&(Vcb->DirResource));
4279ab86116STrevor Thompson     ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
4289ab86116STrevor Thompson     ReleaseAttributeContext(BitmapContext);
4299ab86116STrevor Thompson 
4305e7c1184STrevor Thompson     return Status;
4319ab86116STrevor Thompson }
4329ab86116STrevor Thompson 
433d484d91eSTrevor Thompson /**
434d484d91eSTrevor Thompson * @name MoveAttributes
435d484d91eSTrevor Thompson * @implemented
436d484d91eSTrevor Thompson *
437d484d91eSTrevor Thompson * Moves a block of attributes to a new location in the file Record. The attribute at FirstAttributeToMove
438d484d91eSTrevor Thompson * and every attribute after that will be moved to MoveTo.
439d484d91eSTrevor Thompson *
440d484d91eSTrevor Thompson * @param DeviceExt
441d484d91eSTrevor Thompson * Pointer to the DEVICE_EXTENSION (VCB) of the target volume.
442d484d91eSTrevor Thompson *
443d484d91eSTrevor Thompson * @param FirstAttributeToMove
444d484d91eSTrevor Thompson * Pointer to the first NTFS_ATTR_RECORD that needs to be moved. This pointer must reside within a file record.
445d484d91eSTrevor Thompson *
446d484d91eSTrevor Thompson * @param FirstAttributeOffset
447d484d91eSTrevor Thompson * Offset of FirstAttributeToMove relative to the beginning of the file record.
448d484d91eSTrevor Thompson *
449d484d91eSTrevor Thompson * @param MoveTo
450d484d91eSTrevor Thompson * ULONG_PTR with the memory location that will be the new location of the first attribute being moved.
451d484d91eSTrevor Thompson *
452d484d91eSTrevor Thompson * @return
453d484d91eSTrevor Thompson * The new location of the final attribute (i.e. AttributeEnd marker).
454d484d91eSTrevor Thompson */
455d484d91eSTrevor Thompson PNTFS_ATTR_RECORD
456d484d91eSTrevor Thompson MoveAttributes(PDEVICE_EXTENSION DeviceExt,
457d484d91eSTrevor Thompson                PNTFS_ATTR_RECORD FirstAttributeToMove,
458d484d91eSTrevor Thompson                ULONG FirstAttributeOffset,
459d484d91eSTrevor Thompson                ULONG_PTR MoveTo)
460d484d91eSTrevor Thompson {
461d484d91eSTrevor Thompson     // Get the size of all attributes after this one
462d484d91eSTrevor Thompson     ULONG MemBlockSize = 0;
463d484d91eSTrevor Thompson     PNTFS_ATTR_RECORD CurrentAttribute = FirstAttributeToMove;
464d484d91eSTrevor Thompson     ULONG CurrentOffset = FirstAttributeOffset;
465d484d91eSTrevor Thompson     PNTFS_ATTR_RECORD FinalAttribute;
466d484d91eSTrevor Thompson 
467d484d91eSTrevor Thompson     while (CurrentAttribute->Type != AttributeEnd && CurrentOffset < DeviceExt->NtfsInfo.BytesPerFileRecord)
468d484d91eSTrevor Thompson     {
469d484d91eSTrevor Thompson         CurrentOffset += CurrentAttribute->Length;
470d484d91eSTrevor Thompson         MemBlockSize += CurrentAttribute->Length;
471d484d91eSTrevor Thompson         CurrentAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)CurrentAttribute + CurrentAttribute->Length);
472d484d91eSTrevor Thompson     }
473d484d91eSTrevor Thompson 
474d484d91eSTrevor Thompson     FinalAttribute = (PNTFS_ATTR_RECORD)(MoveTo + MemBlockSize);
475d484d91eSTrevor Thompson     MemBlockSize += sizeof(ULONG) * 2;  // Add the AttributeEnd and file record end
476d484d91eSTrevor Thompson 
477d484d91eSTrevor Thompson     ASSERT(MemBlockSize % ATTR_RECORD_ALIGNMENT == 0);
478d484d91eSTrevor Thompson 
479d484d91eSTrevor Thompson     // Move the attributes after this one
480d484d91eSTrevor Thompson     RtlMoveMemory((PCHAR)MoveTo, FirstAttributeToMove, MemBlockSize);
481d484d91eSTrevor Thompson 
482d484d91eSTrevor Thompson     return FinalAttribute;
483d484d91eSTrevor Thompson }
484d484d91eSTrevor Thompson 
4854dfcd1d5STrevor Thompson NTSTATUS
486d484d91eSTrevor Thompson InternalSetResidentAttributeLength(PDEVICE_EXTENSION DeviceExt,
487d484d91eSTrevor Thompson                                    PNTFS_ATTR_CONTEXT AttrContext,
488760cdfb5STrevor Thompson                                    PFILE_RECORD_HEADER FileRecord,
489760cdfb5STrevor Thompson                                    ULONG AttrOffset,
490760cdfb5STrevor Thompson                                    ULONG DataSize)
491760cdfb5STrevor Thompson {
492c08d37d1STrevor Thompson     PNTFS_ATTR_RECORD Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
493d484d91eSTrevor Thompson     PNTFS_ATTR_RECORD NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)Destination + Destination->Length);
494d484d91eSTrevor Thompson     PNTFS_ATTR_RECORD FinalAttribute;
495d484d91eSTrevor Thompson     ULONG OldAttributeLength = Destination->Length;
496760cdfb5STrevor Thompson     ULONG NextAttributeOffset;
497760cdfb5STrevor Thompson 
498d484d91eSTrevor Thompson     DPRINT1("InternalSetResidentAttributeLength( %p, %p, %p, %lu, %lu )\n", DeviceExt, AttrContext, FileRecord, AttrOffset, DataSize);
499760cdfb5STrevor Thompson 
5004dfcd1d5STrevor Thompson     ASSERT(!AttrContext->pRecord->IsNonResident);
50168a48b27STrevor Thompson 
5024dfcd1d5STrevor Thompson     // Update ValueLength Field
503c08d37d1STrevor Thompson     Destination->Resident.ValueLength = DataSize;
504760cdfb5STrevor Thompson 
5054dfcd1d5STrevor Thompson     // Calculate the record length and end marker offset
5064dfcd1d5STrevor Thompson     Destination->Length = ALIGN_UP_BY(DataSize + AttrContext->pRecord->Resident.ValueOffset, ATTR_RECORD_ALIGNMENT);
5074dfcd1d5STrevor Thompson     NextAttributeOffset = AttrOffset + Destination->Length;
5084dfcd1d5STrevor Thompson 
509d484d91eSTrevor Thompson     // Ensure NextAttributeOffset is aligned to an 8-byte boundary
510d484d91eSTrevor Thompson     ASSERT(NextAttributeOffset % ATTR_RECORD_ALIGNMENT == 0);
5114dfcd1d5STrevor Thompson 
512d484d91eSTrevor Thompson     // Will the new attribute be larger than the old one?
513d484d91eSTrevor Thompson     if (Destination->Length > OldAttributeLength)
514d484d91eSTrevor Thompson     {
5154dfcd1d5STrevor Thompson         // Free the old copy of the attribute in the context, as it will be the wrong length
5164dfcd1d5STrevor Thompson         ExFreePoolWithTag(AttrContext->pRecord, TAG_NTFS);
5174dfcd1d5STrevor Thompson 
518d484d91eSTrevor Thompson         // Create a new copy of the attribute record for the context
5194dfcd1d5STrevor Thompson         AttrContext->pRecord = ExAllocatePoolWithTag(NonPagedPool, Destination->Length, TAG_NTFS);
5204dfcd1d5STrevor Thompson         if (!AttrContext->pRecord)
5214dfcd1d5STrevor Thompson         {
5224dfcd1d5STrevor Thompson             DPRINT1("Unable to allocate memory for attribute!\n");
5234dfcd1d5STrevor Thompson             return STATUS_INSUFFICIENT_RESOURCES;
5244dfcd1d5STrevor Thompson         }
525d484d91eSTrevor Thompson         RtlZeroMemory((PVOID)((ULONG_PTR)AttrContext->pRecord + OldAttributeLength), Destination->Length - OldAttributeLength);
526d484d91eSTrevor Thompson         RtlCopyMemory(AttrContext->pRecord, Destination, OldAttributeLength);
527d484d91eSTrevor Thompson     }
528760cdfb5STrevor Thompson 
529d484d91eSTrevor Thompson     // Are there attributes after this one that need to be moved?
530d484d91eSTrevor Thompson     if (NextAttribute->Type != AttributeEnd)
531d484d91eSTrevor Thompson     {
532d484d91eSTrevor Thompson         // Move the attributes after this one
533d484d91eSTrevor Thompson         FinalAttribute = MoveAttributes(DeviceExt, NextAttribute, NextAttributeOffset, (ULONG_PTR)Destination + Destination->Length);
534d484d91eSTrevor Thompson     }
535d484d91eSTrevor Thompson     else
536d484d91eSTrevor Thompson     {
537d484d91eSTrevor Thompson         // advance to the final "attribute," adjust for the changed length of the attribute we're resizing
538d484d91eSTrevor Thompson         FinalAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute - OldAttributeLength + Destination->Length);
539d484d91eSTrevor Thompson     }
540760cdfb5STrevor Thompson 
541d484d91eSTrevor Thompson     // Update pRecord's length
542d484d91eSTrevor Thompson     AttrContext->pRecord->Length = Destination->Length;
543d484d91eSTrevor Thompson     AttrContext->pRecord->Resident.ValueLength = DataSize;
544d484d91eSTrevor Thompson 
545d484d91eSTrevor Thompson     // set the file record end
546d484d91eSTrevor Thompson     SetFileRecordEnd(FileRecord, FinalAttribute, FILE_RECORD_END);
547d484d91eSTrevor Thompson 
548d484d91eSTrevor Thompson     //NtfsDumpFileRecord(DeviceExt, FileRecord);
5494dfcd1d5STrevor Thompson 
5504dfcd1d5STrevor Thompson     return STATUS_SUCCESS;
551760cdfb5STrevor Thompson }
552c2c66affSColin Finck 
553afe40eb0STrevor Thompson /**
554afe40eb0STrevor Thompson *   @parameter FileRecord
555afe40eb0STrevor Thompson *   Pointer to a file record. Must be a full record at least
556afe40eb0STrevor Thompson *   Fcb->Vcb->NtfsInfo.BytesPerFileRecord bytes large, not just the header.
557afe40eb0STrevor Thompson */
558ba33b9faSTrevor Thompson NTSTATUS
559ba33b9faSTrevor Thompson SetAttributeDataLength(PFILE_OBJECT FileObject,
560ba33b9faSTrevor Thompson                        PNTFS_FCB Fcb,
561ba33b9faSTrevor Thompson                        PNTFS_ATTR_CONTEXT AttrContext,
562ba33b9faSTrevor Thompson                        ULONG AttrOffset,
563ba33b9faSTrevor Thompson                        PFILE_RECORD_HEADER FileRecord,
564ba33b9faSTrevor Thompson                        PLARGE_INTEGER DataSize)
565ba33b9faSTrevor Thompson {
566760cdfb5STrevor Thompson     NTSTATUS Status = STATUS_SUCCESS;
567760cdfb5STrevor Thompson 
568f2e47474STrevor Thompson     DPRINT1("SetAttributeDataLength(%p, %p, %p, %lu, %p, %I64u)\n",
56938c947b7STrevor Thompson             FileObject,
57038c947b7STrevor Thompson             Fcb,
57138c947b7STrevor Thompson             AttrContext,
57238c947b7STrevor Thompson             AttrOffset,
57338c947b7STrevor Thompson             FileRecord,
57438c947b7STrevor Thompson             DataSize->QuadPart);
57538c947b7STrevor Thompson 
576760cdfb5STrevor Thompson     // are we truncating the file?
5774dfcd1d5STrevor Thompson     if (DataSize->QuadPart < AttributeDataLength(AttrContext->pRecord))
578760cdfb5STrevor Thompson     {
5797eb1264fSTrevor Thompson         if (!MmCanFileBeTruncated(FileObject->SectionObjectPointer, DataSize))
580760cdfb5STrevor Thompson         {
581760cdfb5STrevor Thompson             DPRINT1("Can't truncate a memory-mapped file!\n");
582760cdfb5STrevor Thompson             return STATUS_USER_MAPPED_FILE;
583760cdfb5STrevor Thompson         }
584760cdfb5STrevor Thompson     }
585760cdfb5STrevor Thompson 
5864dfcd1d5STrevor Thompson     if (AttrContext->pRecord->IsNonResident)
587ba33b9faSTrevor Thompson     {
5881417f286STrevor Thompson         Status = SetNonResidentAttributeDataLength(Fcb->Vcb,
5891417f286STrevor Thompson                                                    AttrContext,
5901417f286STrevor Thompson                                                    AttrOffset,
5911417f286STrevor Thompson                                                    FileRecord,
5921417f286STrevor Thompson                                                    DataSize);
593760cdfb5STrevor Thompson     }
594760cdfb5STrevor Thompson     else
595760cdfb5STrevor Thompson     {
596760cdfb5STrevor Thompson         // resident attribute
5971417f286STrevor Thompson         Status = SetResidentAttributeDataLength(Fcb->Vcb,
5981417f286STrevor Thompson                                                 AttrContext,
5991417f286STrevor Thompson                                                 AttrOffset,
6001417f286STrevor Thompson                                                 FileRecord,
6011417f286STrevor Thompson                                                 DataSize);
602c25b9747STrevor Thompson     }
603c25b9747STrevor Thompson 
604c25b9747STrevor Thompson     if (!NT_SUCCESS(Status))
605c25b9747STrevor Thompson     {
6061417f286STrevor Thompson         DPRINT1("ERROR: Failed to set size of attribute!\n");
607c25b9747STrevor Thompson         return Status;
608c25b9747STrevor Thompson     }
609ba33b9faSTrevor Thompson 
610ba33b9faSTrevor Thompson     //NtfsDumpFileAttributes(Fcb->Vcb, FileRecord);
611ba33b9faSTrevor Thompson 
612ba33b9faSTrevor Thompson     // write the updated file record back to disk
613760cdfb5STrevor Thompson     Status = UpdateFileRecord(Fcb->Vcb, Fcb->MFTIndex, FileRecord);
614ba33b9faSTrevor Thompson 
615760cdfb5STrevor Thompson     if (NT_SUCCESS(Status))
616ba33b9faSTrevor Thompson     {
6174dfcd1d5STrevor Thompson         if (AttrContext->pRecord->IsNonResident)
6184dfcd1d5STrevor Thompson             Fcb->RFCB.AllocationSize.QuadPart = AttrContext->pRecord->NonResident.AllocatedSize;
6191417f286STrevor Thompson         else
6201417f286STrevor Thompson             Fcb->RFCB.AllocationSize = *DataSize;
621760cdfb5STrevor Thompson         Fcb->RFCB.FileSize = *DataSize;
622760cdfb5STrevor Thompson         Fcb->RFCB.ValidDataLength = *DataSize;
623760cdfb5STrevor Thompson         CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&Fcb->RFCB.AllocationSize);
624ba33b9faSTrevor Thompson     }
625ba33b9faSTrevor Thompson 
626ba33b9faSTrevor Thompson     return STATUS_SUCCESS;
627ba33b9faSTrevor Thompson }
628ba33b9faSTrevor Thompson 
6290409b316STrevor Thompson /**
6300409b316STrevor Thompson * @name SetFileRecordEnd
6310409b316STrevor Thompson * @implemented
6320409b316STrevor Thompson *
6330409b316STrevor Thompson * This small function sets a new endpoint for the file record. It set's the final
6340409b316STrevor Thompson * AttrEnd->Type to AttributeEnd and recalculates the bytes used by the file record.
6350409b316STrevor Thompson *
6360409b316STrevor Thompson * @param FileRecord
6370409b316STrevor Thompson * Pointer to the file record whose endpoint (length) will be set.
6380409b316STrevor Thompson *
6390409b316STrevor Thompson * @param AttrEnd
6400409b316STrevor Thompson * Pointer to section of memory that will receive the AttributeEnd marker. This must point
6410409b316STrevor Thompson * to memory allocated for the FileRecord. Must be aligned to an 8-byte boundary (relative to FileRecord).
6420409b316STrevor Thompson *
6430409b316STrevor Thompson * @param EndMarker
6440409b316STrevor Thompson * This value will be written after AttributeEnd but isn't critical at all. When Windows resizes
6450409b316STrevor Thompson * a file record, it preserves the final ULONG that previously ended the record, even though this
6460409b316STrevor Thompson * value is (to my knowledge) never used. We emulate this behavior.
6470409b316STrevor Thompson *
6480409b316STrevor Thompson */
6490409b316STrevor Thompson VOID
6500409b316STrevor Thompson SetFileRecordEnd(PFILE_RECORD_HEADER FileRecord,
6510409b316STrevor Thompson                  PNTFS_ATTR_RECORD AttrEnd,
6520409b316STrevor Thompson                  ULONG EndMarker)
6530409b316STrevor Thompson {
654d484d91eSTrevor Thompson     // Ensure AttrEnd is aligned on an 8-byte boundary, relative to FileRecord
655d484d91eSTrevor Thompson     ASSERT(((ULONG_PTR)AttrEnd - (ULONG_PTR)FileRecord) % ATTR_RECORD_ALIGNMENT == 0);
656d484d91eSTrevor Thompson 
6570409b316STrevor Thompson     // mark the end of attributes
6580409b316STrevor Thompson     AttrEnd->Type = AttributeEnd;
6590409b316STrevor Thompson 
6600409b316STrevor Thompson     // Restore the "file-record-end marker." The value is never checked but this behavior is consistent with Win2k3.
6610409b316STrevor Thompson     AttrEnd->Length = EndMarker;
6620409b316STrevor Thompson 
6630409b316STrevor Thompson     // recalculate bytes in use
6640409b316STrevor Thompson     FileRecord->BytesInUse = (ULONG_PTR)AttrEnd - (ULONG_PTR)FileRecord + sizeof(ULONG) * 2;
6650409b316STrevor Thompson }
6660409b316STrevor Thompson 
6671417f286STrevor Thompson /**
6681417f286STrevor Thompson * @name SetNonResidentAttributeDataLength
6691417f286STrevor Thompson * @implemented
6701417f286STrevor Thompson *
6711417f286STrevor Thompson * Called by SetAttributeDataLength() to set the size of a non-resident attribute. Doesn't update the file record.
6721417f286STrevor Thompson *
6731417f286STrevor Thompson * @param Vcb
6741417f286STrevor Thompson * Pointer to a DEVICE_EXTENSION describing the target disk.
6751417f286STrevor Thompson *
6761417f286STrevor Thompson * @param AttrContext
6771417f286STrevor Thompson * PNTFS_ATTR_CONTEXT describing the location of the attribute whose size is being set.
6781417f286STrevor Thompson *
6791417f286STrevor Thompson * @param AttrOffset
6801417f286STrevor Thompson * Offset, from the beginning of the record, of the attribute being sized.
6811417f286STrevor Thompson *
6821417f286STrevor Thompson * @param FileRecord
6831417f286STrevor Thompson * Pointer to a file record containing the attribute to be resized. Must be a complete file record,
6841417f286STrevor Thompson * not just the header.
6851417f286STrevor Thompson *
6861417f286STrevor Thompson * @param DataSize
6871417f286STrevor Thompson * Pointer to a LARGE_INTEGER describing the new size of the attribute's data.
6881417f286STrevor Thompson *
6891417f286STrevor Thompson * @return
6901417f286STrevor Thompson * STATUS_SUCCESS on success;
6911417f286STrevor Thompson * STATUS_INSUFFICIENT_RESOURCES if an allocation fails.
6921417f286STrevor Thompson * STATUS_INVALID_PARAMETER if we can't find the last cluster in the data run.
6931417f286STrevor Thompson *
6941417f286STrevor Thompson * @remarks
6959ab86116STrevor Thompson * Called by SetAttributeDataLength() and IncreaseMftSize(). Use SetAttributeDataLength() unless you have a good
6961417f286STrevor Thompson * reason to use this. Doesn't update the file record on disk. Doesn't inform the cache controller of changes with
6971417f286STrevor Thompson * any associated files. Synchronization is the callers responsibility.
6981417f286STrevor Thompson */
6991417f286STrevor Thompson NTSTATUS
7001417f286STrevor Thompson SetNonResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
7011417f286STrevor Thompson                                   PNTFS_ATTR_CONTEXT AttrContext,
7021417f286STrevor Thompson                                   ULONG AttrOffset,
7031417f286STrevor Thompson                                   PFILE_RECORD_HEADER FileRecord,
7041417f286STrevor Thompson                                   PLARGE_INTEGER DataSize)
7051417f286STrevor Thompson {
7061417f286STrevor Thompson     NTSTATUS Status = STATUS_SUCCESS;
7071417f286STrevor Thompson     ULONG BytesPerCluster = Vcb->NtfsInfo.BytesPerCluster;
7081417f286STrevor Thompson     ULONGLONG AllocationSize = ROUND_UP(DataSize->QuadPart, BytesPerCluster);
7091417f286STrevor Thompson     PNTFS_ATTR_RECORD DestinationAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
7104dfcd1d5STrevor Thompson     ULONG ExistingClusters = AttrContext->pRecord->NonResident.AllocatedSize / BytesPerCluster;
7111417f286STrevor Thompson 
7124dfcd1d5STrevor Thompson     ASSERT(AttrContext->pRecord->IsNonResident);
7131417f286STrevor Thompson 
7141417f286STrevor Thompson     // do we need to increase the allocation size?
7154dfcd1d5STrevor Thompson     if (AttrContext->pRecord->NonResident.AllocatedSize < AllocationSize)
7161417f286STrevor Thompson     {
7171417f286STrevor Thompson         ULONG ClustersNeeded = (AllocationSize / BytesPerCluster) - ExistingClusters;
7181417f286STrevor Thompson         LARGE_INTEGER LastClusterInDataRun;
7191417f286STrevor Thompson         ULONG NextAssignedCluster;
7201417f286STrevor Thompson         ULONG AssignedClusters;
7211417f286STrevor Thompson 
7221417f286STrevor Thompson         if (ExistingClusters == 0)
7231417f286STrevor Thompson         {
7241417f286STrevor Thompson             LastClusterInDataRun.QuadPart = 0;
7251417f286STrevor Thompson         }
7261417f286STrevor Thompson         else
7271417f286STrevor Thompson         {
7281417f286STrevor Thompson             if (!FsRtlLookupLargeMcbEntry(&AttrContext->DataRunsMCB,
7294dfcd1d5STrevor Thompson                                           (LONGLONG)AttrContext->pRecord->NonResident.HighestVCN,
7301417f286STrevor Thompson                                           (PLONGLONG)&LastClusterInDataRun.QuadPart,
7311417f286STrevor Thompson                                           NULL,
7321417f286STrevor Thompson                                           NULL,
7331417f286STrevor Thompson                                           NULL,
7341417f286STrevor Thompson                                           NULL))
7351417f286STrevor Thompson             {
7361417f286STrevor Thompson                 DPRINT1("Error looking up final large MCB entry!\n");
7371417f286STrevor Thompson 
7381417f286STrevor Thompson                 // Most likely, HighestVCN went above the largest mapping
7394dfcd1d5STrevor Thompson                 DPRINT1("Highest VCN of record: %I64u\n", AttrContext->pRecord->NonResident.HighestVCN);
7401417f286STrevor Thompson                 return STATUS_INVALID_PARAMETER;
7411417f286STrevor Thompson             }
7421417f286STrevor Thompson         }
7431417f286STrevor Thompson 
7441417f286STrevor Thompson         DPRINT("LastClusterInDataRun: %I64u\n", LastClusterInDataRun.QuadPart);
7454dfcd1d5STrevor Thompson         DPRINT("Highest VCN of record: %I64u\n", AttrContext->pRecord->NonResident.HighestVCN);
7461417f286STrevor Thompson 
7471417f286STrevor Thompson         while (ClustersNeeded > 0)
7481417f286STrevor Thompson         {
7491417f286STrevor Thompson             Status = NtfsAllocateClusters(Vcb,
7501417f286STrevor Thompson                                           LastClusterInDataRun.LowPart + 1,
7511417f286STrevor Thompson                                           ClustersNeeded,
7521417f286STrevor Thompson                                           &NextAssignedCluster,
7531417f286STrevor Thompson                                           &AssignedClusters);
7541417f286STrevor Thompson 
7551417f286STrevor Thompson             if (!NT_SUCCESS(Status))
7561417f286STrevor Thompson             {
7571417f286STrevor Thompson                 DPRINT1("Error: Unable to allocate requested clusters!\n");
7581417f286STrevor Thompson                 return Status;
7591417f286STrevor Thompson             }
7601417f286STrevor Thompson 
7611417f286STrevor Thompson             // now we need to add the clusters we allocated to the data run
7621417f286STrevor Thompson             Status = AddRun(Vcb, AttrContext, AttrOffset, FileRecord, NextAssignedCluster, AssignedClusters);
7631417f286STrevor Thompson             if (!NT_SUCCESS(Status))
7641417f286STrevor Thompson             {
7651417f286STrevor Thompson                 DPRINT1("Error: Unable to add data run!\n");
7661417f286STrevor Thompson                 return Status;
7671417f286STrevor Thompson             }
7681417f286STrevor Thompson 
7691417f286STrevor Thompson             ClustersNeeded -= AssignedClusters;
7701417f286STrevor Thompson             LastClusterInDataRun.LowPart = NextAssignedCluster + AssignedClusters - 1;
7711417f286STrevor Thompson         }
7721417f286STrevor Thompson     }
7734dfcd1d5STrevor Thompson     else if (AttrContext->pRecord->NonResident.AllocatedSize > AllocationSize)
7741417f286STrevor Thompson     {
7751417f286STrevor Thompson         // shrink allocation size
7761417f286STrevor Thompson         ULONG ClustersToFree = ExistingClusters - (AllocationSize / BytesPerCluster);
7771417f286STrevor Thompson         Status = FreeClusters(Vcb, AttrContext, AttrOffset, FileRecord, ClustersToFree);
7781417f286STrevor Thompson     }
7791417f286STrevor Thompson 
7801417f286STrevor Thompson     // TODO: is the file compressed, encrypted, or sparse?
7811417f286STrevor Thompson 
7824dfcd1d5STrevor Thompson     AttrContext->pRecord->NonResident.AllocatedSize = AllocationSize;
7834dfcd1d5STrevor Thompson     AttrContext->pRecord->NonResident.DataSize = DataSize->QuadPart;
7844dfcd1d5STrevor Thompson     AttrContext->pRecord->NonResident.InitializedSize = DataSize->QuadPart;
7851417f286STrevor Thompson 
7861417f286STrevor Thompson     DestinationAttribute->NonResident.AllocatedSize = AllocationSize;
7871417f286STrevor Thompson     DestinationAttribute->NonResident.DataSize = DataSize->QuadPart;
7881417f286STrevor Thompson     DestinationAttribute->NonResident.InitializedSize = DataSize->QuadPart;
7891417f286STrevor Thompson 
790*52c30fdfSTrevor Thompson     // HighestVCN seems to be set incorrectly somewhere. Apply a hack-fix to reset it.
791*52c30fdfSTrevor Thompson     // HACKHACK FIXME: Fix for sparse files; this math won't work in that case.
792*52c30fdfSTrevor Thompson     AttrContext->pRecord->NonResident.HighestVCN = ((ULONGLONG)AllocationSize / Vcb->NtfsInfo.BytesPerCluster) - 1;
793*52c30fdfSTrevor Thompson     DestinationAttribute->NonResident.HighestVCN = AttrContext->pRecord->NonResident.HighestVCN;
794*52c30fdfSTrevor Thompson 
7951417f286STrevor Thompson     DPRINT("Allocated Size: %I64u\n", DestinationAttribute->NonResident.AllocatedSize);
7961417f286STrevor Thompson 
7971417f286STrevor Thompson     return Status;
7981417f286STrevor Thompson }
7991417f286STrevor Thompson 
8001417f286STrevor Thompson /**
8011417f286STrevor Thompson * @name SetResidentAttributeDataLength
8021417f286STrevor Thompson * @implemented
8031417f286STrevor Thompson *
8041417f286STrevor Thompson * Called by SetAttributeDataLength() to set the size of a non-resident attribute. Doesn't update the file record.
8051417f286STrevor Thompson *
8061417f286STrevor Thompson * @param Vcb
8071417f286STrevor Thompson * Pointer to a DEVICE_EXTENSION describing the target disk.
8081417f286STrevor Thompson *
8091417f286STrevor Thompson * @param AttrContext
8101417f286STrevor Thompson * PNTFS_ATTR_CONTEXT describing the location of the attribute whose size is being set.
8111417f286STrevor Thompson *
8121417f286STrevor Thompson * @param AttrOffset
8131417f286STrevor Thompson * Offset, from the beginning of the record, of the attribute being sized.
8141417f286STrevor Thompson *
8151417f286STrevor Thompson * @param FileRecord
8161417f286STrevor Thompson * Pointer to a file record containing the attribute to be resized. Must be a complete file record,
8171417f286STrevor Thompson * not just the header.
8181417f286STrevor Thompson *
8191417f286STrevor Thompson * @param DataSize
8201417f286STrevor Thompson * Pointer to a LARGE_INTEGER describing the new size of the attribute's data.
8211417f286STrevor Thompson *
8221417f286STrevor Thompson * @return
8231417f286STrevor Thompson * STATUS_SUCCESS on success;
8241417f286STrevor Thompson * STATUS_INSUFFICIENT_RESOURCES if an allocation fails.
8251417f286STrevor Thompson * STATUS_INVALID_PARAMETER if AttrContext describes a non-resident attribute.
8261417f286STrevor Thompson * STATUS_NOT_IMPLEMENTED if requested to decrease the size of an attribute that isn't the
8271417f286STrevor Thompson * last attribute listed in the file record.
8281417f286STrevor Thompson *
8291417f286STrevor Thompson * @remarks
8309ab86116STrevor Thompson * Called by SetAttributeDataLength() and IncreaseMftSize(). Use SetAttributeDataLength() unless you have a good
8311417f286STrevor Thompson * reason to use this. Doesn't update the file record on disk. Doesn't inform the cache controller of changes with
8321417f286STrevor Thompson * any associated files. Synchronization is the callers responsibility.
8331417f286STrevor Thompson */
8341417f286STrevor Thompson NTSTATUS
8351417f286STrevor Thompson SetResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
8361417f286STrevor Thompson                                PNTFS_ATTR_CONTEXT AttrContext,
8371417f286STrevor Thompson                                ULONG AttrOffset,
8381417f286STrevor Thompson                                PFILE_RECORD_HEADER FileRecord,
8391417f286STrevor Thompson                                PLARGE_INTEGER DataSize)
8401417f286STrevor Thompson {
8411417f286STrevor Thompson     NTSTATUS Status;
8421417f286STrevor Thompson 
8431417f286STrevor Thompson     // find the next attribute
8444dfcd1d5STrevor Thompson     ULONG NextAttributeOffset = AttrOffset + AttrContext->pRecord->Length;
8451417f286STrevor Thompson     PNTFS_ATTR_RECORD NextAttribute = (PNTFS_ATTR_RECORD)((PCHAR)FileRecord + NextAttributeOffset);
8461417f286STrevor Thompson 
8474dfcd1d5STrevor Thompson     ASSERT(!AttrContext->pRecord->IsNonResident);
8481417f286STrevor Thompson 
8491417f286STrevor Thompson     //NtfsDumpFileAttributes(Vcb, FileRecord);
8501417f286STrevor Thompson 
8511417f286STrevor Thompson     // Do we need to increase the data length?
8524dfcd1d5STrevor Thompson     if (DataSize->QuadPart > AttrContext->pRecord->Resident.ValueLength)
8531417f286STrevor Thompson     {
8541417f286STrevor Thompson         // There's usually padding at the end of a record. Do we need to extend past it?
8554dfcd1d5STrevor Thompson         ULONG MaxValueLength = AttrContext->pRecord->Length - AttrContext->pRecord->Resident.ValueOffset;
8561417f286STrevor Thompson         if (MaxValueLength < DataSize->LowPart)
8571417f286STrevor Thompson         {
8581417f286STrevor Thompson             // If this is the last attribute, we could move the end marker to the very end of the file record
8591417f286STrevor Thompson             MaxValueLength += Vcb->NtfsInfo.BytesPerFileRecord - NextAttributeOffset - (sizeof(ULONG) * 2);
8601417f286STrevor Thompson 
8611417f286STrevor Thompson             if (MaxValueLength < DataSize->LowPart || NextAttribute->Type != AttributeEnd)
8621417f286STrevor Thompson             {
8631417f286STrevor Thompson                 // convert attribute to non-resident
8641417f286STrevor Thompson                 PNTFS_ATTR_RECORD Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
8654dfcd1d5STrevor Thompson                 PNTFS_ATTR_RECORD NewRecord;
8661417f286STrevor Thompson                 LARGE_INTEGER AttribDataSize;
8671417f286STrevor Thompson                 PVOID AttribData;
8684dfcd1d5STrevor Thompson                 ULONG NewRecordLength;
8691417f286STrevor Thompson                 ULONG EndAttributeOffset;
8701417f286STrevor Thompson                 ULONG LengthWritten;
8711417f286STrevor Thompson 
8721417f286STrevor Thompson                 DPRINT1("Converting attribute to non-resident.\n");
8731417f286STrevor Thompson 
8744dfcd1d5STrevor Thompson                 AttribDataSize.QuadPart = AttrContext->pRecord->Resident.ValueLength;
8751417f286STrevor Thompson 
8761417f286STrevor Thompson                 // Is there existing data we need to back-up?
8771417f286STrevor Thompson                 if (AttribDataSize.QuadPart > 0)
8781417f286STrevor Thompson                 {
8791417f286STrevor Thompson                     AttribData = ExAllocatePoolWithTag(NonPagedPool, AttribDataSize.QuadPart, TAG_NTFS);
8801417f286STrevor Thompson                     if (AttribData == NULL)
8811417f286STrevor Thompson                     {
8821417f286STrevor Thompson                         DPRINT1("ERROR: Couldn't allocate memory for attribute data. Can't migrate to non-resident!\n");
8831417f286STrevor Thompson                         return STATUS_INSUFFICIENT_RESOURCES;
8841417f286STrevor Thompson                     }
8851417f286STrevor Thompson 
8861417f286STrevor Thompson                     // read data to temp buffer
8871417f286STrevor Thompson                     Status = ReadAttribute(Vcb, AttrContext, 0, AttribData, AttribDataSize.QuadPart);
8881417f286STrevor Thompson                     if (!NT_SUCCESS(Status))
8891417f286STrevor Thompson                     {
8901417f286STrevor Thompson                         DPRINT1("ERROR: Unable to read attribute before migrating!\n");
8911417f286STrevor Thompson                         ExFreePoolWithTag(AttribData, TAG_NTFS);
8921417f286STrevor Thompson                         return Status;
8931417f286STrevor Thompson                     }
8941417f286STrevor Thompson                 }
8951417f286STrevor Thompson 
8961417f286STrevor Thompson                 // Start by turning this attribute into a 0-length, non-resident attribute, then enlarge it.
8971417f286STrevor Thompson 
8984dfcd1d5STrevor 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
8994dfcd1d5STrevor Thompson                 NewRecordLength = ALIGN_UP_BY(0x41 + (AttrContext->pRecord->NameLength * sizeof(WCHAR)), ATTR_RECORD_ALIGNMENT);
9001417f286STrevor Thompson 
9014dfcd1d5STrevor Thompson                 // Create a new attribute record that will store the 0-length, non-resident attribute
9024dfcd1d5STrevor Thompson                 NewRecord = ExAllocatePoolWithTag(NonPagedPool, NewRecordLength, TAG_NTFS);
9034dfcd1d5STrevor Thompson 
9044dfcd1d5STrevor Thompson                 // Zero out the NonResident structure
9054dfcd1d5STrevor Thompson                 RtlZeroMemory(NewRecord, NewRecordLength);
9064dfcd1d5STrevor Thompson 
9074dfcd1d5STrevor Thompson                 // Copy the data that's common to both non-resident and resident attributes
9084dfcd1d5STrevor Thompson                 RtlCopyMemory(NewRecord, AttrContext->pRecord, FIELD_OFFSET(NTFS_ATTR_RECORD, Resident.ValueLength));
9094dfcd1d5STrevor Thompson 
9104dfcd1d5STrevor Thompson                 // if there's a name
9114dfcd1d5STrevor Thompson                 if (AttrContext->pRecord->NameLength != 0)
9124dfcd1d5STrevor Thompson                 {
9134dfcd1d5STrevor Thompson                     // copy the name
9144dfcd1d5STrevor Thompson                     // An attribute name will be located at offset 0x18 for a resident attribute, 0x40 for non-resident
9154dfcd1d5STrevor Thompson                     RtlCopyMemory((PCHAR)((ULONG_PTR)NewRecord + 0x40),
9164dfcd1d5STrevor Thompson                                   (PCHAR)((ULONG_PTR)AttrContext->pRecord + 0x18),
9174dfcd1d5STrevor Thompson                                   AttrContext->pRecord->NameLength * sizeof(WCHAR));
9184dfcd1d5STrevor Thompson                 }
9194dfcd1d5STrevor Thompson 
9204dfcd1d5STrevor Thompson                 // update the mapping pairs offset, which will be 0x40 (size of a non-resident header) + length in bytes of the name
9214dfcd1d5STrevor Thompson                 NewRecord->NonResident.MappingPairsOffset = 0x40 + (AttrContext->pRecord->NameLength * sizeof(WCHAR));
9221417f286STrevor Thompson 
9231417f286STrevor Thompson                 // update the end of the file record
9241417f286STrevor Thompson                 // calculate position of end markers (1 byte for empty data run)
9254dfcd1d5STrevor Thompson                 EndAttributeOffset = AttrOffset + NewRecord->NonResident.MappingPairsOffset + 1;
9265ab24a5aSTrevor Thompson                 EndAttributeOffset = ALIGN_UP_BY(EndAttributeOffset, ATTR_RECORD_ALIGNMENT);
9271417f286STrevor Thompson 
9281417f286STrevor Thompson                 // Update the length
9294dfcd1d5STrevor Thompson                 NewRecord->Length = EndAttributeOffset - AttrOffset;
9304dfcd1d5STrevor Thompson 
9314dfcd1d5STrevor Thompson                 ASSERT(NewRecord->Length == NewRecordLength);
9324dfcd1d5STrevor Thompson 
9334dfcd1d5STrevor Thompson                 // Copy the new attribute record into the file record
9344dfcd1d5STrevor Thompson                 RtlCopyMemory(Destination, NewRecord, NewRecord->Length);
9351417f286STrevor Thompson 
9361417f286STrevor Thompson                 // Update the file record end
9371417f286STrevor Thompson                 SetFileRecordEnd(FileRecord,
9381417f286STrevor Thompson                                  (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + EndAttributeOffset),
9391417f286STrevor Thompson                                  FILE_RECORD_END);
9401417f286STrevor Thompson 
94168a48b27STrevor Thompson                 // Initialize the MCB, potentially catch an exception
94268a48b27STrevor Thompson                 _SEH2_TRY
94368a48b27STrevor Thompson                 {
94468a48b27STrevor Thompson                     FsRtlInitializeLargeMcb(&AttrContext->DataRunsMCB, NonPagedPool);
94568a48b27STrevor Thompson                 }
94668a48b27STrevor Thompson                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
94768a48b27STrevor Thompson                 {
94868a48b27STrevor Thompson                     DPRINT1("Unable to create LargeMcb!\n");
949935fcd1bSTrevor Thompson                     if (AttribDataSize.QuadPart > 0)
95068a48b27STrevor Thompson                         ExFreePoolWithTag(AttribData, TAG_NTFS);
95168a48b27STrevor Thompson                     _SEH2_YIELD(return _SEH2_GetExceptionCode());
95268a48b27STrevor Thompson                 } _SEH2_END;
95368a48b27STrevor Thompson 
95468a48b27STrevor Thompson                 // Mark the attribute as non-resident (we wait until after we know the LargeMcb was initialized)
9554dfcd1d5STrevor Thompson                 NewRecord->IsNonResident = Destination->IsNonResident = 1;
95668a48b27STrevor Thompson 
95768a48b27STrevor Thompson                 // Update file record on disk
9581417f286STrevor Thompson                 Status = UpdateFileRecord(Vcb, AttrContext->FileMFTIndex, FileRecord);
9591417f286STrevor Thompson                 if (!NT_SUCCESS(Status))
9601417f286STrevor Thompson                 {
9611417f286STrevor Thompson                     DPRINT1("ERROR: Couldn't update file record to continue migration!\n");
9621417f286STrevor Thompson                     if (AttribDataSize.QuadPart > 0)
9631417f286STrevor Thompson                         ExFreePoolWithTag(AttribData, TAG_NTFS);
9641417f286STrevor Thompson                     return Status;
9651417f286STrevor Thompson                 }
9661417f286STrevor Thompson 
9674dfcd1d5STrevor Thompson                 // Now we need to free the old copy of the attribute record in the context and replace it with the new one
9684dfcd1d5STrevor Thompson                 ExFreePoolWithTag(AttrContext->pRecord, TAG_NTFS);
9694dfcd1d5STrevor Thompson                 AttrContext->pRecord = NewRecord;
9704dfcd1d5STrevor Thompson 
9711417f286STrevor Thompson                 // Now we can treat the attribute as non-resident and enlarge it normally
9721417f286STrevor Thompson                 Status = SetNonResidentAttributeDataLength(Vcb, AttrContext, AttrOffset, FileRecord, DataSize);
9731417f286STrevor Thompson                 if (!NT_SUCCESS(Status))
9741417f286STrevor Thompson                 {
9751417f286STrevor Thompson                     DPRINT1("ERROR: Unable to migrate resident attribute!\n");
9761417f286STrevor Thompson                     if (AttribDataSize.QuadPart > 0)
9771417f286STrevor Thompson                         ExFreePoolWithTag(AttribData, TAG_NTFS);
9781417f286STrevor Thompson                     return Status;
9791417f286STrevor Thompson                 }
9801417f286STrevor Thompson 
9811417f286STrevor Thompson                 // restore the back-up attribute, if we made one
9821417f286STrevor Thompson                 if (AttribDataSize.QuadPart > 0)
9831417f286STrevor Thompson                 {
984d484d91eSTrevor Thompson                     Status = WriteAttribute(Vcb, AttrContext, 0, AttribData, AttribDataSize.QuadPart, &LengthWritten, FileRecord);
9851417f286STrevor Thompson                     if (!NT_SUCCESS(Status))
9861417f286STrevor Thompson                     {
9871417f286STrevor Thompson                         DPRINT1("ERROR: Unable to write attribute data to non-resident clusters during migration!\n");
9881417f286STrevor Thompson                         // TODO: Reverse migration so no data is lost
9891417f286STrevor Thompson                         ExFreePoolWithTag(AttribData, TAG_NTFS);
9901417f286STrevor Thompson                         return Status;
9911417f286STrevor Thompson                     }
9921417f286STrevor Thompson 
9931417f286STrevor Thompson                     ExFreePoolWithTag(AttribData, TAG_NTFS);
9941417f286STrevor Thompson                 }
9951417f286STrevor Thompson             }
9961417f286STrevor Thompson         }
9971417f286STrevor Thompson     }
9981417f286STrevor Thompson 
9991417f286STrevor Thompson     // set the new length of the resident attribute (if we didn't migrate it)
10004dfcd1d5STrevor Thompson     if (!AttrContext->pRecord->IsNonResident)
1001d484d91eSTrevor Thompson         return InternalSetResidentAttributeLength(Vcb, AttrContext, FileRecord, AttrOffset, DataSize->LowPart);
10021417f286STrevor Thompson 
10031417f286STrevor Thompson     return STATUS_SUCCESS;
10041417f286STrevor Thompson }
10051417f286STrevor Thompson 
1006c2c66affSColin Finck ULONG
1007c2c66affSColin Finck ReadAttribute(PDEVICE_EXTENSION Vcb,
1008c2c66affSColin Finck               PNTFS_ATTR_CONTEXT Context,
1009c2c66affSColin Finck               ULONGLONG Offset,
1010c2c66affSColin Finck               PCHAR Buffer,
1011c2c66affSColin Finck               ULONG Length)
1012c2c66affSColin Finck {
1013c2c66affSColin Finck     ULONGLONG LastLCN;
1014c2c66affSColin Finck     PUCHAR DataRun;
1015c2c66affSColin Finck     LONGLONG DataRunOffset;
1016c2c66affSColin Finck     ULONGLONG DataRunLength;
1017c2c66affSColin Finck     LONGLONG DataRunStartLCN;
1018c2c66affSColin Finck     ULONGLONG CurrentOffset;
1019c2c66affSColin Finck     ULONG ReadLength;
1020c2c66affSColin Finck     ULONG AlreadyRead;
1021c2c66affSColin Finck     NTSTATUS Status;
1022c2c66affSColin Finck 
102352b9f467STrevor Thompson     //TEMPTEMP
102452b9f467STrevor Thompson     PUCHAR TempBuffer;
102552b9f467STrevor Thompson 
10264dfcd1d5STrevor Thompson     if (!Context->pRecord->IsNonResident)
1027c2c66affSColin Finck     {
10284dfcd1d5STrevor Thompson         // We need to truncate Offset to a ULONG for pointer arithmetic
10294dfcd1d5STrevor Thompson         // The check below should ensure that Offset is well within the range of 32 bits
10304dfcd1d5STrevor Thompson         ULONG LittleOffset = (ULONG)Offset;
10314dfcd1d5STrevor Thompson 
10324dfcd1d5STrevor Thompson         // Ensure that offset isn't beyond the end of the attribute
10334dfcd1d5STrevor Thompson         if (Offset > Context->pRecord->Resident.ValueLength)
1034c2c66affSColin Finck             return 0;
10354dfcd1d5STrevor Thompson         if (Offset + Length > Context->pRecord->Resident.ValueLength)
10364dfcd1d5STrevor Thompson             Length = (ULONG)(Context->pRecord->Resident.ValueLength - Offset);
10374dfcd1d5STrevor Thompson 
10384dfcd1d5STrevor Thompson         RtlCopyMemory(Buffer, (PVOID)((ULONG_PTR)Context->pRecord + Context->pRecord->Resident.ValueOffset + LittleOffset), Length);
1039c2c66affSColin Finck         return Length;
1040c2c66affSColin Finck     }
1041c2c66affSColin Finck 
1042c2c66affSColin Finck     /*
1043c2c66affSColin Finck      * Non-resident attribute
1044c2c66affSColin Finck      */
1045c2c66affSColin Finck 
1046c2c66affSColin Finck     /*
1047c2c66affSColin Finck      * I. Find the corresponding start data run.
1048c2c66affSColin Finck      */
1049c2c66affSColin Finck 
1050c2c66affSColin Finck     AlreadyRead = 0;
1051c2c66affSColin Finck 
1052c2c66affSColin Finck     // FIXME: Cache seems to be non-working. Disable it for now
1053c2c66affSColin Finck     //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
1054c2c66affSColin Finck     if (0)
1055c2c66affSColin Finck     {
1056c2c66affSColin Finck         DataRun = Context->CacheRun;
1057c2c66affSColin Finck         LastLCN = Context->CacheRunLastLCN;
1058c2c66affSColin Finck         DataRunStartLCN = Context->CacheRunStartLCN;
1059c2c66affSColin Finck         DataRunLength = Context->CacheRunLength;
1060c2c66affSColin Finck         CurrentOffset = Context->CacheRunCurrentOffset;
1061c2c66affSColin Finck     }
1062c2c66affSColin Finck     else
1063c2c66affSColin Finck     {
106452b9f467STrevor Thompson         //TEMPTEMP
106552b9f467STrevor Thompson         ULONG UsedBufferSize;
106652b9f467STrevor Thompson         TempBuffer = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
106752b9f467STrevor Thompson 
1068c2c66affSColin Finck         LastLCN = 0;
1069c2c66affSColin Finck         CurrentOffset = 0;
1070c2c66affSColin Finck 
107152b9f467STrevor Thompson         // This will be rewritten in the next iteration to just use the DataRuns MCB directly
107252b9f467STrevor Thompson         ConvertLargeMCBToDataRuns(&Context->DataRunsMCB,
107352b9f467STrevor Thompson                                   TempBuffer,
107452b9f467STrevor Thompson                                   Vcb->NtfsInfo.BytesPerFileRecord,
107552b9f467STrevor Thompson                                   &UsedBufferSize);
107652b9f467STrevor Thompson 
107752b9f467STrevor Thompson         DataRun = TempBuffer;
107852b9f467STrevor Thompson 
1079c2c66affSColin Finck         while (1)
1080c2c66affSColin Finck         {
1081c2c66affSColin Finck             DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
1082c2c66affSColin Finck             if (DataRunOffset != -1)
1083c2c66affSColin Finck             {
1084c2c66affSColin Finck                 /* Normal data run. */
1085c2c66affSColin Finck                 DataRunStartLCN = LastLCN + DataRunOffset;
1086c2c66affSColin Finck                 LastLCN = DataRunStartLCN;
1087c2c66affSColin Finck             }
1088c2c66affSColin Finck             else
1089c2c66affSColin Finck             {
1090c2c66affSColin Finck                 /* Sparse data run. */
1091c2c66affSColin Finck                 DataRunStartLCN = -1;
1092c2c66affSColin Finck             }
1093c2c66affSColin Finck 
1094c2c66affSColin Finck             if (Offset >= CurrentOffset &&
1095c2c66affSColin Finck                 Offset < CurrentOffset + (DataRunLength * Vcb->NtfsInfo.BytesPerCluster))
1096c2c66affSColin Finck             {
1097c2c66affSColin Finck                 break;
1098c2c66affSColin Finck             }
1099c2c66affSColin Finck 
1100c2c66affSColin Finck             if (*DataRun == 0)
1101c2c66affSColin Finck             {
1102c2c66affSColin Finck                 return AlreadyRead;
1103c2c66affSColin Finck             }
1104c2c66affSColin Finck 
1105c2c66affSColin Finck             CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
1106c2c66affSColin Finck         }
1107c2c66affSColin Finck     }
1108c2c66affSColin Finck 
1109c2c66affSColin Finck     /*
1110c2c66affSColin Finck      * II. Go through the run list and read the data
1111c2c66affSColin Finck      */
1112c2c66affSColin Finck 
1113c2c66affSColin Finck     ReadLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset), Length);
1114c2c66affSColin Finck     if (DataRunStartLCN == -1)
1115c2c66affSColin Finck     {
1116c2c66affSColin Finck         RtlZeroMemory(Buffer, ReadLength);
1117c2c66affSColin Finck         Status = STATUS_SUCCESS;
1118c2c66affSColin Finck     }
1119c2c66affSColin Finck     else
1120c2c66affSColin Finck     {
1121c2c66affSColin Finck         Status = NtfsReadDisk(Vcb->StorageDevice,
1122c2c66affSColin Finck                               DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster + Offset - CurrentOffset,
1123c2c66affSColin Finck                               ReadLength,
1124c2c66affSColin Finck                               Vcb->NtfsInfo.BytesPerSector,
1125c2c66affSColin Finck                               (PVOID)Buffer,
1126c2c66affSColin Finck                               FALSE);
1127c2c66affSColin Finck     }
1128c2c66affSColin Finck     if (NT_SUCCESS(Status))
1129c2c66affSColin Finck     {
1130c2c66affSColin Finck         Length -= ReadLength;
1131c2c66affSColin Finck         Buffer += ReadLength;
1132c2c66affSColin Finck         AlreadyRead += ReadLength;
1133c2c66affSColin Finck 
1134c2c66affSColin Finck         if (ReadLength == DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset))
1135c2c66affSColin Finck         {
1136c2c66affSColin Finck             CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
1137c2c66affSColin Finck             DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
1138c2c66affSColin Finck             if (DataRunOffset != (ULONGLONG)-1)
1139c2c66affSColin Finck             {
1140c2c66affSColin Finck                 DataRunStartLCN = LastLCN + DataRunOffset;
1141c2c66affSColin Finck                 LastLCN = DataRunStartLCN;
1142c2c66affSColin Finck             }
1143c2c66affSColin Finck             else
1144c2c66affSColin Finck                 DataRunStartLCN = -1;
1145c2c66affSColin Finck         }
1146c2c66affSColin Finck 
1147c2c66affSColin Finck         while (Length > 0)
1148c2c66affSColin Finck         {
1149c2c66affSColin Finck             ReadLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster, Length);
1150c2c66affSColin Finck             if (DataRunStartLCN == -1)
1151c2c66affSColin Finck                 RtlZeroMemory(Buffer, ReadLength);
1152c2c66affSColin Finck             else
1153c2c66affSColin Finck             {
1154c2c66affSColin Finck                 Status = NtfsReadDisk(Vcb->StorageDevice,
1155c2c66affSColin Finck                                       DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster,
1156c2c66affSColin Finck                                       ReadLength,
1157c2c66affSColin Finck                                       Vcb->NtfsInfo.BytesPerSector,
1158c2c66affSColin Finck                                       (PVOID)Buffer,
1159c2c66affSColin Finck                                       FALSE);
1160c2c66affSColin Finck                 if (!NT_SUCCESS(Status))
1161c2c66affSColin Finck                     break;
1162c2c66affSColin Finck             }
1163c2c66affSColin Finck 
1164c2c66affSColin Finck             Length -= ReadLength;
1165c2c66affSColin Finck             Buffer += ReadLength;
1166c2c66affSColin Finck             AlreadyRead += ReadLength;
1167c2c66affSColin Finck 
1168c2c66affSColin Finck             /* We finished this request, but there still data in this data run. */
1169c2c66affSColin Finck             if (Length == 0 && ReadLength != DataRunLength * Vcb->NtfsInfo.BytesPerCluster)
1170c2c66affSColin Finck                 break;
1171c2c66affSColin Finck 
1172c2c66affSColin Finck             /*
1173c2c66affSColin Finck              * Go to next run in the list.
1174c2c66affSColin Finck              */
1175c2c66affSColin Finck 
1176c2c66affSColin Finck             if (*DataRun == 0)
1177c2c66affSColin Finck                 break;
1178c2c66affSColin Finck             CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
1179c2c66affSColin Finck             DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
1180c2c66affSColin Finck             if (DataRunOffset != -1)
1181c2c66affSColin Finck             {
1182c2c66affSColin Finck                 /* Normal data run. */
1183c2c66affSColin Finck                 DataRunStartLCN = LastLCN + DataRunOffset;
1184c2c66affSColin Finck                 LastLCN = DataRunStartLCN;
1185c2c66affSColin Finck             }
1186c2c66affSColin Finck             else
1187c2c66affSColin Finck             {
1188c2c66affSColin Finck                 /* Sparse data run. */
1189c2c66affSColin Finck                 DataRunStartLCN = -1;
1190c2c66affSColin Finck             }
1191c2c66affSColin Finck         } /* while */
1192c2c66affSColin Finck 
1193c2c66affSColin Finck     } /* if Disk */
1194c2c66affSColin Finck 
119552b9f467STrevor Thompson     // TEMPTEMP
11964dfcd1d5STrevor Thompson     if (Context->pRecord->IsNonResident)
119752b9f467STrevor Thompson         ExFreePoolWithTag(TempBuffer, TAG_NTFS);
119852b9f467STrevor Thompson 
1199c2c66affSColin Finck     Context->CacheRun = DataRun;
1200c2c66affSColin Finck     Context->CacheRunOffset = Offset + AlreadyRead;
1201c2c66affSColin Finck     Context->CacheRunStartLCN = DataRunStartLCN;
1202c2c66affSColin Finck     Context->CacheRunLength = DataRunLength;
1203c2c66affSColin Finck     Context->CacheRunLastLCN = LastLCN;
1204c2c66affSColin Finck     Context->CacheRunCurrentOffset = CurrentOffset;
1205c2c66affSColin Finck 
1206c2c66affSColin Finck     return AlreadyRead;
1207c2c66affSColin Finck }
1208c2c66affSColin Finck 
1209c2c66affSColin Finck 
121058a13831STrevor Thompson /**
121158a13831STrevor Thompson * @name WriteAttribute
121258a13831STrevor Thompson * @implemented
121358a13831STrevor Thompson *
121458a13831STrevor Thompson * Writes an NTFS attribute to the disk. It presently borrows a lot of code from ReadAttribute(),
121558a13831STrevor Thompson * and it still needs more documentation / cleaning up.
121658a13831STrevor Thompson *
121758a13831STrevor Thompson * @param Vcb
121858a13831STrevor Thompson * Volume Control Block indicating which volume to write the attribute to
121958a13831STrevor Thompson *
122058a13831STrevor Thompson * @param Context
122158a13831STrevor Thompson * Pointer to an NTFS_ATTR_CONTEXT that has information about the attribute
122258a13831STrevor Thompson *
122358a13831STrevor Thompson * @param Offset
122458a13831STrevor Thompson * Offset, in bytes, from the beginning of the attribute indicating where to start
122558a13831STrevor Thompson * writing data
122658a13831STrevor Thompson *
122758a13831STrevor Thompson * @param Buffer
122858a13831STrevor Thompson * The data that's being written to the device
122958a13831STrevor Thompson *
123058a13831STrevor Thompson * @param Length
123158a13831STrevor Thompson * How much data will be written, in bytes
123258a13831STrevor Thompson *
123358a13831STrevor Thompson * @param RealLengthWritten
123458a13831STrevor Thompson * Pointer to a ULONG which will receive how much data was written, in bytes
123558a13831STrevor Thompson *
1236d484d91eSTrevor Thompson * @param FileRecord
1237d484d91eSTrevor Thompson * Optional pointer to a FILE_RECORD_HEADER that contains a copy of the file record
1238d484d91eSTrevor Thompson * being written to. Can be NULL, in which case the file record will be read from disk.
1239d484d91eSTrevor Thompson * If not-null, WriteAttribute() will skip reading from disk, and FileRecord
1240d484d91eSTrevor Thompson * will be updated with the newly-written attribute before the function returns.
1241d484d91eSTrevor Thompson *
124258a13831STrevor Thompson * @return
124358a13831STrevor Thompson * STATUS_SUCCESS if successful, an error code otherwise. STATUS_NOT_IMPLEMENTED if
124458a13831STrevor Thompson * writing to a sparse file.
124558a13831STrevor Thompson *
124658a13831STrevor Thompson * @remarks Note that in this context the word "attribute" isn't referring read-only, hidden,
124758a13831STrevor Thompson * etc. - the file's data is actually stored in an attribute in NTFS parlance.
124858a13831STrevor Thompson *
124958a13831STrevor Thompson */
125058a13831STrevor Thompson 
125158a13831STrevor Thompson NTSTATUS
125258a13831STrevor Thompson WriteAttribute(PDEVICE_EXTENSION Vcb,
125358a13831STrevor Thompson                PNTFS_ATTR_CONTEXT Context,
125458a13831STrevor Thompson                ULONGLONG Offset,
125558a13831STrevor Thompson                const PUCHAR Buffer,
125658a13831STrevor Thompson                ULONG Length,
1257d484d91eSTrevor Thompson                PULONG RealLengthWritten,
1258d484d91eSTrevor Thompson                PFILE_RECORD_HEADER FileRecord)
125958a13831STrevor Thompson {
126058a13831STrevor Thompson     ULONGLONG LastLCN;
126158a13831STrevor Thompson     PUCHAR DataRun;
126258a13831STrevor Thompson     LONGLONG DataRunOffset;
126358a13831STrevor Thompson     ULONGLONG DataRunLength;
126458a13831STrevor Thompson     LONGLONG DataRunStartLCN;
126558a13831STrevor Thompson     ULONGLONG CurrentOffset;
126658a13831STrevor Thompson     ULONG WriteLength;
126758a13831STrevor Thompson     NTSTATUS Status;
126858a13831STrevor Thompson     PUCHAR SourceBuffer = Buffer;
126958a13831STrevor Thompson     LONGLONG StartingOffset;
1270d484d91eSTrevor Thompson     BOOLEAN FileRecordAllocated = FALSE;
127158a13831STrevor Thompson 
127252b9f467STrevor Thompson     //TEMPTEMP
127352b9f467STrevor Thompson     PUCHAR TempBuffer;
127452b9f467STrevor Thompson 
127552b9f467STrevor Thompson 
1276d484d91eSTrevor Thompson     DPRINT("WriteAttribute(%p, %p, %I64u, %p, %lu, %p, %p)\n", Vcb, Context, Offset, Buffer, Length, RealLengthWritten, FileRecord);
127758a13831STrevor Thompson 
1278760cdfb5STrevor Thompson     *RealLengthWritten = 0;
1279760cdfb5STrevor Thompson 
128058a13831STrevor Thompson     // is this a resident attribute?
12814dfcd1d5STrevor Thompson     if (!Context->pRecord->IsNonResident)
128258a13831STrevor Thompson     {
1283760cdfb5STrevor Thompson         ULONG AttributeOffset;
1284760cdfb5STrevor Thompson         PNTFS_ATTR_CONTEXT FoundContext;
1285d484d91eSTrevor Thompson         PNTFS_ATTR_RECORD Destination;
1286d484d91eSTrevor Thompson 
1287d484d91eSTrevor Thompson         // Ensure requested data is within the bounds of the attribute
1288d484d91eSTrevor Thompson         ASSERT(Offset + Length <= Context->pRecord->Resident.ValueLength);
128958a13831STrevor Thompson 
12904dfcd1d5STrevor Thompson         if (Offset + Length > Context->pRecord->Resident.ValueLength)
1291760cdfb5STrevor Thompson         {
1292760cdfb5STrevor Thompson             DPRINT1("DRIVER ERROR: Attribute is too small!\n");
1293760cdfb5STrevor Thompson             return STATUS_INVALID_PARAMETER;
1294760cdfb5STrevor Thompson         }
129558a13831STrevor Thompson 
1296d484d91eSTrevor Thompson         // Do we need to read the file record?
1297d484d91eSTrevor Thompson         if (FileRecord == NULL)
1298d484d91eSTrevor Thompson         {
1299760cdfb5STrevor Thompson             FileRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
1300760cdfb5STrevor Thompson             if (!FileRecord)
1301760cdfb5STrevor Thompson             {
1302760cdfb5STrevor Thompson                 DPRINT1("Error: Couldn't allocate file record!\n");
1303760cdfb5STrevor Thompson                 return STATUS_NO_MEMORY;
1304760cdfb5STrevor Thompson             }
1305760cdfb5STrevor Thompson 
1306d484d91eSTrevor Thompson             FileRecordAllocated = TRUE;
1307d484d91eSTrevor Thompson 
1308760cdfb5STrevor Thompson             // read the file record
1309760cdfb5STrevor Thompson             ReadFileRecord(Vcb, Context->FileMFTIndex, FileRecord);
1310d484d91eSTrevor Thompson         }
1311760cdfb5STrevor Thompson 
1312760cdfb5STrevor Thompson         // find where to write the attribute data to
1313760cdfb5STrevor Thompson         Status = FindAttribute(Vcb, FileRecord,
13144dfcd1d5STrevor Thompson                                Context->pRecord->Type,
13154dfcd1d5STrevor Thompson                                (PCWSTR)((ULONG_PTR)Context->pRecord + Context->pRecord->NameOffset),
13164dfcd1d5STrevor Thompson                                Context->pRecord->NameLength,
1317760cdfb5STrevor Thompson                                &FoundContext,
1318760cdfb5STrevor Thompson                                &AttributeOffset);
1319760cdfb5STrevor Thompson 
1320760cdfb5STrevor Thompson         if (!NT_SUCCESS(Status))
1321760cdfb5STrevor Thompson         {
1322760cdfb5STrevor Thompson             DPRINT1("ERROR: Couldn't find matching attribute!\n");
1323d484d91eSTrevor Thompson             if(FileRecordAllocated)
1324760cdfb5STrevor Thompson                 ExFreePoolWithTag(FileRecord, TAG_NTFS);
1325760cdfb5STrevor Thompson             return Status;
1326760cdfb5STrevor Thompson         }
1327760cdfb5STrevor Thompson 
1328d484d91eSTrevor Thompson         Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttributeOffset);
1329760cdfb5STrevor Thompson 
1330d484d91eSTrevor Thompson         DPRINT("Offset: %I64u, AttributeOffset: %u, ValueOffset: %u\n", Offset, AttributeOffset, Context->pRecord->Resident.ValueLength);
1331d484d91eSTrevor Thompson 
1332d484d91eSTrevor Thompson         // Will we be writing past the end of the allocated file record?
1333d484d91eSTrevor Thompson         if (Offset + Length + AttributeOffset + Context->pRecord->Resident.ValueOffset > Vcb->NtfsInfo.BytesPerFileRecord)
1334760cdfb5STrevor Thompson         {
1335760cdfb5STrevor Thompson             DPRINT1("DRIVER ERROR: Data being written extends past end of file record!\n");
1336760cdfb5STrevor Thompson             ReleaseAttributeContext(FoundContext);
1337d484d91eSTrevor Thompson             if (FileRecordAllocated)
1338760cdfb5STrevor Thompson                 ExFreePoolWithTag(FileRecord, TAG_NTFS);
1339760cdfb5STrevor Thompson             return STATUS_INVALID_PARAMETER;
1340760cdfb5STrevor Thompson         }
1341760cdfb5STrevor Thompson 
1342d484d91eSTrevor 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.
1343d484d91eSTrevor Thompson         RtlCopyMemory((PCHAR)((ULONG_PTR)Destination + Context->pRecord->Resident.ValueOffset + (ULONG)Offset), Buffer, Length);
1344760cdfb5STrevor Thompson 
1345760cdfb5STrevor Thompson         Status = UpdateFileRecord(Vcb, Context->FileMFTIndex, FileRecord);
1346760cdfb5STrevor Thompson 
1347d484d91eSTrevor Thompson         // Update the context's copy of the resident attribute
1348d484d91eSTrevor Thompson         ASSERT(Context->pRecord->Length == Destination->Length);
1349d484d91eSTrevor Thompson         RtlCopyMemory((PVOID)Context->pRecord, Destination, Context->pRecord->Length);
1350d484d91eSTrevor Thompson 
1351760cdfb5STrevor Thompson         ReleaseAttributeContext(FoundContext);
1352d484d91eSTrevor Thompson         if (FileRecordAllocated)
1353760cdfb5STrevor Thompson             ExFreePoolWithTag(FileRecord, TAG_NTFS);
1354760cdfb5STrevor Thompson 
1355760cdfb5STrevor Thompson         if (NT_SUCCESS(Status))
1356760cdfb5STrevor Thompson             *RealLengthWritten = Length;
1357760cdfb5STrevor Thompson 
1358760cdfb5STrevor Thompson         return Status;
135958a13831STrevor Thompson     }
136058a13831STrevor Thompson 
136158a13831STrevor Thompson     // This is a non-resident attribute.
136258a13831STrevor Thompson 
136358a13831STrevor Thompson     // I. Find the corresponding start data run.
136458a13831STrevor Thompson 
136558a13831STrevor Thompson     // FIXME: Cache seems to be non-working. Disable it for now
136658a13831STrevor Thompson     //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
136758a13831STrevor Thompson     /*if (0)
136858a13831STrevor Thompson     {
136958a13831STrevor Thompson     DataRun = Context->CacheRun;
137058a13831STrevor Thompson     LastLCN = Context->CacheRunLastLCN;
137158a13831STrevor Thompson     DataRunStartLCN = Context->CacheRunStartLCN;
137258a13831STrevor Thompson     DataRunLength = Context->CacheRunLength;
137358a13831STrevor Thompson     CurrentOffset = Context->CacheRunCurrentOffset;
137458a13831STrevor Thompson     }
137558a13831STrevor Thompson     else*/
137658a13831STrevor Thompson     {
137752b9f467STrevor Thompson         ULONG UsedBufferSize;
137858a13831STrevor Thompson         LastLCN = 0;
137958a13831STrevor Thompson         CurrentOffset = 0;
138058a13831STrevor Thompson 
138152b9f467STrevor Thompson         // This will be rewritten in the next iteration to just use the DataRuns MCB directly
138252b9f467STrevor Thompson         TempBuffer = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
138352b9f467STrevor Thompson 
138452b9f467STrevor Thompson         ConvertLargeMCBToDataRuns(&Context->DataRunsMCB,
138552b9f467STrevor Thompson                                   TempBuffer,
138652b9f467STrevor Thompson                                   Vcb->NtfsInfo.BytesPerFileRecord,
138752b9f467STrevor Thompson                                   &UsedBufferSize);
138852b9f467STrevor Thompson 
138952b9f467STrevor Thompson         DataRun = TempBuffer;
139052b9f467STrevor Thompson 
139158a13831STrevor Thompson         while (1)
139258a13831STrevor Thompson         {
139358a13831STrevor Thompson             DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
139458a13831STrevor Thompson             if (DataRunOffset != -1)
139558a13831STrevor Thompson             {
139658a13831STrevor Thompson                 // Normal data run.
139758a13831STrevor Thompson                 // DPRINT1("Writing to normal data run, LastLCN %I64u DataRunOffset %I64d\n", LastLCN, DataRunOffset);
139858a13831STrevor Thompson                 DataRunStartLCN = LastLCN + DataRunOffset;
139958a13831STrevor Thompson                 LastLCN = DataRunStartLCN;
140058a13831STrevor Thompson             }
140158a13831STrevor Thompson             else
140258a13831STrevor Thompson             {
140358a13831STrevor Thompson                 // Sparse data run. We can't support writing to sparse files yet
140458a13831STrevor Thompson                 // (it may require increasing the allocation size).
140558a13831STrevor Thompson                 DataRunStartLCN = -1;
140658a13831STrevor Thompson                 DPRINT1("FIXME: Writing to sparse files is not supported yet!\n");
140758a13831STrevor Thompson                 return STATUS_NOT_IMPLEMENTED;
140858a13831STrevor Thompson             }
140958a13831STrevor Thompson 
141058a13831STrevor Thompson             // Have we reached the data run we're trying to write to?
141158a13831STrevor Thompson             if (Offset >= CurrentOffset &&
141258a13831STrevor Thompson                 Offset < CurrentOffset + (DataRunLength * Vcb->NtfsInfo.BytesPerCluster))
141358a13831STrevor Thompson             {
141458a13831STrevor Thompson                 break;
141558a13831STrevor Thompson             }
141658a13831STrevor Thompson 
141758a13831STrevor Thompson             if (*DataRun == 0)
141858a13831STrevor Thompson             {
141958a13831STrevor Thompson                 // We reached the last assigned cluster
142058a13831STrevor Thompson                 // TODO: assign new clusters to the end of the file.
14210409b316STrevor Thompson                 // (Presently, this code will rarely be reached, the write will usually have already failed by now)
14220409b316STrevor Thompson                 // [We can reach here by creating a new file record when the MFT isn't large enough]
14230409b316STrevor Thompson                 DPRINT1("FIXME: Master File Table needs to be enlarged.\n");
142458a13831STrevor Thompson                 return STATUS_END_OF_FILE;
142558a13831STrevor Thompson             }
142658a13831STrevor Thompson 
142758a13831STrevor Thompson             CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
142858a13831STrevor Thompson         }
142958a13831STrevor Thompson     }
143058a13831STrevor Thompson 
143158a13831STrevor Thompson     // II. Go through the run list and write the data
143258a13831STrevor Thompson 
143358a13831STrevor Thompson     /* REVIEWME -- As adapted from NtfsReadAttribute():
143458a13831STrevor Thompson     We seem to be making a special case for the first applicable data run, but I'm not sure why.
143558a13831STrevor Thompson     Does it have something to do with (not) caching? Is this strategy equally applicable to writing? */
143658a13831STrevor Thompson 
143758a13831STrevor Thompson     WriteLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset), Length);
143858a13831STrevor Thompson 
143958a13831STrevor Thompson     StartingOffset = DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster + Offset - CurrentOffset;
144058a13831STrevor Thompson 
144158a13831STrevor Thompson     // Write the data to the disk
144258a13831STrevor Thompson     Status = NtfsWriteDisk(Vcb->StorageDevice,
144358a13831STrevor Thompson                            StartingOffset,
144458a13831STrevor Thompson                            WriteLength,
144558a13831STrevor Thompson                            Vcb->NtfsInfo.BytesPerSector,
144658a13831STrevor Thompson                            (PVOID)SourceBuffer);
144758a13831STrevor Thompson 
144858a13831STrevor Thompson     // Did the write fail?
144958a13831STrevor Thompson     if (!NT_SUCCESS(Status))
145058a13831STrevor Thompson     {
145158a13831STrevor Thompson         Context->CacheRun = DataRun;
145258a13831STrevor Thompson         Context->CacheRunOffset = Offset;
145358a13831STrevor Thompson         Context->CacheRunStartLCN = DataRunStartLCN;
145458a13831STrevor Thompson         Context->CacheRunLength = DataRunLength;
145558a13831STrevor Thompson         Context->CacheRunLastLCN = LastLCN;
145658a13831STrevor Thompson         Context->CacheRunCurrentOffset = CurrentOffset;
145758a13831STrevor Thompson 
145858a13831STrevor Thompson         return Status;
145958a13831STrevor Thompson     }
146058a13831STrevor Thompson 
146158a13831STrevor Thompson     Length -= WriteLength;
146258a13831STrevor Thompson     SourceBuffer += WriteLength;
146358a13831STrevor Thompson     *RealLengthWritten += WriteLength;
146458a13831STrevor Thompson 
146558a13831STrevor Thompson     // Did we write to the end of the data run?
146658a13831STrevor Thompson     if (WriteLength == DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset))
146758a13831STrevor Thompson     {
146858a13831STrevor Thompson         // Advance to the next data run
146958a13831STrevor Thompson         CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
147058a13831STrevor Thompson         DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
147158a13831STrevor Thompson 
147258a13831STrevor Thompson         if (DataRunOffset != (ULONGLONG)-1)
147358a13831STrevor Thompson         {
147458a13831STrevor Thompson             DataRunStartLCN = LastLCN + DataRunOffset;
147558a13831STrevor Thompson             LastLCN = DataRunStartLCN;
147658a13831STrevor Thompson         }
147758a13831STrevor Thompson         else
147858a13831STrevor Thompson             DataRunStartLCN = -1;
147958a13831STrevor Thompson     }
148058a13831STrevor Thompson 
148158a13831STrevor Thompson     // Do we have more data to write?
148258a13831STrevor Thompson     while (Length > 0)
148358a13831STrevor Thompson     {
148458a13831STrevor Thompson         // Make sure we don't write past the end of the current data run
148558a13831STrevor Thompson         WriteLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster, Length);
148658a13831STrevor Thompson 
148758a13831STrevor Thompson         // Are we dealing with a sparse data run?
148858a13831STrevor Thompson         if (DataRunStartLCN == -1)
148958a13831STrevor Thompson         {
149058a13831STrevor Thompson             DPRINT1("FIXME: Don't know how to write to sparse files yet! (DataRunStartLCN == -1)\n");
149158a13831STrevor Thompson             return STATUS_NOT_IMPLEMENTED;
149258a13831STrevor Thompson         }
149358a13831STrevor Thompson         else
149458a13831STrevor Thompson         {
149558a13831STrevor Thompson             // write the data to the disk
149658a13831STrevor Thompson             Status = NtfsWriteDisk(Vcb->StorageDevice,
149758a13831STrevor Thompson                                    DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster,
149858a13831STrevor Thompson                                    WriteLength,
149958a13831STrevor Thompson                                    Vcb->NtfsInfo.BytesPerSector,
150058a13831STrevor Thompson                                    (PVOID)SourceBuffer);
150158a13831STrevor Thompson             if (!NT_SUCCESS(Status))
150258a13831STrevor Thompson                 break;
150358a13831STrevor Thompson         }
150458a13831STrevor Thompson 
150558a13831STrevor Thompson         Length -= WriteLength;
150658a13831STrevor Thompson         SourceBuffer += WriteLength;
1507ea6b9622STrevor Thompson         *RealLengthWritten += WriteLength;
150858a13831STrevor Thompson 
150958a13831STrevor Thompson         // We finished this request, but there's still data in this data run.
151058a13831STrevor Thompson         if (Length == 0 && WriteLength != DataRunLength * Vcb->NtfsInfo.BytesPerCluster)
151158a13831STrevor Thompson             break;
151258a13831STrevor Thompson 
151358a13831STrevor Thompson         // Go to next run in the list.
151458a13831STrevor Thompson 
151558a13831STrevor Thompson         if (*DataRun == 0)
151658a13831STrevor Thompson         {
151758a13831STrevor Thompson             // that was the last run
151858a13831STrevor Thompson             if (Length > 0)
151958a13831STrevor Thompson             {
152058a13831STrevor Thompson                 // Failed sanity check.
152158a13831STrevor Thompson                 DPRINT1("Encountered EOF before expected!\n");
152258a13831STrevor Thompson                 return STATUS_END_OF_FILE;
152358a13831STrevor Thompson             }
152458a13831STrevor Thompson 
152558a13831STrevor Thompson             break;
152658a13831STrevor Thompson         }
152758a13831STrevor Thompson 
152858a13831STrevor Thompson         // Advance to the next data run
152958a13831STrevor Thompson         CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
153058a13831STrevor Thompson         DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
153158a13831STrevor Thompson         if (DataRunOffset != -1)
153258a13831STrevor Thompson         {
153358a13831STrevor Thompson             // Normal data run.
153458a13831STrevor Thompson             DataRunStartLCN = LastLCN + DataRunOffset;
153558a13831STrevor Thompson             LastLCN = DataRunStartLCN;
153658a13831STrevor Thompson         }
153758a13831STrevor Thompson         else
153858a13831STrevor Thompson         {
153958a13831STrevor Thompson             // Sparse data run.
154058a13831STrevor Thompson             DataRunStartLCN = -1;
154158a13831STrevor Thompson         }
154258a13831STrevor Thompson     } // end while (Length > 0) [more data to write]
154358a13831STrevor Thompson 
154452b9f467STrevor Thompson     // TEMPTEMP
15454dfcd1d5STrevor Thompson     if (Context->pRecord->IsNonResident)
154652b9f467STrevor Thompson         ExFreePoolWithTag(TempBuffer, TAG_NTFS);
154752b9f467STrevor Thompson 
154858a13831STrevor Thompson     Context->CacheRun = DataRun;
154958a13831STrevor Thompson     Context->CacheRunOffset = Offset + *RealLengthWritten;
155058a13831STrevor Thompson     Context->CacheRunStartLCN = DataRunStartLCN;
155158a13831STrevor Thompson     Context->CacheRunLength = DataRunLength;
155258a13831STrevor Thompson     Context->CacheRunLastLCN = LastLCN;
155358a13831STrevor Thompson     Context->CacheRunCurrentOffset = CurrentOffset;
155458a13831STrevor Thompson 
155558a13831STrevor Thompson     return Status;
155658a13831STrevor Thompson }
155758a13831STrevor Thompson 
1558c2c66affSColin Finck NTSTATUS
1559c2c66affSColin Finck ReadFileRecord(PDEVICE_EXTENSION Vcb,
1560c2c66affSColin Finck                ULONGLONG index,
1561c2c66affSColin Finck                PFILE_RECORD_HEADER file)
1562c2c66affSColin Finck {
1563c2c66affSColin Finck     ULONGLONG BytesRead;
1564c2c66affSColin Finck 
1565c2c66affSColin Finck     DPRINT("ReadFileRecord(%p, %I64x, %p)\n", Vcb, index, file);
1566c2c66affSColin Finck 
1567c2c66affSColin Finck     BytesRead = ReadAttribute(Vcb, Vcb->MFTContext, index * Vcb->NtfsInfo.BytesPerFileRecord, (PCHAR)file, Vcb->NtfsInfo.BytesPerFileRecord);
1568c2c66affSColin Finck     if (BytesRead != Vcb->NtfsInfo.BytesPerFileRecord)
1569c2c66affSColin Finck     {
157038c947b7STrevor Thompson         DPRINT1("ReadFileRecord failed: %I64u read, %lu expected\n", BytesRead, Vcb->NtfsInfo.BytesPerFileRecord);
1571c2c66affSColin Finck         return STATUS_PARTIAL_COPY;
1572c2c66affSColin Finck     }
1573c2c66affSColin Finck 
1574c2c66affSColin Finck     /* Apply update sequence array fixups. */
1575760cdfb5STrevor Thompson     DPRINT("Sequence number: %u\n", file->SequenceNumber);
1576c2c66affSColin Finck     return FixupUpdateSequenceArray(Vcb, &file->Ntfs);
1577c2c66affSColin Finck }
1578c2c66affSColin Finck 
1579a7a2c0d7STrevor Thompson 
1580a7a2c0d7STrevor Thompson /**
1581a7a2c0d7STrevor Thompson * Searches a file's parent directory (given the parent's index in the mft)
1582a7a2c0d7STrevor Thompson * for the given file. Upon finding an index entry for that file, updates
1583a7a2c0d7STrevor Thompson * Data Size and Allocated Size values in the $FILE_NAME attribute of that entry.
1584a7a2c0d7STrevor Thompson *
1585a7a2c0d7STrevor Thompson * (Most of this code was copied from NtfsFindMftRecord)
1586a7a2c0d7STrevor Thompson */
1587a7a2c0d7STrevor Thompson NTSTATUS
1588a7a2c0d7STrevor Thompson UpdateFileNameRecord(PDEVICE_EXTENSION Vcb,
1589a7a2c0d7STrevor Thompson                      ULONGLONG ParentMFTIndex,
1590a7a2c0d7STrevor Thompson                      PUNICODE_STRING FileName,
1591a7a2c0d7STrevor Thompson                      BOOLEAN DirSearch,
1592a7a2c0d7STrevor Thompson                      ULONGLONG NewDataSize,
1593032be029STrevor Thompson                      ULONGLONG NewAllocationSize,
1594032be029STrevor Thompson                      BOOLEAN CaseSensitive)
1595a7a2c0d7STrevor Thompson {
1596a7a2c0d7STrevor Thompson     PFILE_RECORD_HEADER MftRecord;
1597a7a2c0d7STrevor Thompson     PNTFS_ATTR_CONTEXT IndexRootCtx;
1598a7a2c0d7STrevor Thompson     PINDEX_ROOT_ATTRIBUTE IndexRoot;
1599a7a2c0d7STrevor Thompson     PCHAR IndexRecord;
1600a7a2c0d7STrevor Thompson     PINDEX_ENTRY_ATTRIBUTE IndexEntry, IndexEntryEnd;
1601a7a2c0d7STrevor Thompson     NTSTATUS Status;
1602a7a2c0d7STrevor Thompson     ULONG CurrentEntry = 0;
1603a7a2c0d7STrevor Thompson 
160438c947b7STrevor Thompson     DPRINT("UpdateFileNameRecord(%p, %I64d, %wZ, %s, %I64u, %I64u, %s)\n",
1605032be029STrevor Thompson            Vcb,
1606032be029STrevor Thompson            ParentMFTIndex,
1607032be029STrevor Thompson            FileName,
160838c947b7STrevor Thompson            DirSearch ? "TRUE" : "FALSE",
1609032be029STrevor Thompson            NewDataSize,
1610032be029STrevor Thompson            NewAllocationSize,
1611032be029STrevor Thompson            CaseSensitive ? "TRUE" : "FALSE");
1612a7a2c0d7STrevor Thompson 
1613a7a2c0d7STrevor Thompson     MftRecord = ExAllocatePoolWithTag(NonPagedPool,
1614a7a2c0d7STrevor Thompson                                       Vcb->NtfsInfo.BytesPerFileRecord,
1615a7a2c0d7STrevor Thompson                                       TAG_NTFS);
1616a7a2c0d7STrevor Thompson     if (MftRecord == NULL)
1617a7a2c0d7STrevor Thompson     {
1618a7a2c0d7STrevor Thompson         return STATUS_INSUFFICIENT_RESOURCES;
1619a7a2c0d7STrevor Thompson     }
1620a7a2c0d7STrevor Thompson 
1621a7a2c0d7STrevor Thompson     Status = ReadFileRecord(Vcb, ParentMFTIndex, MftRecord);
1622a7a2c0d7STrevor Thompson     if (!NT_SUCCESS(Status))
1623a7a2c0d7STrevor Thompson     {
1624a7a2c0d7STrevor Thompson         ExFreePoolWithTag(MftRecord, TAG_NTFS);
1625a7a2c0d7STrevor Thompson         return Status;
1626a7a2c0d7STrevor Thompson     }
1627a7a2c0d7STrevor Thompson 
1628a7a2c0d7STrevor Thompson     ASSERT(MftRecord->Ntfs.Type == NRH_FILE_TYPE);
1629a7a2c0d7STrevor Thompson     Status = FindAttribute(Vcb, MftRecord, AttributeIndexRoot, L"$I30", 4, &IndexRootCtx, NULL);
1630a7a2c0d7STrevor Thompson     if (!NT_SUCCESS(Status))
1631a7a2c0d7STrevor Thompson     {
1632a7a2c0d7STrevor Thompson         ExFreePoolWithTag(MftRecord, TAG_NTFS);
1633a7a2c0d7STrevor Thompson         return Status;
1634a7a2c0d7STrevor Thompson     }
1635a7a2c0d7STrevor Thompson 
1636a7a2c0d7STrevor Thompson     IndexRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerIndexRecord, TAG_NTFS);
1637a7a2c0d7STrevor Thompson     if (IndexRecord == NULL)
1638a7a2c0d7STrevor Thompson     {
1639a7a2c0d7STrevor Thompson         ReleaseAttributeContext(IndexRootCtx);
1640a7a2c0d7STrevor Thompson         ExFreePoolWithTag(MftRecord, TAG_NTFS);
1641a7a2c0d7STrevor Thompson         return STATUS_INSUFFICIENT_RESOURCES;
1642a7a2c0d7STrevor Thompson     }
1643a7a2c0d7STrevor Thompson 
16444dfcd1d5STrevor Thompson     Status = ReadAttribute(Vcb, IndexRootCtx, 0, IndexRecord, AttributeDataLength(IndexRootCtx->pRecord));
1645f5b7f90fSTrevor Thompson     if (!NT_SUCCESS(Status))
1646f5b7f90fSTrevor Thompson     {
1647f5b7f90fSTrevor Thompson         DPRINT1("ERROR: Failed to read Index Root!\n");
1648f5b7f90fSTrevor Thompson         ExFreePoolWithTag(IndexRecord, TAG_NTFS);
1649f5b7f90fSTrevor Thompson         ReleaseAttributeContext(IndexRootCtx);
1650f5b7f90fSTrevor Thompson         ExFreePoolWithTag(MftRecord, TAG_NTFS);
1651f5b7f90fSTrevor Thompson     }
1652f5b7f90fSTrevor Thompson 
1653a7a2c0d7STrevor Thompson     IndexRoot = (PINDEX_ROOT_ATTRIBUTE)IndexRecord;
1654a7a2c0d7STrevor Thompson     IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)&IndexRoot->Header + IndexRoot->Header.FirstEntryOffset);
1655a7a2c0d7STrevor Thompson     // Index root is always resident.
1656a7a2c0d7STrevor Thompson     IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)(IndexRecord + IndexRoot->Header.TotalSizeOfEntries);
1657a7a2c0d7STrevor Thompson 
1658a7a2c0d7STrevor Thompson     DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb->NtfsInfo.BytesPerIndexRecord, IndexRoot->SizeOfEntry);
1659a7a2c0d7STrevor Thompson 
1660a7a2c0d7STrevor Thompson     Status = UpdateIndexEntryFileNameSize(Vcb,
1661a7a2c0d7STrevor Thompson                                           MftRecord,
1662a7a2c0d7STrevor Thompson                                           IndexRecord,
1663a7a2c0d7STrevor Thompson                                           IndexRoot->SizeOfEntry,
1664a7a2c0d7STrevor Thompson                                           IndexEntry,
1665a7a2c0d7STrevor Thompson                                           IndexEntryEnd,
1666a7a2c0d7STrevor Thompson                                           FileName,
1667a7a2c0d7STrevor Thompson                                           &CurrentEntry,
1668a7a2c0d7STrevor Thompson                                           &CurrentEntry,
1669a7a2c0d7STrevor Thompson                                           DirSearch,
1670a7a2c0d7STrevor Thompson                                           NewDataSize,
1671032be029STrevor Thompson                                           NewAllocationSize,
1672032be029STrevor Thompson                                           CaseSensitive);
1673a7a2c0d7STrevor Thompson 
1674f5b7f90fSTrevor Thompson     if (Status == STATUS_PENDING)
1675f5b7f90fSTrevor Thompson     {
1676f5b7f90fSTrevor Thompson         // we need to write the index root attribute back to disk
1677f5b7f90fSTrevor Thompson         ULONG LengthWritten;
1678d484d91eSTrevor Thompson         Status = WriteAttribute(Vcb, IndexRootCtx, 0, (PUCHAR)IndexRecord, AttributeDataLength(IndexRootCtx->pRecord), &LengthWritten, MftRecord);
1679f5b7f90fSTrevor Thompson         if (!NT_SUCCESS(Status))
1680f5b7f90fSTrevor Thompson         {
1681f5b7f90fSTrevor Thompson             DPRINT1("ERROR: Couldn't update Index Root!\n");
1682f5b7f90fSTrevor Thompson         }
1683f5b7f90fSTrevor Thompson 
1684f5b7f90fSTrevor Thompson     }
1685f5b7f90fSTrevor Thompson 
1686a7a2c0d7STrevor Thompson     ReleaseAttributeContext(IndexRootCtx);
1687a7a2c0d7STrevor Thompson     ExFreePoolWithTag(IndexRecord, TAG_NTFS);
1688a7a2c0d7STrevor Thompson     ExFreePoolWithTag(MftRecord, TAG_NTFS);
1689a7a2c0d7STrevor Thompson 
1690a7a2c0d7STrevor Thompson     return Status;
1691a7a2c0d7STrevor Thompson }
1692a7a2c0d7STrevor Thompson 
1693a7a2c0d7STrevor Thompson /**
1694a7a2c0d7STrevor Thompson * Recursively searches directory index and applies the size update to the $FILE_NAME attribute of the
1695a7a2c0d7STrevor Thompson * proper index entry.
1696a7a2c0d7STrevor Thompson * (Heavily based on BrowseIndexEntries)
1697a7a2c0d7STrevor Thompson */
1698a7a2c0d7STrevor Thompson NTSTATUS
1699a7a2c0d7STrevor Thompson UpdateIndexEntryFileNameSize(PDEVICE_EXTENSION Vcb,
1700a7a2c0d7STrevor Thompson                              PFILE_RECORD_HEADER MftRecord,
1701a7a2c0d7STrevor Thompson                              PCHAR IndexRecord,
1702a7a2c0d7STrevor Thompson                              ULONG IndexBlockSize,
1703a7a2c0d7STrevor Thompson                              PINDEX_ENTRY_ATTRIBUTE FirstEntry,
1704a7a2c0d7STrevor Thompson                              PINDEX_ENTRY_ATTRIBUTE LastEntry,
1705a7a2c0d7STrevor Thompson                              PUNICODE_STRING FileName,
1706a7a2c0d7STrevor Thompson                              PULONG StartEntry,
1707a7a2c0d7STrevor Thompson                              PULONG CurrentEntry,
1708a7a2c0d7STrevor Thompson                              BOOLEAN DirSearch,
1709a7a2c0d7STrevor Thompson                              ULONGLONG NewDataSize,
1710032be029STrevor Thompson                              ULONGLONG NewAllocatedSize,
1711032be029STrevor Thompson                              BOOLEAN CaseSensitive)
1712a7a2c0d7STrevor Thompson {
1713a7a2c0d7STrevor Thompson     NTSTATUS Status;
1714a7a2c0d7STrevor Thompson     ULONG RecordOffset;
1715a7a2c0d7STrevor Thompson     PINDEX_ENTRY_ATTRIBUTE IndexEntry;
1716a7a2c0d7STrevor Thompson     PNTFS_ATTR_CONTEXT IndexAllocationCtx;
1717a7a2c0d7STrevor Thompson     ULONGLONG IndexAllocationSize;
1718a7a2c0d7STrevor Thompson     PINDEX_BUFFER IndexBuffer;
1719a7a2c0d7STrevor Thompson 
172038c947b7STrevor Thompson     DPRINT("UpdateIndexEntrySize(%p, %p, %p, %lu, %p, %p, %wZ, %lu, %lu, %s, %I64u, %I64u, %s)\n",
172138c947b7STrevor Thompson            Vcb,
172238c947b7STrevor Thompson            MftRecord,
172338c947b7STrevor Thompson            IndexRecord,
172438c947b7STrevor Thompson            IndexBlockSize,
172538c947b7STrevor Thompson            FirstEntry,
172638c947b7STrevor Thompson            LastEntry,
172738c947b7STrevor Thompson            FileName,
172838c947b7STrevor Thompson            *StartEntry,
172938c947b7STrevor Thompson            *CurrentEntry,
173038c947b7STrevor Thompson            DirSearch ? "TRUE" : "FALSE",
173138c947b7STrevor Thompson            NewDataSize,
173238c947b7STrevor Thompson            NewAllocatedSize,
173338c947b7STrevor Thompson            CaseSensitive ? "TRUE" : "FALSE");
1734a7a2c0d7STrevor Thompson 
1735a7a2c0d7STrevor Thompson     // find the index entry responsible for the file we're trying to update
1736a7a2c0d7STrevor Thompson     IndexEntry = FirstEntry;
1737a7a2c0d7STrevor Thompson     while (IndexEntry < LastEntry &&
1738a7a2c0d7STrevor Thompson            !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
1739a7a2c0d7STrevor Thompson     {
1740935fcd1bSTrevor Thompson         if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) > NTFS_FILE_FIRST_USER_FILE &&
1741a7a2c0d7STrevor Thompson             *CurrentEntry >= *StartEntry &&
1742a7a2c0d7STrevor Thompson             IndexEntry->FileName.NameType != NTFS_FILE_NAME_DOS &&
1743032be029STrevor Thompson             CompareFileName(FileName, IndexEntry, DirSearch, CaseSensitive))
1744a7a2c0d7STrevor Thompson         {
1745a7a2c0d7STrevor Thompson             *StartEntry = *CurrentEntry;
1746a7a2c0d7STrevor Thompson             IndexEntry->FileName.DataSize = NewDataSize;
1747a7a2c0d7STrevor Thompson             IndexEntry->FileName.AllocatedSize = NewAllocatedSize;
1748a7a2c0d7STrevor Thompson             // indicate that the caller will still need to write the structure to the disk
1749a7a2c0d7STrevor Thompson             return STATUS_PENDING;
1750a7a2c0d7STrevor Thompson         }
1751a7a2c0d7STrevor Thompson 
1752a7a2c0d7STrevor Thompson         (*CurrentEntry) += 1;
1753a7a2c0d7STrevor Thompson         ASSERT(IndexEntry->Length >= sizeof(INDEX_ENTRY_ATTRIBUTE));
1754a7a2c0d7STrevor Thompson         IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
1755a7a2c0d7STrevor Thompson     }
1756a7a2c0d7STrevor Thompson 
1757a7a2c0d7STrevor Thompson     /* If we're already browsing a subnode */
1758a7a2c0d7STrevor Thompson     if (IndexRecord == NULL)
1759a7a2c0d7STrevor Thompson     {
1760a7a2c0d7STrevor Thompson         return STATUS_OBJECT_PATH_NOT_FOUND;
1761a7a2c0d7STrevor Thompson     }
1762a7a2c0d7STrevor Thompson 
1763a7a2c0d7STrevor Thompson     /* If there's no subnode */
1764a7a2c0d7STrevor Thompson     if (!(IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE))
1765a7a2c0d7STrevor Thompson     {
1766a7a2c0d7STrevor Thompson         return STATUS_OBJECT_PATH_NOT_FOUND;
1767a7a2c0d7STrevor Thompson     }
1768a7a2c0d7STrevor Thompson 
1769a7a2c0d7STrevor Thompson     Status = FindAttribute(Vcb, MftRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationCtx, NULL);
1770a7a2c0d7STrevor Thompson     if (!NT_SUCCESS(Status))
1771a7a2c0d7STrevor Thompson     {
1772a7a2c0d7STrevor Thompson         DPRINT("Corrupted filesystem!\n");
1773a7a2c0d7STrevor Thompson         return Status;
1774a7a2c0d7STrevor Thompson     }
1775a7a2c0d7STrevor Thompson 
17764dfcd1d5STrevor Thompson     IndexAllocationSize = AttributeDataLength(IndexAllocationCtx->pRecord);
1777a7a2c0d7STrevor Thompson     Status = STATUS_OBJECT_PATH_NOT_FOUND;
1778a7a2c0d7STrevor Thompson     for (RecordOffset = 0; RecordOffset < IndexAllocationSize; RecordOffset += IndexBlockSize)
1779a7a2c0d7STrevor Thompson     {
1780a7a2c0d7STrevor Thompson         ReadAttribute(Vcb, IndexAllocationCtx, RecordOffset, IndexRecord, IndexBlockSize);
1781a7a2c0d7STrevor Thompson         Status = FixupUpdateSequenceArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs);
1782a7a2c0d7STrevor Thompson         if (!NT_SUCCESS(Status))
1783a7a2c0d7STrevor Thompson         {
1784a7a2c0d7STrevor Thompson             break;
1785a7a2c0d7STrevor Thompson         }
1786a7a2c0d7STrevor Thompson 
1787a7a2c0d7STrevor Thompson         IndexBuffer = (PINDEX_BUFFER)IndexRecord;
1788a7a2c0d7STrevor Thompson         ASSERT(IndexBuffer->Ntfs.Type == NRH_INDX_TYPE);
1789a7a2c0d7STrevor Thompson         ASSERT(IndexBuffer->Header.AllocatedSize + FIELD_OFFSET(INDEX_BUFFER, Header) == IndexBlockSize);
1790a7a2c0d7STrevor Thompson         FirstEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.FirstEntryOffset);
1791a7a2c0d7STrevor Thompson         LastEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.TotalSizeOfEntries);
1792a7a2c0d7STrevor Thompson         ASSERT(LastEntry <= (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexBuffer + IndexBlockSize));
1793a7a2c0d7STrevor Thompson 
1794032be029STrevor Thompson         Status = UpdateIndexEntryFileNameSize(NULL,
1795032be029STrevor Thompson                                               NULL,
1796032be029STrevor Thompson                                               NULL,
1797032be029STrevor Thompson                                               0,
1798032be029STrevor Thompson                                               FirstEntry,
1799032be029STrevor Thompson                                               LastEntry,
1800032be029STrevor Thompson                                               FileName,
1801032be029STrevor Thompson                                               StartEntry,
1802032be029STrevor Thompson                                               CurrentEntry,
1803032be029STrevor Thompson                                               DirSearch,
1804032be029STrevor Thompson                                               NewDataSize,
1805032be029STrevor Thompson                                               NewAllocatedSize,
1806032be029STrevor Thompson                                               CaseSensitive);
1807a7a2c0d7STrevor Thompson         if (Status == STATUS_PENDING)
1808a7a2c0d7STrevor Thompson         {
1809a7a2c0d7STrevor Thompson             // write the index record back to disk
1810a7a2c0d7STrevor Thompson             ULONG Written;
1811a7a2c0d7STrevor Thompson 
1812a7a2c0d7STrevor Thompson             // first we need to update the fixup values for the index block
1813a7a2c0d7STrevor Thompson             Status = AddFixupArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs);
1814a7a2c0d7STrevor Thompson             if (!NT_SUCCESS(Status))
1815a7a2c0d7STrevor Thompson             {
1816a7a2c0d7STrevor Thompson                 DPRINT1("Error: Failed to update fixup sequence array!\n");
1817a7a2c0d7STrevor Thompson                 break;
1818a7a2c0d7STrevor Thompson             }
1819a7a2c0d7STrevor Thompson 
1820d484d91eSTrevor Thompson             Status = WriteAttribute(Vcb, IndexAllocationCtx, RecordOffset, (const PUCHAR)IndexRecord, IndexBlockSize, &Written, MftRecord);
1821a7a2c0d7STrevor Thompson             if (!NT_SUCCESS(Status))
1822a7a2c0d7STrevor Thompson             {
1823a7a2c0d7STrevor Thompson                 DPRINT1("ERROR Performing write!\n");
1824a7a2c0d7STrevor Thompson                 break;
1825a7a2c0d7STrevor Thompson             }
1826a7a2c0d7STrevor Thompson 
1827a7a2c0d7STrevor Thompson             Status = STATUS_SUCCESS;
1828a7a2c0d7STrevor Thompson             break;
1829a7a2c0d7STrevor Thompson         }
1830a7a2c0d7STrevor Thompson         if (NT_SUCCESS(Status))
1831a7a2c0d7STrevor Thompson         {
1832a7a2c0d7STrevor Thompson             break;
1833a7a2c0d7STrevor Thompson         }
1834a7a2c0d7STrevor Thompson     }
1835a7a2c0d7STrevor Thompson 
1836a7a2c0d7STrevor Thompson     ReleaseAttributeContext(IndexAllocationCtx);
1837a7a2c0d7STrevor Thompson     return Status;
1838a7a2c0d7STrevor Thompson }
1839a7a2c0d7STrevor Thompson 
1840ba33b9faSTrevor Thompson /**
18410409b316STrevor Thompson * @name UpdateFileRecord
1842ba33b9faSTrevor Thompson * @implemented
18430409b316STrevor Thompson *
1844ba33b9faSTrevor Thompson * Writes a file record to the master file table, at a given index.
18450409b316STrevor Thompson *
18460409b316STrevor Thompson * @param Vcb
18470409b316STrevor Thompson * Pointer to the DEVICE_EXTENSION of the target drive being written to.
18480409b316STrevor Thompson *
18490409b316STrevor Thompson * @param MftIndex
18500409b316STrevor Thompson * Target index in the master file table to store the file record.
18510409b316STrevor Thompson *
18520409b316STrevor Thompson * @param FileRecord
18530409b316STrevor Thompson * Pointer to the complete file record which will be written to the master file table.
18540409b316STrevor Thompson *
18550409b316STrevor Thompson * @return
18560409b316STrevor Thompson * STATUS_SUCCESSFUL on success. An error passed from WriteAttribute() otherwise.
18570409b316STrevor Thompson *
1858ba33b9faSTrevor Thompson */
1859ba33b9faSTrevor Thompson NTSTATUS
1860ba33b9faSTrevor Thompson UpdateFileRecord(PDEVICE_EXTENSION Vcb,
18610409b316STrevor Thompson                  ULONGLONG MftIndex,
18620409b316STrevor Thompson                  PFILE_RECORD_HEADER FileRecord)
1863ba33b9faSTrevor Thompson {
1864ba33b9faSTrevor Thompson     ULONG BytesWritten;
1865ba33b9faSTrevor Thompson     NTSTATUS Status = STATUS_SUCCESS;
1866ba33b9faSTrevor Thompson 
18670409b316STrevor Thompson     DPRINT("UpdateFileRecord(%p, 0x%I64x, %p)\n", Vcb, MftIndex, FileRecord);
1868ba33b9faSTrevor Thompson 
1869ba33b9faSTrevor Thompson     // Add the fixup array to prepare the data for writing to disk
18700409b316STrevor Thompson     AddFixupArray(Vcb, &FileRecord->Ntfs);
1871ba33b9faSTrevor Thompson 
1872ba33b9faSTrevor Thompson     // write the file record to the master file table
1873d484d91eSTrevor Thompson     Status = WriteAttribute(Vcb,
1874d484d91eSTrevor Thompson                             Vcb->MFTContext,
1875d484d91eSTrevor Thompson                             MftIndex * Vcb->NtfsInfo.BytesPerFileRecord,
1876d484d91eSTrevor Thompson                             (const PUCHAR)FileRecord,
1877d484d91eSTrevor Thompson                             Vcb->NtfsInfo.BytesPerFileRecord,
1878d484d91eSTrevor Thompson                             &BytesWritten,
1879d484d91eSTrevor Thompson                             FileRecord);
1880ba33b9faSTrevor Thompson 
1881ba33b9faSTrevor Thompson     if (!NT_SUCCESS(Status))
1882ba33b9faSTrevor Thompson     {
18836ab30720STrevor Thompson         DPRINT1("UpdateFileRecord failed: %lu written, %lu expected\n", BytesWritten, Vcb->NtfsInfo.BytesPerFileRecord);
1884ba33b9faSTrevor Thompson     }
1885ba33b9faSTrevor Thompson 
1886268a139eSTrevor Thompson     // remove the fixup array (so the file record pointer can still be used)
18870409b316STrevor Thompson     FixupUpdateSequenceArray(Vcb, &FileRecord->Ntfs);
1888268a139eSTrevor Thompson 
1889ba33b9faSTrevor Thompson     return Status;
1890ba33b9faSTrevor Thompson }
1891ba33b9faSTrevor Thompson 
1892c2c66affSColin Finck 
1893c2c66affSColin Finck NTSTATUS
1894c2c66affSColin Finck FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb,
1895c2c66affSColin Finck                          PNTFS_RECORD_HEADER Record)
1896c2c66affSColin Finck {
1897c2c66affSColin Finck     USHORT *USA;
1898c2c66affSColin Finck     USHORT USANumber;
1899c2c66affSColin Finck     USHORT USACount;
1900c2c66affSColin Finck     USHORT *Block;
1901c2c66affSColin Finck 
1902c2c66affSColin Finck     USA = (USHORT*)((PCHAR)Record + Record->UsaOffset);
1903c2c66affSColin Finck     USANumber = *(USA++);
1904c2c66affSColin Finck     USACount = Record->UsaCount - 1; /* Exclude the USA Number. */
1905c2c66affSColin Finck     Block = (USHORT*)((PCHAR)Record + Vcb->NtfsInfo.BytesPerSector - 2);
1906c2c66affSColin Finck 
1907ba33b9faSTrevor Thompson     DPRINT("FixupUpdateSequenceArray(%p, %p)\nUSANumber: %u\tUSACount: %u\n", Vcb, Record, USANumber, USACount);
1908ba33b9faSTrevor Thompson 
1909c2c66affSColin Finck     while (USACount)
1910c2c66affSColin Finck     {
1911c2c66affSColin Finck         if (*Block != USANumber)
1912c2c66affSColin Finck         {
1913c2c66affSColin Finck             DPRINT1("Mismatch with USA: %u read, %u expected\n" , *Block, USANumber);
1914c2c66affSColin Finck             return STATUS_UNSUCCESSFUL;
1915c2c66affSColin Finck         }
1916c2c66affSColin Finck         *Block = *(USA++);
1917c2c66affSColin Finck         Block = (USHORT*)((PCHAR)Block + Vcb->NtfsInfo.BytesPerSector);
1918c2c66affSColin Finck         USACount--;
1919c2c66affSColin Finck     }
1920c2c66affSColin Finck 
1921c2c66affSColin Finck     return STATUS_SUCCESS;
1922c2c66affSColin Finck }
1923c2c66affSColin Finck 
19240409b316STrevor Thompson /**
19250409b316STrevor Thompson * @name AddNewMftEntry
19260409b316STrevor Thompson * @implemented
19270409b316STrevor Thompson *
19280409b316STrevor Thompson * Adds a file record to the master file table of a given device.
19290409b316STrevor Thompson *
19300409b316STrevor Thompson * @param FileRecord
19310409b316STrevor Thompson * Pointer to a complete file record which will be saved to disk.
19320409b316STrevor Thompson *
19330409b316STrevor Thompson * @param DeviceExt
19340409b316STrevor Thompson * Pointer to the DEVICE_EXTENSION of the target drive.
19350409b316STrevor Thompson *
1936e0048b13STrevor Thompson * @param DestinationIndex
1937e0048b13STrevor Thompson * Pointer to a ULONGLONG which will receive the MFT index where the file record was stored.
1938e0048b13STrevor Thompson *
193998ddf610STrevor Thompson * @param CanWait
194098ddf610STrevor Thompson * Boolean indicating if the function is allowed to wait for exclusive access to the master file table.
194198ddf610STrevor Thompson * This will only be relevant if the MFT doesn't have any free file records and needs to be enlarged.
194298ddf610STrevor Thompson *
19430409b316STrevor Thompson * @return
19440409b316STrevor Thompson * STATUS_SUCCESS on success.
19450409b316STrevor Thompson * STATUS_OBJECT_NAME_NOT_FOUND if we can't find the MFT's $Bitmap or if we weren't able
19460409b316STrevor Thompson * to read the attribute.
19470409b316STrevor Thompson * STATUS_INSUFFICIENT_RESOURCES if we can't allocate enough memory for a copy of $Bitmap.
194898ddf610STrevor Thompson * STATUS_CANT_WAIT if CanWait was FALSE and the function could not get immediate, exclusive access to the MFT.
19490409b316STrevor Thompson */
19500409b316STrevor Thompson NTSTATUS
19510409b316STrevor Thompson AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
1952e0048b13STrevor Thompson                PDEVICE_EXTENSION DeviceExt,
195398ddf610STrevor Thompson                PULONGLONG DestinationIndex,
195498ddf610STrevor Thompson                BOOLEAN CanWait)
19550409b316STrevor Thompson {
19560409b316STrevor Thompson     NTSTATUS Status = STATUS_SUCCESS;
19570409b316STrevor Thompson     ULONGLONG MftIndex;
19580409b316STrevor Thompson     RTL_BITMAP Bitmap;
19590409b316STrevor Thompson     ULONGLONG BitmapDataSize;
19600409b316STrevor Thompson     ULONGLONG AttrBytesRead;
196168a48b27STrevor Thompson     PUCHAR BitmapData;
1962b033f00fSTrevor Thompson     PUCHAR BitmapBuffer;
19630409b316STrevor Thompson     ULONG LengthWritten;
19649ab86116STrevor Thompson     PNTFS_ATTR_CONTEXT BitmapContext;
19659ab86116STrevor Thompson     LARGE_INTEGER BitmapBits;
19669ab86116STrevor Thompson     UCHAR SystemReservedBits;
19679ab86116STrevor Thompson 
196898ddf610STrevor Thompson     DPRINT1("AddNewMftEntry(%p, %p, %p, %s)\n", FileRecord, DeviceExt, DestinationIndex, CanWait ? "TRUE" : "FALSE");
19690409b316STrevor Thompson 
19700409b316STrevor Thompson     // First, we have to read the mft's $Bitmap attribute
1971b033f00fSTrevor Thompson 
1972b033f00fSTrevor Thompson     // Find the attribute
19730409b316STrevor Thompson     Status = FindAttribute(DeviceExt, DeviceExt->MasterFileTable, AttributeBitmap, L"", 0, &BitmapContext, NULL);
19740409b316STrevor Thompson     if (!NT_SUCCESS(Status))
19750409b316STrevor Thompson     {
19760409b316STrevor Thompson         DPRINT1("ERROR: Couldn't find $Bitmap attribute of master file table!\n");
19770409b316STrevor Thompson         return Status;
19780409b316STrevor Thompson     }
19790409b316STrevor Thompson 
1980b033f00fSTrevor Thompson     // Get size of bitmap
19814dfcd1d5STrevor Thompson     BitmapDataSize = AttributeDataLength(BitmapContext->pRecord);
1982b033f00fSTrevor Thompson 
1983b033f00fSTrevor Thompson     // RtlInitializeBitmap wants a ULONG-aligned pointer, and wants the memory passed to it to be a ULONG-multiple
1984b033f00fSTrevor Thompson     // Allocate a buffer for the $Bitmap attribute plus enough to ensure we can get a ULONG-aligned pointer
1985b033f00fSTrevor Thompson     BitmapBuffer = ExAllocatePoolWithTag(NonPagedPool, BitmapDataSize + sizeof(ULONG), TAG_NTFS);
1986b033f00fSTrevor Thompson     if (!BitmapBuffer)
19870409b316STrevor Thompson     {
19880409b316STrevor Thompson         ReleaseAttributeContext(BitmapContext);
19890409b316STrevor Thompson         return STATUS_INSUFFICIENT_RESOURCES;
19900409b316STrevor Thompson     }
19910409b316STrevor Thompson 
1992b033f00fSTrevor Thompson     // Get a ULONG-aligned pointer for the bitmap itself
1993b033f00fSTrevor Thompson     BitmapData = (PUCHAR)ALIGN_UP_BY((ULONG_PTR)BitmapBuffer, sizeof(ULONG));
1994b033f00fSTrevor Thompson 
19950409b316STrevor Thompson     // read $Bitmap attribute
1996935fcd1bSTrevor Thompson     AttrBytesRead = ReadAttribute(DeviceExt, BitmapContext, 0, (PCHAR)BitmapData, BitmapDataSize);
19970409b316STrevor Thompson 
1998b033f00fSTrevor Thompson     if (AttrBytesRead != BitmapDataSize)
19990409b316STrevor Thompson     {
20000409b316STrevor Thompson         DPRINT1("ERROR: Unable to read $Bitmap attribute of master file table!\n");
2001b033f00fSTrevor Thompson         ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
20020409b316STrevor Thompson         ReleaseAttributeContext(BitmapContext);
20030409b316STrevor Thompson         return STATUS_OBJECT_NAME_NOT_FOUND;
20040409b316STrevor Thompson     }
20050409b316STrevor Thompson 
200668a48b27STrevor Thompson     // We need to backup the bits for records 0x10 - 0x17 (3rd byte of bitmap) and mark these records
200768a48b27STrevor Thompson     // as in-use so we don't assign files to those indices. They're reserved for the system (e.g. ChkDsk).
200868a48b27STrevor Thompson     SystemReservedBits = BitmapData[2];
200968a48b27STrevor Thompson     BitmapData[2] = 0xff;
20109ab86116STrevor Thompson 
20119ab86116STrevor Thompson     // Calculate bit count
20124dfcd1d5STrevor Thompson     BitmapBits.QuadPart = AttributeDataLength(DeviceExt->MFTContext->pRecord) /
20139ab86116STrevor Thompson                           DeviceExt->NtfsInfo.BytesPerFileRecord;
20149ab86116STrevor Thompson     if (BitmapBits.HighPart != 0)
20159ab86116STrevor Thompson     {
201668a48b27STrevor Thompson         DPRINT1("\tFIXME: bitmap sizes beyond 32bits are not yet supported! (Your NTFS volume is too large)\n");
2017b033f00fSTrevor Thompson         NtfsGlobalData->EnableWriteSupport = FALSE;
2018b033f00fSTrevor Thompson         ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
201968a48b27STrevor Thompson         ReleaseAttributeContext(BitmapContext);
202068a48b27STrevor Thompson         return STATUS_NOT_IMPLEMENTED;
20219ab86116STrevor Thompson     }
20229ab86116STrevor Thompson 
20230409b316STrevor Thompson     // convert buffer into bitmap
20249ab86116STrevor Thompson     RtlInitializeBitMap(&Bitmap, (PULONG)BitmapData, BitmapBits.LowPart);
20250409b316STrevor Thompson 
20260409b316STrevor Thompson     // set next available bit, preferrably after 23rd bit
20270409b316STrevor Thompson     MftIndex = RtlFindClearBitsAndSet(&Bitmap, 1, 24);
20280409b316STrevor Thompson     if ((LONG)MftIndex == -1)
20290409b316STrevor Thompson     {
20309ab86116STrevor Thompson         DPRINT1("Couldn't find free space in MFT for file record, increasing MFT size.\n");
20310409b316STrevor Thompson 
2032b033f00fSTrevor Thompson         ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
20330409b316STrevor Thompson         ReleaseAttributeContext(BitmapContext);
20340409b316STrevor Thompson 
20359ab86116STrevor Thompson         // Couldn't find a free record in the MFT, add some blank records and try again
203698ddf610STrevor Thompson         Status = IncreaseMftSize(DeviceExt, CanWait);
20379ab86116STrevor Thompson         if (!NT_SUCCESS(Status))
20389ab86116STrevor Thompson         {
20399ab86116STrevor Thompson             DPRINT1("ERROR: Couldn't find space in MFT for file or increase MFT size!\n");
20409ab86116STrevor Thompson             return Status;
20419ab86116STrevor Thompson         }
20429ab86116STrevor Thompson 
204398ddf610STrevor Thompson         return AddNewMftEntry(FileRecord, DeviceExt, DestinationIndex, CanWait);
20440409b316STrevor Thompson     }
20450409b316STrevor Thompson 
20460409b316STrevor Thompson     DPRINT1("Creating file record at MFT index: %I64u\n", MftIndex);
20470409b316STrevor Thompson 
20480409b316STrevor Thompson     // update file record with index
20490409b316STrevor Thompson     FileRecord->MFTRecordNumber = MftIndex;
20500409b316STrevor Thompson 
20510409b316STrevor Thompson     // [BitmapData should have been updated via RtlFindClearBitsAndSet()]
20520409b316STrevor Thompson 
20539ab86116STrevor Thompson     // Restore the system reserved bits
205468a48b27STrevor Thompson     BitmapData[2] = SystemReservedBits;
20559ab86116STrevor Thompson 
20560409b316STrevor Thompson     // write the bitmap back to the MFT's $Bitmap attribute
2057d484d91eSTrevor Thompson     Status = WriteAttribute(DeviceExt, BitmapContext, 0, BitmapData, BitmapDataSize, &LengthWritten, FileRecord);
20580409b316STrevor Thompson     if (!NT_SUCCESS(Status))
20590409b316STrevor Thompson     {
20600409b316STrevor Thompson         DPRINT1("ERROR encountered when writing $Bitmap attribute!\n");
2061b033f00fSTrevor Thompson         ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
20620409b316STrevor Thompson         ReleaseAttributeContext(BitmapContext);
20630409b316STrevor Thompson         return Status;
20640409b316STrevor Thompson     }
20650409b316STrevor Thompson 
20660409b316STrevor Thompson     // update the file record (write it to disk)
20670409b316STrevor Thompson     Status = UpdateFileRecord(DeviceExt, MftIndex, FileRecord);
20680409b316STrevor Thompson 
20690409b316STrevor Thompson     if (!NT_SUCCESS(Status))
20700409b316STrevor Thompson     {
20710409b316STrevor Thompson         DPRINT1("ERROR: Unable to write file record!\n");
2072b033f00fSTrevor Thompson         ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
20730409b316STrevor Thompson         ReleaseAttributeContext(BitmapContext);
20740409b316STrevor Thompson         return Status;
20750409b316STrevor Thompson     }
20760409b316STrevor Thompson 
2077e0048b13STrevor Thompson     *DestinationIndex = MftIndex;
2078e0048b13STrevor Thompson 
2079b033f00fSTrevor Thompson     ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
20800409b316STrevor Thompson     ReleaseAttributeContext(BitmapContext);
20810409b316STrevor Thompson 
20820409b316STrevor Thompson     return Status;
20830409b316STrevor Thompson }
20840409b316STrevor Thompson 
2085935fcd1bSTrevor Thompson /**
2086935fcd1bSTrevor Thompson * @name NtfsAddFilenameToDirectory
2087935fcd1bSTrevor Thompson * @implemented
2088935fcd1bSTrevor Thompson *
2089935fcd1bSTrevor Thompson * Adds a $FILE_NAME attribute to a given directory index.
2090935fcd1bSTrevor Thompson *
2091935fcd1bSTrevor Thompson * @param DeviceExt
2092935fcd1bSTrevor Thompson * Points to the target disk's DEVICE_EXTENSION.
2093935fcd1bSTrevor Thompson *
2094935fcd1bSTrevor Thompson * @param DirectoryMftIndex
2095935fcd1bSTrevor Thompson * Mft index of the parent directory which will receive the file.
2096935fcd1bSTrevor Thompson *
2097935fcd1bSTrevor Thompson * @param FileReferenceNumber
2098935fcd1bSTrevor Thompson * File reference of the file to be added to the directory. This is a combination of the
2099935fcd1bSTrevor Thompson * Mft index and sequence number.
2100935fcd1bSTrevor Thompson *
2101935fcd1bSTrevor Thompson * @param FilenameAttribute
2102935fcd1bSTrevor Thompson * Pointer to the FILENAME_ATTRIBUTE of the file being added to the directory.
2103935fcd1bSTrevor Thompson *
2104935fcd1bSTrevor Thompson * @param CaseSensitive
2105935fcd1bSTrevor Thompson * Boolean indicating if the function should operate in case-sensitive mode. This will be TRUE
2106935fcd1bSTrevor Thompson * if an application created the file with the FILE_FLAG_POSIX_SEMANTICS flag.
2107935fcd1bSTrevor Thompson *
2108935fcd1bSTrevor Thompson * @return
2109935fcd1bSTrevor Thompson * STATUS_SUCCESS on success.
2110935fcd1bSTrevor Thompson * STATUS_INSUFFICIENT_RESOURCES if an allocation fails.
2111935fcd1bSTrevor Thompson * STATUS_NOT_IMPLEMENTED if target address isn't at the end of the given file record.
2112935fcd1bSTrevor Thompson *
2113935fcd1bSTrevor Thompson * @remarks
2114935fcd1bSTrevor Thompson * WIP - Can only support a few files in a directory.
2115935fcd1bSTrevor Thompson * One FILENAME_ATTRIBUTE is added to the directory's index for each link to that file. So, each
2116935fcd1bSTrevor Thompson * file which contains one FILENAME_ATTRIBUTE for a long name and another for the 8.3 name, will
2117935fcd1bSTrevor Thompson * get both attributes added to its parent directory.
2118935fcd1bSTrevor Thompson */
2119935fcd1bSTrevor Thompson NTSTATUS
2120935fcd1bSTrevor Thompson NtfsAddFilenameToDirectory(PDEVICE_EXTENSION DeviceExt,
2121935fcd1bSTrevor Thompson                            ULONGLONG DirectoryMftIndex,
2122935fcd1bSTrevor Thompson                            ULONGLONG FileReferenceNumber,
2123935fcd1bSTrevor Thompson                            PFILENAME_ATTRIBUTE FilenameAttribute,
2124935fcd1bSTrevor Thompson                            BOOLEAN CaseSensitive)
2125935fcd1bSTrevor Thompson {
2126935fcd1bSTrevor Thompson     NTSTATUS Status = STATUS_SUCCESS;
2127935fcd1bSTrevor Thompson     PFILE_RECORD_HEADER ParentFileRecord;
2128935fcd1bSTrevor Thompson     PNTFS_ATTR_CONTEXT IndexRootContext;
2129935fcd1bSTrevor Thompson     PINDEX_ROOT_ATTRIBUTE I30IndexRoot;
2130935fcd1bSTrevor Thompson     ULONG IndexRootOffset;
2131935fcd1bSTrevor Thompson     ULONGLONG I30IndexRootLength;
2132935fcd1bSTrevor Thompson     ULONG LengthWritten;
2133935fcd1bSTrevor Thompson     PINDEX_ROOT_ATTRIBUTE NewIndexRoot;
2134935fcd1bSTrevor Thompson     ULONG AttributeLength;
213588a7c3d1STrevor Thompson     PNTFS_ATTR_RECORD NextAttribute;
2136935fcd1bSTrevor Thompson     PB_TREE NewTree;
2137935fcd1bSTrevor Thompson     ULONG BtreeIndexLength;
213888a7c3d1STrevor Thompson     ULONG MaxIndexRootSize;
2139*52c30fdfSTrevor Thompson     PB_TREE_KEY NewLeftKey;
2140*52c30fdfSTrevor Thompson     PB_TREE_FILENAME_NODE NewRightHandNode;
2141935fcd1bSTrevor Thompson 
2142935fcd1bSTrevor Thompson     // Allocate memory for the parent directory
2143935fcd1bSTrevor Thompson     ParentFileRecord = ExAllocatePoolWithTag(NonPagedPool,
2144935fcd1bSTrevor Thompson                                              DeviceExt->NtfsInfo.BytesPerFileRecord,
2145935fcd1bSTrevor Thompson                                              TAG_NTFS);
2146935fcd1bSTrevor Thompson     if (!ParentFileRecord)
2147935fcd1bSTrevor Thompson     {
2148935fcd1bSTrevor Thompson         DPRINT1("ERROR: Couldn't allocate memory for file record!\n");
2149935fcd1bSTrevor Thompson         return STATUS_INSUFFICIENT_RESOURCES;
2150935fcd1bSTrevor Thompson     }
2151935fcd1bSTrevor Thompson 
2152935fcd1bSTrevor Thompson     // Open the parent directory
2153935fcd1bSTrevor Thompson     Status = ReadFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
2154935fcd1bSTrevor Thompson     if (!NT_SUCCESS(Status))
2155935fcd1bSTrevor Thompson     {
2156935fcd1bSTrevor Thompson         ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
2157935fcd1bSTrevor Thompson         DPRINT1("ERROR: Couldn't read parent directory with index %I64u\n",
2158935fcd1bSTrevor Thompson                 DirectoryMftIndex);
2159935fcd1bSTrevor Thompson         return Status;
2160935fcd1bSTrevor Thompson     }
2161935fcd1bSTrevor Thompson 
2162*52c30fdfSTrevor Thompson #ifndef NDEBUG
2163935fcd1bSTrevor Thompson     DPRINT1("Dumping old parent file record:\n");
2164935fcd1bSTrevor Thompson     NtfsDumpFileRecord(DeviceExt, ParentFileRecord);
2165*52c30fdfSTrevor Thompson #endif
2166935fcd1bSTrevor Thompson 
2167935fcd1bSTrevor Thompson     // Find the index root attribute for the directory
2168935fcd1bSTrevor Thompson     Status = FindAttribute(DeviceExt,
2169935fcd1bSTrevor Thompson                            ParentFileRecord,
2170935fcd1bSTrevor Thompson                            AttributeIndexRoot,
2171935fcd1bSTrevor Thompson                            L"$I30",
2172935fcd1bSTrevor Thompson                            4,
2173935fcd1bSTrevor Thompson                            &IndexRootContext,
2174935fcd1bSTrevor Thompson                            &IndexRootOffset);
2175935fcd1bSTrevor Thompson     if (!NT_SUCCESS(Status))
2176935fcd1bSTrevor Thompson     {
2177935fcd1bSTrevor Thompson         DPRINT1("ERROR: Couldn't find $I30 $INDEX_ROOT attribute for parent directory with MFT #: %I64u!\n",
2178935fcd1bSTrevor Thompson                 DirectoryMftIndex);
2179935fcd1bSTrevor Thompson         ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
2180935fcd1bSTrevor Thompson         return Status;
2181935fcd1bSTrevor Thompson     }
2182935fcd1bSTrevor Thompson 
2183935fcd1bSTrevor Thompson     // Find the maximum index size given what the file record can hold
218488a7c3d1STrevor Thompson     // First, find the max index size assuming index root is the last attribute
218588a7c3d1STrevor Thompson     MaxIndexRootSize = DeviceExt->NtfsInfo.BytesPerFileRecord               // Start with the size of a file record
218688a7c3d1STrevor Thompson                        - IndexRootOffset                                    // Subtract the length of everything that comes before index root
218788a7c3d1STrevor Thompson                        - IndexRootContext->pRecord->Resident.ValueOffset    // Subtract the length of the attribute header for index root
2188*52c30fdfSTrevor Thompson                        - sizeof(INDEX_ROOT_ATTRIBUTE)                       // Subtract the length of the index root header
218988a7c3d1STrevor Thompson                        - (sizeof(ULONG) * 2);                               // Subtract the length of the file record end marker and padding
219088a7c3d1STrevor Thompson 
219188a7c3d1STrevor Thompson     // Are there attributes after this one?
219288a7c3d1STrevor Thompson     NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)ParentFileRecord + IndexRootOffset + IndexRootContext->pRecord->Length);
219388a7c3d1STrevor Thompson     if (NextAttribute->Type != AttributeEnd)
219488a7c3d1STrevor Thompson     {
219588a7c3d1STrevor Thompson         // Find the length of all attributes after this one, not counting the end marker
219688a7c3d1STrevor Thompson         ULONG LengthOfAttributes = 0;
219788a7c3d1STrevor Thompson         PNTFS_ATTR_RECORD CurrentAttribute = NextAttribute;
219888a7c3d1STrevor Thompson         while (CurrentAttribute->Type != AttributeEnd)
219988a7c3d1STrevor Thompson         {
220088a7c3d1STrevor Thompson             LengthOfAttributes += CurrentAttribute->Length;
220188a7c3d1STrevor Thompson             CurrentAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)CurrentAttribute + CurrentAttribute->Length);
220288a7c3d1STrevor Thompson         }
220388a7c3d1STrevor Thompson 
220488a7c3d1STrevor Thompson         // Leave room for the existing attributes
220588a7c3d1STrevor Thompson         MaxIndexRootSize -= LengthOfAttributes;
220688a7c3d1STrevor Thompson     }
2207935fcd1bSTrevor Thompson 
2208935fcd1bSTrevor Thompson     // Allocate memory for the index root data
22094dfcd1d5STrevor Thompson     I30IndexRootLength = AttributeDataLength(IndexRootContext->pRecord);
2210935fcd1bSTrevor Thompson     I30IndexRoot = ExAllocatePoolWithTag(NonPagedPool, I30IndexRootLength, TAG_NTFS);
2211935fcd1bSTrevor Thompson     if (!I30IndexRoot)
2212935fcd1bSTrevor Thompson     {
2213935fcd1bSTrevor Thompson         DPRINT1("ERROR: Couldn't allocate memory for index root attribute!\n");
2214935fcd1bSTrevor Thompson         ReleaseAttributeContext(IndexRootContext);
2215935fcd1bSTrevor Thompson         ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
2216935fcd1bSTrevor Thompson         return STATUS_INSUFFICIENT_RESOURCES;
2217935fcd1bSTrevor Thompson     }
2218935fcd1bSTrevor Thompson 
2219935fcd1bSTrevor Thompson     // Read the Index Root
2220935fcd1bSTrevor Thompson     Status = ReadAttribute(DeviceExt, IndexRootContext, 0, (PCHAR)I30IndexRoot, I30IndexRootLength);
2221935fcd1bSTrevor Thompson     if (!NT_SUCCESS(Status))
2222935fcd1bSTrevor Thompson     {
2223935fcd1bSTrevor Thompson         DPRINT1("ERROR: Couln't read index root attribute for Mft index #%I64u\n", DirectoryMftIndex);
2224935fcd1bSTrevor Thompson         ReleaseAttributeContext(IndexRootContext);
2225935fcd1bSTrevor Thompson         ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2226935fcd1bSTrevor Thompson         ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
2227935fcd1bSTrevor Thompson         return Status;
2228935fcd1bSTrevor Thompson     }
2229935fcd1bSTrevor Thompson 
2230935fcd1bSTrevor Thompson     // Convert the index to a B*Tree
223192d1f92aSTrevor Thompson     Status = CreateBTreeFromIndex(DeviceExt,
223292d1f92aSTrevor Thompson                                   ParentFileRecord,
223392d1f92aSTrevor Thompson                                   IndexRootContext,
223492d1f92aSTrevor Thompson                                   I30IndexRoot,
223592d1f92aSTrevor Thompson                                   &NewTree);
2236935fcd1bSTrevor Thompson     if (!NT_SUCCESS(Status))
2237935fcd1bSTrevor Thompson     {
2238935fcd1bSTrevor Thompson         DPRINT1("ERROR: Failed to create B-Tree from Index!\n");
2239935fcd1bSTrevor Thompson         ReleaseAttributeContext(IndexRootContext);
2240935fcd1bSTrevor Thompson         ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2241935fcd1bSTrevor Thompson         ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
2242935fcd1bSTrevor Thompson         return Status;
2243935fcd1bSTrevor Thompson     }
2244935fcd1bSTrevor Thompson 
2245*52c30fdfSTrevor Thompson #ifndef NDEBUG
2246935fcd1bSTrevor Thompson     DumpBTree(NewTree);
2247*52c30fdfSTrevor Thompson #endif
2248935fcd1bSTrevor Thompson 
2249935fcd1bSTrevor Thompson     // Insert the key for the file we're adding
2250*52c30fdfSTrevor Thompson     Status = NtfsInsertKey(NewTree,
2251*52c30fdfSTrevor Thompson                            FileReferenceNumber,
2252*52c30fdfSTrevor Thompson                            FilenameAttribute,
2253*52c30fdfSTrevor Thompson                            NewTree->RootNode,
2254*52c30fdfSTrevor Thompson                            CaseSensitive,
2255*52c30fdfSTrevor Thompson                            MaxIndexRootSize,
2256*52c30fdfSTrevor Thompson                            I30IndexRoot->SizeOfEntry,
2257*52c30fdfSTrevor Thompson                            &NewLeftKey,
2258*52c30fdfSTrevor Thompson                            &NewRightHandNode);
2259935fcd1bSTrevor Thompson     if (!NT_SUCCESS(Status))
2260935fcd1bSTrevor Thompson     {
2261935fcd1bSTrevor Thompson         DPRINT1("ERROR: Failed to insert key into B-Tree!\n");
2262935fcd1bSTrevor Thompson         DestroyBTree(NewTree);
2263935fcd1bSTrevor Thompson         ReleaseAttributeContext(IndexRootContext);
2264935fcd1bSTrevor Thompson         ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2265935fcd1bSTrevor Thompson         ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
2266935fcd1bSTrevor Thompson         return Status;
2267935fcd1bSTrevor Thompson     }
2268935fcd1bSTrevor Thompson 
2269*52c30fdfSTrevor Thompson #ifndef NDEBUG
2270935fcd1bSTrevor Thompson     DumpBTree(NewTree);
2271*52c30fdfSTrevor Thompson #endif
2272935fcd1bSTrevor Thompson 
2273*52c30fdfSTrevor Thompson     // The root node can't be split
2274*52c30fdfSTrevor Thompson     ASSERT(NewLeftKey == NULL);
2275*52c30fdfSTrevor Thompson     ASSERT(NewRightHandNode == NULL);
2276*52c30fdfSTrevor Thompson 
2277*52c30fdfSTrevor Thompson     // Convert B*Tree back to Index
2278*52c30fdfSTrevor Thompson 
2279*52c30fdfSTrevor Thompson     // Updating the index allocation can change the size available for the index root,
2280*52c30fdfSTrevor Thompson     // And if the index root is demoted, the index allocation will need to be updated again,
2281*52c30fdfSTrevor Thompson     // which may change the size available for index root... etc.
2282*52c30fdfSTrevor Thompson     // My solution is to decrease index root to the size it would be if it was demoted,
2283*52c30fdfSTrevor Thompson     // then UpdateIndexAllocation will have an accurate representation of the maximum space
2284*52c30fdfSTrevor Thompson     // it can use in the file record. There's still a chance that the act of allocating an
2285*52c30fdfSTrevor Thompson     // index node after demoting the index root will increase the size of the file record beyond
2286*52c30fdfSTrevor Thompson     // it's limit, but if that happens, an attribute-list will most definitely be needed.
2287*52c30fdfSTrevor Thompson     // This a bit hacky, but it seems to be functional.
2288*52c30fdfSTrevor Thompson 
2289*52c30fdfSTrevor Thompson     // Calculate the minimum size of the index root attribute, considering one dummy key and one VCN
2290*52c30fdfSTrevor Thompson     LARGE_INTEGER MinIndexRootSize;
2291*52c30fdfSTrevor Thompson     MinIndexRootSize.QuadPart = sizeof(INDEX_ROOT_ATTRIBUTE) // size of the index root headers
2292*52c30fdfSTrevor Thompson                                 + 0x18; // Size of dummy key with a VCN for a subnode
2293*52c30fdfSTrevor Thompson     ASSERT(MinIndexRootSize.QuadPart % ATTR_RECORD_ALIGNMENT == 0);
2294*52c30fdfSTrevor Thompson 
2295*52c30fdfSTrevor Thompson     // Temporarily shrink the index root to it's minimal size
2296*52c30fdfSTrevor Thompson     AttributeLength = MinIndexRootSize.LowPart;
2297*52c30fdfSTrevor Thompson     AttributeLength += sizeof(INDEX_ROOT_ATTRIBUTE);
2298*52c30fdfSTrevor Thompson 
2299*52c30fdfSTrevor Thompson 
2300*52c30fdfSTrevor Thompson     // FIXME: IndexRoot will probably be invalid until we're finished. If we fail before we finish, the directory will probably be toast.
2301*52c30fdfSTrevor Thompson     // The potential for catastrophic data-loss exists!!! :)
2302*52c30fdfSTrevor Thompson 
2303*52c30fdfSTrevor Thompson     // Update the length of the attribute in the file record of the parent directory
2304*52c30fdfSTrevor Thompson     Status = InternalSetResidentAttributeLength(DeviceExt,
2305*52c30fdfSTrevor Thompson                                                 IndexRootContext,
2306*52c30fdfSTrevor Thompson                                                 ParentFileRecord,
2307*52c30fdfSTrevor Thompson                                                 IndexRootOffset,
2308*52c30fdfSTrevor Thompson                                                 AttributeLength);
2309*52c30fdfSTrevor Thompson     if (!NT_SUCCESS(Status))
2310*52c30fdfSTrevor Thompson     {
2311*52c30fdfSTrevor Thompson         DPRINT1("ERROR: Unable to set length of index root!\n");
2312*52c30fdfSTrevor Thompson         DestroyBTree(NewTree);
2313*52c30fdfSTrevor Thompson         ReleaseAttributeContext(IndexRootContext);
2314*52c30fdfSTrevor Thompson         ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2315*52c30fdfSTrevor Thompson         ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
2316*52c30fdfSTrevor Thompson         return Status;
2317*52c30fdfSTrevor Thompson     }
2318*52c30fdfSTrevor Thompson 
2319*52c30fdfSTrevor Thompson     // Update the index allocation
232092d1f92aSTrevor Thompson     Status = UpdateIndexAllocation(DeviceExt, NewTree, I30IndexRoot->SizeOfEntry, ParentFileRecord);
232192d1f92aSTrevor Thompson     if (!NT_SUCCESS(Status))
232292d1f92aSTrevor Thompson     {
232392d1f92aSTrevor Thompson         DPRINT1("ERROR: Failed to update index allocation from B-Tree!\n");
232492d1f92aSTrevor Thompson         DestroyBTree(NewTree);
232592d1f92aSTrevor Thompson         ReleaseAttributeContext(IndexRootContext);
232692d1f92aSTrevor Thompson         ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
232792d1f92aSTrevor Thompson         ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
232892d1f92aSTrevor Thompson         return Status;
232992d1f92aSTrevor Thompson     }
233092d1f92aSTrevor Thompson 
2331*52c30fdfSTrevor Thompson #ifndef NDEBUG
2332*52c30fdfSTrevor Thompson     DPRINT1("Index Allocation updated\n");
2333*52c30fdfSTrevor Thompson     DumpBTree(NewTree);
2334*52c30fdfSTrevor Thompson #endif
2335*52c30fdfSTrevor Thompson 
2336*52c30fdfSTrevor Thompson     // Find the maximum index root size given what the file record can hold
2337*52c30fdfSTrevor Thompson     // First, find the max index size assuming index root is the last attribute
2338*52c30fdfSTrevor Thompson     ULONG NewMaxIndexRootSize =
2339*52c30fdfSTrevor Thompson        DeviceExt->NtfsInfo.BytesPerFileRecord                // Start with the size of a file record
2340*52c30fdfSTrevor Thompson         - IndexRootOffset                                    // Subtract the length of everything that comes before index root
2341*52c30fdfSTrevor Thompson         - IndexRootContext->pRecord->Resident.ValueOffset    // Subtract the length of the attribute header for index root
2342*52c30fdfSTrevor Thompson         - sizeof(INDEX_ROOT_ATTRIBUTE)                       // Subtract the length of the index root header
2343*52c30fdfSTrevor Thompson         - (sizeof(ULONG) * 2);                               // Subtract the length of the file record end marker and padding
2344*52c30fdfSTrevor Thompson 
2345*52c30fdfSTrevor Thompson     // Are there attributes after this one?
2346*52c30fdfSTrevor Thompson     NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)ParentFileRecord + IndexRootOffset + IndexRootContext->pRecord->Length);
2347*52c30fdfSTrevor Thompson     if (NextAttribute->Type != AttributeEnd)
2348*52c30fdfSTrevor Thompson     {
2349*52c30fdfSTrevor Thompson         // Find the length of all attributes after this one, not counting the end marker
2350*52c30fdfSTrevor Thompson         ULONG LengthOfAttributes = 0;
2351*52c30fdfSTrevor Thompson         PNTFS_ATTR_RECORD CurrentAttribute = NextAttribute;
2352*52c30fdfSTrevor Thompson         while (CurrentAttribute->Type != AttributeEnd)
2353*52c30fdfSTrevor Thompson         {
2354*52c30fdfSTrevor Thompson             LengthOfAttributes += CurrentAttribute->Length;
2355*52c30fdfSTrevor Thompson             CurrentAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)CurrentAttribute + CurrentAttribute->Length);
2356*52c30fdfSTrevor Thompson         }
2357*52c30fdfSTrevor Thompson 
2358*52c30fdfSTrevor Thompson         // Leave room for the existing attributes
2359*52c30fdfSTrevor Thompson         NewMaxIndexRootSize -= LengthOfAttributes;
2360*52c30fdfSTrevor Thompson     }
2361*52c30fdfSTrevor Thompson 
2362*52c30fdfSTrevor Thompson     // The index allocation and index bitmap may have grown, leaving less room for the index root,
2363*52c30fdfSTrevor Thompson     // so now we need to double-check that index root isn't too large
2364*52c30fdfSTrevor Thompson     ULONG NodeSize = GetSizeOfIndexEntries(NewTree->RootNode);
2365*52c30fdfSTrevor Thompson     if (NodeSize > NewMaxIndexRootSize)
2366*52c30fdfSTrevor Thompson     {
2367*52c30fdfSTrevor Thompson         DPRINT1("Demoting index root.\nNodeSize: 0x%lx\nNewMaxIndexRootSize: 0x%lx\n", NodeSize, NewMaxIndexRootSize);
2368*52c30fdfSTrevor Thompson 
2369*52c30fdfSTrevor Thompson         Status = DemoteBTreeRoot(NewTree);
2370*52c30fdfSTrevor Thompson         if (!NT_SUCCESS(Status))
2371*52c30fdfSTrevor Thompson         {
2372*52c30fdfSTrevor Thompson             DPRINT1("ERROR: Failed to demote index root!\n");
2373*52c30fdfSTrevor Thompson             DestroyBTree(NewTree);
2374*52c30fdfSTrevor Thompson             ReleaseAttributeContext(IndexRootContext);
2375*52c30fdfSTrevor Thompson             ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2376*52c30fdfSTrevor Thompson             ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
2377*52c30fdfSTrevor Thompson             return Status;
2378*52c30fdfSTrevor Thompson         }
2379*52c30fdfSTrevor Thompson 
2380*52c30fdfSTrevor Thompson         // We need to update the index allocation once more
2381*52c30fdfSTrevor Thompson         Status = UpdateIndexAllocation(DeviceExt, NewTree, I30IndexRoot->SizeOfEntry, ParentFileRecord);
2382*52c30fdfSTrevor Thompson         if (!NT_SUCCESS(Status))
2383*52c30fdfSTrevor Thompson         {
2384*52c30fdfSTrevor Thompson             DPRINT1("ERROR: Failed to update index allocation from B-Tree!\n");
2385*52c30fdfSTrevor Thompson             DestroyBTree(NewTree);
2386*52c30fdfSTrevor Thompson             ReleaseAttributeContext(IndexRootContext);
2387*52c30fdfSTrevor Thompson             ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2388*52c30fdfSTrevor Thompson             ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
2389*52c30fdfSTrevor Thompson             return Status;
2390*52c30fdfSTrevor Thompson         }
2391*52c30fdfSTrevor Thompson 
2392*52c30fdfSTrevor Thompson         // re-recalculate max size of index root
2393*52c30fdfSTrevor Thompson         NewMaxIndexRootSize =
2394*52c30fdfSTrevor Thompson             // Find the maximum index size given what the file record can hold
2395*52c30fdfSTrevor Thompson             // First, find the max index size assuming index root is the last attribute
2396*52c30fdfSTrevor Thompson             DeviceExt->NtfsInfo.BytesPerFileRecord               // Start with the size of a file record
2397*52c30fdfSTrevor Thompson             - IndexRootOffset                                    // Subtract the length of everything that comes before index root
2398*52c30fdfSTrevor Thompson             - IndexRootContext->pRecord->Resident.ValueOffset    // Subtract the length of the attribute header for index root
2399*52c30fdfSTrevor Thompson             - sizeof(INDEX_ROOT_ATTRIBUTE)                       // Subtract the length of the index root header
2400*52c30fdfSTrevor Thompson             - (sizeof(ULONG) * 2);                               // Subtract the length of the file record end marker and padding
2401*52c30fdfSTrevor Thompson 
2402*52c30fdfSTrevor Thompson                                                                  // Are there attributes after this one?
2403*52c30fdfSTrevor Thompson         NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)ParentFileRecord + IndexRootOffset + IndexRootContext->pRecord->Length);
2404*52c30fdfSTrevor Thompson         if (NextAttribute->Type != AttributeEnd)
2405*52c30fdfSTrevor Thompson         {
2406*52c30fdfSTrevor Thompson             // Find the length of all attributes after this one, not counting the end marker
2407*52c30fdfSTrevor Thompson             ULONG LengthOfAttributes = 0;
2408*52c30fdfSTrevor Thompson             PNTFS_ATTR_RECORD CurrentAttribute = NextAttribute;
2409*52c30fdfSTrevor Thompson             while (CurrentAttribute->Type != AttributeEnd)
2410*52c30fdfSTrevor Thompson             {
2411*52c30fdfSTrevor Thompson                 LengthOfAttributes += CurrentAttribute->Length;
2412*52c30fdfSTrevor Thompson                 CurrentAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)CurrentAttribute + CurrentAttribute->Length);
2413*52c30fdfSTrevor Thompson             }
2414*52c30fdfSTrevor Thompson 
2415*52c30fdfSTrevor Thompson             // Leave room for the existing attributes
2416*52c30fdfSTrevor Thompson             NewMaxIndexRootSize -= LengthOfAttributes;
2417*52c30fdfSTrevor Thompson         }
2418*52c30fdfSTrevor Thompson 
2419*52c30fdfSTrevor Thompson 
2420*52c30fdfSTrevor Thompson     }
2421*52c30fdfSTrevor Thompson 
242292d1f92aSTrevor Thompson     // Create the Index Root from the B*Tree
2423*52c30fdfSTrevor Thompson     Status = CreateIndexRootFromBTree(DeviceExt, NewTree, NewMaxIndexRootSize, &NewIndexRoot, &BtreeIndexLength);
2424935fcd1bSTrevor Thompson     if (!NT_SUCCESS(Status))
2425935fcd1bSTrevor Thompson     {
2426935fcd1bSTrevor Thompson         DPRINT1("ERROR: Failed to create Index root from B-Tree!\n");
2427935fcd1bSTrevor Thompson         DestroyBTree(NewTree);
2428935fcd1bSTrevor Thompson         ReleaseAttributeContext(IndexRootContext);
2429935fcd1bSTrevor Thompson         ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2430935fcd1bSTrevor Thompson         ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
2431935fcd1bSTrevor Thompson         return Status;
2432935fcd1bSTrevor Thompson     }
2433935fcd1bSTrevor Thompson 
2434935fcd1bSTrevor Thompson     // We're done with the B-Tree now
2435935fcd1bSTrevor Thompson     DestroyBTree(NewTree);
2436935fcd1bSTrevor Thompson 
2437935fcd1bSTrevor Thompson     // Write back the new index root attribute to the parent directory file record
2438935fcd1bSTrevor Thompson 
2439935fcd1bSTrevor Thompson     // First, we need to resize the attribute.
2440935fcd1bSTrevor Thompson     // CreateIndexRootFromBTree() should have verified that the index root fits within MaxIndexSize.
2441*52c30fdfSTrevor Thompson     // We can't set the size as we normally would, because $INDEX_ROOT must always be resident.
2442935fcd1bSTrevor Thompson     AttributeLength = NewIndexRoot->Header.AllocatedSize + FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header);
24435579428bSTrevor Thompson 
24444dfcd1d5STrevor Thompson     if (AttributeLength != IndexRootContext->pRecord->Resident.ValueLength)
24455579428bSTrevor Thompson     {
2446935fcd1bSTrevor Thompson         // Update the length of the attribute in the file record of the parent directory
2447d484d91eSTrevor Thompson         Status = InternalSetResidentAttributeLength(DeviceExt,
2448d484d91eSTrevor Thompson                                                     IndexRootContext,
2449935fcd1bSTrevor Thompson                                                     ParentFileRecord,
2450935fcd1bSTrevor Thompson                                                     IndexRootOffset,
2451935fcd1bSTrevor Thompson                                                     AttributeLength);
24524dfcd1d5STrevor Thompson         if (!NT_SUCCESS(Status))
24534dfcd1d5STrevor Thompson         {
24544dfcd1d5STrevor Thompson             ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
24554dfcd1d5STrevor Thompson             ReleaseAttributeContext(IndexRootContext);
24564dfcd1d5STrevor Thompson             ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
24574dfcd1d5STrevor Thompson             ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
24584dfcd1d5STrevor Thompson             DPRINT1("ERROR: Unable to set resident attribute length!\n");
24594dfcd1d5STrevor Thompson             return Status;
24604dfcd1d5STrevor Thompson         }
24614dfcd1d5STrevor Thompson 
24625579428bSTrevor Thompson     }
2463935fcd1bSTrevor Thompson 
2464935fcd1bSTrevor Thompson     NT_ASSERT(ParentFileRecord->BytesInUse <= DeviceExt->NtfsInfo.BytesPerFileRecord);
2465935fcd1bSTrevor Thompson 
2466935fcd1bSTrevor Thompson     Status = UpdateFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
2467935fcd1bSTrevor Thompson     if (!NT_SUCCESS(Status))
2468935fcd1bSTrevor Thompson     {
2469935fcd1bSTrevor Thompson         DPRINT1("ERROR: Failed to update file record of directory with index: %llx\n", DirectoryMftIndex);
2470935fcd1bSTrevor Thompson         ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
2471935fcd1bSTrevor Thompson         ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
2472935fcd1bSTrevor Thompson         ReleaseAttributeContext(IndexRootContext);
2473935fcd1bSTrevor Thompson         ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2474935fcd1bSTrevor Thompson         return Status;
2475935fcd1bSTrevor Thompson     }
2476935fcd1bSTrevor Thompson 
2477935fcd1bSTrevor Thompson     // Write the new index root to disk
2478935fcd1bSTrevor Thompson     Status = WriteAttribute(DeviceExt,
2479935fcd1bSTrevor Thompson                             IndexRootContext,
2480935fcd1bSTrevor Thompson                             0,
2481935fcd1bSTrevor Thompson                             (PUCHAR)NewIndexRoot,
2482935fcd1bSTrevor Thompson                             AttributeLength,
2483d484d91eSTrevor Thompson                             &LengthWritten,
2484d484d91eSTrevor Thompson                             ParentFileRecord);
2485b033f00fSTrevor Thompson     if (!NT_SUCCESS(Status) || LengthWritten != AttributeLength)
2486935fcd1bSTrevor Thompson     {
2487935fcd1bSTrevor Thompson         DPRINT1("ERROR: Unable to write new index root attribute to parent directory!\n");
2488935fcd1bSTrevor Thompson         ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
2489935fcd1bSTrevor Thompson         ReleaseAttributeContext(IndexRootContext);
2490935fcd1bSTrevor Thompson         ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2491935fcd1bSTrevor Thompson         ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
2492935fcd1bSTrevor Thompson         return Status;
2493935fcd1bSTrevor Thompson     }
2494935fcd1bSTrevor Thompson 
2495935fcd1bSTrevor Thompson     // re-read the parent file record, so we can dump it
2496935fcd1bSTrevor Thompson     Status = ReadFileRecord(DeviceExt, DirectoryMftIndex, ParentFileRecord);
2497935fcd1bSTrevor Thompson     if (!NT_SUCCESS(Status))
2498935fcd1bSTrevor Thompson     {
2499935fcd1bSTrevor Thompson         DPRINT1("ERROR: Couldn't read parent directory after messing with it!\n");
2500935fcd1bSTrevor Thompson     }
2501935fcd1bSTrevor Thompson     else
2502935fcd1bSTrevor Thompson     {
2503*52c30fdfSTrevor Thompson #ifndef NDEBUG
2504*52c30fdfSTrevor Thompson         DPRINT1("Dumping new B-Tree:\n");
2505*52c30fdfSTrevor Thompson 
2506*52c30fdfSTrevor Thompson         Status = CreateBTreeFromIndex(DeviceExt, ParentFileRecord, IndexRootContext, NewIndexRoot, &NewTree);
2507*52c30fdfSTrevor Thompson         if (!NT_SUCCESS(Status))
2508*52c30fdfSTrevor Thompson         {
2509*52c30fdfSTrevor Thompson             DPRINT1("ERROR: Couldn't re-create b-tree\n");
2510*52c30fdfSTrevor Thompson             return Status;
2511*52c30fdfSTrevor Thompson         }
2512*52c30fdfSTrevor Thompson 
2513*52c30fdfSTrevor Thompson         DumpBTree(NewTree);
2514*52c30fdfSTrevor Thompson 
2515*52c30fdfSTrevor Thompson         DestroyBTree(NewTree);
2516*52c30fdfSTrevor Thompson 
2517935fcd1bSTrevor Thompson         NtfsDumpFileRecord(DeviceExt, ParentFileRecord);
2518*52c30fdfSTrevor Thompson #endif
2519935fcd1bSTrevor Thompson     }
2520935fcd1bSTrevor Thompson 
2521935fcd1bSTrevor Thompson     // Cleanup
2522935fcd1bSTrevor Thompson     ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
2523935fcd1bSTrevor Thompson     ReleaseAttributeContext(IndexRootContext);
2524935fcd1bSTrevor Thompson     ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
2525935fcd1bSTrevor Thompson     ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
2526935fcd1bSTrevor Thompson 
2527935fcd1bSTrevor Thompson     return Status;
2528935fcd1bSTrevor Thompson }
2529935fcd1bSTrevor Thompson 
2530ba33b9faSTrevor Thompson NTSTATUS
2531ba33b9faSTrevor Thompson AddFixupArray(PDEVICE_EXTENSION Vcb,
25324f8133f4STrevor Thompson               PNTFS_RECORD_HEADER Record)
2533ba33b9faSTrevor Thompson {
2534ba33b9faSTrevor Thompson     USHORT *pShortToFixUp;
253568a48b27STrevor Thompson     ULONG ArrayEntryCount = Record->UsaCount - 1;
253668a48b27STrevor Thompson     ULONG Offset = Vcb->NtfsInfo.BytesPerSector - 2;
253768a48b27STrevor Thompson     ULONG i;
2538ba33b9faSTrevor Thompson 
25394f8133f4STrevor Thompson     PFIXUP_ARRAY fixupArray = (PFIXUP_ARRAY)((UCHAR*)Record + Record->UsaOffset);
2540ba33b9faSTrevor Thompson 
2541ba33b9faSTrevor Thompson     DPRINT("AddFixupArray(%p, %p)\n fixupArray->USN: %u, ArrayEntryCount: %u\n", Vcb, Record, fixupArray->USN, ArrayEntryCount);
2542ba33b9faSTrevor Thompson 
2543ba33b9faSTrevor Thompson     fixupArray->USN++;
2544ba33b9faSTrevor Thompson 
2545ba33b9faSTrevor Thompson     for (i = 0; i < ArrayEntryCount; i++)
2546ba33b9faSTrevor Thompson     {
2547ba33b9faSTrevor Thompson         DPRINT("USN: %u\tOffset: %u\n", fixupArray->USN, Offset);
2548ba33b9faSTrevor Thompson 
25494f8133f4STrevor Thompson         pShortToFixUp = (USHORT*)((PCHAR)Record + Offset);
2550ba33b9faSTrevor Thompson         fixupArray->Array[i] = *pShortToFixUp;
2551ba33b9faSTrevor Thompson         *pShortToFixUp = fixupArray->USN;
2552ba33b9faSTrevor Thompson         Offset += Vcb->NtfsInfo.BytesPerSector;
2553ba33b9faSTrevor Thompson     }
2554ba33b9faSTrevor Thompson 
2555ba33b9faSTrevor Thompson     return STATUS_SUCCESS;
2556ba33b9faSTrevor Thompson }
2557c2c66affSColin Finck 
2558c2c66affSColin Finck NTSTATUS
2559c2c66affSColin Finck ReadLCN(PDEVICE_EXTENSION Vcb,
2560c2c66affSColin Finck         ULONGLONG lcn,
2561c2c66affSColin Finck         ULONG count,
2562c2c66affSColin Finck         PVOID buffer)
2563c2c66affSColin Finck {
2564c2c66affSColin Finck     LARGE_INTEGER DiskSector;
2565c2c66affSColin Finck 
2566c2c66affSColin Finck     DiskSector.QuadPart = lcn;
2567c2c66affSColin Finck 
2568c2c66affSColin Finck     return NtfsReadSectors(Vcb->StorageDevice,
2569c2c66affSColin Finck                            DiskSector.u.LowPart * Vcb->NtfsInfo.SectorsPerCluster,
2570c2c66affSColin Finck                            count * Vcb->NtfsInfo.SectorsPerCluster,
2571c2c66affSColin Finck                            Vcb->NtfsInfo.BytesPerSector,
2572c2c66affSColin Finck                            buffer,
2573c2c66affSColin Finck                            FALSE);
2574c2c66affSColin Finck }
2575c2c66affSColin Finck 
2576c2c66affSColin Finck 
2577c2c66affSColin Finck BOOLEAN
2578c2c66affSColin Finck CompareFileName(PUNICODE_STRING FileName,
2579c2c66affSColin Finck                 PINDEX_ENTRY_ATTRIBUTE IndexEntry,
2580032be029STrevor Thompson                 BOOLEAN DirSearch,
2581032be029STrevor Thompson                 BOOLEAN CaseSensitive)
2582c2c66affSColin Finck {
2583c2c66affSColin Finck     BOOLEAN Ret, Alloc = FALSE;
2584c2c66affSColin Finck     UNICODE_STRING EntryName;
2585c2c66affSColin Finck 
2586c2c66affSColin Finck     EntryName.Buffer = IndexEntry->FileName.Name;
2587c2c66affSColin Finck     EntryName.Length =
2588c2c66affSColin Finck     EntryName.MaximumLength = IndexEntry->FileName.NameLength * sizeof(WCHAR);
2589c2c66affSColin Finck 
2590c2c66affSColin Finck     if (DirSearch)
2591c2c66affSColin Finck     {
2592c2c66affSColin Finck         UNICODE_STRING IntFileName;
2593032be029STrevor Thompson         if (!CaseSensitive)
2594c2c66affSColin Finck         {
2595c2c66affSColin Finck             NT_VERIFY(NT_SUCCESS(RtlUpcaseUnicodeString(&IntFileName, FileName, TRUE)));
2596c2c66affSColin Finck             Alloc = TRUE;
2597c2c66affSColin Finck         }
2598c2c66affSColin Finck         else
2599c2c66affSColin Finck         {
2600c2c66affSColin Finck             IntFileName = *FileName;
2601c2c66affSColin Finck         }
2602c2c66affSColin Finck 
2603032be029STrevor Thompson         Ret = FsRtlIsNameInExpression(&IntFileName, &EntryName, !CaseSensitive, NULL);
2604c2c66affSColin Finck 
2605c2c66affSColin Finck         if (Alloc)
2606c2c66affSColin Finck         {
2607c2c66affSColin Finck             RtlFreeUnicodeString(&IntFileName);
2608c2c66affSColin Finck         }
2609c2c66affSColin Finck 
2610c2c66affSColin Finck         return Ret;
2611c2c66affSColin Finck     }
2612c2c66affSColin Finck     else
2613c2c66affSColin Finck     {
2614032be029STrevor Thompson         return (RtlCompareUnicodeString(FileName, &EntryName, !CaseSensitive) == 0);
2615c2c66affSColin Finck     }
2616c2c66affSColin Finck }
2617c2c66affSColin Finck 
26185e7c1184STrevor Thompson /**
26195e7c1184STrevor Thompson * @name UpdateMftMirror
26205e7c1184STrevor Thompson * @implemented
26215e7c1184STrevor Thompson *
26225e7c1184STrevor Thompson * Backs-up the first ~4 master file table entries to the $MFTMirr file.
26235e7c1184STrevor Thompson *
26245e7c1184STrevor Thompson * @param Vcb
26255e7c1184STrevor Thompson * Pointer to an NTFS_VCB for the volume whose Mft mirror is being updated.
26265e7c1184STrevor Thompson *
26275e7c1184STrevor Thompson * @returninja livecd
26285e7c1184STrevor Thompson 
26295e7c1184STrevor Thompson * STATUS_SUCCESS on success.
26305e7c1184STrevor Thompson * STATUS_INSUFFICIENT_RESOURCES if an allocation failed.
26315e7c1184STrevor Thompson * STATUS_UNSUCCESSFUL if we couldn't read the master file table.
26325e7c1184STrevor Thompson *
26335e7c1184STrevor Thompson * @remarks
26345e7c1184STrevor Thompson * NTFS maintains up-to-date copies of the first several mft entries in the $MFTMirr file. Usually, the first 4 file
26355e7c1184STrevor Thompson * records from the mft are stored. The exact number of entries is determined by the size of $MFTMirr's $DATA.
26365e7c1184STrevor Thompson * If $MFTMirr is not up-to-date, chkdsk will reject every change it can find prior to when $MFTMirr was last updated.
26375e7c1184STrevor Thompson * Therefore, it's recommended to call this function if the volume changes considerably. For instance, IncreaseMftSize()
26385e7c1184STrevor Thompson * relies on this function to keep chkdsk from deleting the mft entries it creates. Note that under most instances, creating
26395e7c1184STrevor Thompson * or deleting a file will not affect the first ~four mft entries, and so will not require updating the mft mirror.
26405e7c1184STrevor Thompson */
26415e7c1184STrevor Thompson NTSTATUS
26425e7c1184STrevor Thompson UpdateMftMirror(PNTFS_VCB Vcb)
26435e7c1184STrevor Thompson {
26445e7c1184STrevor Thompson     PFILE_RECORD_HEADER MirrorFileRecord;
26455e7c1184STrevor Thompson     PNTFS_ATTR_CONTEXT MirrDataContext;
26465e7c1184STrevor Thompson     PNTFS_ATTR_CONTEXT MftDataContext;
26475e7c1184STrevor Thompson     PCHAR DataBuffer;
26485e7c1184STrevor Thompson     ULONGLONG DataLength;
26495e7c1184STrevor Thompson     NTSTATUS Status;
26505e7c1184STrevor Thompson     ULONG BytesRead;
26515e7c1184STrevor Thompson     ULONG LengthWritten;
26525e7c1184STrevor Thompson 
26535e7c1184STrevor Thompson     // Allocate memory for the Mft mirror file record
26545e7c1184STrevor Thompson     MirrorFileRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
26555e7c1184STrevor Thompson     if (!MirrorFileRecord)
26565e7c1184STrevor Thompson     {
26575e7c1184STrevor Thompson         DPRINT1("Error: Failed to allocate memory for $MFTMirr!\n");
26585e7c1184STrevor Thompson         return STATUS_INSUFFICIENT_RESOURCES;
26595e7c1184STrevor Thompson     }
26605e7c1184STrevor Thompson 
26615e7c1184STrevor Thompson     // Read the Mft Mirror file record
26625e7c1184STrevor Thompson     Status = ReadFileRecord(Vcb, NTFS_FILE_MFTMIRR, MirrorFileRecord);
26635e7c1184STrevor Thompson     if (!NT_SUCCESS(Status))
26645e7c1184STrevor Thompson     {
26655e7c1184STrevor Thompson         DPRINT1("ERROR: Failed to read $MFTMirr!\n");
26665e7c1184STrevor Thompson         ExFreePoolWithTag(MirrorFileRecord, TAG_NTFS);
26675e7c1184STrevor Thompson         return Status;
26685e7c1184STrevor Thompson     }
26695e7c1184STrevor Thompson 
26705e7c1184STrevor Thompson     // Find the $DATA attribute of $MFTMirr
26715e7c1184STrevor Thompson     Status = FindAttribute(Vcb, MirrorFileRecord, AttributeData, L"", 0, &MirrDataContext, NULL);
26725e7c1184STrevor Thompson     if (!NT_SUCCESS(Status))
26735e7c1184STrevor Thompson     {
26745e7c1184STrevor Thompson         DPRINT1("ERROR: Couldn't find $DATA attribute!\n");
26755e7c1184STrevor Thompson         ExFreePoolWithTag(MirrorFileRecord, TAG_NTFS);
26765e7c1184STrevor Thompson         return Status;
26775e7c1184STrevor Thompson     }
26785e7c1184STrevor Thompson 
26795e7c1184STrevor Thompson     // Find the $DATA attribute of $MFT
26805e7c1184STrevor Thompson     Status = FindAttribute(Vcb, Vcb->MasterFileTable, AttributeData, L"", 0, &MftDataContext, NULL);
26815e7c1184STrevor Thompson     if (!NT_SUCCESS(Status))
26825e7c1184STrevor Thompson     {
26835e7c1184STrevor Thompson         DPRINT1("ERROR: Couldn't find $DATA attribute!\n");
26845e7c1184STrevor Thompson         ReleaseAttributeContext(MirrDataContext);
26855e7c1184STrevor Thompson         ExFreePoolWithTag(MirrorFileRecord, TAG_NTFS);
26865e7c1184STrevor Thompson         return Status;
26875e7c1184STrevor Thompson     }
26885e7c1184STrevor Thompson 
26895e7c1184STrevor Thompson     // Get the size of the mirror's $DATA attribute
26905e7c1184STrevor Thompson     DataLength = AttributeDataLength(MirrDataContext->pRecord);
26915e7c1184STrevor Thompson 
26925e7c1184STrevor Thompson     ASSERT(DataLength % Vcb->NtfsInfo.BytesPerFileRecord == 0);
26935e7c1184STrevor Thompson 
26945e7c1184STrevor Thompson     // Create buffer for the mirror's $DATA attribute
26955e7c1184STrevor Thompson     DataBuffer = ExAllocatePoolWithTag(NonPagedPool, DataLength, TAG_NTFS);
26965e7c1184STrevor Thompson     if (!DataBuffer)
26975e7c1184STrevor Thompson     {
26985e7c1184STrevor Thompson         DPRINT1("Error: Couldn't allocate memory for $DATA buffer!\n");
26995e7c1184STrevor Thompson         ReleaseAttributeContext(MftDataContext);
27005e7c1184STrevor Thompson         ReleaseAttributeContext(MirrDataContext);
27015e7c1184STrevor Thompson         ExFreePoolWithTag(MirrorFileRecord, TAG_NTFS);
27025e7c1184STrevor Thompson         return STATUS_INSUFFICIENT_RESOURCES;
27035e7c1184STrevor Thompson     }
27045e7c1184STrevor Thompson 
27055e7c1184STrevor Thompson     ASSERT(DataLength < ULONG_MAX);
27065e7c1184STrevor Thompson 
27075e7c1184STrevor Thompson     // Back up the first several entries of the Mft's $DATA Attribute
27085e7c1184STrevor Thompson     BytesRead = ReadAttribute(Vcb, MftDataContext, 0, DataBuffer, (ULONG)DataLength);
27095e7c1184STrevor Thompson     if (BytesRead != (ULONG)DataLength)
27105e7c1184STrevor Thompson     {
27115e7c1184STrevor Thompson         DPRINT1("Error: Failed to read $DATA for $MFTMirr!\n");
27125e7c1184STrevor Thompson         ReleaseAttributeContext(MftDataContext);
27135e7c1184STrevor Thompson         ReleaseAttributeContext(MirrDataContext);
27145e7c1184STrevor Thompson         ExFreePoolWithTag(DataBuffer, TAG_NTFS);
27155e7c1184STrevor Thompson         ExFreePoolWithTag(MirrorFileRecord, TAG_NTFS);
27165e7c1184STrevor Thompson         return STATUS_UNSUCCESSFUL;
27175e7c1184STrevor Thompson     }
27185e7c1184STrevor Thompson 
27195e7c1184STrevor Thompson     // Write the mirror's $DATA attribute
27205e7c1184STrevor Thompson     Status = WriteAttribute(Vcb,
27215e7c1184STrevor Thompson                              MirrDataContext,
27225e7c1184STrevor Thompson                              0,
27235e7c1184STrevor Thompson                              (PUCHAR)DataBuffer,
27245e7c1184STrevor Thompson                              DataLength,
27255e7c1184STrevor Thompson                              &LengthWritten,
27265e7c1184STrevor Thompson                              MirrorFileRecord);
27275e7c1184STrevor Thompson     if (!NT_SUCCESS(Status))
27285e7c1184STrevor Thompson     {
27295e7c1184STrevor Thompson         DPRINT1("ERROR: Failed to write $DATA attribute of $MFTMirr!\n");
27305e7c1184STrevor Thompson     }
27315e7c1184STrevor Thompson 
27325e7c1184STrevor Thompson     // Cleanup
27335e7c1184STrevor Thompson     ReleaseAttributeContext(MftDataContext);
27345e7c1184STrevor Thompson     ReleaseAttributeContext(MirrDataContext);
27355e7c1184STrevor Thompson     ExFreePoolWithTag(DataBuffer, TAG_NTFS);
27365e7c1184STrevor Thompson     ExFreePoolWithTag(MirrorFileRecord, TAG_NTFS);
27375e7c1184STrevor Thompson 
27385e7c1184STrevor Thompson     return Status;
27395e7c1184STrevor Thompson }
27405e7c1184STrevor Thompson 
2741c2c66affSColin Finck #if 0
2742c2c66affSColin Finck static
2743c2c66affSColin Finck VOID
2744c2c66affSColin Finck DumpIndexEntry(PINDEX_ENTRY_ATTRIBUTE IndexEntry)
2745c2c66affSColin Finck {
2746c2c66affSColin Finck     DPRINT1("Entry: %p\n", IndexEntry);
2747c2c66affSColin Finck     DPRINT1("\tData.Directory.IndexedFile: %I64x\n", IndexEntry->Data.Directory.IndexedFile);
2748c2c66affSColin Finck     DPRINT1("\tLength: %u\n", IndexEntry->Length);
2749c2c66affSColin Finck     DPRINT1("\tKeyLength: %u\n", IndexEntry->KeyLength);
2750c2c66affSColin Finck     DPRINT1("\tFlags: %x\n", IndexEntry->Flags);
2751c2c66affSColin Finck     DPRINT1("\tReserved: %x\n", IndexEntry->Reserved);
2752c2c66affSColin Finck     DPRINT1("\t\tDirectoryFileReferenceNumber: %I64x\n", IndexEntry->FileName.DirectoryFileReferenceNumber);
2753c2c66affSColin Finck     DPRINT1("\t\tCreationTime: %I64u\n", IndexEntry->FileName.CreationTime);
2754c2c66affSColin Finck     DPRINT1("\t\tChangeTime: %I64u\n", IndexEntry->FileName.ChangeTime);
2755c2c66affSColin Finck     DPRINT1("\t\tLastWriteTime: %I64u\n", IndexEntry->FileName.LastWriteTime);
2756c2c66affSColin Finck     DPRINT1("\t\tLastAccessTime: %I64u\n", IndexEntry->FileName.LastAccessTime);
2757c2c66affSColin Finck     DPRINT1("\t\tAllocatedSize: %I64u\n", IndexEntry->FileName.AllocatedSize);
2758c2c66affSColin Finck     DPRINT1("\t\tDataSize: %I64u\n", IndexEntry->FileName.DataSize);
2759c2c66affSColin Finck     DPRINT1("\t\tFileAttributes: %x\n", IndexEntry->FileName.FileAttributes);
2760c2c66affSColin Finck     DPRINT1("\t\tNameLength: %u\n", IndexEntry->FileName.NameLength);
2761c2c66affSColin Finck     DPRINT1("\t\tNameType: %x\n", IndexEntry->FileName.NameType);
2762c2c66affSColin Finck     DPRINT1("\t\tName: %.*S\n", IndexEntry->FileName.NameLength, IndexEntry->FileName.Name);
2763c2c66affSColin Finck }
2764c2c66affSColin Finck #endif
2765c2c66affSColin Finck 
2766c2c66affSColin Finck NTSTATUS
2767c2c66affSColin Finck BrowseIndexEntries(PDEVICE_EXTENSION Vcb,
2768c2c66affSColin Finck                    PFILE_RECORD_HEADER MftRecord,
2769c2c66affSColin Finck                    PCHAR IndexRecord,
2770c2c66affSColin Finck                    ULONG IndexBlockSize,
2771c2c66affSColin Finck                    PINDEX_ENTRY_ATTRIBUTE FirstEntry,
2772c2c66affSColin Finck                    PINDEX_ENTRY_ATTRIBUTE LastEntry,
2773c2c66affSColin Finck                    PUNICODE_STRING FileName,
2774c2c66affSColin Finck                    PULONG StartEntry,
2775c2c66affSColin Finck                    PULONG CurrentEntry,
2776c2c66affSColin Finck                    BOOLEAN DirSearch,
2777032be029STrevor Thompson                    BOOLEAN CaseSensitive,
2778c2c66affSColin Finck                    ULONGLONG *OutMFTIndex)
2779c2c66affSColin Finck {
2780c2c66affSColin Finck     NTSTATUS Status;
2781c2c66affSColin Finck     ULONG RecordOffset;
2782c2c66affSColin Finck     PINDEX_ENTRY_ATTRIBUTE IndexEntry;
2783c2c66affSColin Finck     PNTFS_ATTR_CONTEXT IndexAllocationCtx;
2784c2c66affSColin Finck     ULONGLONG IndexAllocationSize;
2785c2c66affSColin Finck     PINDEX_BUFFER IndexBuffer;
2786c2c66affSColin Finck 
278738c947b7STrevor Thompson     DPRINT("BrowseIndexEntries(%p, %p, %p, %lu, %p, %p, %wZ, %lu, %lu, %s, %s, %p)\n",
2788032be029STrevor Thompson            Vcb,
2789032be029STrevor Thompson            MftRecord,
2790032be029STrevor Thompson            IndexRecord,
2791032be029STrevor Thompson            IndexBlockSize,
2792032be029STrevor Thompson            FirstEntry,
2793032be029STrevor Thompson            LastEntry,
2794032be029STrevor Thompson            FileName,
2795032be029STrevor Thompson            *StartEntry,
2796032be029STrevor Thompson            *CurrentEntry,
2797032be029STrevor Thompson            DirSearch ? "TRUE" : "FALSE",
2798032be029STrevor Thompson            CaseSensitive ? "TRUE" : "FALSE",
2799032be029STrevor Thompson            OutMFTIndex);
2800c2c66affSColin Finck 
2801c2c66affSColin Finck     IndexEntry = FirstEntry;
2802c2c66affSColin Finck     while (IndexEntry < LastEntry &&
2803c2c66affSColin Finck            !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
2804c2c66affSColin Finck     {
2805935fcd1bSTrevor Thompson         if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) >= NTFS_FILE_FIRST_USER_FILE &&
2806c2c66affSColin Finck             *CurrentEntry >= *StartEntry &&
2807c2c66affSColin Finck             IndexEntry->FileName.NameType != NTFS_FILE_NAME_DOS &&
2808032be029STrevor Thompson             CompareFileName(FileName, IndexEntry, DirSearch, CaseSensitive))
2809c2c66affSColin Finck         {
2810c2c66affSColin Finck             *StartEntry = *CurrentEntry;
2811c2c66affSColin Finck             *OutMFTIndex = (IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK);
2812c2c66affSColin Finck             return STATUS_SUCCESS;
2813c2c66affSColin Finck         }
2814c2c66affSColin Finck 
2815c2c66affSColin Finck         (*CurrentEntry) += 1;
2816c2c66affSColin Finck         ASSERT(IndexEntry->Length >= sizeof(INDEX_ENTRY_ATTRIBUTE));
2817c2c66affSColin Finck         IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
2818c2c66affSColin Finck     }
2819c2c66affSColin Finck 
2820c2c66affSColin Finck     /* If we're already browsing a subnode */
2821c2c66affSColin Finck     if (IndexRecord == NULL)
2822c2c66affSColin Finck     {
2823c2c66affSColin Finck         return STATUS_OBJECT_PATH_NOT_FOUND;
2824c2c66affSColin Finck     }
2825c2c66affSColin Finck 
2826c2c66affSColin Finck     /* If there's no subnode */
2827c2c66affSColin Finck     if (!(IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE))
2828c2c66affSColin Finck     {
2829c2c66affSColin Finck         return STATUS_OBJECT_PATH_NOT_FOUND;
2830c2c66affSColin Finck     }
2831c2c66affSColin Finck 
2832ba33b9faSTrevor Thompson     Status = FindAttribute(Vcb, MftRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationCtx, NULL);
2833c2c66affSColin Finck     if (!NT_SUCCESS(Status))
2834c2c66affSColin Finck     {
283538c947b7STrevor Thompson         DPRINT1("Corrupted filesystem!\n");
2836c2c66affSColin Finck         return Status;
2837c2c66affSColin Finck     }
2838c2c66affSColin Finck 
28394dfcd1d5STrevor Thompson     IndexAllocationSize = AttributeDataLength(IndexAllocationCtx->pRecord);
2840c2c66affSColin Finck     Status = STATUS_OBJECT_PATH_NOT_FOUND;
2841c2c66affSColin Finck     for (RecordOffset = 0; RecordOffset < IndexAllocationSize; RecordOffset += IndexBlockSize)
2842c2c66affSColin Finck     {
2843c2c66affSColin Finck         ReadAttribute(Vcb, IndexAllocationCtx, RecordOffset, IndexRecord, IndexBlockSize);
2844c2c66affSColin Finck         Status = FixupUpdateSequenceArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs);
2845c2c66affSColin Finck         if (!NT_SUCCESS(Status))
2846c2c66affSColin Finck         {
2847c2c66affSColin Finck             break;
2848c2c66affSColin Finck         }
2849c2c66affSColin Finck 
2850c2c66affSColin Finck         IndexBuffer = (PINDEX_BUFFER)IndexRecord;
2851c2c66affSColin Finck         ASSERT(IndexBuffer->Ntfs.Type == NRH_INDX_TYPE);
2852c2c66affSColin Finck         ASSERT(IndexBuffer->Header.AllocatedSize + FIELD_OFFSET(INDEX_BUFFER, Header) == IndexBlockSize);
2853c2c66affSColin Finck         FirstEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.FirstEntryOffset);
2854c2c66affSColin Finck         LastEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.TotalSizeOfEntries);
2855c2c66affSColin Finck         ASSERT(LastEntry <= (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexBuffer + IndexBlockSize));
2856c2c66affSColin Finck 
2857032be029STrevor Thompson         Status = BrowseIndexEntries(NULL,
2858032be029STrevor Thompson                                     NULL,
2859032be029STrevor Thompson                                     NULL,
2860032be029STrevor Thompson                                     0,
2861032be029STrevor Thompson                                     FirstEntry,
2862032be029STrevor Thompson                                     LastEntry,
2863032be029STrevor Thompson                                     FileName,
2864032be029STrevor Thompson                                     StartEntry,
2865032be029STrevor Thompson                                     CurrentEntry,
2866032be029STrevor Thompson                                     DirSearch,
2867032be029STrevor Thompson                                     CaseSensitive,
2868032be029STrevor Thompson                                     OutMFTIndex);
2869c2c66affSColin Finck         if (NT_SUCCESS(Status))
2870c2c66affSColin Finck         {
2871c2c66affSColin Finck             break;
2872c2c66affSColin Finck         }
2873c2c66affSColin Finck     }
2874c2c66affSColin Finck 
2875c2c66affSColin Finck     ReleaseAttributeContext(IndexAllocationCtx);
2876c2c66affSColin Finck     return Status;
2877c2c66affSColin Finck }
2878c2c66affSColin Finck 
2879c2c66affSColin Finck NTSTATUS
2880c2c66affSColin Finck NtfsFindMftRecord(PDEVICE_EXTENSION Vcb,
2881c2c66affSColin Finck                   ULONGLONG MFTIndex,
2882c2c66affSColin Finck                   PUNICODE_STRING FileName,
2883c2c66affSColin Finck                   PULONG FirstEntry,
2884c2c66affSColin Finck                   BOOLEAN DirSearch,
288538c947b7STrevor Thompson                   BOOLEAN CaseSensitive,
288638c947b7STrevor Thompson                   ULONGLONG *OutMFTIndex)
2887c2c66affSColin Finck {
2888c2c66affSColin Finck     PFILE_RECORD_HEADER MftRecord;
2889c2c66affSColin Finck     PNTFS_ATTR_CONTEXT IndexRootCtx;
2890c2c66affSColin Finck     PINDEX_ROOT_ATTRIBUTE IndexRoot;
2891c2c66affSColin Finck     PCHAR IndexRecord;
2892c2c66affSColin Finck     PINDEX_ENTRY_ATTRIBUTE IndexEntry, IndexEntryEnd;
2893c2c66affSColin Finck     NTSTATUS Status;
2894c2c66affSColin Finck     ULONG CurrentEntry = 0;
2895c2c66affSColin Finck 
289638c947b7STrevor Thompson     DPRINT("NtfsFindMftRecord(%p, %I64d, %wZ, %lu, %s, %s, %p)\n",
289738c947b7STrevor Thompson            Vcb,
289838c947b7STrevor Thompson            MFTIndex,
289938c947b7STrevor Thompson            FileName,
290038c947b7STrevor Thompson            *FirstEntry,
290138c947b7STrevor Thompson            DirSearch ? "TRUE" : "FALSE",
290238c947b7STrevor Thompson            CaseSensitive ? "TRUE" : "FALSE",
290338c947b7STrevor Thompson            OutMFTIndex);
2904c2c66affSColin Finck 
2905c2c66affSColin Finck     MftRecord = ExAllocatePoolWithTag(NonPagedPool,
2906c2c66affSColin Finck                                       Vcb->NtfsInfo.BytesPerFileRecord,
2907c2c66affSColin Finck                                       TAG_NTFS);
2908c2c66affSColin Finck     if (MftRecord == NULL)
2909c2c66affSColin Finck     {
2910c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
2911c2c66affSColin Finck     }
2912c2c66affSColin Finck 
2913c2c66affSColin Finck     Status = ReadFileRecord(Vcb, MFTIndex, MftRecord);
2914c2c66affSColin Finck     if (!NT_SUCCESS(Status))
2915c2c66affSColin Finck     {
2916c2c66affSColin Finck         ExFreePoolWithTag(MftRecord, TAG_NTFS);
2917c2c66affSColin Finck         return Status;
2918c2c66affSColin Finck     }
2919c2c66affSColin Finck 
2920c2c66affSColin Finck     ASSERT(MftRecord->Ntfs.Type == NRH_FILE_TYPE);
2921ba33b9faSTrevor Thompson     Status = FindAttribute(Vcb, MftRecord, AttributeIndexRoot, L"$I30", 4, &IndexRootCtx, NULL);
2922c2c66affSColin Finck     if (!NT_SUCCESS(Status))
2923c2c66affSColin Finck     {
2924c2c66affSColin Finck         ExFreePoolWithTag(MftRecord, TAG_NTFS);
2925c2c66affSColin Finck         return Status;
2926c2c66affSColin Finck     }
2927c2c66affSColin Finck 
2928c2c66affSColin Finck     IndexRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerIndexRecord, TAG_NTFS);
2929c2c66affSColin Finck     if (IndexRecord == NULL)
2930c2c66affSColin Finck     {
2931c2c66affSColin Finck         ReleaseAttributeContext(IndexRootCtx);
2932c2c66affSColin Finck         ExFreePoolWithTag(MftRecord, TAG_NTFS);
2933c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
2934c2c66affSColin Finck     }
2935c2c66affSColin Finck 
2936c2c66affSColin Finck     ReadAttribute(Vcb, IndexRootCtx, 0, IndexRecord, Vcb->NtfsInfo.BytesPerIndexRecord);
2937c2c66affSColin Finck     IndexRoot = (PINDEX_ROOT_ATTRIBUTE)IndexRecord;
2938c2c66affSColin Finck     IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)&IndexRoot->Header + IndexRoot->Header.FirstEntryOffset);
2939c2c66affSColin Finck     /* Index root is always resident. */
2940c2c66affSColin Finck     IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)(IndexRecord + IndexRoot->Header.TotalSizeOfEntries);
2941c2c66affSColin Finck     ReleaseAttributeContext(IndexRootCtx);
2942c2c66affSColin Finck 
2943c2c66affSColin Finck     DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb->NtfsInfo.BytesPerIndexRecord, IndexRoot->SizeOfEntry);
2944c2c66affSColin Finck 
2945032be029STrevor Thompson     Status = BrowseIndexEntries(Vcb,
2946032be029STrevor Thompson                                 MftRecord,
2947032be029STrevor Thompson                                 IndexRecord,
2948032be029STrevor Thompson                                 IndexRoot->SizeOfEntry,
2949032be029STrevor Thompson                                 IndexEntry,
2950032be029STrevor Thompson                                 IndexEntryEnd,
2951032be029STrevor Thompson                                 FileName,
2952032be029STrevor Thompson                                 FirstEntry,
2953032be029STrevor Thompson                                 &CurrentEntry,
2954032be029STrevor Thompson                                 DirSearch,
2955032be029STrevor Thompson                                 CaseSensitive,
2956032be029STrevor Thompson                                 OutMFTIndex);
2957c2c66affSColin Finck 
2958c2c66affSColin Finck     ExFreePoolWithTag(IndexRecord, TAG_NTFS);
2959c2c66affSColin Finck     ExFreePoolWithTag(MftRecord, TAG_NTFS);
2960c2c66affSColin Finck 
2961c2c66affSColin Finck     return Status;
2962c2c66affSColin Finck }
2963c2c66affSColin Finck 
2964c2c66affSColin Finck NTSTATUS
2965c2c66affSColin Finck NtfsLookupFileAt(PDEVICE_EXTENSION Vcb,
2966c2c66affSColin Finck                  PUNICODE_STRING PathName,
2967948e9190STrevor Thompson                  BOOLEAN CaseSensitive,
2968c2c66affSColin Finck                  PFILE_RECORD_HEADER *FileRecord,
2969c2c66affSColin Finck                  PULONGLONG MFTIndex,
2970948e9190STrevor Thompson                  ULONGLONG CurrentMFTIndex)
2971c2c66affSColin Finck {
2972c2c66affSColin Finck     UNICODE_STRING Current, Remaining;
2973c2c66affSColin Finck     NTSTATUS Status;
2974c2c66affSColin Finck     ULONG FirstEntry = 0;
2975c2c66affSColin Finck 
2976948e9190STrevor Thompson     DPRINT("NtfsLookupFileAt(%p, %wZ, %s, %p, %p, %I64x)\n",
2977948e9190STrevor Thompson            Vcb,
2978948e9190STrevor Thompson            PathName,
2979948e9190STrevor Thompson            CaseSensitive ? "TRUE" : "FALSE",
2980948e9190STrevor Thompson            FileRecord,
2981948e9190STrevor Thompson            MFTIndex,
2982948e9190STrevor Thompson            CurrentMFTIndex);
2983c2c66affSColin Finck 
2984c2c66affSColin Finck     FsRtlDissectName(*PathName, &Current, &Remaining);
2985c2c66affSColin Finck 
2986c2c66affSColin Finck     while (Current.Length != 0)
2987c2c66affSColin Finck     {
2988c2c66affSColin Finck         DPRINT("Current: %wZ\n", &Current);
2989c2c66affSColin Finck 
299038c947b7STrevor Thompson         Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, &Current, &FirstEntry, FALSE, CaseSensitive, &CurrentMFTIndex);
2991c2c66affSColin Finck         if (!NT_SUCCESS(Status))
2992c2c66affSColin Finck         {
2993c2c66affSColin Finck             return Status;
2994c2c66affSColin Finck         }
2995c2c66affSColin Finck 
2996c2c66affSColin Finck         if (Remaining.Length == 0)
2997c2c66affSColin Finck             break;
2998c2c66affSColin Finck 
2999c2c66affSColin Finck         FsRtlDissectName(Current, &Current, &Remaining);
3000c2c66affSColin Finck     }
3001c2c66affSColin Finck 
3002c2c66affSColin Finck     *FileRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
3003c2c66affSColin Finck     if (*FileRecord == NULL)
3004c2c66affSColin Finck     {
3005c2c66affSColin Finck         DPRINT("NtfsLookupFileAt: Can't allocate MFT record\n");
3006c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
3007c2c66affSColin Finck     }
3008c2c66affSColin Finck 
3009c2c66affSColin Finck     Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord);
3010c2c66affSColin Finck     if (!NT_SUCCESS(Status))
3011c2c66affSColin Finck     {
3012c2c66affSColin Finck         DPRINT("NtfsLookupFileAt: Can't read MFT record\n");
3013c2c66affSColin Finck         ExFreePoolWithTag(*FileRecord, TAG_NTFS);
3014c2c66affSColin Finck         return Status;
3015c2c66affSColin Finck     }
3016c2c66affSColin Finck 
3017c2c66affSColin Finck     *MFTIndex = CurrentMFTIndex;
3018c2c66affSColin Finck 
3019c2c66affSColin Finck     return STATUS_SUCCESS;
3020c2c66affSColin Finck }
3021c2c66affSColin Finck 
3022c2c66affSColin Finck NTSTATUS
3023c2c66affSColin Finck NtfsLookupFile(PDEVICE_EXTENSION Vcb,
3024c2c66affSColin Finck                PUNICODE_STRING PathName,
3025948e9190STrevor Thompson                BOOLEAN CaseSensitive,
3026c2c66affSColin Finck                PFILE_RECORD_HEADER *FileRecord,
3027948e9190STrevor Thompson                PULONGLONG MFTIndex)
3028c2c66affSColin Finck {
3029948e9190STrevor Thompson     return NtfsLookupFileAt(Vcb, PathName, CaseSensitive, FileRecord, MFTIndex, NTFS_FILE_ROOT);
3030c2c66affSColin Finck }
3031c2c66affSColin Finck 
30327643831dSTrevor Thompson /**
30337643831dSTrevor Thompson * @name NtfsDumpFileRecord
30347643831dSTrevor Thompson * @implemented
30357643831dSTrevor Thompson *
30367643831dSTrevor Thompson * Provides diagnostic information about a file record. Prints a hex dump
30377643831dSTrevor Thompson * of the entire record (based on the size reported by FileRecord->ByesInUse),
30387643831dSTrevor Thompson * then prints a dump of each attribute.
30397643831dSTrevor Thompson *
30407643831dSTrevor Thompson * @param Vcb
30417643831dSTrevor Thompson * Pointer to a DEVICE_EXTENSION describing the volume.
30427643831dSTrevor Thompson *
30437643831dSTrevor Thompson * @param FileRecord
30447643831dSTrevor Thompson * Pointer to the file record to be analyzed.
30457643831dSTrevor Thompson *
30467643831dSTrevor Thompson * @remarks
30477643831dSTrevor Thompson * FileRecord must be a complete file record at least FileRecord->BytesAllocated
30487643831dSTrevor Thompson * in size, and not just the header.
30497643831dSTrevor Thompson *
30507643831dSTrevor Thompson */
30517643831dSTrevor Thompson VOID
30527643831dSTrevor Thompson NtfsDumpFileRecord(PDEVICE_EXTENSION Vcb,
30537643831dSTrevor Thompson                    PFILE_RECORD_HEADER FileRecord)
30547643831dSTrevor Thompson {
30557643831dSTrevor Thompson     ULONG i, j;
30567643831dSTrevor Thompson 
30577643831dSTrevor Thompson     // dump binary data, 8 bytes at a time
30587643831dSTrevor Thompson     for (i = 0; i < FileRecord->BytesInUse; i += 8)
30597643831dSTrevor Thompson     {
30607643831dSTrevor Thompson         // display current offset, in hex
30617643831dSTrevor Thompson         DbgPrint("\t%03x\t", i);
30627643831dSTrevor Thompson 
30637643831dSTrevor Thompson         // display hex value of each of the next 8 bytes
30647643831dSTrevor Thompson         for (j = 0; j < 8; j++)
30657643831dSTrevor Thompson             DbgPrint("%02x ", *(PUCHAR)((ULONG_PTR)FileRecord + i + j));
30667643831dSTrevor Thompson         DbgPrint("\n");
30677643831dSTrevor Thompson     }
30687643831dSTrevor Thompson 
30697643831dSTrevor Thompson     NtfsDumpFileAttributes(Vcb, FileRecord);
30707643831dSTrevor Thompson }
30717643831dSTrevor Thompson 
3072c2c66affSColin Finck NTSTATUS
3073c2c66affSColin Finck NtfsFindFileAt(PDEVICE_EXTENSION Vcb,
3074c2c66affSColin Finck                PUNICODE_STRING SearchPattern,
3075c2c66affSColin Finck                PULONG FirstEntry,
3076c2c66affSColin Finck                PFILE_RECORD_HEADER *FileRecord,
3077c2c66affSColin Finck                PULONGLONG MFTIndex,
3078032be029STrevor Thompson                ULONGLONG CurrentMFTIndex,
3079032be029STrevor Thompson                BOOLEAN CaseSensitive)
3080c2c66affSColin Finck {
3081c2c66affSColin Finck     NTSTATUS Status;
3082c2c66affSColin Finck 
308338c947b7STrevor Thompson     DPRINT("NtfsFindFileAt(%p, %wZ, %lu, %p, %p, %I64x, %s)\n",
3084032be029STrevor Thompson            Vcb,
3085032be029STrevor Thompson            SearchPattern,
3086032be029STrevor Thompson            *FirstEntry,
3087032be029STrevor Thompson            FileRecord,
3088032be029STrevor Thompson            MFTIndex,
3089032be029STrevor Thompson            CurrentMFTIndex,
3090032be029STrevor Thompson            (CaseSensitive ? "TRUE" : "FALSE"));
3091c2c66affSColin Finck 
309238c947b7STrevor Thompson     Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, SearchPattern, FirstEntry, TRUE, CaseSensitive, &CurrentMFTIndex);
3093c2c66affSColin Finck     if (!NT_SUCCESS(Status))
3094c2c66affSColin Finck     {
3095c2c66affSColin Finck         DPRINT("NtfsFindFileAt: NtfsFindMftRecord() failed with status 0x%08lx\n", Status);
3096c2c66affSColin Finck         return Status;
3097c2c66affSColin Finck     }
3098c2c66affSColin Finck 
3099c2c66affSColin Finck     *FileRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
3100c2c66affSColin Finck     if (*FileRecord == NULL)
3101c2c66affSColin Finck     {
3102c2c66affSColin Finck         DPRINT("NtfsFindFileAt: Can't allocate MFT record\n");
3103c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
3104c2c66affSColin Finck     }
3105c2c66affSColin Finck 
3106c2c66affSColin Finck     Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord);
3107c2c66affSColin Finck     if (!NT_SUCCESS(Status))
3108c2c66affSColin Finck     {
3109c2c66affSColin Finck         DPRINT("NtfsFindFileAt: Can't read MFT record\n");
3110c2c66affSColin Finck         ExFreePoolWithTag(*FileRecord, TAG_NTFS);
3111c2c66affSColin Finck         return Status;
3112c2c66affSColin Finck     }
3113c2c66affSColin Finck 
3114c2c66affSColin Finck     *MFTIndex = CurrentMFTIndex;
3115c2c66affSColin Finck 
3116c2c66affSColin Finck     return STATUS_SUCCESS;
3117c2c66affSColin Finck }
3118c2c66affSColin Finck 
3119c2c66affSColin Finck /* EOF */
3120