xref: /reactos/drivers/filesystems/ntfs/mft.c (revision 6ab30720)
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"
33c2c66affSColin Finck 
34c2c66affSColin Finck #define NDEBUG
3558a13831STrevor Thompson #undef 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,
46c2c66affSColin Finck                                     FIELD_OFFSET(NTFS_ATTR_CONTEXT, Record) + AttrRecord->Length,
47c2c66affSColin Finck                                     TAG_NTFS);
48c2c66affSColin Finck     RtlCopyMemory(&Context->Record, AttrRecord, AttrRecord->Length);
49c2c66affSColin Finck     if (AttrRecord->IsNonResident)
50c2c66affSColin Finck     {
51c2c66affSColin Finck         LONGLONG DataRunOffset;
52c2c66affSColin Finck         ULONGLONG DataRunLength;
53c2c66affSColin Finck 
54c2c66affSColin Finck         Context->CacheRun = (PUCHAR)&Context->Record + Context->Record.NonResident.MappingPairsOffset;
55c2c66affSColin Finck         Context->CacheRunOffset = 0;
56c2c66affSColin Finck         Context->CacheRun = DecodeRun(Context->CacheRun, &DataRunOffset, &DataRunLength);
57c2c66affSColin Finck         Context->CacheRunLength = DataRunLength;
58c2c66affSColin Finck         if (DataRunOffset != -1)
59c2c66affSColin Finck         {
60c2c66affSColin Finck             /* Normal run. */
61c2c66affSColin Finck             Context->CacheRunStartLCN =
62c2c66affSColin Finck             Context->CacheRunLastLCN = DataRunOffset;
63c2c66affSColin Finck         }
64c2c66affSColin Finck         else
65c2c66affSColin Finck         {
66c2c66affSColin Finck             /* Sparse run. */
67c2c66affSColin Finck             Context->CacheRunStartLCN = -1;
68c2c66affSColin Finck             Context->CacheRunLastLCN = 0;
69c2c66affSColin Finck         }
70c2c66affSColin Finck         Context->CacheRunCurrentOffset = 0;
71c2c66affSColin Finck     }
72c2c66affSColin Finck 
73c2c66affSColin Finck     return Context;
74c2c66affSColin Finck }
75c2c66affSColin Finck 
76c2c66affSColin Finck 
77c2c66affSColin Finck VOID
78c2c66affSColin Finck ReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context)
79c2c66affSColin Finck {
80c2c66affSColin Finck     ExFreePoolWithTag(Context, TAG_NTFS);
81c2c66affSColin Finck }
82c2c66affSColin Finck 
83c2c66affSColin Finck 
84ba33b9faSTrevor Thompson /**
85ba33b9faSTrevor Thompson * @name FindAttribute
86ba33b9faSTrevor Thompson * @implemented
87ba33b9faSTrevor Thompson *
88ba33b9faSTrevor Thompson * Searches a file record for an attribute matching the given type and name.
89ba33b9faSTrevor Thompson *
90ba33b9faSTrevor Thompson * @param Offset
91ba33b9faSTrevor Thompson * Optional pointer to a ULONG that will receive the offset of the found attribute
92ba33b9faSTrevor Thompson * from the beginning of the record. Can be set to NULL.
93ba33b9faSTrevor Thompson */
94c2c66affSColin Finck NTSTATUS
95c2c66affSColin Finck FindAttribute(PDEVICE_EXTENSION Vcb,
96c2c66affSColin Finck               PFILE_RECORD_HEADER MftRecord,
97c2c66affSColin Finck               ULONG Type,
98c2c66affSColin Finck               PCWSTR Name,
99c2c66affSColin Finck               ULONG NameLength,
100ba33b9faSTrevor Thompson               PNTFS_ATTR_CONTEXT * AttrCtx,
101ba33b9faSTrevor Thompson               PULONG Offset)
102c2c66affSColin Finck {
103c2c66affSColin Finck     BOOLEAN Found;
104c2c66affSColin Finck     NTSTATUS Status;
105c2c66affSColin Finck     FIND_ATTR_CONTXT Context;
106c2c66affSColin Finck     PNTFS_ATTR_RECORD Attribute;
107c2c66affSColin Finck 
108c2c66affSColin Finck     DPRINT("FindAttribute(%p, %p, 0x%x, %S, %u, %p)\n", Vcb, MftRecord, Type, Name, NameLength, AttrCtx);
109c2c66affSColin Finck 
110c2c66affSColin Finck     Found = FALSE;
111c2c66affSColin Finck     Status = FindFirstAttribute(&Context, Vcb, MftRecord, FALSE, &Attribute);
112c2c66affSColin Finck     while (NT_SUCCESS(Status))
113c2c66affSColin Finck     {
114c2c66affSColin Finck         if (Attribute->Type == Type && Attribute->NameLength == NameLength)
115c2c66affSColin Finck         {
116c2c66affSColin Finck             if (NameLength != 0)
117c2c66affSColin Finck             {
118c2c66affSColin Finck                 PWCHAR AttrName;
119c2c66affSColin Finck 
120c2c66affSColin Finck                 AttrName = (PWCHAR)((PCHAR)Attribute + Attribute->NameOffset);
121c2c66affSColin Finck                 DPRINT("%.*S, %.*S\n", Attribute->NameLength, AttrName, NameLength, Name);
122c2c66affSColin Finck                 if (RtlCompareMemory(AttrName, Name, NameLength << 1) == (NameLength << 1))
123c2c66affSColin Finck                 {
124c2c66affSColin Finck                     Found = TRUE;
125c2c66affSColin Finck                 }
126c2c66affSColin Finck             }
127c2c66affSColin Finck             else
128c2c66affSColin Finck             {
129c2c66affSColin Finck                 Found = TRUE;
130c2c66affSColin Finck             }
131c2c66affSColin Finck 
132c2c66affSColin Finck             if (Found)
133c2c66affSColin Finck             {
134c2c66affSColin Finck                 /* Found it, fill up the context and return. */
135c2c66affSColin Finck                 DPRINT("Found context\n");
136c2c66affSColin Finck                 *AttrCtx = PrepareAttributeContext(Attribute);
137ba33b9faSTrevor Thompson 
138760cdfb5STrevor Thompson                 (*AttrCtx)->FileMFTIndex = MftRecord->MFTRecordNumber;
139760cdfb5STrevor Thompson 
140ba33b9faSTrevor Thompson                 if (Offset != NULL)
141ba33b9faSTrevor Thompson                     *Offset = Context.Offset;
142ba33b9faSTrevor Thompson 
143c2c66affSColin Finck                 FindCloseAttribute(&Context);
144c2c66affSColin Finck                 return STATUS_SUCCESS;
145c2c66affSColin Finck             }
146c2c66affSColin Finck         }
147c2c66affSColin Finck 
148c2c66affSColin Finck         Status = FindNextAttribute(&Context, &Attribute);
149c2c66affSColin Finck     }
150c2c66affSColin Finck 
151c2c66affSColin Finck     FindCloseAttribute(&Context);
152c2c66affSColin Finck     return STATUS_OBJECT_NAME_NOT_FOUND;
153c2c66affSColin Finck }
154c2c66affSColin Finck 
155c2c66affSColin Finck 
15677fc65dcSTrevor Thompson ULONGLONG
157c2c66affSColin Finck AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord)
158c2c66affSColin Finck {
159c2c66affSColin Finck     if (AttrRecord->IsNonResident)
160c2c66affSColin Finck         return AttrRecord->NonResident.AllocatedSize;
161c2c66affSColin Finck     else
162c2c66affSColin Finck         return AttrRecord->Resident.ValueLength;
163c2c66affSColin Finck }
164c2c66affSColin Finck 
165c2c66affSColin Finck 
166c2c66affSColin Finck ULONGLONG
167c2c66affSColin Finck AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord)
168c2c66affSColin Finck {
169c2c66affSColin Finck     if (AttrRecord->IsNonResident)
170c2c66affSColin Finck         return AttrRecord->NonResident.DataSize;
171c2c66affSColin Finck     else
172c2c66affSColin Finck         return AttrRecord->Resident.ValueLength;
173c2c66affSColin Finck }
174c2c66affSColin Finck 
175760cdfb5STrevor Thompson void
176760cdfb5STrevor Thompson InternalSetResidentAttributeLength(PNTFS_ATTR_CONTEXT AttrContext,
177760cdfb5STrevor Thompson                                    PFILE_RECORD_HEADER FileRecord,
178760cdfb5STrevor Thompson                                    ULONG AttrOffset,
179760cdfb5STrevor Thompson                                    ULONG DataSize)
180760cdfb5STrevor Thompson {
181c08d37d1STrevor Thompson     PNTFS_ATTR_RECORD Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
182760cdfb5STrevor Thompson     ULONG NextAttributeOffset;
183760cdfb5STrevor Thompson 
184760cdfb5STrevor Thompson     DPRINT("InternalSetResidentAttributeLength( %p, %p, %lu, %lu )\n", AttrContext, FileRecord, AttrOffset, DataSize);
185760cdfb5STrevor Thompson 
186760cdfb5STrevor Thompson     // update ValueLength Field
187c08d37d1STrevor Thompson     AttrContext->Record.Resident.ValueLength =
188c08d37d1STrevor Thompson     Destination->Resident.ValueLength = DataSize;
189760cdfb5STrevor Thompson 
190760cdfb5STrevor Thompson     // calculate the record length and end marker offset
191c08d37d1STrevor Thompson     AttrContext->Record.Length =
192c08d37d1STrevor Thompson     Destination->Length = DataSize + AttrContext->Record.Resident.ValueOffset;
193760cdfb5STrevor Thompson     NextAttributeOffset = AttrOffset + AttrContext->Record.Length;
194760cdfb5STrevor Thompson 
195760cdfb5STrevor Thompson     // Ensure NextAttributeOffset is aligned to an 8-byte boundary
196760cdfb5STrevor Thompson     if (NextAttributeOffset % 8 != 0)
197760cdfb5STrevor Thompson     {
198c08d37d1STrevor Thompson         USHORT Padding = 8 - (NextAttributeOffset % 8);
199760cdfb5STrevor Thompson         NextAttributeOffset += Padding;
200760cdfb5STrevor Thompson         AttrContext->Record.Length += Padding;
201c08d37d1STrevor Thompson         Destination->Length += Padding;
202760cdfb5STrevor Thompson     }
203760cdfb5STrevor Thompson 
204c08d37d1STrevor Thompson     // advance Destination to the final "attribute" and write the end type
205a135ef58STrevor Thompson     Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)Destination + Destination->Length);
206c08d37d1STrevor Thompson     Destination->Type = AttributeEnd;
207760cdfb5STrevor Thompson 
208c08d37d1STrevor Thompson     // write the final marker (which shares the same offset and type as the Length field)
209c08d37d1STrevor Thompson     Destination->Length = FILE_RECORD_END;
210760cdfb5STrevor Thompson 
211c08d37d1STrevor Thompson     FileRecord->BytesInUse = NextAttributeOffset + (sizeof(ULONG) * 2);
212760cdfb5STrevor Thompson }
213c2c66affSColin Finck 
214afe40eb0STrevor Thompson /**
215afe40eb0STrevor Thompson *   @parameter FileRecord
216afe40eb0STrevor Thompson *   Pointer to a file record. Must be a full record at least
217afe40eb0STrevor Thompson *   Fcb->Vcb->NtfsInfo.BytesPerFileRecord bytes large, not just the header.
218afe40eb0STrevor Thompson */
219ba33b9faSTrevor Thompson NTSTATUS
220ba33b9faSTrevor Thompson SetAttributeDataLength(PFILE_OBJECT FileObject,
221ba33b9faSTrevor Thompson                        PNTFS_FCB Fcb,
222ba33b9faSTrevor Thompson                        PNTFS_ATTR_CONTEXT AttrContext,
223ba33b9faSTrevor Thompson                        ULONG AttrOffset,
224ba33b9faSTrevor Thompson                        PFILE_RECORD_HEADER FileRecord,
225ba33b9faSTrevor Thompson                        PLARGE_INTEGER DataSize)
226ba33b9faSTrevor Thompson {
227760cdfb5STrevor Thompson     NTSTATUS Status = STATUS_SUCCESS;
228920e2f02STrevor Thompson     ULONG BytesPerCluster = Fcb->Vcb->NtfsInfo.BytesPerCluster;
229760cdfb5STrevor Thompson 
230760cdfb5STrevor Thompson     // are we truncating the file?
2317eb1264fSTrevor Thompson     if (DataSize->QuadPart < AttributeDataLength(&AttrContext->Record))
232760cdfb5STrevor Thompson     {
2337eb1264fSTrevor Thompson         if (!MmCanFileBeTruncated(FileObject->SectionObjectPointer, DataSize))
234760cdfb5STrevor Thompson         {
235760cdfb5STrevor Thompson             DPRINT1("Can't truncate a memory-mapped file!\n");
236760cdfb5STrevor Thompson             return STATUS_USER_MAPPED_FILE;
237760cdfb5STrevor Thompson         }
238760cdfb5STrevor Thompson     }
239760cdfb5STrevor Thompson 
240ba33b9faSTrevor Thompson     if (AttrContext->Record.IsNonResident)
241ba33b9faSTrevor Thompson     {
24277fc65dcSTrevor Thompson         ULONGLONG AllocationSize = ROUND_UP(DataSize->QuadPart, BytesPerCluster);
243afe40eb0STrevor Thompson         PNTFS_ATTR_RECORD DestinationAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
244920e2f02STrevor Thompson         ULONG ExistingClusters = AttrContext->Record.NonResident.AllocatedSize / BytesPerCluster;
24577fc65dcSTrevor Thompson 
246ba33b9faSTrevor Thompson         // do we need to increase the allocation size?
24777fc65dcSTrevor Thompson         if (AttrContext->Record.NonResident.AllocatedSize < AllocationSize)
248ba33b9faSTrevor Thompson         {
24977fc65dcSTrevor Thompson             ULONG ClustersNeeded = (AllocationSize / BytesPerCluster) - ExistingClusters;
25077fc65dcSTrevor Thompson             LARGE_INTEGER LastClusterInDataRun;
25177fc65dcSTrevor Thompson             ULONG NextAssignedCluster;
25277fc65dcSTrevor Thompson             ULONG AssignedClusters;
25377fc65dcSTrevor Thompson 
254a135ef58STrevor Thompson             NTSTATUS Status = GetLastClusterInDataRun(Fcb->Vcb, &AttrContext->Record, (PULONGLONG)&LastClusterInDataRun.QuadPart);
25577fc65dcSTrevor Thompson 
25677fc65dcSTrevor Thompson             DPRINT1("GetLastClusterInDataRun returned: %I64u\n", LastClusterInDataRun.QuadPart);
25777fc65dcSTrevor Thompson             DPRINT1("Highest VCN of record: %I64u\n", AttrContext->Record.NonResident.HighestVCN);
25877fc65dcSTrevor Thompson 
25977fc65dcSTrevor Thompson             while (ClustersNeeded > 0)
26077fc65dcSTrevor Thompson             {
26177fc65dcSTrevor Thompson                 Status = NtfsAllocateClusters(Fcb->Vcb,
26277fc65dcSTrevor Thompson                                               LastClusterInDataRun.LowPart + 1,
26377fc65dcSTrevor Thompson                                               ClustersNeeded,
26477fc65dcSTrevor Thompson                                               &NextAssignedCluster,
26577fc65dcSTrevor Thompson                                               &AssignedClusters);
26677fc65dcSTrevor Thompson 
26777fc65dcSTrevor Thompson                 if (!NT_SUCCESS(Status))
26877fc65dcSTrevor Thompson                 {
26977fc65dcSTrevor Thompson                     DPRINT1("Error: Unable to allocate requested clusters!\n");
27077fc65dcSTrevor Thompson                     return Status;
27177fc65dcSTrevor Thompson                 }
27277fc65dcSTrevor Thompson 
27377fc65dcSTrevor Thompson                 // now we need to add the clusters we allocated to the data run
274afe40eb0STrevor Thompson                 Status = AddRun(Fcb->Vcb, AttrContext, AttrOffset, FileRecord, NextAssignedCluster, AssignedClusters);
27577fc65dcSTrevor Thompson                 if (!NT_SUCCESS(Status))
27677fc65dcSTrevor Thompson                 {
27777fc65dcSTrevor Thompson                     DPRINT1("Error: Unable to add data run!\n");
27877fc65dcSTrevor Thompson                     return Status;
27977fc65dcSTrevor Thompson                 }
28077fc65dcSTrevor Thompson 
28177fc65dcSTrevor Thompson                 ClustersNeeded -= AssignedClusters;
28277fc65dcSTrevor Thompson                 LastClusterInDataRun.LowPart = NextAssignedCluster + AssignedClusters - 1;
28377fc65dcSTrevor Thompson             }
284afe40eb0STrevor Thompson         }
285afe40eb0STrevor Thompson         else if (AttrContext->Record.NonResident.AllocatedSize > AllocationSize)
286afe40eb0STrevor Thompson         {
287920e2f02STrevor Thompson             // shrink allocation size
288920e2f02STrevor Thompson             ULONG ClustersToFree = ExistingClusters - (AllocationSize / BytesPerCluster);
289920e2f02STrevor Thompson             Status = FreeClusters(Fcb->Vcb, AttrContext, AttrOffset, FileRecord, ClustersToFree);
290ba33b9faSTrevor Thompson         }
291ba33b9faSTrevor Thompson 
292ba33b9faSTrevor Thompson         // TODO: is the file compressed, encrypted, or sparse?
293ba33b9faSTrevor Thompson 
294ba33b9faSTrevor Thompson         // NOTE: we need to have acquired the main resource exclusively, as well as(?) the PagingIoResource
295ba33b9faSTrevor Thompson 
296afe40eb0STrevor Thompson         Fcb->RFCB.AllocationSize.QuadPart = AllocationSize;
297afe40eb0STrevor Thompson         AttrContext->Record.NonResident.AllocatedSize = AllocationSize;
298ba33b9faSTrevor Thompson         AttrContext->Record.NonResident.DataSize = DataSize->QuadPart;
299ba33b9faSTrevor Thompson         AttrContext->Record.NonResident.InitializedSize = DataSize->QuadPart;
300ba33b9faSTrevor Thompson 
301afe40eb0STrevor Thompson         DestinationAttribute->NonResident.AllocatedSize = AllocationSize;
302afe40eb0STrevor Thompson         DestinationAttribute->NonResident.DataSize = DataSize->QuadPart;
303afe40eb0STrevor Thompson         DestinationAttribute->NonResident.InitializedSize = DataSize->QuadPart;
304afe40eb0STrevor Thompson 
305afe40eb0STrevor Thompson         DPRINT("Allocated Size: %I64u\n", DestinationAttribute->NonResident.AllocatedSize);
306760cdfb5STrevor Thompson     }
307760cdfb5STrevor Thompson     else
308760cdfb5STrevor Thompson     {
309760cdfb5STrevor Thompson         // resident attribute
310ba33b9faSTrevor Thompson 
311760cdfb5STrevor Thompson         // find the next attribute
312760cdfb5STrevor Thompson         ULONG NextAttributeOffset = AttrOffset + AttrContext->Record.Length;
313760cdfb5STrevor Thompson         PNTFS_ATTR_RECORD NextAttribute = (PNTFS_ATTR_RECORD)((PCHAR)FileRecord + NextAttributeOffset);
314ba33b9faSTrevor Thompson 
315ba33b9faSTrevor Thompson         //NtfsDumpFileAttributes(Fcb->Vcb, FileRecord);
316ba33b9faSTrevor Thompson 
317760cdfb5STrevor Thompson         // Do we need to increase the data length?
318760cdfb5STrevor Thompson         if (DataSize->QuadPart > AttrContext->Record.Resident.ValueLength)
319760cdfb5STrevor Thompson         {
320760cdfb5STrevor Thompson             // There's usually padding at the end of a record. Do we need to extend past it?
321760cdfb5STrevor Thompson             ULONG MaxValueLength = AttrContext->Record.Length - AttrContext->Record.Resident.ValueOffset;
322760cdfb5STrevor Thompson             if (MaxValueLength < DataSize->LowPart)
323760cdfb5STrevor Thompson             {
324760cdfb5STrevor Thompson                 // If this is the last attribute, we could move the end marker to the very end of the file record
325760cdfb5STrevor Thompson                 MaxValueLength += Fcb->Vcb->NtfsInfo.BytesPerFileRecord - NextAttributeOffset - (sizeof(ULONG) * 2);
326760cdfb5STrevor Thompson 
327760cdfb5STrevor Thompson                 if (MaxValueLength < DataSize->LowPart || NextAttribute->Type != AttributeEnd)
328760cdfb5STrevor Thompson                 {
329760cdfb5STrevor Thompson                     DPRINT1("FIXME: Need to convert attribute to non-resident!\n");
330760cdfb5STrevor Thompson                     return STATUS_NOT_IMPLEMENTED;
331760cdfb5STrevor Thompson                 }
332760cdfb5STrevor Thompson             }
333760cdfb5STrevor Thompson         }
334760cdfb5STrevor Thompson         else if (DataSize->LowPart < AttrContext->Record.Resident.ValueLength)
335760cdfb5STrevor Thompson         {
336760cdfb5STrevor Thompson             // we need to decrease the length
337760cdfb5STrevor Thompson             if (NextAttribute->Type != AttributeEnd)
338760cdfb5STrevor Thompson             {
339760cdfb5STrevor Thompson                 DPRINT1("FIXME: Don't know how to decrease length of resident attribute unless it's the final attribute!\n");
340760cdfb5STrevor Thompson                 return STATUS_NOT_IMPLEMENTED;
341760cdfb5STrevor Thompson             }
342760cdfb5STrevor Thompson         }
343760cdfb5STrevor Thompson 
344760cdfb5STrevor Thompson         InternalSetResidentAttributeLength(AttrContext, FileRecord, AttrOffset, DataSize->LowPart);
345760cdfb5STrevor Thompson     }
346ba33b9faSTrevor Thompson 
347ba33b9faSTrevor Thompson     //NtfsDumpFileAttributes(Fcb->Vcb, FileRecord);
348ba33b9faSTrevor Thompson 
349ba33b9faSTrevor Thompson     // write the updated file record back to disk
350760cdfb5STrevor Thompson     Status = UpdateFileRecord(Fcb->Vcb, Fcb->MFTIndex, FileRecord);
351ba33b9faSTrevor Thompson 
352760cdfb5STrevor Thompson     if (NT_SUCCESS(Status))
353ba33b9faSTrevor Thompson     {
354760cdfb5STrevor Thompson         Fcb->RFCB.FileSize = *DataSize;
355760cdfb5STrevor Thompson         Fcb->RFCB.ValidDataLength = *DataSize;
356760cdfb5STrevor Thompson         CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&Fcb->RFCB.AllocationSize);
357ba33b9faSTrevor Thompson     }
358ba33b9faSTrevor Thompson 
359ba33b9faSTrevor Thompson     return STATUS_SUCCESS;
360ba33b9faSTrevor Thompson }
361ba33b9faSTrevor Thompson 
362c2c66affSColin Finck ULONG
363c2c66affSColin Finck ReadAttribute(PDEVICE_EXTENSION Vcb,
364c2c66affSColin Finck               PNTFS_ATTR_CONTEXT Context,
365c2c66affSColin Finck               ULONGLONG Offset,
366c2c66affSColin Finck               PCHAR Buffer,
367c2c66affSColin Finck               ULONG Length)
368c2c66affSColin Finck {
369c2c66affSColin Finck     ULONGLONG LastLCN;
370c2c66affSColin Finck     PUCHAR DataRun;
371c2c66affSColin Finck     LONGLONG DataRunOffset;
372c2c66affSColin Finck     ULONGLONG DataRunLength;
373c2c66affSColin Finck     LONGLONG DataRunStartLCN;
374c2c66affSColin Finck     ULONGLONG CurrentOffset;
375c2c66affSColin Finck     ULONG ReadLength;
376c2c66affSColin Finck     ULONG AlreadyRead;
377c2c66affSColin Finck     NTSTATUS Status;
378c2c66affSColin Finck 
379c2c66affSColin Finck     if (!Context->Record.IsNonResident)
380c2c66affSColin Finck     {
381c2c66affSColin Finck         if (Offset > Context->Record.Resident.ValueLength)
382c2c66affSColin Finck             return 0;
383c2c66affSColin Finck         if (Offset + Length > Context->Record.Resident.ValueLength)
384c2c66affSColin Finck             Length = (ULONG)(Context->Record.Resident.ValueLength - Offset);
385c2c66affSColin Finck         RtlCopyMemory(Buffer, (PCHAR)&Context->Record + Context->Record.Resident.ValueOffset + Offset, Length);
386c2c66affSColin Finck         return Length;
387c2c66affSColin Finck     }
388c2c66affSColin Finck 
389c2c66affSColin Finck     /*
390c2c66affSColin Finck      * Non-resident attribute
391c2c66affSColin Finck      */
392c2c66affSColin Finck 
393c2c66affSColin Finck     /*
394c2c66affSColin Finck      * I. Find the corresponding start data run.
395c2c66affSColin Finck      */
396c2c66affSColin Finck 
397c2c66affSColin Finck     AlreadyRead = 0;
398c2c66affSColin Finck 
399c2c66affSColin Finck     // FIXME: Cache seems to be non-working. Disable it for now
400c2c66affSColin Finck     //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
401c2c66affSColin Finck     if (0)
402c2c66affSColin Finck     {
403c2c66affSColin Finck         DataRun = Context->CacheRun;
404c2c66affSColin Finck         LastLCN = Context->CacheRunLastLCN;
405c2c66affSColin Finck         DataRunStartLCN = Context->CacheRunStartLCN;
406c2c66affSColin Finck         DataRunLength = Context->CacheRunLength;
407c2c66affSColin Finck         CurrentOffset = Context->CacheRunCurrentOffset;
408c2c66affSColin Finck     }
409c2c66affSColin Finck     else
410c2c66affSColin Finck     {
411c2c66affSColin Finck         LastLCN = 0;
412c2c66affSColin Finck         DataRun = (PUCHAR)&Context->Record + Context->Record.NonResident.MappingPairsOffset;
413c2c66affSColin Finck         CurrentOffset = 0;
414c2c66affSColin Finck 
415c2c66affSColin Finck         while (1)
416c2c66affSColin Finck         {
417c2c66affSColin Finck             DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
418c2c66affSColin Finck             if (DataRunOffset != -1)
419c2c66affSColin Finck             {
420c2c66affSColin Finck                 /* Normal data run. */
421c2c66affSColin Finck                 DataRunStartLCN = LastLCN + DataRunOffset;
422c2c66affSColin Finck                 LastLCN = DataRunStartLCN;
423c2c66affSColin Finck             }
424c2c66affSColin Finck             else
425c2c66affSColin Finck             {
426c2c66affSColin Finck                 /* Sparse data run. */
427c2c66affSColin Finck                 DataRunStartLCN = -1;
428c2c66affSColin Finck             }
429c2c66affSColin Finck 
430c2c66affSColin Finck             if (Offset >= CurrentOffset &&
431c2c66affSColin Finck                 Offset < CurrentOffset + (DataRunLength * Vcb->NtfsInfo.BytesPerCluster))
432c2c66affSColin Finck             {
433c2c66affSColin Finck                 break;
434c2c66affSColin Finck             }
435c2c66affSColin Finck 
436c2c66affSColin Finck             if (*DataRun == 0)
437c2c66affSColin Finck             {
438c2c66affSColin Finck                 return AlreadyRead;
439c2c66affSColin Finck             }
440c2c66affSColin Finck 
441c2c66affSColin Finck             CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
442c2c66affSColin Finck         }
443c2c66affSColin Finck     }
444c2c66affSColin Finck 
445c2c66affSColin Finck     /*
446c2c66affSColin Finck      * II. Go through the run list and read the data
447c2c66affSColin Finck      */
448c2c66affSColin Finck 
449c2c66affSColin Finck     ReadLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset), Length);
450c2c66affSColin Finck     if (DataRunStartLCN == -1)
451c2c66affSColin Finck     {
452c2c66affSColin Finck         RtlZeroMemory(Buffer, ReadLength);
453c2c66affSColin Finck         Status = STATUS_SUCCESS;
454c2c66affSColin Finck     }
455c2c66affSColin Finck     else
456c2c66affSColin Finck     {
457c2c66affSColin Finck         Status = NtfsReadDisk(Vcb->StorageDevice,
458c2c66affSColin Finck                               DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster + Offset - CurrentOffset,
459c2c66affSColin Finck                               ReadLength,
460c2c66affSColin Finck                               Vcb->NtfsInfo.BytesPerSector,
461c2c66affSColin Finck                               (PVOID)Buffer,
462c2c66affSColin Finck                               FALSE);
463c2c66affSColin Finck     }
464c2c66affSColin Finck     if (NT_SUCCESS(Status))
465c2c66affSColin Finck     {
466c2c66affSColin Finck         Length -= ReadLength;
467c2c66affSColin Finck         Buffer += ReadLength;
468c2c66affSColin Finck         AlreadyRead += ReadLength;
469c2c66affSColin Finck 
470c2c66affSColin Finck         if (ReadLength == DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset))
471c2c66affSColin Finck         {
472c2c66affSColin Finck             CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
473c2c66affSColin Finck             DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
474c2c66affSColin Finck             if (DataRunOffset != (ULONGLONG)-1)
475c2c66affSColin Finck             {
476c2c66affSColin Finck                 DataRunStartLCN = LastLCN + DataRunOffset;
477c2c66affSColin Finck                 LastLCN = DataRunStartLCN;
478c2c66affSColin Finck             }
479c2c66affSColin Finck             else
480c2c66affSColin Finck                 DataRunStartLCN = -1;
481c2c66affSColin Finck         }
482c2c66affSColin Finck 
483c2c66affSColin Finck         while (Length > 0)
484c2c66affSColin Finck         {
485c2c66affSColin Finck             ReadLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster, Length);
486c2c66affSColin Finck             if (DataRunStartLCN == -1)
487c2c66affSColin Finck                 RtlZeroMemory(Buffer, ReadLength);
488c2c66affSColin Finck             else
489c2c66affSColin Finck             {
490c2c66affSColin Finck                 Status = NtfsReadDisk(Vcb->StorageDevice,
491c2c66affSColin Finck                                       DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster,
492c2c66affSColin Finck                                       ReadLength,
493c2c66affSColin Finck                                       Vcb->NtfsInfo.BytesPerSector,
494c2c66affSColin Finck                                       (PVOID)Buffer,
495c2c66affSColin Finck                                       FALSE);
496c2c66affSColin Finck                 if (!NT_SUCCESS(Status))
497c2c66affSColin Finck                     break;
498c2c66affSColin Finck             }
499c2c66affSColin Finck 
500c2c66affSColin Finck             Length -= ReadLength;
501c2c66affSColin Finck             Buffer += ReadLength;
502c2c66affSColin Finck             AlreadyRead += ReadLength;
503c2c66affSColin Finck 
504c2c66affSColin Finck             /* We finished this request, but there still data in this data run. */
505c2c66affSColin Finck             if (Length == 0 && ReadLength != DataRunLength * Vcb->NtfsInfo.BytesPerCluster)
506c2c66affSColin Finck                 break;
507c2c66affSColin Finck 
508c2c66affSColin Finck             /*
509c2c66affSColin Finck              * Go to next run in the list.
510c2c66affSColin Finck              */
511c2c66affSColin Finck 
512c2c66affSColin Finck             if (*DataRun == 0)
513c2c66affSColin Finck                 break;
514c2c66affSColin Finck             CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
515c2c66affSColin Finck             DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
516c2c66affSColin Finck             if (DataRunOffset != -1)
517c2c66affSColin Finck             {
518c2c66affSColin Finck                 /* Normal data run. */
519c2c66affSColin Finck                 DataRunStartLCN = LastLCN + DataRunOffset;
520c2c66affSColin Finck                 LastLCN = DataRunStartLCN;
521c2c66affSColin Finck             }
522c2c66affSColin Finck             else
523c2c66affSColin Finck             {
524c2c66affSColin Finck                 /* Sparse data run. */
525c2c66affSColin Finck                 DataRunStartLCN = -1;
526c2c66affSColin Finck             }
527c2c66affSColin Finck         } /* while */
528c2c66affSColin Finck 
529c2c66affSColin Finck     } /* if Disk */
530c2c66affSColin Finck 
531c2c66affSColin Finck     Context->CacheRun = DataRun;
532c2c66affSColin Finck     Context->CacheRunOffset = Offset + AlreadyRead;
533c2c66affSColin Finck     Context->CacheRunStartLCN = DataRunStartLCN;
534c2c66affSColin Finck     Context->CacheRunLength = DataRunLength;
535c2c66affSColin Finck     Context->CacheRunLastLCN = LastLCN;
536c2c66affSColin Finck     Context->CacheRunCurrentOffset = CurrentOffset;
537c2c66affSColin Finck 
538c2c66affSColin Finck     return AlreadyRead;
539c2c66affSColin Finck }
540c2c66affSColin Finck 
541c2c66affSColin Finck 
54258a13831STrevor Thompson /**
54358a13831STrevor Thompson * @name WriteAttribute
54458a13831STrevor Thompson * @implemented
54558a13831STrevor Thompson *
54658a13831STrevor Thompson * Writes an NTFS attribute to the disk. It presently borrows a lot of code from ReadAttribute(),
54758a13831STrevor Thompson * and it still needs more documentation / cleaning up.
54858a13831STrevor Thompson *
54958a13831STrevor Thompson * @param Vcb
55058a13831STrevor Thompson * Volume Control Block indicating which volume to write the attribute to
55158a13831STrevor Thompson *
55258a13831STrevor Thompson * @param Context
55358a13831STrevor Thompson * Pointer to an NTFS_ATTR_CONTEXT that has information about the attribute
55458a13831STrevor Thompson *
55558a13831STrevor Thompson * @param Offset
55658a13831STrevor Thompson * Offset, in bytes, from the beginning of the attribute indicating where to start
55758a13831STrevor Thompson * writing data
55858a13831STrevor Thompson *
55958a13831STrevor Thompson * @param Buffer
56058a13831STrevor Thompson * The data that's being written to the device
56158a13831STrevor Thompson *
56258a13831STrevor Thompson * @param Length
56358a13831STrevor Thompson * How much data will be written, in bytes
56458a13831STrevor Thompson *
56558a13831STrevor Thompson * @param RealLengthWritten
56658a13831STrevor Thompson * Pointer to a ULONG which will receive how much data was written, in bytes
56758a13831STrevor Thompson *
56858a13831STrevor Thompson * @return
56958a13831STrevor Thompson * STATUS_SUCCESS if successful, an error code otherwise. STATUS_NOT_IMPLEMENTED if
57058a13831STrevor Thompson * writing to a sparse file.
57158a13831STrevor Thompson *
57258a13831STrevor Thompson * @remarks Note that in this context the word "attribute" isn't referring read-only, hidden,
57358a13831STrevor Thompson * etc. - the file's data is actually stored in an attribute in NTFS parlance.
57458a13831STrevor Thompson *
57558a13831STrevor Thompson */
57658a13831STrevor Thompson 
57758a13831STrevor Thompson NTSTATUS
57858a13831STrevor Thompson WriteAttribute(PDEVICE_EXTENSION Vcb,
57958a13831STrevor Thompson                PNTFS_ATTR_CONTEXT Context,
58058a13831STrevor Thompson                ULONGLONG Offset,
58158a13831STrevor Thompson                const PUCHAR Buffer,
58258a13831STrevor Thompson                ULONG Length,
58358a13831STrevor Thompson                PULONG RealLengthWritten)
58458a13831STrevor Thompson {
58558a13831STrevor Thompson     ULONGLONG LastLCN;
58658a13831STrevor Thompson     PUCHAR DataRun;
58758a13831STrevor Thompson     LONGLONG DataRunOffset;
58858a13831STrevor Thompson     ULONGLONG DataRunLength;
58958a13831STrevor Thompson     LONGLONG DataRunStartLCN;
59058a13831STrevor Thompson     ULONGLONG CurrentOffset;
59158a13831STrevor Thompson     ULONG WriteLength;
59258a13831STrevor Thompson     NTSTATUS Status;
59358a13831STrevor Thompson     PUCHAR SourceBuffer = Buffer;
59458a13831STrevor Thompson     LONGLONG StartingOffset;
59558a13831STrevor Thompson 
59677fc65dcSTrevor Thompson     DPRINT("WriteAttribute(%p, %p, %I64u, %p, %lu, %p)\n", Vcb, Context, Offset, Buffer, Length, RealLengthWritten);
59758a13831STrevor Thompson 
598760cdfb5STrevor Thompson     *RealLengthWritten = 0;
599760cdfb5STrevor Thompson 
60058a13831STrevor Thompson     // is this a resident attribute?
60158a13831STrevor Thompson     if (!Context->Record.IsNonResident)
60258a13831STrevor Thompson     {
603760cdfb5STrevor Thompson         ULONG AttributeOffset;
604760cdfb5STrevor Thompson         PNTFS_ATTR_CONTEXT FoundContext;
605760cdfb5STrevor Thompson         PFILE_RECORD_HEADER FileRecord;
60658a13831STrevor Thompson 
60758a13831STrevor Thompson         if (Offset + Length > Context->Record.Resident.ValueLength)
608760cdfb5STrevor Thompson         {
609760cdfb5STrevor Thompson             DPRINT1("DRIVER ERROR: Attribute is too small!\n");
610760cdfb5STrevor Thompson             return STATUS_INVALID_PARAMETER;
611760cdfb5STrevor Thompson         }
61258a13831STrevor Thompson 
613760cdfb5STrevor Thompson         FileRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
614760cdfb5STrevor Thompson 
615760cdfb5STrevor Thompson         if (!FileRecord)
616760cdfb5STrevor Thompson         {
617760cdfb5STrevor Thompson             DPRINT1("Error: Couldn't allocate file record!\n");
618760cdfb5STrevor Thompson             return STATUS_NO_MEMORY;
619760cdfb5STrevor Thompson         }
620760cdfb5STrevor Thompson 
621760cdfb5STrevor Thompson         // read the file record
622760cdfb5STrevor Thompson         ReadFileRecord(Vcb, Context->FileMFTIndex, FileRecord);
623760cdfb5STrevor Thompson 
624760cdfb5STrevor Thompson         // find where to write the attribute data to
625760cdfb5STrevor Thompson         Status = FindAttribute(Vcb, FileRecord,
626760cdfb5STrevor Thompson                                Context->Record.Type,
627760cdfb5STrevor Thompson                                (PCWSTR)((PCHAR)&Context->Record + Context->Record.NameOffset),
628760cdfb5STrevor Thompson                                Context->Record.NameLength,
629760cdfb5STrevor Thompson                                &FoundContext,
630760cdfb5STrevor Thompson                                &AttributeOffset);
631760cdfb5STrevor Thompson 
632760cdfb5STrevor Thompson         if (!NT_SUCCESS(Status))
633760cdfb5STrevor Thompson         {
634760cdfb5STrevor Thompson             DPRINT1("ERROR: Couldn't find matching attribute!\n");
635760cdfb5STrevor Thompson             ExFreePoolWithTag(FileRecord, TAG_NTFS);
636760cdfb5STrevor Thompson             return Status;
637760cdfb5STrevor Thompson         }
638760cdfb5STrevor Thompson 
639760cdfb5STrevor Thompson         DPRINT("Offset: %I64u, AttributeOffset: %u, ValueOffset: %u\n", Offset, AttributeOffset, Context->Record.Resident.ValueLength);
640760cdfb5STrevor Thompson         Offset += AttributeOffset + Context->Record.Resident.ValueOffset;
641760cdfb5STrevor Thompson 
642760cdfb5STrevor Thompson         if (Offset + Length > Vcb->NtfsInfo.BytesPerFileRecord)
643760cdfb5STrevor Thompson         {
644760cdfb5STrevor Thompson             DPRINT1("DRIVER ERROR: Data being written extends past end of file record!\n");
645760cdfb5STrevor Thompson             ReleaseAttributeContext(FoundContext);
646760cdfb5STrevor Thompson             ExFreePoolWithTag(FileRecord, TAG_NTFS);
647760cdfb5STrevor Thompson             return STATUS_INVALID_PARAMETER;
648760cdfb5STrevor Thompson         }
649760cdfb5STrevor Thompson 
650760cdfb5STrevor Thompson         // copy the data being written into the file record
651760cdfb5STrevor Thompson         RtlCopyMemory((PCHAR)FileRecord + Offset, Buffer, Length);
652760cdfb5STrevor Thompson 
653760cdfb5STrevor Thompson         Status = UpdateFileRecord(Vcb, Context->FileMFTIndex, FileRecord);
654760cdfb5STrevor Thompson 
655760cdfb5STrevor Thompson         ReleaseAttributeContext(FoundContext);
656760cdfb5STrevor Thompson         ExFreePoolWithTag(FileRecord, TAG_NTFS);
657760cdfb5STrevor Thompson 
658760cdfb5STrevor Thompson         if (NT_SUCCESS(Status))
659760cdfb5STrevor Thompson             *RealLengthWritten = Length;
660760cdfb5STrevor Thompson 
661760cdfb5STrevor Thompson         return Status;
66258a13831STrevor Thompson     }
66358a13831STrevor Thompson 
66458a13831STrevor Thompson     // This is a non-resident attribute.
66558a13831STrevor Thompson 
66658a13831STrevor Thompson     // I. Find the corresponding start data run.
66758a13831STrevor Thompson 
66858a13831STrevor Thompson     // FIXME: Cache seems to be non-working. Disable it for now
66958a13831STrevor Thompson     //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
67058a13831STrevor Thompson     /*if (0)
67158a13831STrevor Thompson     {
67258a13831STrevor Thompson     DataRun = Context->CacheRun;
67358a13831STrevor Thompson     LastLCN = Context->CacheRunLastLCN;
67458a13831STrevor Thompson     DataRunStartLCN = Context->CacheRunStartLCN;
67558a13831STrevor Thompson     DataRunLength = Context->CacheRunLength;
67658a13831STrevor Thompson     CurrentOffset = Context->CacheRunCurrentOffset;
67758a13831STrevor Thompson     }
67858a13831STrevor Thompson     else*/
67958a13831STrevor Thompson     {
68058a13831STrevor Thompson         LastLCN = 0;
68158a13831STrevor Thompson         DataRun = (PUCHAR)&Context->Record + Context->Record.NonResident.MappingPairsOffset;
68258a13831STrevor Thompson         CurrentOffset = 0;
68358a13831STrevor Thompson 
68458a13831STrevor Thompson         while (1)
68558a13831STrevor Thompson         {
68658a13831STrevor Thompson             DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
68758a13831STrevor Thompson             if (DataRunOffset != -1)
68858a13831STrevor Thompson             {
68958a13831STrevor Thompson                 // Normal data run.
69058a13831STrevor Thompson                 // DPRINT1("Writing to normal data run, LastLCN %I64u DataRunOffset %I64d\n", LastLCN, DataRunOffset);
69158a13831STrevor Thompson                 DataRunStartLCN = LastLCN + DataRunOffset;
69258a13831STrevor Thompson                 LastLCN = DataRunStartLCN;
69358a13831STrevor Thompson             }
69458a13831STrevor Thompson             else
69558a13831STrevor Thompson             {
69658a13831STrevor Thompson                 // Sparse data run. We can't support writing to sparse files yet
69758a13831STrevor Thompson                 // (it may require increasing the allocation size).
69858a13831STrevor Thompson                 DataRunStartLCN = -1;
69958a13831STrevor Thompson                 DPRINT1("FIXME: Writing to sparse files is not supported yet!\n");
70058a13831STrevor Thompson                 return STATUS_NOT_IMPLEMENTED;
70158a13831STrevor Thompson             }
70258a13831STrevor Thompson 
70358a13831STrevor Thompson             // Have we reached the data run we're trying to write to?
70458a13831STrevor Thompson             if (Offset >= CurrentOffset &&
70558a13831STrevor Thompson                 Offset < CurrentOffset + (DataRunLength * Vcb->NtfsInfo.BytesPerCluster))
70658a13831STrevor Thompson             {
70758a13831STrevor Thompson                 break;
70858a13831STrevor Thompson             }
70958a13831STrevor Thompson 
71058a13831STrevor Thompson             if (*DataRun == 0)
71158a13831STrevor Thompson             {
71258a13831STrevor Thompson                 // We reached the last assigned cluster
71358a13831STrevor Thompson                 // TODO: assign new clusters to the end of the file.
71458a13831STrevor Thompson                 // (Presently, this code will never be reached, the write should have already failed by now)
71558a13831STrevor Thompson                 return STATUS_END_OF_FILE;
71658a13831STrevor Thompson             }
71758a13831STrevor Thompson 
71858a13831STrevor Thompson             CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
71958a13831STrevor Thompson         }
72058a13831STrevor Thompson     }
72158a13831STrevor Thompson 
72258a13831STrevor Thompson     // II. Go through the run list and write the data
72358a13831STrevor Thompson 
72458a13831STrevor Thompson     /* REVIEWME -- As adapted from NtfsReadAttribute():
72558a13831STrevor Thompson     We seem to be making a special case for the first applicable data run, but I'm not sure why.
72658a13831STrevor Thompson     Does it have something to do with (not) caching? Is this strategy equally applicable to writing? */
72758a13831STrevor Thompson 
72858a13831STrevor Thompson     WriteLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset), Length);
72958a13831STrevor Thompson 
73058a13831STrevor Thompson     StartingOffset = DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster + Offset - CurrentOffset;
73158a13831STrevor Thompson 
73258a13831STrevor Thompson     // Write the data to the disk
73358a13831STrevor Thompson     Status = NtfsWriteDisk(Vcb->StorageDevice,
73458a13831STrevor Thompson                            StartingOffset,
73558a13831STrevor Thompson                            WriteLength,
73658a13831STrevor Thompson                            Vcb->NtfsInfo.BytesPerSector,
73758a13831STrevor Thompson                            (PVOID)SourceBuffer);
73858a13831STrevor Thompson 
73958a13831STrevor Thompson     // Did the write fail?
74058a13831STrevor Thompson     if (!NT_SUCCESS(Status))
74158a13831STrevor Thompson     {
74258a13831STrevor Thompson         Context->CacheRun = DataRun;
74358a13831STrevor Thompson         Context->CacheRunOffset = Offset;
74458a13831STrevor Thompson         Context->CacheRunStartLCN = DataRunStartLCN;
74558a13831STrevor Thompson         Context->CacheRunLength = DataRunLength;
74658a13831STrevor Thompson         Context->CacheRunLastLCN = LastLCN;
74758a13831STrevor Thompson         Context->CacheRunCurrentOffset = CurrentOffset;
74858a13831STrevor Thompson 
74958a13831STrevor Thompson         return Status;
75058a13831STrevor Thompson     }
75158a13831STrevor Thompson 
75258a13831STrevor Thompson     Length -= WriteLength;
75358a13831STrevor Thompson     SourceBuffer += WriteLength;
75458a13831STrevor Thompson     *RealLengthWritten += WriteLength;
75558a13831STrevor Thompson 
75658a13831STrevor Thompson     // Did we write to the end of the data run?
75758a13831STrevor Thompson     if (WriteLength == DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset))
75858a13831STrevor Thompson     {
75958a13831STrevor Thompson         // Advance to the next data run
76058a13831STrevor Thompson         CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
76158a13831STrevor Thompson         DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
76258a13831STrevor Thompson 
76358a13831STrevor Thompson         if (DataRunOffset != (ULONGLONG)-1)
76458a13831STrevor Thompson         {
76558a13831STrevor Thompson             DataRunStartLCN = LastLCN + DataRunOffset;
76658a13831STrevor Thompson             LastLCN = DataRunStartLCN;
76758a13831STrevor Thompson         }
76858a13831STrevor Thompson         else
76958a13831STrevor Thompson             DataRunStartLCN = -1;
77058a13831STrevor Thompson     }
77158a13831STrevor Thompson 
77258a13831STrevor Thompson     // Do we have more data to write?
77358a13831STrevor Thompson     while (Length > 0)
77458a13831STrevor Thompson     {
77558a13831STrevor Thompson         // Make sure we don't write past the end of the current data run
77658a13831STrevor Thompson         WriteLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster, Length);
77758a13831STrevor Thompson 
77858a13831STrevor Thompson         // Are we dealing with a sparse data run?
77958a13831STrevor Thompson         if (DataRunStartLCN == -1)
78058a13831STrevor Thompson         {
78158a13831STrevor Thompson             DPRINT1("FIXME: Don't know how to write to sparse files yet! (DataRunStartLCN == -1)\n");
78258a13831STrevor Thompson             return STATUS_NOT_IMPLEMENTED;
78358a13831STrevor Thompson         }
78458a13831STrevor Thompson         else
78558a13831STrevor Thompson         {
78658a13831STrevor Thompson             // write the data to the disk
78758a13831STrevor Thompson             Status = NtfsWriteDisk(Vcb->StorageDevice,
78858a13831STrevor Thompson                                    DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster,
78958a13831STrevor Thompson                                    WriteLength,
79058a13831STrevor Thompson                                    Vcb->NtfsInfo.BytesPerSector,
79158a13831STrevor Thompson                                    (PVOID)SourceBuffer);
79258a13831STrevor Thompson             if (!NT_SUCCESS(Status))
79358a13831STrevor Thompson                 break;
79458a13831STrevor Thompson         }
79558a13831STrevor Thompson 
79658a13831STrevor Thompson         Length -= WriteLength;
79758a13831STrevor Thompson         SourceBuffer += WriteLength;
798ea6b9622STrevor Thompson         *RealLengthWritten += WriteLength;
79958a13831STrevor Thompson 
80058a13831STrevor Thompson         // We finished this request, but there's still data in this data run.
80158a13831STrevor Thompson         if (Length == 0 && WriteLength != DataRunLength * Vcb->NtfsInfo.BytesPerCluster)
80258a13831STrevor Thompson             break;
80358a13831STrevor Thompson 
80458a13831STrevor Thompson         // Go to next run in the list.
80558a13831STrevor Thompson 
80658a13831STrevor Thompson         if (*DataRun == 0)
80758a13831STrevor Thompson         {
80858a13831STrevor Thompson             // that was the last run
80958a13831STrevor Thompson             if (Length > 0)
81058a13831STrevor Thompson             {
81158a13831STrevor Thompson                 // Failed sanity check.
81258a13831STrevor Thompson                 DPRINT1("Encountered EOF before expected!\n");
81358a13831STrevor Thompson                 return STATUS_END_OF_FILE;
81458a13831STrevor Thompson             }
81558a13831STrevor Thompson 
81658a13831STrevor Thompson             break;
81758a13831STrevor Thompson         }
81858a13831STrevor Thompson 
81958a13831STrevor Thompson         // Advance to the next data run
82058a13831STrevor Thompson         CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
82158a13831STrevor Thompson         DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
82258a13831STrevor Thompson         if (DataRunOffset != -1)
82358a13831STrevor Thompson         {
82458a13831STrevor Thompson             // Normal data run.
82558a13831STrevor Thompson             DataRunStartLCN = LastLCN + DataRunOffset;
82658a13831STrevor Thompson             LastLCN = DataRunStartLCN;
82758a13831STrevor Thompson         }
82858a13831STrevor Thompson         else
82958a13831STrevor Thompson         {
83058a13831STrevor Thompson             // Sparse data run.
83158a13831STrevor Thompson             DataRunStartLCN = -1;
83258a13831STrevor Thompson         }
83358a13831STrevor Thompson     } // end while (Length > 0) [more data to write]
83458a13831STrevor Thompson 
83558a13831STrevor Thompson     Context->CacheRun = DataRun;
83658a13831STrevor Thompson     Context->CacheRunOffset = Offset + *RealLengthWritten;
83758a13831STrevor Thompson     Context->CacheRunStartLCN = DataRunStartLCN;
83858a13831STrevor Thompson     Context->CacheRunLength = DataRunLength;
83958a13831STrevor Thompson     Context->CacheRunLastLCN = LastLCN;
84058a13831STrevor Thompson     Context->CacheRunCurrentOffset = CurrentOffset;
84158a13831STrevor Thompson 
84258a13831STrevor Thompson     return Status;
84358a13831STrevor Thompson }
84458a13831STrevor Thompson 
845c2c66affSColin Finck NTSTATUS
846c2c66affSColin Finck ReadFileRecord(PDEVICE_EXTENSION Vcb,
847c2c66affSColin Finck                ULONGLONG index,
848c2c66affSColin Finck                PFILE_RECORD_HEADER file)
849c2c66affSColin Finck {
850c2c66affSColin Finck     ULONGLONG BytesRead;
851c2c66affSColin Finck 
852c2c66affSColin Finck     DPRINT("ReadFileRecord(%p, %I64x, %p)\n", Vcb, index, file);
853c2c66affSColin Finck 
854c2c66affSColin Finck     BytesRead = ReadAttribute(Vcb, Vcb->MFTContext, index * Vcb->NtfsInfo.BytesPerFileRecord, (PCHAR)file, Vcb->NtfsInfo.BytesPerFileRecord);
855c2c66affSColin Finck     if (BytesRead != Vcb->NtfsInfo.BytesPerFileRecord)
856c2c66affSColin Finck     {
857c2c66affSColin Finck         DPRINT1("ReadFileRecord failed: %I64u read, %u expected\n", BytesRead, Vcb->NtfsInfo.BytesPerFileRecord);
858c2c66affSColin Finck         return STATUS_PARTIAL_COPY;
859c2c66affSColin Finck     }
860c2c66affSColin Finck 
861c2c66affSColin Finck     /* Apply update sequence array fixups. */
862760cdfb5STrevor Thompson     DPRINT("Sequence number: %u\n", file->SequenceNumber);
863c2c66affSColin Finck     return FixupUpdateSequenceArray(Vcb, &file->Ntfs);
864c2c66affSColin Finck }
865c2c66affSColin Finck 
866a7a2c0d7STrevor Thompson 
867a7a2c0d7STrevor Thompson /**
868a7a2c0d7STrevor Thompson * Searches a file's parent directory (given the parent's index in the mft)
869a7a2c0d7STrevor Thompson * for the given file. Upon finding an index entry for that file, updates
870a7a2c0d7STrevor Thompson * Data Size and Allocated Size values in the $FILE_NAME attribute of that entry.
871a7a2c0d7STrevor Thompson *
872a7a2c0d7STrevor Thompson * (Most of this code was copied from NtfsFindMftRecord)
873a7a2c0d7STrevor Thompson */
874a7a2c0d7STrevor Thompson NTSTATUS
875a7a2c0d7STrevor Thompson UpdateFileNameRecord(PDEVICE_EXTENSION Vcb,
876a7a2c0d7STrevor Thompson                      ULONGLONG ParentMFTIndex,
877a7a2c0d7STrevor Thompson                      PUNICODE_STRING FileName,
878a7a2c0d7STrevor Thompson                      BOOLEAN DirSearch,
879a7a2c0d7STrevor Thompson                      ULONGLONG NewDataSize,
880a7a2c0d7STrevor Thompson                      ULONGLONG NewAllocationSize)
881a7a2c0d7STrevor Thompson {
882a7a2c0d7STrevor Thompson     PFILE_RECORD_HEADER MftRecord;
883a7a2c0d7STrevor Thompson     PNTFS_ATTR_CONTEXT IndexRootCtx;
884a7a2c0d7STrevor Thompson     PINDEX_ROOT_ATTRIBUTE IndexRoot;
885a7a2c0d7STrevor Thompson     PCHAR IndexRecord;
886a7a2c0d7STrevor Thompson     PINDEX_ENTRY_ATTRIBUTE IndexEntry, IndexEntryEnd;
887a7a2c0d7STrevor Thompson     NTSTATUS Status;
888a7a2c0d7STrevor Thompson     ULONG CurrentEntry = 0;
889a7a2c0d7STrevor Thompson 
890a7a2c0d7STrevor Thompson     DPRINT("UpdateFileNameRecord(%p, %I64d, %wZ, %u, %I64u, %I64u)\n", Vcb, ParentMFTIndex, FileName, DirSearch, NewDataSize, NewAllocationSize);
891a7a2c0d7STrevor Thompson 
892a7a2c0d7STrevor Thompson     MftRecord = ExAllocatePoolWithTag(NonPagedPool,
893a7a2c0d7STrevor Thompson                                       Vcb->NtfsInfo.BytesPerFileRecord,
894a7a2c0d7STrevor Thompson                                       TAG_NTFS);
895a7a2c0d7STrevor Thompson     if (MftRecord == NULL)
896a7a2c0d7STrevor Thompson     {
897a7a2c0d7STrevor Thompson         return STATUS_INSUFFICIENT_RESOURCES;
898a7a2c0d7STrevor Thompson     }
899a7a2c0d7STrevor Thompson 
900a7a2c0d7STrevor Thompson     Status = ReadFileRecord(Vcb, ParentMFTIndex, MftRecord);
901a7a2c0d7STrevor Thompson     if (!NT_SUCCESS(Status))
902a7a2c0d7STrevor Thompson     {
903a7a2c0d7STrevor Thompson         ExFreePoolWithTag(MftRecord, TAG_NTFS);
904a7a2c0d7STrevor Thompson         return Status;
905a7a2c0d7STrevor Thompson     }
906a7a2c0d7STrevor Thompson 
907a7a2c0d7STrevor Thompson     ASSERT(MftRecord->Ntfs.Type == NRH_FILE_TYPE);
908a7a2c0d7STrevor Thompson     Status = FindAttribute(Vcb, MftRecord, AttributeIndexRoot, L"$I30", 4, &IndexRootCtx, NULL);
909a7a2c0d7STrevor Thompson     if (!NT_SUCCESS(Status))
910a7a2c0d7STrevor Thompson     {
911a7a2c0d7STrevor Thompson         ExFreePoolWithTag(MftRecord, TAG_NTFS);
912a7a2c0d7STrevor Thompson         return Status;
913a7a2c0d7STrevor Thompson     }
914a7a2c0d7STrevor Thompson 
915a7a2c0d7STrevor Thompson     IndexRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerIndexRecord, TAG_NTFS);
916a7a2c0d7STrevor Thompson     if (IndexRecord == NULL)
917a7a2c0d7STrevor Thompson     {
918a7a2c0d7STrevor Thompson         ReleaseAttributeContext(IndexRootCtx);
919a7a2c0d7STrevor Thompson         ExFreePoolWithTag(MftRecord, TAG_NTFS);
920a7a2c0d7STrevor Thompson         return STATUS_INSUFFICIENT_RESOURCES;
921a7a2c0d7STrevor Thompson     }
922a7a2c0d7STrevor Thompson 
923a7a2c0d7STrevor Thompson     ReadAttribute(Vcb, IndexRootCtx, 0, IndexRecord, Vcb->NtfsInfo.BytesPerIndexRecord);
924a7a2c0d7STrevor Thompson     IndexRoot = (PINDEX_ROOT_ATTRIBUTE)IndexRecord;
925a7a2c0d7STrevor Thompson     IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)&IndexRoot->Header + IndexRoot->Header.FirstEntryOffset);
926a7a2c0d7STrevor Thompson     // Index root is always resident.
927a7a2c0d7STrevor Thompson     IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)(IndexRecord + IndexRoot->Header.TotalSizeOfEntries);
928a7a2c0d7STrevor Thompson 
929a7a2c0d7STrevor Thompson     DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb->NtfsInfo.BytesPerIndexRecord, IndexRoot->SizeOfEntry);
930a7a2c0d7STrevor Thompson 
931a7a2c0d7STrevor Thompson     Status = UpdateIndexEntryFileNameSize(Vcb,
932a7a2c0d7STrevor Thompson                                           MftRecord,
933a7a2c0d7STrevor Thompson                                           IndexRecord,
934a7a2c0d7STrevor Thompson                                           IndexRoot->SizeOfEntry,
935a7a2c0d7STrevor Thompson                                           IndexEntry,
936a7a2c0d7STrevor Thompson                                           IndexEntryEnd,
937a7a2c0d7STrevor Thompson                                           FileName,
938a7a2c0d7STrevor Thompson                                           &CurrentEntry,
939a7a2c0d7STrevor Thompson                                           &CurrentEntry,
940a7a2c0d7STrevor Thompson                                           DirSearch,
941a7a2c0d7STrevor Thompson                                           NewDataSize,
942a7a2c0d7STrevor Thompson                                           NewAllocationSize);
943a7a2c0d7STrevor Thompson 
944a7a2c0d7STrevor Thompson     ReleaseAttributeContext(IndexRootCtx);
945a7a2c0d7STrevor Thompson     ExFreePoolWithTag(IndexRecord, TAG_NTFS);
946a7a2c0d7STrevor Thompson     ExFreePoolWithTag(MftRecord, TAG_NTFS);
947a7a2c0d7STrevor Thompson 
948a7a2c0d7STrevor Thompson     return Status;
949a7a2c0d7STrevor Thompson }
950a7a2c0d7STrevor Thompson 
951a7a2c0d7STrevor Thompson /**
952a7a2c0d7STrevor Thompson * Recursively searches directory index and applies the size update to the $FILE_NAME attribute of the
953a7a2c0d7STrevor Thompson * proper index entry.
954a7a2c0d7STrevor Thompson * (Heavily based on BrowseIndexEntries)
955a7a2c0d7STrevor Thompson */
956a7a2c0d7STrevor Thompson NTSTATUS
957a7a2c0d7STrevor Thompson UpdateIndexEntryFileNameSize(PDEVICE_EXTENSION Vcb,
958a7a2c0d7STrevor Thompson                              PFILE_RECORD_HEADER MftRecord,
959a7a2c0d7STrevor Thompson                              PCHAR IndexRecord,
960a7a2c0d7STrevor Thompson                              ULONG IndexBlockSize,
961a7a2c0d7STrevor Thompson                              PINDEX_ENTRY_ATTRIBUTE FirstEntry,
962a7a2c0d7STrevor Thompson                              PINDEX_ENTRY_ATTRIBUTE LastEntry,
963a7a2c0d7STrevor Thompson                              PUNICODE_STRING FileName,
964a7a2c0d7STrevor Thompson                              PULONG StartEntry,
965a7a2c0d7STrevor Thompson                              PULONG CurrentEntry,
966a7a2c0d7STrevor Thompson                              BOOLEAN DirSearch,
967a7a2c0d7STrevor Thompson                              ULONGLONG NewDataSize,
968a7a2c0d7STrevor Thompson                              ULONGLONG NewAllocatedSize)
969a7a2c0d7STrevor Thompson {
970a7a2c0d7STrevor Thompson     NTSTATUS Status;
971a7a2c0d7STrevor Thompson     ULONG RecordOffset;
972a7a2c0d7STrevor Thompson     PINDEX_ENTRY_ATTRIBUTE IndexEntry;
973a7a2c0d7STrevor Thompson     PNTFS_ATTR_CONTEXT IndexAllocationCtx;
974a7a2c0d7STrevor Thompson     ULONGLONG IndexAllocationSize;
975a7a2c0d7STrevor Thompson     PINDEX_BUFFER IndexBuffer;
976a7a2c0d7STrevor Thompson 
977a7a2c0d7STrevor Thompson     DPRINT("UpdateIndexEntrySize(%p, %p, %p, %u, %p, %p, %wZ, %u, %u, %u, %I64u, %I64u)\n", Vcb, MftRecord, IndexRecord, IndexBlockSize, FirstEntry, LastEntry, FileName, *StartEntry, *CurrentEntry, DirSearch, NewDataSize, NewAllocatedSize);
978a7a2c0d7STrevor Thompson 
979a7a2c0d7STrevor Thompson     // find the index entry responsible for the file we're trying to update
980a7a2c0d7STrevor Thompson     IndexEntry = FirstEntry;
981a7a2c0d7STrevor Thompson     while (IndexEntry < LastEntry &&
982a7a2c0d7STrevor Thompson            !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
983a7a2c0d7STrevor Thompson     {
984a7a2c0d7STrevor Thompson         if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) > 0x10 &&
985a7a2c0d7STrevor Thompson             *CurrentEntry >= *StartEntry &&
986a7a2c0d7STrevor Thompson             IndexEntry->FileName.NameType != NTFS_FILE_NAME_DOS &&
987a7a2c0d7STrevor Thompson             CompareFileName(FileName, IndexEntry, DirSearch))
988a7a2c0d7STrevor Thompson         {
989a7a2c0d7STrevor Thompson             *StartEntry = *CurrentEntry;
990a7a2c0d7STrevor Thompson             IndexEntry->FileName.DataSize = NewDataSize;
991a7a2c0d7STrevor Thompson             IndexEntry->FileName.AllocatedSize = NewAllocatedSize;
992a7a2c0d7STrevor Thompson             // indicate that the caller will still need to write the structure to the disk
993a7a2c0d7STrevor Thompson             return STATUS_PENDING;
994a7a2c0d7STrevor Thompson         }
995a7a2c0d7STrevor Thompson 
996a7a2c0d7STrevor Thompson         (*CurrentEntry) += 1;
997a7a2c0d7STrevor Thompson         ASSERT(IndexEntry->Length >= sizeof(INDEX_ENTRY_ATTRIBUTE));
998a7a2c0d7STrevor Thompson         IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
999a7a2c0d7STrevor Thompson     }
1000a7a2c0d7STrevor Thompson 
1001a7a2c0d7STrevor Thompson     /* If we're already browsing a subnode */
1002a7a2c0d7STrevor Thompson     if (IndexRecord == NULL)
1003a7a2c0d7STrevor Thompson     {
1004a7a2c0d7STrevor Thompson         return STATUS_OBJECT_PATH_NOT_FOUND;
1005a7a2c0d7STrevor Thompson     }
1006a7a2c0d7STrevor Thompson 
1007a7a2c0d7STrevor Thompson     /* If there's no subnode */
1008a7a2c0d7STrevor Thompson     if (!(IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE))
1009a7a2c0d7STrevor Thompson     {
1010a7a2c0d7STrevor Thompson         return STATUS_OBJECT_PATH_NOT_FOUND;
1011a7a2c0d7STrevor Thompson     }
1012a7a2c0d7STrevor Thompson 
1013a7a2c0d7STrevor Thompson     Status = FindAttribute(Vcb, MftRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationCtx, NULL);
1014a7a2c0d7STrevor Thompson     if (!NT_SUCCESS(Status))
1015a7a2c0d7STrevor Thompson     {
1016a7a2c0d7STrevor Thompson         DPRINT("Corrupted filesystem!\n");
1017a7a2c0d7STrevor Thompson         return Status;
1018a7a2c0d7STrevor Thompson     }
1019a7a2c0d7STrevor Thompson 
1020a7a2c0d7STrevor Thompson     IndexAllocationSize = AttributeDataLength(&IndexAllocationCtx->Record);
1021a7a2c0d7STrevor Thompson     Status = STATUS_OBJECT_PATH_NOT_FOUND;
1022a7a2c0d7STrevor Thompson     for (RecordOffset = 0; RecordOffset < IndexAllocationSize; RecordOffset += IndexBlockSize)
1023a7a2c0d7STrevor Thompson     {
1024a7a2c0d7STrevor Thompson         ReadAttribute(Vcb, IndexAllocationCtx, RecordOffset, IndexRecord, IndexBlockSize);
1025a7a2c0d7STrevor Thompson         Status = FixupUpdateSequenceArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs);
1026a7a2c0d7STrevor Thompson         if (!NT_SUCCESS(Status))
1027a7a2c0d7STrevor Thompson         {
1028a7a2c0d7STrevor Thompson             break;
1029a7a2c0d7STrevor Thompson         }
1030a7a2c0d7STrevor Thompson 
1031a7a2c0d7STrevor Thompson         IndexBuffer = (PINDEX_BUFFER)IndexRecord;
1032a7a2c0d7STrevor Thompson         ASSERT(IndexBuffer->Ntfs.Type == NRH_INDX_TYPE);
1033a7a2c0d7STrevor Thompson         ASSERT(IndexBuffer->Header.AllocatedSize + FIELD_OFFSET(INDEX_BUFFER, Header) == IndexBlockSize);
1034a7a2c0d7STrevor Thompson         FirstEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.FirstEntryOffset);
1035a7a2c0d7STrevor Thompson         LastEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.TotalSizeOfEntries);
1036a7a2c0d7STrevor Thompson         ASSERT(LastEntry <= (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexBuffer + IndexBlockSize));
1037a7a2c0d7STrevor Thompson 
1038a7a2c0d7STrevor Thompson         Status = UpdateIndexEntryFileNameSize(NULL, NULL, NULL, 0, FirstEntry, LastEntry, FileName, StartEntry, CurrentEntry, DirSearch, NewDataSize, NewAllocatedSize);
1039a7a2c0d7STrevor Thompson         if (Status == STATUS_PENDING)
1040a7a2c0d7STrevor Thompson         {
1041a7a2c0d7STrevor Thompson             // write the index record back to disk
1042a7a2c0d7STrevor Thompson             ULONG Written;
1043a7a2c0d7STrevor Thompson 
1044a7a2c0d7STrevor Thompson             // first we need to update the fixup values for the index block
1045a7a2c0d7STrevor Thompson             Status = AddFixupArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs);
1046a7a2c0d7STrevor Thompson             if (!NT_SUCCESS(Status))
1047a7a2c0d7STrevor Thompson             {
1048a7a2c0d7STrevor Thompson                 DPRINT1("Error: Failed to update fixup sequence array!\n");
1049a7a2c0d7STrevor Thompson                 break;
1050a7a2c0d7STrevor Thompson             }
1051a7a2c0d7STrevor Thompson 
1052a7a2c0d7STrevor Thompson             Status = WriteAttribute(Vcb, IndexAllocationCtx, RecordOffset, (const PUCHAR)IndexRecord, IndexBlockSize, &Written);
1053a7a2c0d7STrevor Thompson             if (!NT_SUCCESS(Status))
1054a7a2c0d7STrevor Thompson             {
1055a7a2c0d7STrevor Thompson                 DPRINT1("ERROR Performing write!\n");
1056a7a2c0d7STrevor Thompson                 break;
1057a7a2c0d7STrevor Thompson             }
1058a7a2c0d7STrevor Thompson 
1059a7a2c0d7STrevor Thompson             Status = STATUS_SUCCESS;
1060a7a2c0d7STrevor Thompson             break;
1061a7a2c0d7STrevor Thompson         }
1062a7a2c0d7STrevor Thompson         if (NT_SUCCESS(Status))
1063a7a2c0d7STrevor Thompson         {
1064a7a2c0d7STrevor Thompson             break;
1065a7a2c0d7STrevor Thompson         }
1066a7a2c0d7STrevor Thompson     }
1067a7a2c0d7STrevor Thompson 
1068a7a2c0d7STrevor Thompson     ReleaseAttributeContext(IndexAllocationCtx);
1069a7a2c0d7STrevor Thompson     return Status;
1070a7a2c0d7STrevor Thompson }
1071a7a2c0d7STrevor Thompson 
1072ba33b9faSTrevor Thompson /**
1073ba33b9faSTrevor Thompson * UpdateFileRecord
1074ba33b9faSTrevor Thompson * @implemented
1075ba33b9faSTrevor Thompson * Writes a file record to the master file table, at a given index.
1076ba33b9faSTrevor Thompson */
1077ba33b9faSTrevor Thompson NTSTATUS
1078ba33b9faSTrevor Thompson UpdateFileRecord(PDEVICE_EXTENSION Vcb,
1079ba33b9faSTrevor Thompson                  ULONGLONG index,
1080ba33b9faSTrevor Thompson                  PFILE_RECORD_HEADER file)
1081ba33b9faSTrevor Thompson {
1082ba33b9faSTrevor Thompson     ULONG BytesWritten;
1083ba33b9faSTrevor Thompson     NTSTATUS Status = STATUS_SUCCESS;
1084ba33b9faSTrevor Thompson 
1085ba33b9faSTrevor Thompson     DPRINT("UpdateFileRecord(%p, %I64x, %p)\n", Vcb, index, file);
1086ba33b9faSTrevor Thompson 
1087ba33b9faSTrevor Thompson     // Add the fixup array to prepare the data for writing to disk
10884f8133f4STrevor Thompson     AddFixupArray(Vcb, &file->Ntfs);
1089ba33b9faSTrevor Thompson 
1090ba33b9faSTrevor Thompson     // write the file record to the master file table
1091ba33b9faSTrevor Thompson     Status = WriteAttribute(Vcb, Vcb->MFTContext, index * Vcb->NtfsInfo.BytesPerFileRecord, (const PUCHAR)file, Vcb->NtfsInfo.BytesPerFileRecord, &BytesWritten);
1092ba33b9faSTrevor Thompson 
1093ba33b9faSTrevor Thompson     if (!NT_SUCCESS(Status))
1094ba33b9faSTrevor Thompson     {
1095*6ab30720STrevor Thompson         DPRINT1("UpdateFileRecord failed: %lu written, %lu expected\n", BytesWritten, Vcb->NtfsInfo.BytesPerFileRecord);
1096ba33b9faSTrevor Thompson     }
1097ba33b9faSTrevor Thompson 
1098268a139eSTrevor Thompson     // remove the fixup array (so the file record pointer can still be used)
109963e83c7fSTrevor Thompson     FixupUpdateSequenceArray(Vcb, &file->Ntfs);
1100268a139eSTrevor Thompson 
1101ba33b9faSTrevor Thompson     return Status;
1102ba33b9faSTrevor Thompson }
1103ba33b9faSTrevor Thompson 
1104c2c66affSColin Finck 
1105c2c66affSColin Finck NTSTATUS
1106c2c66affSColin Finck FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb,
1107c2c66affSColin Finck                          PNTFS_RECORD_HEADER Record)
1108c2c66affSColin Finck {
1109c2c66affSColin Finck     USHORT *USA;
1110c2c66affSColin Finck     USHORT USANumber;
1111c2c66affSColin Finck     USHORT USACount;
1112c2c66affSColin Finck     USHORT *Block;
1113c2c66affSColin Finck 
1114c2c66affSColin Finck     USA = (USHORT*)((PCHAR)Record + Record->UsaOffset);
1115c2c66affSColin Finck     USANumber = *(USA++);
1116c2c66affSColin Finck     USACount = Record->UsaCount - 1; /* Exclude the USA Number. */
1117c2c66affSColin Finck     Block = (USHORT*)((PCHAR)Record + Vcb->NtfsInfo.BytesPerSector - 2);
1118c2c66affSColin Finck 
1119ba33b9faSTrevor Thompson     DPRINT("FixupUpdateSequenceArray(%p, %p)\nUSANumber: %u\tUSACount: %u\n", Vcb, Record, USANumber, USACount);
1120ba33b9faSTrevor Thompson 
1121c2c66affSColin Finck     while (USACount)
1122c2c66affSColin Finck     {
1123c2c66affSColin Finck         if (*Block != USANumber)
1124c2c66affSColin Finck         {
1125c2c66affSColin Finck             DPRINT1("Mismatch with USA: %u read, %u expected\n" , *Block, USANumber);
1126c2c66affSColin Finck             return STATUS_UNSUCCESSFUL;
1127c2c66affSColin Finck         }
1128c2c66affSColin Finck         *Block = *(USA++);
1129c2c66affSColin Finck         Block = (USHORT*)((PCHAR)Block + Vcb->NtfsInfo.BytesPerSector);
1130c2c66affSColin Finck         USACount--;
1131c2c66affSColin Finck     }
1132c2c66affSColin Finck 
1133c2c66affSColin Finck     return STATUS_SUCCESS;
1134c2c66affSColin Finck }
1135c2c66affSColin Finck 
1136ba33b9faSTrevor Thompson NTSTATUS
1137ba33b9faSTrevor Thompson AddFixupArray(PDEVICE_EXTENSION Vcb,
11384f8133f4STrevor Thompson               PNTFS_RECORD_HEADER Record)
1139ba33b9faSTrevor Thompson {
1140ba33b9faSTrevor Thompson     USHORT *pShortToFixUp;
11414f8133f4STrevor Thompson     unsigned int ArrayEntryCount = Record->UsaCount - 1;
1142ba33b9faSTrevor Thompson     unsigned int Offset = Vcb->NtfsInfo.BytesPerSector - 2;
1143ba33b9faSTrevor Thompson     int i;
1144ba33b9faSTrevor Thompson 
11454f8133f4STrevor Thompson     PFIXUP_ARRAY fixupArray = (PFIXUP_ARRAY)((UCHAR*)Record + Record->UsaOffset);
1146ba33b9faSTrevor Thompson 
1147ba33b9faSTrevor Thompson     DPRINT("AddFixupArray(%p, %p)\n fixupArray->USN: %u, ArrayEntryCount: %u\n", Vcb, Record, fixupArray->USN, ArrayEntryCount);
1148ba33b9faSTrevor Thompson 
1149ba33b9faSTrevor Thompson     fixupArray->USN++;
1150ba33b9faSTrevor Thompson 
1151ba33b9faSTrevor Thompson     for (i = 0; i < ArrayEntryCount; i++)
1152ba33b9faSTrevor Thompson     {
1153ba33b9faSTrevor Thompson         DPRINT("USN: %u\tOffset: %u\n", fixupArray->USN, Offset);
1154ba33b9faSTrevor Thompson 
11554f8133f4STrevor Thompson         pShortToFixUp = (USHORT*)((PCHAR)Record + Offset);
1156ba33b9faSTrevor Thompson         fixupArray->Array[i] = *pShortToFixUp;
1157ba33b9faSTrevor Thompson         *pShortToFixUp = fixupArray->USN;
1158ba33b9faSTrevor Thompson         Offset += Vcb->NtfsInfo.BytesPerSector;
1159ba33b9faSTrevor Thompson     }
1160ba33b9faSTrevor Thompson 
1161ba33b9faSTrevor Thompson     return STATUS_SUCCESS;
1162ba33b9faSTrevor Thompson }
1163c2c66affSColin Finck 
1164c2c66affSColin Finck NTSTATUS
1165c2c66affSColin Finck ReadLCN(PDEVICE_EXTENSION Vcb,
1166c2c66affSColin Finck         ULONGLONG lcn,
1167c2c66affSColin Finck         ULONG count,
1168c2c66affSColin Finck         PVOID buffer)
1169c2c66affSColin Finck {
1170c2c66affSColin Finck     LARGE_INTEGER DiskSector;
1171c2c66affSColin Finck 
1172c2c66affSColin Finck     DiskSector.QuadPart = lcn;
1173c2c66affSColin Finck 
1174c2c66affSColin Finck     return NtfsReadSectors(Vcb->StorageDevice,
1175c2c66affSColin Finck                            DiskSector.u.LowPart * Vcb->NtfsInfo.SectorsPerCluster,
1176c2c66affSColin Finck                            count * Vcb->NtfsInfo.SectorsPerCluster,
1177c2c66affSColin Finck                            Vcb->NtfsInfo.BytesPerSector,
1178c2c66affSColin Finck                            buffer,
1179c2c66affSColin Finck                            FALSE);
1180c2c66affSColin Finck }
1181c2c66affSColin Finck 
1182c2c66affSColin Finck 
1183c2c66affSColin Finck BOOLEAN
1184c2c66affSColin Finck CompareFileName(PUNICODE_STRING FileName,
1185c2c66affSColin Finck                 PINDEX_ENTRY_ATTRIBUTE IndexEntry,
1186c2c66affSColin Finck                 BOOLEAN DirSearch)
1187c2c66affSColin Finck {
1188c2c66affSColin Finck     BOOLEAN Ret, Alloc = FALSE;
1189c2c66affSColin Finck     UNICODE_STRING EntryName;
1190c2c66affSColin Finck 
1191c2c66affSColin Finck     EntryName.Buffer = IndexEntry->FileName.Name;
1192c2c66affSColin Finck     EntryName.Length =
1193c2c66affSColin Finck     EntryName.MaximumLength = IndexEntry->FileName.NameLength * sizeof(WCHAR);
1194c2c66affSColin Finck 
1195c2c66affSColin Finck     if (DirSearch)
1196c2c66affSColin Finck     {
1197c2c66affSColin Finck         UNICODE_STRING IntFileName;
1198c2c66affSColin Finck         if (IndexEntry->FileName.NameType != NTFS_FILE_NAME_POSIX)
1199c2c66affSColin Finck         {
1200c2c66affSColin Finck             NT_VERIFY(NT_SUCCESS(RtlUpcaseUnicodeString(&IntFileName, FileName, TRUE)));
1201c2c66affSColin Finck             Alloc = TRUE;
1202c2c66affSColin Finck         }
1203c2c66affSColin Finck         else
1204c2c66affSColin Finck         {
1205c2c66affSColin Finck             IntFileName = *FileName;
1206c2c66affSColin Finck         }
1207c2c66affSColin Finck 
1208c2c66affSColin Finck         Ret = FsRtlIsNameInExpression(&IntFileName, &EntryName, (IndexEntry->FileName.NameType != NTFS_FILE_NAME_POSIX), NULL);
1209c2c66affSColin Finck 
1210c2c66affSColin Finck         if (Alloc)
1211c2c66affSColin Finck         {
1212c2c66affSColin Finck             RtlFreeUnicodeString(&IntFileName);
1213c2c66affSColin Finck         }
1214c2c66affSColin Finck 
1215c2c66affSColin Finck         return Ret;
1216c2c66affSColin Finck     }
1217c2c66affSColin Finck     else
1218c2c66affSColin Finck     {
1219c2c66affSColin Finck         return (RtlCompareUnicodeString(FileName, &EntryName, (IndexEntry->FileName.NameType != NTFS_FILE_NAME_POSIX)) == 0);
1220c2c66affSColin Finck     }
1221c2c66affSColin Finck }
1222c2c66affSColin Finck 
1223c2c66affSColin Finck #if 0
1224c2c66affSColin Finck static
1225c2c66affSColin Finck VOID
1226c2c66affSColin Finck DumpIndexEntry(PINDEX_ENTRY_ATTRIBUTE IndexEntry)
1227c2c66affSColin Finck {
1228c2c66affSColin Finck     DPRINT1("Entry: %p\n", IndexEntry);
1229c2c66affSColin Finck     DPRINT1("\tData.Directory.IndexedFile: %I64x\n", IndexEntry->Data.Directory.IndexedFile);
1230c2c66affSColin Finck     DPRINT1("\tLength: %u\n", IndexEntry->Length);
1231c2c66affSColin Finck     DPRINT1("\tKeyLength: %u\n", IndexEntry->KeyLength);
1232c2c66affSColin Finck     DPRINT1("\tFlags: %x\n", IndexEntry->Flags);
1233c2c66affSColin Finck     DPRINT1("\tReserved: %x\n", IndexEntry->Reserved);
1234c2c66affSColin Finck     DPRINT1("\t\tDirectoryFileReferenceNumber: %I64x\n", IndexEntry->FileName.DirectoryFileReferenceNumber);
1235c2c66affSColin Finck     DPRINT1("\t\tCreationTime: %I64u\n", IndexEntry->FileName.CreationTime);
1236c2c66affSColin Finck     DPRINT1("\t\tChangeTime: %I64u\n", IndexEntry->FileName.ChangeTime);
1237c2c66affSColin Finck     DPRINT1("\t\tLastWriteTime: %I64u\n", IndexEntry->FileName.LastWriteTime);
1238c2c66affSColin Finck     DPRINT1("\t\tLastAccessTime: %I64u\n", IndexEntry->FileName.LastAccessTime);
1239c2c66affSColin Finck     DPRINT1("\t\tAllocatedSize: %I64u\n", IndexEntry->FileName.AllocatedSize);
1240c2c66affSColin Finck     DPRINT1("\t\tDataSize: %I64u\n", IndexEntry->FileName.DataSize);
1241c2c66affSColin Finck     DPRINT1("\t\tFileAttributes: %x\n", IndexEntry->FileName.FileAttributes);
1242c2c66affSColin Finck     DPRINT1("\t\tNameLength: %u\n", IndexEntry->FileName.NameLength);
1243c2c66affSColin Finck     DPRINT1("\t\tNameType: %x\n", IndexEntry->FileName.NameType);
1244c2c66affSColin Finck     DPRINT1("\t\tName: %.*S\n", IndexEntry->FileName.NameLength, IndexEntry->FileName.Name);
1245c2c66affSColin Finck }
1246c2c66affSColin Finck #endif
1247c2c66affSColin Finck 
1248c2c66affSColin Finck NTSTATUS
1249c2c66affSColin Finck BrowseIndexEntries(PDEVICE_EXTENSION Vcb,
1250c2c66affSColin Finck                    PFILE_RECORD_HEADER MftRecord,
1251c2c66affSColin Finck                    PCHAR IndexRecord,
1252c2c66affSColin Finck                    ULONG IndexBlockSize,
1253c2c66affSColin Finck                    PINDEX_ENTRY_ATTRIBUTE FirstEntry,
1254c2c66affSColin Finck                    PINDEX_ENTRY_ATTRIBUTE LastEntry,
1255c2c66affSColin Finck                    PUNICODE_STRING FileName,
1256c2c66affSColin Finck                    PULONG StartEntry,
1257c2c66affSColin Finck                    PULONG CurrentEntry,
1258c2c66affSColin Finck                    BOOLEAN DirSearch,
1259c2c66affSColin Finck                    ULONGLONG *OutMFTIndex)
1260c2c66affSColin Finck {
1261c2c66affSColin Finck     NTSTATUS Status;
1262c2c66affSColin Finck     ULONG RecordOffset;
1263c2c66affSColin Finck     PINDEX_ENTRY_ATTRIBUTE IndexEntry;
1264c2c66affSColin Finck     PNTFS_ATTR_CONTEXT IndexAllocationCtx;
1265c2c66affSColin Finck     ULONGLONG IndexAllocationSize;
1266c2c66affSColin Finck     PINDEX_BUFFER IndexBuffer;
1267c2c66affSColin Finck 
1268c2c66affSColin Finck     DPRINT("BrowseIndexEntries(%p, %p, %p, %u, %p, %p, %wZ, %u, %u, %u, %p)\n", Vcb, MftRecord, IndexRecord, IndexBlockSize, FirstEntry, LastEntry, FileName, *StartEntry, *CurrentEntry, DirSearch, OutMFTIndex);
1269c2c66affSColin Finck 
1270c2c66affSColin Finck     IndexEntry = FirstEntry;
1271c2c66affSColin Finck     while (IndexEntry < LastEntry &&
1272c2c66affSColin Finck            !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
1273c2c66affSColin Finck     {
1274c2c66affSColin Finck         if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) > 0x10 &&
1275c2c66affSColin Finck             *CurrentEntry >= *StartEntry &&
1276c2c66affSColin Finck             IndexEntry->FileName.NameType != NTFS_FILE_NAME_DOS &&
1277c2c66affSColin Finck             CompareFileName(FileName, IndexEntry, DirSearch))
1278c2c66affSColin Finck         {
1279c2c66affSColin Finck             *StartEntry = *CurrentEntry;
1280c2c66affSColin Finck             *OutMFTIndex = (IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK);
1281c2c66affSColin Finck             return STATUS_SUCCESS;
1282c2c66affSColin Finck         }
1283c2c66affSColin Finck 
1284c2c66affSColin Finck         (*CurrentEntry) += 1;
1285c2c66affSColin Finck         ASSERT(IndexEntry->Length >= sizeof(INDEX_ENTRY_ATTRIBUTE));
1286c2c66affSColin Finck         IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
1287c2c66affSColin Finck     }
1288c2c66affSColin Finck 
1289c2c66affSColin Finck     /* If we're already browsing a subnode */
1290c2c66affSColin Finck     if (IndexRecord == NULL)
1291c2c66affSColin Finck     {
1292c2c66affSColin Finck         return STATUS_OBJECT_PATH_NOT_FOUND;
1293c2c66affSColin Finck     }
1294c2c66affSColin Finck 
1295c2c66affSColin Finck     /* If there's no subnode */
1296c2c66affSColin Finck     if (!(IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE))
1297c2c66affSColin Finck     {
1298c2c66affSColin Finck         return STATUS_OBJECT_PATH_NOT_FOUND;
1299c2c66affSColin Finck     }
1300c2c66affSColin Finck 
1301ba33b9faSTrevor Thompson     Status = FindAttribute(Vcb, MftRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationCtx, NULL);
1302c2c66affSColin Finck     if (!NT_SUCCESS(Status))
1303c2c66affSColin Finck     {
1304c2c66affSColin Finck         DPRINT("Corrupted filesystem!\n");
1305c2c66affSColin Finck         return Status;
1306c2c66affSColin Finck     }
1307c2c66affSColin Finck 
1308c2c66affSColin Finck     IndexAllocationSize = AttributeDataLength(&IndexAllocationCtx->Record);
1309c2c66affSColin Finck     Status = STATUS_OBJECT_PATH_NOT_FOUND;
1310c2c66affSColin Finck     for (RecordOffset = 0; RecordOffset < IndexAllocationSize; RecordOffset += IndexBlockSize)
1311c2c66affSColin Finck     {
1312c2c66affSColin Finck         ReadAttribute(Vcb, IndexAllocationCtx, RecordOffset, IndexRecord, IndexBlockSize);
1313c2c66affSColin Finck         Status = FixupUpdateSequenceArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs);
1314c2c66affSColin Finck         if (!NT_SUCCESS(Status))
1315c2c66affSColin Finck         {
1316c2c66affSColin Finck             break;
1317c2c66affSColin Finck         }
1318c2c66affSColin Finck 
1319c2c66affSColin Finck         IndexBuffer = (PINDEX_BUFFER)IndexRecord;
1320c2c66affSColin Finck         ASSERT(IndexBuffer->Ntfs.Type == NRH_INDX_TYPE);
1321c2c66affSColin Finck         ASSERT(IndexBuffer->Header.AllocatedSize + FIELD_OFFSET(INDEX_BUFFER, Header) == IndexBlockSize);
1322c2c66affSColin Finck         FirstEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.FirstEntryOffset);
1323c2c66affSColin Finck         LastEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.TotalSizeOfEntries);
1324c2c66affSColin Finck         ASSERT(LastEntry <= (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexBuffer + IndexBlockSize));
1325c2c66affSColin Finck 
1326c2c66affSColin Finck         Status = BrowseIndexEntries(NULL, NULL, NULL, 0, FirstEntry, LastEntry, FileName, StartEntry, CurrentEntry, DirSearch, OutMFTIndex);
1327c2c66affSColin Finck         if (NT_SUCCESS(Status))
1328c2c66affSColin Finck         {
1329c2c66affSColin Finck             break;
1330c2c66affSColin Finck         }
1331c2c66affSColin Finck     }
1332c2c66affSColin Finck 
1333c2c66affSColin Finck     ReleaseAttributeContext(IndexAllocationCtx);
1334c2c66affSColin Finck     return Status;
1335c2c66affSColin Finck }
1336c2c66affSColin Finck 
1337c2c66affSColin Finck NTSTATUS
1338c2c66affSColin Finck NtfsFindMftRecord(PDEVICE_EXTENSION Vcb,
1339c2c66affSColin Finck                   ULONGLONG MFTIndex,
1340c2c66affSColin Finck                   PUNICODE_STRING FileName,
1341c2c66affSColin Finck                   PULONG FirstEntry,
1342c2c66affSColin Finck                   BOOLEAN DirSearch,
1343c2c66affSColin Finck                   ULONGLONG *OutMFTIndex)
1344c2c66affSColin Finck {
1345c2c66affSColin Finck     PFILE_RECORD_HEADER MftRecord;
1346c2c66affSColin Finck     PNTFS_ATTR_CONTEXT IndexRootCtx;
1347c2c66affSColin Finck     PINDEX_ROOT_ATTRIBUTE IndexRoot;
1348c2c66affSColin Finck     PCHAR IndexRecord;
1349c2c66affSColin Finck     PINDEX_ENTRY_ATTRIBUTE IndexEntry, IndexEntryEnd;
1350c2c66affSColin Finck     NTSTATUS Status;
1351c2c66affSColin Finck     ULONG CurrentEntry = 0;
1352c2c66affSColin Finck 
1353c2c66affSColin Finck     DPRINT("NtfsFindMftRecord(%p, %I64d, %wZ, %u, %u, %p)\n", Vcb, MFTIndex, FileName, *FirstEntry, DirSearch, OutMFTIndex);
1354c2c66affSColin Finck 
1355c2c66affSColin Finck     MftRecord = ExAllocatePoolWithTag(NonPagedPool,
1356c2c66affSColin Finck                                       Vcb->NtfsInfo.BytesPerFileRecord,
1357c2c66affSColin Finck                                       TAG_NTFS);
1358c2c66affSColin Finck     if (MftRecord == NULL)
1359c2c66affSColin Finck     {
1360c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
1361c2c66affSColin Finck     }
1362c2c66affSColin Finck 
1363c2c66affSColin Finck     Status = ReadFileRecord(Vcb, MFTIndex, MftRecord);
1364c2c66affSColin Finck     if (!NT_SUCCESS(Status))
1365c2c66affSColin Finck     {
1366c2c66affSColin Finck         ExFreePoolWithTag(MftRecord, TAG_NTFS);
1367c2c66affSColin Finck         return Status;
1368c2c66affSColin Finck     }
1369c2c66affSColin Finck 
1370c2c66affSColin Finck     ASSERT(MftRecord->Ntfs.Type == NRH_FILE_TYPE);
1371ba33b9faSTrevor Thompson     Status = FindAttribute(Vcb, MftRecord, AttributeIndexRoot, L"$I30", 4, &IndexRootCtx, NULL);
1372c2c66affSColin Finck     if (!NT_SUCCESS(Status))
1373c2c66affSColin Finck     {
1374c2c66affSColin Finck         ExFreePoolWithTag(MftRecord, TAG_NTFS);
1375c2c66affSColin Finck         return Status;
1376c2c66affSColin Finck     }
1377c2c66affSColin Finck 
1378c2c66affSColin Finck     IndexRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerIndexRecord, TAG_NTFS);
1379c2c66affSColin Finck     if (IndexRecord == NULL)
1380c2c66affSColin Finck     {
1381c2c66affSColin Finck         ReleaseAttributeContext(IndexRootCtx);
1382c2c66affSColin Finck         ExFreePoolWithTag(MftRecord, TAG_NTFS);
1383c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
1384c2c66affSColin Finck     }
1385c2c66affSColin Finck 
1386c2c66affSColin Finck     ReadAttribute(Vcb, IndexRootCtx, 0, IndexRecord, Vcb->NtfsInfo.BytesPerIndexRecord);
1387c2c66affSColin Finck     IndexRoot = (PINDEX_ROOT_ATTRIBUTE)IndexRecord;
1388c2c66affSColin Finck     IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)&IndexRoot->Header + IndexRoot->Header.FirstEntryOffset);
1389c2c66affSColin Finck     /* Index root is always resident. */
1390c2c66affSColin Finck     IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)(IndexRecord + IndexRoot->Header.TotalSizeOfEntries);
1391c2c66affSColin Finck     ReleaseAttributeContext(IndexRootCtx);
1392c2c66affSColin Finck 
1393c2c66affSColin Finck     DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb->NtfsInfo.BytesPerIndexRecord, IndexRoot->SizeOfEntry);
1394c2c66affSColin Finck 
1395c2c66affSColin Finck     Status = BrowseIndexEntries(Vcb, MftRecord, IndexRecord, IndexRoot->SizeOfEntry, IndexEntry, IndexEntryEnd, FileName, FirstEntry, &CurrentEntry, DirSearch, OutMFTIndex);
1396c2c66affSColin Finck 
1397c2c66affSColin Finck     ExFreePoolWithTag(IndexRecord, TAG_NTFS);
1398c2c66affSColin Finck     ExFreePoolWithTag(MftRecord, TAG_NTFS);
1399c2c66affSColin Finck 
1400c2c66affSColin Finck     return Status;
1401c2c66affSColin Finck }
1402c2c66affSColin Finck 
1403c2c66affSColin Finck NTSTATUS
1404c2c66affSColin Finck NtfsLookupFileAt(PDEVICE_EXTENSION Vcb,
1405c2c66affSColin Finck                  PUNICODE_STRING PathName,
1406c2c66affSColin Finck                  PFILE_RECORD_HEADER *FileRecord,
1407c2c66affSColin Finck                  PULONGLONG MFTIndex,
1408c2c66affSColin Finck                  ULONGLONG CurrentMFTIndex)
1409c2c66affSColin Finck {
1410c2c66affSColin Finck     UNICODE_STRING Current, Remaining;
1411c2c66affSColin Finck     NTSTATUS Status;
1412c2c66affSColin Finck     ULONG FirstEntry = 0;
1413c2c66affSColin Finck 
1414c2c66affSColin Finck     DPRINT("NtfsLookupFileAt(%p, %wZ, %p, %I64x)\n", Vcb, PathName, FileRecord, CurrentMFTIndex);
1415c2c66affSColin Finck 
1416c2c66affSColin Finck     FsRtlDissectName(*PathName, &Current, &Remaining);
1417c2c66affSColin Finck 
1418c2c66affSColin Finck     while (Current.Length != 0)
1419c2c66affSColin Finck     {
1420c2c66affSColin Finck         DPRINT("Current: %wZ\n", &Current);
1421c2c66affSColin Finck 
1422c2c66affSColin Finck         Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, &Current, &FirstEntry, FALSE, &CurrentMFTIndex);
1423c2c66affSColin Finck         if (!NT_SUCCESS(Status))
1424c2c66affSColin Finck         {
1425c2c66affSColin Finck             return Status;
1426c2c66affSColin Finck         }
1427c2c66affSColin Finck 
1428c2c66affSColin Finck         if (Remaining.Length == 0)
1429c2c66affSColin Finck             break;
1430c2c66affSColin Finck 
1431c2c66affSColin Finck         FsRtlDissectName(Current, &Current, &Remaining);
1432c2c66affSColin Finck     }
1433c2c66affSColin Finck 
1434c2c66affSColin Finck     *FileRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
1435c2c66affSColin Finck     if (*FileRecord == NULL)
1436c2c66affSColin Finck     {
1437c2c66affSColin Finck         DPRINT("NtfsLookupFileAt: Can't allocate MFT record\n");
1438c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
1439c2c66affSColin Finck     }
1440c2c66affSColin Finck 
1441c2c66affSColin Finck     Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord);
1442c2c66affSColin Finck     if (!NT_SUCCESS(Status))
1443c2c66affSColin Finck     {
1444c2c66affSColin Finck         DPRINT("NtfsLookupFileAt: Can't read MFT record\n");
1445c2c66affSColin Finck         ExFreePoolWithTag(*FileRecord, TAG_NTFS);
1446c2c66affSColin Finck         return Status;
1447c2c66affSColin Finck     }
1448c2c66affSColin Finck 
1449c2c66affSColin Finck     *MFTIndex = CurrentMFTIndex;
1450c2c66affSColin Finck 
1451c2c66affSColin Finck     return STATUS_SUCCESS;
1452c2c66affSColin Finck }
1453c2c66affSColin Finck 
1454c2c66affSColin Finck NTSTATUS
1455c2c66affSColin Finck NtfsLookupFile(PDEVICE_EXTENSION Vcb,
1456c2c66affSColin Finck                PUNICODE_STRING PathName,
1457c2c66affSColin Finck                PFILE_RECORD_HEADER *FileRecord,
1458c2c66affSColin Finck                PULONGLONG MFTIndex)
1459c2c66affSColin Finck {
1460c2c66affSColin Finck     return NtfsLookupFileAt(Vcb, PathName, FileRecord, MFTIndex, NTFS_FILE_ROOT);
1461c2c66affSColin Finck }
1462c2c66affSColin Finck 
1463c2c66affSColin Finck NTSTATUS
1464c2c66affSColin Finck NtfsFindFileAt(PDEVICE_EXTENSION Vcb,
1465c2c66affSColin Finck                PUNICODE_STRING SearchPattern,
1466c2c66affSColin Finck                PULONG FirstEntry,
1467c2c66affSColin Finck                PFILE_RECORD_HEADER *FileRecord,
1468c2c66affSColin Finck                PULONGLONG MFTIndex,
1469c2c66affSColin Finck                ULONGLONG CurrentMFTIndex)
1470c2c66affSColin Finck {
1471c2c66affSColin Finck     NTSTATUS Status;
1472c2c66affSColin Finck 
1473c2c66affSColin Finck     DPRINT("NtfsFindFileAt(%p, %wZ, %u, %p, %p, %I64x)\n", Vcb, SearchPattern, *FirstEntry, FileRecord, MFTIndex, CurrentMFTIndex);
1474c2c66affSColin Finck 
1475c2c66affSColin Finck     Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, SearchPattern, FirstEntry, TRUE, &CurrentMFTIndex);
1476c2c66affSColin Finck     if (!NT_SUCCESS(Status))
1477c2c66affSColin Finck     {
1478c2c66affSColin Finck         DPRINT("NtfsFindFileAt: NtfsFindMftRecord() failed with status 0x%08lx\n", Status);
1479c2c66affSColin Finck         return Status;
1480c2c66affSColin Finck     }
1481c2c66affSColin Finck 
1482c2c66affSColin Finck     *FileRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
1483c2c66affSColin Finck     if (*FileRecord == NULL)
1484c2c66affSColin Finck     {
1485c2c66affSColin Finck         DPRINT("NtfsFindFileAt: Can't allocate MFT record\n");
1486c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
1487c2c66affSColin Finck     }
1488c2c66affSColin Finck 
1489c2c66affSColin Finck     Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord);
1490c2c66affSColin Finck     if (!NT_SUCCESS(Status))
1491c2c66affSColin Finck     {
1492c2c66affSColin Finck         DPRINT("NtfsFindFileAt: Can't read MFT record\n");
1493c2c66affSColin Finck         ExFreePoolWithTag(*FileRecord, TAG_NTFS);
1494c2c66affSColin Finck         return Status;
1495c2c66affSColin Finck     }
1496c2c66affSColin Finck 
1497c2c66affSColin Finck     *MFTIndex = CurrentMFTIndex;
1498c2c66affSColin Finck 
1499c2c66affSColin Finck     return STATUS_SUCCESS;
1500c2c66affSColin Finck }
1501c2c66affSColin Finck 
1502c2c66affSColin Finck /* EOF */
1503