[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 */
27
28 /* INCLUDES *****************************************************************/
29
30 #include "ntfs.h"
31
32 #define NDEBUG
33 #include <debug.h>
34
35 /* FUNCTIONS ****************************************************************/
36
37 PNTFS_ATTR_CONTEXT
38 PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord)
39 {
40 PNTFS_ATTR_CONTEXT Context;
41
42 Context = ExAllocatePoolWithTag(NonPagedPool,
43 FIELD_OFFSET(NTFS_ATTR_CONTEXT, Record) + AttrRecord->Length,
44 TAG_NTFS);
45 RtlCopyMemory(&Context->Record, AttrRecord, AttrRecord->Length);
46 if (AttrRecord->IsNonResident)
47 {
48 LONGLONG DataRunOffset;
49 ULONGLONG DataRunLength;
50
51 Context->CacheRun = (PUCHAR)&Context->Record + Context->Record.NonResident.MappingPairsOffset;
52 Context->CacheRunOffset = 0;
53 Context->CacheRun = DecodeRun(Context->CacheRun, &DataRunOffset, &DataRunLength);
54 Context->CacheRunLength = DataRunLength;
55 if (DataRunOffset != -1)
56 {
57 /* Normal run. */
58 Context->CacheRunStartLCN =
59 Context->CacheRunLastLCN = DataRunOffset;
60 }
61 else
62 {
63 /* Sparse run. */
64 Context->CacheRunStartLCN = -1;
65 Context->CacheRunLastLCN = 0;
66 }
67 Context->CacheRunCurrentOffset = 0;
68 }
69
70 return Context;
71 }
72
73
74 VOID
75 ReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context)
76 {
77 ExFreePoolWithTag(Context, TAG_NTFS);
78 }
79
80
81 PNTFS_ATTR_CONTEXT
82 FindAttributeHelper(PDEVICE_EXTENSION Vcb,
83 PNTFS_ATTR_RECORD AttrRecord,
84 PNTFS_ATTR_RECORD AttrRecordEnd,
85 ULONG Type,
86 PCWSTR Name,
87 ULONG NameLength)
88 {
89 DPRINT("FindAttributeHelper(%p, %p, %p, 0x%x, %S, %u)\n", Vcb, AttrRecord, AttrRecordEnd, Type, Name, NameLength);
90
91 while (AttrRecord < AttrRecordEnd)
92 {
93 DPRINT("AttrRecord->Type = 0x%x\n", AttrRecord->Type);
94
95 if (AttrRecord->Type == AttributeEnd)
96 break;
97
98 if (AttrRecord->Type == AttributeAttributeList)
99 {
100 PNTFS_ATTR_CONTEXT Context;
101 PNTFS_ATTR_CONTEXT ListContext;
102 PVOID ListBuffer;
103 ULONGLONG ListSize;
104 PNTFS_ATTR_RECORD ListAttrRecord;
105 PNTFS_ATTR_RECORD ListAttrRecordEnd;
106
107 // Do not handle non-resident yet
108 ASSERT(!(AttrRecord->IsNonResident & 1));
109
110 ListContext = PrepareAttributeContext(AttrRecord);
111
112 ListSize = AttributeDataLength(&ListContext->Record);
113 if(ListSize <= 0xFFFFFFFF)
114 ListBuffer = ExAllocatePoolWithTag(NonPagedPool, (ULONG)ListSize, TAG_NTFS);
115 else
116 ListBuffer = NULL;
117
118 if(!ListBuffer)
119 {
120 DPRINT("Failed to allocate memory: %x\n", (ULONG)ListSize);
121 continue;
122 }
123
124 ListAttrRecord = (PNTFS_ATTR_RECORD)ListBuffer;
125 ListAttrRecordEnd = (PNTFS_ATTR_RECORD)((PCHAR)ListBuffer + ListSize);
126
127 if (ReadAttribute(Vcb, ListContext, 0, ListBuffer, (ULONG)ListSize) == ListSize)
128 {
129 Context = FindAttributeHelper(Vcb, ListAttrRecord, ListAttrRecordEnd,
130 Type, Name, NameLength);
131
132 ReleaseAttributeContext(ListContext);
133 ExFreePoolWithTag(ListBuffer, TAG_NTFS);
134
135 if (Context != NULL)
136 {
137 DPRINT("Found context = %p\n", Context);
138 return Context;
139 }
140 }
141 }
142
143 if (AttrRecord->Type == Type)
144 {
145 if (AttrRecord->NameLength == NameLength)
146 {
147 PWCHAR AttrName;
148
149 AttrName = (PWCHAR)((PCHAR)AttrRecord + AttrRecord->NameOffset);
150 DPRINT("%.*S, %.*S\n", AttrRecord->NameLength, AttrName, NameLength, Name);
151 if (RtlCompareMemory(AttrName, Name, NameLength << 1) == (NameLength << 1))
152 {
153 /* Found it, fill up the context and return. */
154 DPRINT("Found context\n");
155 return PrepareAttributeContext(AttrRecord);
156 }
157 }
158 }
159
160 if (AttrRecord->Length == 0)
161 {
162 DPRINT("Null length attribute record\n");
163 return NULL;
164 }
165 AttrRecord = (PNTFS_ATTR_RECORD)((PCHAR)AttrRecord + AttrRecord->Length);
166 }
167
168 DPRINT("Ended\n");
169 return NULL;
170 }
171
172
173 NTSTATUS
174 FindAttribute(PDEVICE_EXTENSION Vcb,
175 PFILE_RECORD_HEADER MftRecord,
176 ULONG Type,
177 PCWSTR Name,
178 ULONG NameLength,
179 PNTFS_ATTR_CONTEXT * AttrCtx)
180 {
181 PNTFS_ATTR_RECORD AttrRecord;
182 PNTFS_ATTR_RECORD AttrRecordEnd;
183
184 DPRINT("FindAttribute(%p, %p, 0x%x, %S, %u, %p)\n", Vcb, MftRecord, Type, Name, NameLength, AttrCtx);
185
186 AttrRecord = (PNTFS_ATTR_RECORD)((PCHAR)MftRecord + MftRecord->AttributeOffset);
187 AttrRecordEnd = (PNTFS_ATTR_RECORD)((PCHAR)MftRecord + Vcb->NtfsInfo.BytesPerFileRecord);
188
189 *AttrCtx = FindAttributeHelper(Vcb, AttrRecord, AttrRecordEnd, Type, Name, NameLength);
190 if (*AttrCtx == NULL)
191 {
192 return STATUS_OBJECT_NAME_NOT_FOUND;
193 }
194
195 return STATUS_SUCCESS;
196 }
197
198
199 ULONG
200 AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord)
201 {
202 if (AttrRecord->IsNonResident)
203 return AttrRecord->NonResident.AllocatedSize;
204 else
205 return AttrRecord->Resident.ValueLength;
206 }
207
208
209 ULONGLONG
210 AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord)
211 {
212 if (AttrRecord->IsNonResident)
213 return AttrRecord->NonResident.DataSize;
214 else
215 return AttrRecord->Resident.ValueLength;
216 }
217
218
219 ULONG
220 ReadAttribute(PDEVICE_EXTENSION Vcb,
221 PNTFS_ATTR_CONTEXT Context,
222 ULONGLONG Offset,
223 PCHAR Buffer,
224 ULONG Length)
225 {
226 ULONGLONG LastLCN;
227 PUCHAR DataRun;
228 LONGLONG DataRunOffset;
229 ULONGLONG DataRunLength;
230 LONGLONG DataRunStartLCN;
231 ULONGLONG CurrentOffset;
232 ULONG ReadLength;
233 ULONG AlreadyRead;
234 NTSTATUS Status;
235
236 if (!Context->Record.IsNonResident)
237 {
238 if (Offset > Context->Record.Resident.ValueLength)
239 return 0;
240 if (Offset + Length > Context->Record.Resident.ValueLength)
241 Length = (ULONG)(Context->Record.Resident.ValueLength - Offset);
242 RtlCopyMemory(Buffer, (PCHAR)&Context->Record + Context->Record.Resident.ValueOffset + Offset, Length);
243 return Length;
244 }
245
246 /*
247 * Non-resident attribute
248 */
249
250 /*
251 * I. Find the corresponding start data run.
252 */
253
254 AlreadyRead = 0;
255
256 // FIXME: Cache seems to be non-working. Disable it for now
257 //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
258 if (0)
259 {
260 DataRun = Context->CacheRun;
261 LastLCN = Context->CacheRunLastLCN;
262 DataRunStartLCN = Context->CacheRunStartLCN;
263 DataRunLength = Context->CacheRunLength;
264 CurrentOffset = Context->CacheRunCurrentOffset;
265 }
266 else
267 {
268 LastLCN = 0;
269 DataRun = (PUCHAR)&Context->Record + Context->Record.NonResident.MappingPairsOffset;
270 CurrentOffset = 0;
271
272 while (1)
273 {
274 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
275 if (DataRunOffset != -1)
276 {
277 /* Normal data run. */
278 DataRunStartLCN = LastLCN + DataRunOffset;
279 LastLCN = DataRunStartLCN;
280 }
281 else
282 {
283 /* Sparse data run. */
284 DataRunStartLCN = -1;
285 }
286
287 if (Offset >= CurrentOffset &&
288 Offset < CurrentOffset + (DataRunLength * Vcb->NtfsInfo.BytesPerCluster))
289 {
290 break;
291 }
292
293 if (*DataRun == 0)
294 {
295 return AlreadyRead;
296 }
297
298 CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
299 }
300 }
301
302 /*
303 * II. Go through the run list and read the data
304 */
305
306 ReadLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset), Length);
307 if (DataRunStartLCN == -1)
308 RtlZeroMemory(Buffer, ReadLength);
309 Status = NtfsReadDisk(Vcb->StorageDevice,
310 DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster + Offset - CurrentOffset,
311 ReadLength,
312 (PVOID)Buffer,
313 FALSE);
314 if (NT_SUCCESS(Status))
315 {
316 Length -= ReadLength;
317 Buffer += ReadLength;
318 AlreadyRead += ReadLength;
319
320 if (ReadLength == DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset))
321 {
322 CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
323 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
324 if (DataRunLength != (ULONGLONG)-1)
325 {
326 DataRunStartLCN = LastLCN + DataRunOffset;
327 LastLCN = DataRunStartLCN;
328 }
329 else
330 DataRunStartLCN = -1;
331
332 if (*DataRun == 0)
333 return AlreadyRead;
334 }
335
336 while (Length > 0)
337 {
338 ReadLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster, Length);
339 if (DataRunStartLCN == -1)
340 RtlZeroMemory(Buffer, ReadLength);
341 else
342 {
343 Status = NtfsReadDisk(Vcb->StorageDevice,
344 DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster,
345 ReadLength,
346 (PVOID)Buffer,
347 FALSE);
348 if (!NT_SUCCESS(Status))
349 break;
350 }
351
352 Length -= ReadLength;
353 Buffer += ReadLength;
354 AlreadyRead += ReadLength;
355
356 /* We finished this request, but there still data in this data run. */
357 if (Length == 0 && ReadLength != DataRunLength * Vcb->NtfsInfo.BytesPerCluster)
358 break;
359
360 /*
361 * Go to next run in the list.
362 */
363
364 if (*DataRun == 0)
365 break;
366 CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
367 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
368 if (DataRunOffset != -1)
369 {
370 /* Normal data run. */
371 DataRunStartLCN = LastLCN + DataRunOffset;
372 LastLCN = DataRunStartLCN;
373 }
374 else
375 {
376 /* Sparse data run. */
377 DataRunStartLCN = -1;
378 }
379 } /* while */
380
381 } /* if Disk */
382
383 Context->CacheRun = DataRun;
384 Context->CacheRunOffset = Offset + AlreadyRead;
385 Context->CacheRunStartLCN = DataRunStartLCN;
386 Context->CacheRunLength = DataRunLength;
387 Context->CacheRunLastLCN = LastLCN;
388 Context->CacheRunCurrentOffset = CurrentOffset;
389
390 return AlreadyRead;
391 }
392
393
394 NTSTATUS
395 ReadFileRecord(PDEVICE_EXTENSION Vcb,
396 ULONGLONG index,
397 PFILE_RECORD_HEADER file)
398 {
399 ULONGLONG BytesRead;
400
401 DPRINT("ReadFileRecord(%p, %I64x, %p)\n", Vcb, index, file);
402
403 BytesRead = ReadAttribute(Vcb, Vcb->MFTContext, index * Vcb->NtfsInfo.BytesPerFileRecord, (PCHAR)file, Vcb->NtfsInfo.BytesPerFileRecord);
404 if (BytesRead != Vcb->NtfsInfo.BytesPerFileRecord)
405 {
406 DPRINT1("ReadFileRecord failed: %I64u read, %u expected\n", BytesRead, Vcb->NtfsInfo.BytesPerFileRecord);
407 return STATUS_PARTIAL_COPY;
408 }
409
410 /* Apply update sequence array fixups. */
411 return FixupUpdateSequenceArray(Vcb, &file->Ntfs);
412 }
413
414
415 NTSTATUS
416 FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb,
417 PNTFS_RECORD_HEADER Record)
418 {
419 USHORT *USA;
420 USHORT USANumber;
421 USHORT USACount;
422 USHORT *Block;
423
424 USA = (USHORT*)((PCHAR)Record + Record->UsaOffset);
425 USANumber = *(USA++);
426 USACount = Record->UsaCount - 1; /* Exclude the USA Number. */
427 Block = (USHORT*)((PCHAR)Record + Vcb->NtfsInfo.BytesPerSector - 2);
428
429 while (USACount)
430 {
431 if (*Block != USANumber)
432 {
433 DPRINT1("Mismatch with USA: %u read, %u expected\n" , *Block, USANumber);
434 return STATUS_UNSUCCESSFUL;
435 }
436 *Block = *(USA++);
437 Block = (USHORT*)((PCHAR)Block + Vcb->NtfsInfo.BytesPerSector);
438 USACount--;
439 }
440
441 return STATUS_SUCCESS;
442 }
443
444
445 NTSTATUS
446 ReadLCN(PDEVICE_EXTENSION Vcb,
447 ULONGLONG lcn,
448 ULONG count,
449 PVOID buffer)
450 {
451 LARGE_INTEGER DiskSector;
452
453 DiskSector.QuadPart = lcn;
454
455 return NtfsReadSectors(Vcb->StorageDevice,
456 DiskSector.u.LowPart * Vcb->NtfsInfo.SectorsPerCluster,
457 count * Vcb->NtfsInfo.SectorsPerCluster,
458 Vcb->NtfsInfo.BytesPerSector,
459 buffer,
460 FALSE);
461 }
462
463
464 BOOLEAN
465 CompareFileName(PUNICODE_STRING FileName,
466 PINDEX_ENTRY_ATTRIBUTE IndexEntry,
467 BOOLEAN DirSearch)
468 {
469 UNICODE_STRING EntryName;
470
471 EntryName.Buffer = IndexEntry->FileName.Name;
472 EntryName.Length =
473 EntryName.MaximumLength = IndexEntry->FileName.NameLength * sizeof(WCHAR);
474
475 if (DirSearch)
476 {
477 return FsRtlIsNameInExpression(FileName, &EntryName, (IndexEntry->FileName.NameType != NTFS_FILE_NAME_POSIX), NULL);
478 }
479 else
480 {
481 return (RtlCompareUnicodeString(FileName, &EntryName, (IndexEntry->FileName.NameType != NTFS_FILE_NAME_POSIX)) == 0);
482 }
483 }
484
485
486 NTSTATUS
487 NtfsFindMftRecord(PDEVICE_EXTENSION Vcb,
488 ULONGLONG MFTIndex,
489 PUNICODE_STRING FileName,
490 PULONG FirstEntry,
491 BOOLEAN DirSearch,
492 ULONGLONG *OutMFTIndex)
493 {
494 PFILE_RECORD_HEADER MftRecord;
495 //ULONG Magic;
496 PNTFS_ATTR_CONTEXT IndexRootCtx;
497 PNTFS_ATTR_CONTEXT IndexBitmapCtx;
498 PNTFS_ATTR_CONTEXT IndexAllocationCtx;
499 PINDEX_ROOT_ATTRIBUTE IndexRoot;
500 PINDEX_BUFFER IndexBuffer;
501 ULONGLONG BitmapDataSize;
502 ULONGLONG IndexAllocationSize;
503 PCHAR BitmapData;
504 PCHAR IndexRecord;
505 PINDEX_ENTRY_ATTRIBUTE IndexEntry, IndexEntryEnd;
506 ULONG RecordOffset;
507 ULONG IndexBlockSize;
508 NTSTATUS Status;
509 ULONG CurrentEntry = 0;
510
511 DPRINT("NtfsFindMftRecord(%p, %I64d, %wZ, %p, %u, %p)\n", Vcb, MFTIndex, FileName, FirstEntry, DirSearch, OutMFTIndex);
512
513 MftRecord = ExAllocatePoolWithTag(NonPagedPool,
514 Vcb->NtfsInfo.BytesPerFileRecord,
515 TAG_NTFS);
516 if (MftRecord == NULL)
517 {
518 return STATUS_INSUFFICIENT_RESOURCES;
519 }
520
521 if (NT_SUCCESS(ReadFileRecord(Vcb, MFTIndex, MftRecord)))
522 {
523 //Magic = MftRecord->Magic;
524
525 Status = FindAttribute(Vcb, MftRecord, AttributeIndexRoot, L"$I30", 4, &IndexRootCtx);
526 if (!NT_SUCCESS(Status))
527 {
528 ExFreePoolWithTag(MftRecord, TAG_NTFS);
529 return Status;
530 }
531
532 IndexRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerIndexRecord, TAG_NTFS);
533 if (IndexRecord == NULL)
534 {
535 ExFreePoolWithTag(MftRecord, TAG_NTFS);
536 return STATUS_INSUFFICIENT_RESOURCES;
537 }
538
539 ReadAttribute(Vcb, IndexRootCtx, 0, IndexRecord, Vcb->NtfsInfo.BytesPerIndexRecord);
540 IndexRoot = (PINDEX_ROOT_ATTRIBUTE)IndexRecord;
541 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)&IndexRoot->Header + IndexRoot->Header.FirstEntryOffset);
542 /* Index root is always resident. */
543 IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)(IndexRecord + IndexRootCtx->Record.Resident.ValueLength);
544 ReleaseAttributeContext(IndexRootCtx);
545
546 DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb->NtfsInfo.BytesPerIndexRecord, IndexRoot->SizeOfEntry);
547
548 while (IndexEntry < IndexEntryEnd &&
549 !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
550 {
551 if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) > 0x10 &&
552 CurrentEntry >= *FirstEntry &&
553 CompareFileName(FileName, IndexEntry, DirSearch))
554 {
555 *OutMFTIndex = (IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK);
556 *FirstEntry = CurrentEntry;
557 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
558 ExFreePoolWithTag(MftRecord, TAG_NTFS);
559 return STATUS_SUCCESS;
560 }
561
562 ++CurrentEntry;
563 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
564 }
565
566 if (IndexRoot->Header.Flags & INDEX_ROOT_LARGE)
567 {
568 DPRINT("Large Index!\n");
569
570 IndexBlockSize = IndexRoot->SizeOfEntry;
571
572 Status = FindAttribute(Vcb, MftRecord, AttributeBitmap, L"$I30", 4, &IndexBitmapCtx);
573 if (!NT_SUCCESS(Status))
574 {
575 DPRINT1("Corrupted filesystem!\n");
576 ExFreePoolWithTag(MftRecord, TAG_NTFS);
577 return Status;
578 }
579 BitmapDataSize = AttributeDataLength(&IndexBitmapCtx->Record);
580 DPRINT("BitmapDataSize: %x\n", (ULONG)BitmapDataSize);
581 if(BitmapDataSize <= 0xFFFFFFFF)
582 BitmapData = ExAllocatePoolWithTag(NonPagedPool, (ULONG)BitmapDataSize, TAG_NTFS);
583 else
584 BitmapData = NULL;
585
586 if (BitmapData == NULL)
587 {
588 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
589 ExFreePoolWithTag(MftRecord, TAG_NTFS);
590 return STATUS_INSUFFICIENT_RESOURCES;
591 }
592 ReadAttribute(Vcb, IndexBitmapCtx, 0, BitmapData, (ULONG)BitmapDataSize);
593 ReleaseAttributeContext(IndexBitmapCtx);
594
595 Status = FindAttribute(Vcb, MftRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationCtx);
596 if (!NT_SUCCESS(Status))
597 {
598 DPRINT("Corrupted filesystem!\n");
599 ExFreePoolWithTag(BitmapData, TAG_NTFS);
600 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
601 ExFreePoolWithTag(MftRecord, TAG_NTFS);
602 return Status;
603 }
604 IndexAllocationSize = AttributeDataLength(&IndexAllocationCtx->Record);
605
606 RecordOffset = 0;
607
608 for (;;)
609 {
610 DPRINT("RecordOffset: %x IndexAllocationSize: %x\n", RecordOffset, IndexAllocationSize);
611 for (; RecordOffset < IndexAllocationSize;)
612 {
613 UCHAR Bit = 1 << ((RecordOffset / IndexBlockSize) & 7);
614 ULONG Byte = (RecordOffset / IndexBlockSize) >> 3;
615 if ((BitmapData[Byte] & Bit))
616 break;
617 RecordOffset += IndexBlockSize;
618 }
619
620 if (RecordOffset >= IndexAllocationSize)
621 {
622 break;
623 }
624
625 ReadAttribute(Vcb, IndexAllocationCtx, RecordOffset, IndexRecord, IndexBlockSize);
626
627 if (!NT_SUCCESS(FixupUpdateSequenceArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs)))
628 {
629 break;
630 }
631
632 IndexBuffer = (PINDEX_BUFFER)IndexRecord;
633 ASSERT(IndexBuffer->Ntfs.Type == 'XDNI');
634 ASSERT(IndexBuffer->Header.AllocatedSize + 0x18 == IndexBlockSize);
635 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.FirstEntryOffset);
636 IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.TotalSizeOfEntries);
637 ASSERT(IndexEntryEnd <= (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexBuffer + IndexBlockSize));
638
639 while (IndexEntry < IndexEntryEnd &&
640 !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
641 {
642 if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) > 0x10 &&
643 CurrentEntry >= *FirstEntry &&
644 CompareFileName(FileName, IndexEntry, DirSearch))
645 {
646 DPRINT("File found\n");
647 *OutMFTIndex = (IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK);
648 *FirstEntry = CurrentEntry;
649 ExFreePoolWithTag(BitmapData, TAG_NTFS);
650 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
651 ExFreePoolWithTag(MftRecord, TAG_NTFS);
652 ReleaseAttributeContext(IndexAllocationCtx);
653 return STATUS_SUCCESS;
654 }
655
656 ++CurrentEntry;
657 ASSERT(IndexEntry->Length >= sizeof(INDEX_ENTRY_ATTRIBUTE));
658 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
659 }
660
661 RecordOffset += IndexBlockSize;
662 }
663
664 ReleaseAttributeContext(IndexAllocationCtx);
665 ExFreePoolWithTag(BitmapData, TAG_NTFS);
666 }
667
668 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
669 }
670 else
671 {
672 DPRINT("Can't read MFT record\n");
673 }
674 ExFreePoolWithTag(MftRecord, TAG_NTFS);
675
676 return STATUS_OBJECT_PATH_NOT_FOUND;
677 }
678
679 NTSTATUS
680 NtfsLookupFileAt(PDEVICE_EXTENSION Vcb,
681 PUNICODE_STRING PathName,
682 PFILE_RECORD_HEADER *FileRecord,
683 PNTFS_ATTR_CONTEXT *DataContext,
684 PULONGLONG MFTIndex,
685 ULONGLONG CurrentMFTIndex)
686 {
687 UNICODE_STRING Current, Remaining;
688 NTSTATUS Status;
689 ULONG FirstEntry = 0;
690
691 DPRINT("NtfsLookupFileAt(%p, %wZ, %p, %p, %I64x)\n", Vcb, PathName, FileRecord, DataContext, CurrentMFTIndex);
692
693 FsRtlDissectName(*PathName, &Current, &Remaining);
694
695 while (Current.Length != 0)
696 {
697 DPRINT("Current: %wZ\n", &Current);
698
699 Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, &Current, &FirstEntry, FALSE, &CurrentMFTIndex);
700 if (!NT_SUCCESS(Status))
701 {
702 return Status;
703 }
704
705 if (Remaining.Length == 0)
706 break;
707
708 FsRtlDissectName(Current, &Current, &Remaining);
709 }
710
711 *FileRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
712 if (*FileRecord == NULL)
713 {
714 DPRINT("NtfsLookupFileAt: Can't allocate MFT record\n");
715 return STATUS_INSUFFICIENT_RESOURCES;
716 }
717
718 Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord);
719 if (!NT_SUCCESS(Status))
720 {
721 DPRINT("NtfsLookupFileAt: Can't read MFT record\n");
722 ExFreePoolWithTag(*FileRecord, TAG_NTFS);
723 return Status;
724 }
725
726 if (!((*FileRecord)->Flags & FRH_DIRECTORY))
727 {
728 Status = FindAttribute(Vcb, *FileRecord, AttributeData, L"", 0, DataContext);
729 if (!NT_SUCCESS(Status))
730 {
731 DPRINT("NtfsLookupFileAt: Can't find data attribute\n");
732 ExFreePoolWithTag(*FileRecord, TAG_NTFS);
733 return Status;
734 }
735 }
736 else
737 {
738 *DataContext = NULL;
739 }
740
741 *MFTIndex = CurrentMFTIndex;
742
743 return STATUS_SUCCESS;
744 }
745
746 NTSTATUS
747 NtfsLookupFile(PDEVICE_EXTENSION Vcb,
748 PUNICODE_STRING PathName,
749 PFILE_RECORD_HEADER *FileRecord,
750 PNTFS_ATTR_CONTEXT *DataContext,
751 PULONGLONG MFTIndex)
752 {
753 return NtfsLookupFileAt(Vcb, PathName, FileRecord, DataContext, MFTIndex, NTFS_FILE_ROOT);
754 }
755
756 NTSTATUS
757 NtfsFindFileAt(PDEVICE_EXTENSION Vcb,
758 PUNICODE_STRING SearchPattern,
759 PULONG FirstEntry,
760 PFILE_RECORD_HEADER *FileRecord,
761 PNTFS_ATTR_CONTEXT *DataContext,
762 PULONGLONG MFTIndex,
763 ULONGLONG CurrentMFTIndex)
764 {
765 NTSTATUS Status;
766
767 DPRINT("NtfsFindFileAt(%p, %wZ, %p, %p, %p, %p, %I64x)\n", Vcb, SearchPattern, FirstEntry, FileRecord, DataContext, MFTIndex, CurrentMFTIndex);
768
769 Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, SearchPattern, FirstEntry, TRUE, &CurrentMFTIndex);
770 if (!NT_SUCCESS(Status))
771 {
772 DPRINT("NtfsFindFileAt: NtfsFindMftRecord() failed with status 0x%08lx\n", Status);
773 return Status;
774 }
775
776 *FileRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
777 if (*FileRecord == NULL)
778 {
779 DPRINT("NtfsFindFileAt: Can't allocate MFT record\n");
780 return STATUS_INSUFFICIENT_RESOURCES;
781 }
782
783 Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord);
784 if (!NT_SUCCESS(Status))
785 {
786 DPRINT("NtfsFindFileAt: Can't read MFT record\n");
787 ExFreePoolWithTag(*FileRecord, TAG_NTFS);
788 return Status;
789 }
790
791 if (!((*FileRecord)->Flags & FRH_DIRECTORY))
792 {
793 Status = FindAttribute(Vcb, *FileRecord, AttributeData, L"", 0, DataContext);
794 if (!NT_SUCCESS(Status))
795 {
796 DPRINT("NtfsFindFileAt: Can't find data attribute\n");
797 ExFreePoolWithTag(*FileRecord, TAG_NTFS);
798 return Status;
799 }
800 }
801 else
802 {
803 *DataContext = NULL;
804 }
805
806 *MFTIndex = CurrentMFTIndex;
807
808 return STATUS_SUCCESS;
809 }
810
811 /* EOF */