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