[NTFS]
[reactos.git] / reactos / drivers / filesystems / ntfs / mft.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 2002, 2014 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
18 *
19 * COPYRIGHT: See COPYING in the top level directory
20 * PROJECT: ReactOS kernel
21 * FILE: drivers/filesystem/ntfs/mft.c
22 * PURPOSE: NTFS filesystem driver
23 * PROGRAMMERS: Eric Kohl
24 * Valentin Verkhovsky
25 * Pierre Schweitzer (pierre@reactos.org)
26 * Hervé Poussineau (hpoussin@reactos.org)
27 */
28
29 /* INCLUDES *****************************************************************/
30
31 #include "ntfs.h"
32
33 #define NDEBUG
34 #include <debug.h>
35
36 /* FUNCTIONS ****************************************************************/
37
38 PNTFS_ATTR_CONTEXT
39 PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord)
40 {
41 PNTFS_ATTR_CONTEXT Context;
42
43 Context = ExAllocatePoolWithTag(NonPagedPool,
44 FIELD_OFFSET(NTFS_ATTR_CONTEXT, Record) + AttrRecord->Length,
45 TAG_NTFS);
46 RtlCopyMemory(&Context->Record, AttrRecord, AttrRecord->Length);
47 if (AttrRecord->IsNonResident)
48 {
49 LONGLONG DataRunOffset;
50 ULONGLONG DataRunLength;
51
52 Context->CacheRun = (PUCHAR)&Context->Record + Context->Record.NonResident.MappingPairsOffset;
53 Context->CacheRunOffset = 0;
54 Context->CacheRun = DecodeRun(Context->CacheRun, &DataRunOffset, &DataRunLength);
55 Context->CacheRunLength = DataRunLength;
56 if (DataRunOffset != -1)
57 {
58 /* Normal run. */
59 Context->CacheRunStartLCN =
60 Context->CacheRunLastLCN = DataRunOffset;
61 }
62 else
63 {
64 /* Sparse run. */
65 Context->CacheRunStartLCN = -1;
66 Context->CacheRunLastLCN = 0;
67 }
68 Context->CacheRunCurrentOffset = 0;
69 }
70
71 return Context;
72 }
73
74
75 VOID
76 ReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context)
77 {
78 ExFreePoolWithTag(Context, TAG_NTFS);
79 }
80
81
82 NTSTATUS
83 FindAttribute(PDEVICE_EXTENSION Vcb,
84 PFILE_RECORD_HEADER MftRecord,
85 ULONG Type,
86 PCWSTR Name,
87 ULONG NameLength,
88 PNTFS_ATTR_CONTEXT * AttrCtx)
89 {
90 NTSTATUS Status;
91 FIND_ATTR_CONTXT Context;
92 PNTFS_ATTR_RECORD Attribute;
93
94 DPRINT("FindAttribute(%p, %p, 0x%x, %S, %u, %p)\n", Vcb, MftRecord, Type, Name, NameLength, AttrCtx);
95
96 Status = FindFirstAttribute(&Context, Vcb, MftRecord, FALSE, &Attribute);
97 while (NT_SUCCESS(Status))
98 {
99 if (Attribute->Type == Type && Attribute->NameLength == NameLength)
100 {
101 PWCHAR AttrName;
102
103 AttrName = (PWCHAR)((PCHAR)Attribute + Attribute->NameOffset);
104 DPRINT("%.*S, %.*S\n", Attribute->NameLength, AttrName, NameLength, Name);
105 if (RtlCompareMemory(AttrName, Name, NameLength << 1) == (NameLength << 1))
106 {
107 /* Found it, fill up the context and return. */
108 DPRINT("Found context\n");
109 *AttrCtx = PrepareAttributeContext(Attribute);
110 FindCloseAttribute(&Context);
111 return STATUS_SUCCESS;
112 }
113 }
114
115 Status = FindNextAttribute(&Context, &Attribute);
116 }
117
118 FindCloseAttribute(&Context);
119 return STATUS_OBJECT_NAME_NOT_FOUND;
120 }
121
122
123 ULONG
124 AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord)
125 {
126 if (AttrRecord->IsNonResident)
127 return AttrRecord->NonResident.AllocatedSize;
128 else
129 return AttrRecord->Resident.ValueLength;
130 }
131
132
133 ULONGLONG
134 AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord)
135 {
136 if (AttrRecord->IsNonResident)
137 return AttrRecord->NonResident.DataSize;
138 else
139 return AttrRecord->Resident.ValueLength;
140 }
141
142
143 ULONG
144 ReadAttribute(PDEVICE_EXTENSION Vcb,
145 PNTFS_ATTR_CONTEXT Context,
146 ULONGLONG Offset,
147 PCHAR Buffer,
148 ULONG Length)
149 {
150 ULONGLONG LastLCN;
151 PUCHAR DataRun;
152 LONGLONG DataRunOffset;
153 ULONGLONG DataRunLength;
154 LONGLONG DataRunStartLCN;
155 ULONGLONG CurrentOffset;
156 ULONG ReadLength;
157 ULONG AlreadyRead;
158 NTSTATUS Status;
159
160 if (!Context->Record.IsNonResident)
161 {
162 if (Offset > Context->Record.Resident.ValueLength)
163 return 0;
164 if (Offset + Length > Context->Record.Resident.ValueLength)
165 Length = (ULONG)(Context->Record.Resident.ValueLength - Offset);
166 RtlCopyMemory(Buffer, (PCHAR)&Context->Record + Context->Record.Resident.ValueOffset + Offset, Length);
167 return Length;
168 }
169
170 /*
171 * Non-resident attribute
172 */
173
174 /*
175 * I. Find the corresponding start data run.
176 */
177
178 AlreadyRead = 0;
179
180 // FIXME: Cache seems to be non-working. Disable it for now
181 //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
182 if (0)
183 {
184 DataRun = Context->CacheRun;
185 LastLCN = Context->CacheRunLastLCN;
186 DataRunStartLCN = Context->CacheRunStartLCN;
187 DataRunLength = Context->CacheRunLength;
188 CurrentOffset = Context->CacheRunCurrentOffset;
189 }
190 else
191 {
192 LastLCN = 0;
193 DataRun = (PUCHAR)&Context->Record + Context->Record.NonResident.MappingPairsOffset;
194 CurrentOffset = 0;
195
196 while (1)
197 {
198 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
199 if (DataRunOffset != -1)
200 {
201 /* Normal data run. */
202 DataRunStartLCN = LastLCN + DataRunOffset;
203 LastLCN = DataRunStartLCN;
204 }
205 else
206 {
207 /* Sparse data run. */
208 DataRunStartLCN = -1;
209 }
210
211 if (Offset >= CurrentOffset &&
212 Offset < CurrentOffset + (DataRunLength * Vcb->NtfsInfo.BytesPerCluster))
213 {
214 break;
215 }
216
217 if (*DataRun == 0)
218 {
219 return AlreadyRead;
220 }
221
222 CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
223 }
224 }
225
226 /*
227 * II. Go through the run list and read the data
228 */
229
230 ReadLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset), Length);
231 if (DataRunStartLCN == -1)
232 RtlZeroMemory(Buffer, ReadLength);
233 Status = NtfsReadDisk(Vcb->StorageDevice,
234 DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster + Offset - CurrentOffset,
235 ReadLength,
236 Vcb->NtfsInfo.BytesPerSector,
237 (PVOID)Buffer,
238 FALSE);
239 if (NT_SUCCESS(Status))
240 {
241 Length -= ReadLength;
242 Buffer += ReadLength;
243 AlreadyRead += ReadLength;
244
245 if (ReadLength == DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset))
246 {
247 CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
248 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
249 if (DataRunLength != (ULONGLONG)-1)
250 {
251 DataRunStartLCN = LastLCN + DataRunOffset;
252 LastLCN = DataRunStartLCN;
253 }
254 else
255 DataRunStartLCN = -1;
256
257 if (*DataRun == 0)
258 return AlreadyRead;
259 }
260
261 while (Length > 0)
262 {
263 ReadLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster, Length);
264 if (DataRunStartLCN == -1)
265 RtlZeroMemory(Buffer, ReadLength);
266 else
267 {
268 Status = NtfsReadDisk(Vcb->StorageDevice,
269 DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster,
270 ReadLength,
271 Vcb->NtfsInfo.BytesPerSector,
272 (PVOID)Buffer,
273 FALSE);
274 if (!NT_SUCCESS(Status))
275 break;
276 }
277
278 Length -= ReadLength;
279 Buffer += ReadLength;
280 AlreadyRead += ReadLength;
281
282 /* We finished this request, but there still data in this data run. */
283 if (Length == 0 && ReadLength != DataRunLength * Vcb->NtfsInfo.BytesPerCluster)
284 break;
285
286 /*
287 * Go to next run in the list.
288 */
289
290 if (*DataRun == 0)
291 break;
292 CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
293 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
294 if (DataRunOffset != -1)
295 {
296 /* Normal data run. */
297 DataRunStartLCN = LastLCN + DataRunOffset;
298 LastLCN = DataRunStartLCN;
299 }
300 else
301 {
302 /* Sparse data run. */
303 DataRunStartLCN = -1;
304 }
305 } /* while */
306
307 } /* if Disk */
308
309 Context->CacheRun = DataRun;
310 Context->CacheRunOffset = Offset + AlreadyRead;
311 Context->CacheRunStartLCN = DataRunStartLCN;
312 Context->CacheRunLength = DataRunLength;
313 Context->CacheRunLastLCN = LastLCN;
314 Context->CacheRunCurrentOffset = CurrentOffset;
315
316 return AlreadyRead;
317 }
318
319
320 NTSTATUS
321 ReadFileRecord(PDEVICE_EXTENSION Vcb,
322 ULONGLONG index,
323 PFILE_RECORD_HEADER file)
324 {
325 ULONGLONG BytesRead;
326
327 DPRINT("ReadFileRecord(%p, %I64x, %p)\n", Vcb, index, file);
328
329 BytesRead = ReadAttribute(Vcb, Vcb->MFTContext, index * Vcb->NtfsInfo.BytesPerFileRecord, (PCHAR)file, Vcb->NtfsInfo.BytesPerFileRecord);
330 if (BytesRead != Vcb->NtfsInfo.BytesPerFileRecord)
331 {
332 DPRINT1("ReadFileRecord failed: %I64u read, %u expected\n", BytesRead, Vcb->NtfsInfo.BytesPerFileRecord);
333 return STATUS_PARTIAL_COPY;
334 }
335
336 /* Apply update sequence array fixups. */
337 return FixupUpdateSequenceArray(Vcb, &file->Ntfs);
338 }
339
340
341 NTSTATUS
342 FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb,
343 PNTFS_RECORD_HEADER Record)
344 {
345 USHORT *USA;
346 USHORT USANumber;
347 USHORT USACount;
348 USHORT *Block;
349
350 USA = (USHORT*)((PCHAR)Record + Record->UsaOffset);
351 USANumber = *(USA++);
352 USACount = Record->UsaCount - 1; /* Exclude the USA Number. */
353 Block = (USHORT*)((PCHAR)Record + Vcb->NtfsInfo.BytesPerSector - 2);
354
355 while (USACount)
356 {
357 if (*Block != USANumber)
358 {
359 DPRINT1("Mismatch with USA: %u read, %u expected\n" , *Block, USANumber);
360 return STATUS_UNSUCCESSFUL;
361 }
362 *Block = *(USA++);
363 Block = (USHORT*)((PCHAR)Block + Vcb->NtfsInfo.BytesPerSector);
364 USACount--;
365 }
366
367 return STATUS_SUCCESS;
368 }
369
370
371 NTSTATUS
372 ReadLCN(PDEVICE_EXTENSION Vcb,
373 ULONGLONG lcn,
374 ULONG count,
375 PVOID buffer)
376 {
377 LARGE_INTEGER DiskSector;
378
379 DiskSector.QuadPart = lcn;
380
381 return NtfsReadSectors(Vcb->StorageDevice,
382 DiskSector.u.LowPart * Vcb->NtfsInfo.SectorsPerCluster,
383 count * Vcb->NtfsInfo.SectorsPerCluster,
384 Vcb->NtfsInfo.BytesPerSector,
385 buffer,
386 FALSE);
387 }
388
389
390 BOOLEAN
391 CompareFileName(PUNICODE_STRING FileName,
392 PINDEX_ENTRY_ATTRIBUTE IndexEntry,
393 BOOLEAN DirSearch)
394 {
395 BOOLEAN Ret, Alloc = FALSE;
396 UNICODE_STRING EntryName;
397
398 EntryName.Buffer = IndexEntry->FileName.Name;
399 EntryName.Length =
400 EntryName.MaximumLength = IndexEntry->FileName.NameLength * sizeof(WCHAR);
401
402 if (DirSearch)
403 {
404 UNICODE_STRING IntFileName;
405 if (IndexEntry->FileName.NameType != NTFS_FILE_NAME_POSIX)
406 {
407 NT_VERIFY(NT_SUCCESS(RtlUpcaseUnicodeString(&IntFileName, FileName, TRUE)));
408 Alloc = TRUE;
409 }
410 else
411 {
412 IntFileName = *FileName;
413 }
414
415 Ret = FsRtlIsNameInExpression(&IntFileName, &EntryName, (IndexEntry->FileName.NameType != NTFS_FILE_NAME_POSIX), NULL);
416
417 if (Alloc)
418 {
419 RtlFreeUnicodeString(&IntFileName);
420 }
421
422 return Ret;
423 }
424 else
425 {
426 return (RtlCompareUnicodeString(FileName, &EntryName, (IndexEntry->FileName.NameType != NTFS_FILE_NAME_POSIX)) == 0);
427 }
428 }
429
430 #if 0
431 static
432 VOID
433 DumpIndexEntry(PINDEX_ENTRY_ATTRIBUTE IndexEntry)
434 {
435 DPRINT1("Entry: %p\n", IndexEntry);
436 DPRINT1("\tData.Directory.IndexedFile: %I64x\n", IndexEntry->Data.Directory.IndexedFile);
437 DPRINT1("\tLength: %u\n", IndexEntry->Length);
438 DPRINT1("\tKeyLength: %u\n", IndexEntry->KeyLength);
439 DPRINT1("\tFlags: %x\n", IndexEntry->Flags);
440 DPRINT1("\tReserved: %x\n", IndexEntry->Reserved);
441 DPRINT1("\t\tDirectoryFileReferenceNumber: %I64x\n", IndexEntry->FileName.DirectoryFileReferenceNumber);
442 DPRINT1("\t\tCreationTime: %I64u\n", IndexEntry->FileName.CreationTime);
443 DPRINT1("\t\tChangeTime: %I64u\n", IndexEntry->FileName.ChangeTime);
444 DPRINT1("\t\tLastWriteTime: %I64u\n", IndexEntry->FileName.LastWriteTime);
445 DPRINT1("\t\tLastAccessTime: %I64u\n", IndexEntry->FileName.LastAccessTime);
446 DPRINT1("\t\tAllocatedSize: %I64u\n", IndexEntry->FileName.AllocatedSize);
447 DPRINT1("\t\tDataSize: %I64u\n", IndexEntry->FileName.DataSize);
448 DPRINT1("\t\tFileAttributes: %x\n", IndexEntry->FileName.FileAttributes);
449 DPRINT1("\t\tNameLength: %u\n", IndexEntry->FileName.NameLength);
450 DPRINT1("\t\tNameType: %x\n", IndexEntry->FileName.NameType);
451 DPRINT1("\t\tName: %.*S\n", IndexEntry->FileName.NameLength, IndexEntry->FileName.Name);
452 }
453 #endif
454
455 NTSTATUS
456 BrowseIndexEntries(PDEVICE_EXTENSION Vcb,
457 PFILE_RECORD_HEADER MftRecord,
458 PCHAR IndexRecord,
459 ULONG IndexBlockSize,
460 PINDEX_ENTRY_ATTRIBUTE FirstEntry,
461 PINDEX_ENTRY_ATTRIBUTE LastEntry,
462 PUNICODE_STRING FileName,
463 PULONG StartEntry,
464 PULONG CurrentEntry,
465 BOOLEAN DirSearch,
466 ULONGLONG *OutMFTIndex)
467 {
468 NTSTATUS Status;
469 ULONG RecordOffset;
470 PINDEX_ENTRY_ATTRIBUTE IndexEntry;
471 PNTFS_ATTR_CONTEXT IndexAllocationCtx;
472 ULONGLONG IndexAllocationSize;
473 PINDEX_BUFFER IndexBuffer;
474
475 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);
476
477 IndexEntry = FirstEntry;
478 while (IndexEntry < LastEntry &&
479 !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
480 {
481 if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) > 0x10 &&
482 *CurrentEntry >= *StartEntry &&
483 IndexEntry->FileName.NameType != NTFS_FILE_NAME_DOS &&
484 CompareFileName(FileName, IndexEntry, DirSearch))
485 {
486 *StartEntry = *CurrentEntry;
487 *OutMFTIndex = (IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK);
488 return STATUS_SUCCESS;
489 }
490
491 (*CurrentEntry) += 1;
492 ASSERT(IndexEntry->Length >= sizeof(INDEX_ENTRY_ATTRIBUTE));
493 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
494 }
495
496 /* If we're already browsing a subnode */
497 if (IndexRecord == NULL)
498 {
499 return STATUS_OBJECT_PATH_NOT_FOUND;
500 }
501
502 /* If there's no subnode */
503 if (!(IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE))
504 {
505 return STATUS_OBJECT_PATH_NOT_FOUND;
506 }
507
508 Status = FindAttribute(Vcb, MftRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationCtx);
509 if (!NT_SUCCESS(Status))
510 {
511 DPRINT("Corrupted filesystem!\n");
512 return Status;
513 }
514
515 IndexAllocationSize = AttributeDataLength(&IndexAllocationCtx->Record);
516 Status = STATUS_OBJECT_PATH_NOT_FOUND;
517 for (RecordOffset = 0; RecordOffset < IndexAllocationSize; RecordOffset += IndexBlockSize)
518 {
519 ReadAttribute(Vcb, IndexAllocationCtx, RecordOffset, IndexRecord, IndexBlockSize);
520 Status = FixupUpdateSequenceArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs);
521 if (!NT_SUCCESS(Status))
522 {
523 break;
524 }
525
526 IndexBuffer = (PINDEX_BUFFER)IndexRecord;
527 ASSERT(IndexBuffer->Ntfs.Type == NRH_INDX_TYPE);
528 ASSERT(IndexBuffer->Header.AllocatedSize + FIELD_OFFSET(INDEX_BUFFER, Header) == IndexBlockSize);
529 FirstEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.FirstEntryOffset);
530 LastEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.TotalSizeOfEntries);
531 ASSERT(LastEntry <= (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexBuffer + IndexBlockSize));
532
533 Status = BrowseIndexEntries(NULL, NULL, NULL, 0, FirstEntry, LastEntry, FileName, StartEntry, CurrentEntry, DirSearch, OutMFTIndex);
534 if (NT_SUCCESS(Status))
535 {
536 break;
537 }
538 }
539
540 ReleaseAttributeContext(IndexAllocationCtx);
541 return Status;
542 }
543
544 NTSTATUS
545 NtfsFindMftRecord(PDEVICE_EXTENSION Vcb,
546 ULONGLONG MFTIndex,
547 PUNICODE_STRING FileName,
548 PULONG FirstEntry,
549 BOOLEAN DirSearch,
550 ULONGLONG *OutMFTIndex)
551 {
552 PFILE_RECORD_HEADER MftRecord;
553 PNTFS_ATTR_CONTEXT IndexRootCtx;
554 PINDEX_ROOT_ATTRIBUTE IndexRoot;
555 PCHAR IndexRecord;
556 PINDEX_ENTRY_ATTRIBUTE IndexEntry, IndexEntryEnd;
557 NTSTATUS Status;
558 ULONG CurrentEntry = 0;
559
560 DPRINT("NtfsFindMftRecord(%p, %I64d, %wZ, %u, %u, %p)\n", Vcb, MFTIndex, FileName, *FirstEntry, DirSearch, OutMFTIndex);
561
562 MftRecord = ExAllocatePoolWithTag(NonPagedPool,
563 Vcb->NtfsInfo.BytesPerFileRecord,
564 TAG_NTFS);
565 if (MftRecord == NULL)
566 {
567 return STATUS_INSUFFICIENT_RESOURCES;
568 }
569
570 Status = ReadFileRecord(Vcb, MFTIndex, MftRecord);
571 if (!NT_SUCCESS(Status))
572 {
573 ExFreePoolWithTag(MftRecord, TAG_NTFS);
574 return Status;
575 }
576
577 ASSERT(MftRecord->Ntfs.Type == NRH_FILE_TYPE);
578 Status = FindAttribute(Vcb, MftRecord, AttributeIndexRoot, L"$I30", 4, &IndexRootCtx);
579 if (!NT_SUCCESS(Status))
580 {
581 ExFreePoolWithTag(MftRecord, TAG_NTFS);
582 return Status;
583 }
584
585 IndexRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerIndexRecord, TAG_NTFS);
586 if (IndexRecord == NULL)
587 {
588 ReleaseAttributeContext(IndexRootCtx);
589 ExFreePoolWithTag(MftRecord, TAG_NTFS);
590 return STATUS_INSUFFICIENT_RESOURCES;
591 }
592
593 ReadAttribute(Vcb, IndexRootCtx, 0, IndexRecord, Vcb->NtfsInfo.BytesPerIndexRecord);
594 IndexRoot = (PINDEX_ROOT_ATTRIBUTE)IndexRecord;
595 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)&IndexRoot->Header + IndexRoot->Header.FirstEntryOffset);
596 /* Index root is always resident. */
597 IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)(IndexRecord + IndexRoot->Header.TotalSizeOfEntries);
598 ReleaseAttributeContext(IndexRootCtx);
599
600 DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb->NtfsInfo.BytesPerIndexRecord, IndexRoot->SizeOfEntry);
601
602 Status = BrowseIndexEntries(Vcb, MftRecord, IndexRecord, IndexRoot->SizeOfEntry, IndexEntry, IndexEntryEnd, FileName, FirstEntry, &CurrentEntry, DirSearch, OutMFTIndex);
603
604 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
605 ExFreePoolWithTag(MftRecord, TAG_NTFS);
606
607 return Status;
608 }
609
610 NTSTATUS
611 NtfsLookupFileAt(PDEVICE_EXTENSION Vcb,
612 PUNICODE_STRING PathName,
613 PFILE_RECORD_HEADER *FileRecord,
614 PULONGLONG MFTIndex,
615 ULONGLONG CurrentMFTIndex)
616 {
617 UNICODE_STRING Current, Remaining;
618 NTSTATUS Status;
619 ULONG FirstEntry = 0;
620
621 DPRINT("NtfsLookupFileAt(%p, %wZ, %p, %I64x)\n", Vcb, PathName, FileRecord, CurrentMFTIndex);
622
623 FsRtlDissectName(*PathName, &Current, &Remaining);
624
625 while (Current.Length != 0)
626 {
627 DPRINT("Current: %wZ\n", &Current);
628
629 Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, &Current, &FirstEntry, FALSE, &CurrentMFTIndex);
630 if (!NT_SUCCESS(Status))
631 {
632 return Status;
633 }
634
635 if (Remaining.Length == 0)
636 break;
637
638 FsRtlDissectName(Current, &Current, &Remaining);
639 }
640
641 *FileRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
642 if (*FileRecord == NULL)
643 {
644 DPRINT("NtfsLookupFileAt: Can't allocate MFT record\n");
645 return STATUS_INSUFFICIENT_RESOURCES;
646 }
647
648 Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord);
649 if (!NT_SUCCESS(Status))
650 {
651 DPRINT("NtfsLookupFileAt: Can't read MFT record\n");
652 ExFreePoolWithTag(*FileRecord, TAG_NTFS);
653 return Status;
654 }
655
656 *MFTIndex = CurrentMFTIndex;
657
658 return STATUS_SUCCESS;
659 }
660
661 NTSTATUS
662 NtfsLookupFile(PDEVICE_EXTENSION Vcb,
663 PUNICODE_STRING PathName,
664 PFILE_RECORD_HEADER *FileRecord,
665 PULONGLONG MFTIndex)
666 {
667 return NtfsLookupFileAt(Vcb, PathName, FileRecord, MFTIndex, NTFS_FILE_ROOT);
668 }
669
670 NTSTATUS
671 NtfsFindFileAt(PDEVICE_EXTENSION Vcb,
672 PUNICODE_STRING SearchPattern,
673 PULONG FirstEntry,
674 PFILE_RECORD_HEADER *FileRecord,
675 PULONGLONG MFTIndex,
676 ULONGLONG CurrentMFTIndex)
677 {
678 NTSTATUS Status;
679
680 DPRINT("NtfsFindFileAt(%p, %wZ, %u, %p, %p, %I64x)\n", Vcb, SearchPattern, *FirstEntry, FileRecord, MFTIndex, CurrentMFTIndex);
681
682 Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, SearchPattern, FirstEntry, TRUE, &CurrentMFTIndex);
683 if (!NT_SUCCESS(Status))
684 {
685 DPRINT("NtfsFindFileAt: NtfsFindMftRecord() failed with status 0x%08lx\n", Status);
686 return Status;
687 }
688
689 *FileRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
690 if (*FileRecord == NULL)
691 {
692 DPRINT("NtfsFindFileAt: Can't allocate MFT record\n");
693 return STATUS_INSUFFICIENT_RESOURCES;
694 }
695
696 Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord);
697 if (!NT_SUCCESS(Status))
698 {
699 DPRINT("NtfsFindFileAt: Can't read MFT record\n");
700 ExFreePoolWithTag(*FileRecord, TAG_NTFS);
701 return Status;
702 }
703
704 *MFTIndex = CurrentMFTIndex;
705
706 return STATUS_SUCCESS;
707 }
708
709 /* EOF */