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