9b658c62e0ea8fb8b43694392d2721d730ca9774
[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 PNTFS_ATTR_CONTEXT
83 FindAttributeHelper(PDEVICE_EXTENSION Vcb,
84 PNTFS_ATTR_RECORD AttrRecord,
85 PNTFS_ATTR_RECORD AttrRecordEnd,
86 ULONG Type,
87 PCWSTR Name,
88 ULONG NameLength)
89 {
90 DPRINT("FindAttributeHelper(%p, %p, %p, 0x%x, %S, %u)\n", Vcb, AttrRecord, AttrRecordEnd, Type, Name, NameLength);
91
92 while (AttrRecord < AttrRecordEnd)
93 {
94 DPRINT("AttrRecord->Type = 0x%x\n", AttrRecord->Type);
95
96 if (AttrRecord->Type == AttributeEnd)
97 break;
98
99 if (AttrRecord->Type == AttributeAttributeList)
100 {
101 PNTFS_ATTR_CONTEXT Context;
102 PNTFS_ATTR_CONTEXT ListContext;
103 PVOID ListBuffer;
104 ULONGLONG ListSize;
105 PNTFS_ATTR_RECORD ListAttrRecord;
106 PNTFS_ATTR_RECORD ListAttrRecordEnd;
107
108 ListContext = PrepareAttributeContext(AttrRecord);
109
110 ListSize = AttributeDataLength(&ListContext->Record);
111 if(ListSize <= 0xFFFFFFFF)
112 ListBuffer = ExAllocatePoolWithTag(NonPagedPool, (ULONG)ListSize, TAG_NTFS);
113 else
114 ListBuffer = NULL;
115
116 if(!ListBuffer)
117 {
118 DPRINT("Failed to allocate memory: %x\n", (ULONG)ListSize);
119 continue;
120 }
121
122 ListAttrRecord = (PNTFS_ATTR_RECORD)ListBuffer;
123 ListAttrRecordEnd = (PNTFS_ATTR_RECORD)((PCHAR)ListBuffer + ListSize);
124
125 if (ReadAttribute(Vcb, ListContext, 0, ListBuffer, (ULONG)ListSize) == ListSize)
126 {
127 Context = FindAttributeHelper(Vcb, ListAttrRecord, ListAttrRecordEnd,
128 Type, Name, NameLength);
129
130 ReleaseAttributeContext(ListContext);
131 ExFreePoolWithTag(ListBuffer, TAG_NTFS);
132
133 if (Context != NULL)
134 {
135 if (AttrRecord->IsNonResident) DPRINT("Found context = %p\n", Context);
136 return Context;
137 }
138 }
139 }
140
141 if (AttrRecord->Type == Type)
142 {
143 if (AttrRecord->NameLength == NameLength)
144 {
145 PWCHAR AttrName;
146
147 AttrName = (PWCHAR)((PCHAR)AttrRecord + AttrRecord->NameOffset);
148 DPRINT("%.*S, %.*S\n", AttrRecord->NameLength, AttrName, NameLength, Name);
149 if (RtlCompareMemory(AttrName, Name, NameLength << 1) == (NameLength << 1))
150 {
151 /* Found it, fill up the context and return. */
152 DPRINT("Found context\n");
153 return PrepareAttributeContext(AttrRecord);
154 }
155 }
156 }
157
158 if (AttrRecord->Length == 0)
159 {
160 DPRINT("Null length attribute record\n");
161 return NULL;
162 }
163 AttrRecord = (PNTFS_ATTR_RECORD)((PCHAR)AttrRecord + AttrRecord->Length);
164 }
165
166 DPRINT("Ended\n");
167 return NULL;
168 }
169
170
171 NTSTATUS
172 FindAttribute(PDEVICE_EXTENSION Vcb,
173 PFILE_RECORD_HEADER MftRecord,
174 ULONG Type,
175 PCWSTR Name,
176 ULONG NameLength,
177 PNTFS_ATTR_CONTEXT * AttrCtx)
178 {
179 PNTFS_ATTR_RECORD AttrRecord;
180 PNTFS_ATTR_RECORD AttrRecordEnd;
181
182 DPRINT("FindAttribute(%p, %p, 0x%x, %S, %u, %p)\n", Vcb, MftRecord, Type, Name, NameLength, AttrCtx);
183
184 AttrRecord = (PNTFS_ATTR_RECORD)((PCHAR)MftRecord + MftRecord->AttributeOffset);
185 AttrRecordEnd = (PNTFS_ATTR_RECORD)((PCHAR)MftRecord + Vcb->NtfsInfo.BytesPerFileRecord);
186
187 *AttrCtx = FindAttributeHelper(Vcb, AttrRecord, AttrRecordEnd, Type, Name, NameLength);
188 if (*AttrCtx == NULL)
189 {
190 return STATUS_OBJECT_NAME_NOT_FOUND;
191 }
192
193 return STATUS_SUCCESS;
194 }
195
196
197 ULONG
198 AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord)
199 {
200 if (AttrRecord->IsNonResident)
201 return AttrRecord->NonResident.AllocatedSize;
202 else
203 return AttrRecord->Resident.ValueLength;
204 }
205
206
207 ULONGLONG
208 AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord)
209 {
210 if (AttrRecord->IsNonResident)
211 return AttrRecord->NonResident.DataSize;
212 else
213 return AttrRecord->Resident.ValueLength;
214 }
215
216
217 ULONG
218 ReadAttribute(PDEVICE_EXTENSION Vcb,
219 PNTFS_ATTR_CONTEXT Context,
220 ULONGLONG Offset,
221 PCHAR Buffer,
222 ULONG Length)
223 {
224 ULONGLONG LastLCN;
225 PUCHAR DataRun;
226 LONGLONG DataRunOffset;
227 ULONGLONG DataRunLength;
228 LONGLONG DataRunStartLCN;
229 ULONGLONG CurrentOffset;
230 ULONG ReadLength;
231 ULONG AlreadyRead;
232 NTSTATUS Status;
233
234 if (!Context->Record.IsNonResident)
235 {
236 if (Offset > Context->Record.Resident.ValueLength)
237 return 0;
238 if (Offset + Length > Context->Record.Resident.ValueLength)
239 Length = (ULONG)(Context->Record.Resident.ValueLength - Offset);
240 RtlCopyMemory(Buffer, (PCHAR)&Context->Record + Context->Record.Resident.ValueOffset + Offset, Length);
241 return Length;
242 }
243
244 /*
245 * Non-resident attribute
246 */
247
248 /*
249 * I. Find the corresponding start data run.
250 */
251
252 AlreadyRead = 0;
253
254 // FIXME: Cache seems to be non-working. Disable it for now
255 //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
256 if (0)
257 {
258 DataRun = Context->CacheRun;
259 LastLCN = Context->CacheRunLastLCN;
260 DataRunStartLCN = Context->CacheRunStartLCN;
261 DataRunLength = Context->CacheRunLength;
262 CurrentOffset = Context->CacheRunCurrentOffset;
263 }
264 else
265 {
266 LastLCN = 0;
267 DataRun = (PUCHAR)&Context->Record + Context->Record.NonResident.MappingPairsOffset;
268 CurrentOffset = 0;
269
270 while (1)
271 {
272 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
273 if (DataRunOffset != -1)
274 {
275 /* Normal data run. */
276 DataRunStartLCN = LastLCN + DataRunOffset;
277 LastLCN = DataRunStartLCN;
278 }
279 else
280 {
281 /* Sparse data run. */
282 DataRunStartLCN = -1;
283 }
284
285 if (Offset >= CurrentOffset &&
286 Offset < CurrentOffset + (DataRunLength * Vcb->NtfsInfo.BytesPerCluster))
287 {
288 break;
289 }
290
291 if (*DataRun == 0)
292 {
293 return AlreadyRead;
294 }
295
296 CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
297 }
298 }
299
300 /*
301 * II. Go through the run list and read the data
302 */
303
304 ReadLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset), Length);
305 if (DataRunStartLCN == -1)
306 RtlZeroMemory(Buffer, ReadLength);
307 Status = NtfsReadDisk(Vcb->StorageDevice,
308 DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster + Offset - CurrentOffset,
309 ReadLength,
310 Vcb->NtfsInfo.BytesPerSector,
311 (PVOID)Buffer,
312 FALSE);
313 if (NT_SUCCESS(Status))
314 {
315 Length -= ReadLength;
316 Buffer += ReadLength;
317 AlreadyRead += ReadLength;
318
319 if (ReadLength == DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset))
320 {
321 CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
322 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
323 if (DataRunLength != (ULONGLONG)-1)
324 {
325 DataRunStartLCN = LastLCN + DataRunOffset;
326 LastLCN = DataRunStartLCN;
327 }
328 else
329 DataRunStartLCN = -1;
330
331 if (*DataRun == 0)
332 return AlreadyRead;
333 }
334
335 while (Length > 0)
336 {
337 ReadLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster, Length);
338 if (DataRunStartLCN == -1)
339 RtlZeroMemory(Buffer, ReadLength);
340 else
341 {
342 Status = NtfsReadDisk(Vcb->StorageDevice,
343 DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster,
344 ReadLength,
345 Vcb->NtfsInfo.BytesPerSector,
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 BOOLEAN Ret, Alloc = FALSE;
470 UNICODE_STRING EntryName;
471
472 EntryName.Buffer = IndexEntry->FileName.Name;
473 EntryName.Length =
474 EntryName.MaximumLength = IndexEntry->FileName.NameLength * sizeof(WCHAR);
475
476 if (DirSearch)
477 {
478 UNICODE_STRING IntFileName;
479 if (IndexEntry->FileName.NameType != NTFS_FILE_NAME_POSIX)
480 {
481 NT_VERIFY(NT_SUCCESS(RtlUpcaseUnicodeString(&IntFileName, FileName, TRUE)));
482 Alloc = TRUE;
483 }
484 else
485 {
486 IntFileName = *FileName;
487 }
488
489 Ret = FsRtlIsNameInExpression(&IntFileName, &EntryName, (IndexEntry->FileName.NameType != NTFS_FILE_NAME_POSIX), NULL);
490
491 if (Alloc)
492 {
493 RtlFreeUnicodeString(&IntFileName);
494 }
495
496 return Ret;
497 }
498 else
499 {
500 return (RtlCompareUnicodeString(FileName, &EntryName, (IndexEntry->FileName.NameType != NTFS_FILE_NAME_POSIX)) == 0);
501 }
502 }
503
504
505 NTSTATUS
506 NtfsFindMftRecord(PDEVICE_EXTENSION Vcb,
507 ULONGLONG MFTIndex,
508 PUNICODE_STRING FileName,
509 PULONG FirstEntry,
510 BOOLEAN DirSearch,
511 ULONGLONG *OutMFTIndex)
512 {
513 PFILE_RECORD_HEADER MftRecord;
514 PNTFS_ATTR_CONTEXT IndexRootCtx;
515 PNTFS_ATTR_CONTEXT IndexBitmapCtx;
516 PNTFS_ATTR_CONTEXT IndexAllocationCtx;
517 PINDEX_ROOT_ATTRIBUTE IndexRoot;
518 PINDEX_BUFFER IndexBuffer;
519 ULONGLONG BitmapDataSize;
520 ULONGLONG IndexAllocationSize;
521 PCHAR BitmapData;
522 PCHAR IndexRecord;
523 PINDEX_ENTRY_ATTRIBUTE IndexEntry, IndexEntryEnd;
524 ULONG RecordOffset;
525 ULONG IndexBlockSize;
526 NTSTATUS Status;
527 ULONG CurrentEntry = 0;
528
529 DPRINT("NtfsFindMftRecord(%p, %I64d, %wZ, %p, %u, %p)\n", Vcb, MFTIndex, FileName, FirstEntry, DirSearch, OutMFTIndex);
530
531 MftRecord = ExAllocatePoolWithTag(NonPagedPool,
532 Vcb->NtfsInfo.BytesPerFileRecord,
533 TAG_NTFS);
534 if (MftRecord == NULL)
535 {
536 return STATUS_INSUFFICIENT_RESOURCES;
537 }
538
539 if (NT_SUCCESS(ReadFileRecord(Vcb, MFTIndex, MftRecord)))
540 {
541 ASSERT(MftRecord->Ntfs.Type == NRH_FILE_TYPE);
542
543 Status = FindAttribute(Vcb, MftRecord, AttributeIndexRoot, L"$I30", 4, &IndexRootCtx);
544 if (!NT_SUCCESS(Status))
545 {
546 ExFreePoolWithTag(MftRecord, TAG_NTFS);
547 return Status;
548 }
549
550 IndexRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerIndexRecord, TAG_NTFS);
551 if (IndexRecord == NULL)
552 {
553 ExFreePoolWithTag(MftRecord, TAG_NTFS);
554 return STATUS_INSUFFICIENT_RESOURCES;
555 }
556
557 ReadAttribute(Vcb, IndexRootCtx, 0, IndexRecord, Vcb->NtfsInfo.BytesPerIndexRecord);
558 IndexRoot = (PINDEX_ROOT_ATTRIBUTE)IndexRecord;
559 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)&IndexRoot->Header + IndexRoot->Header.FirstEntryOffset);
560 /* Index root is always resident. */
561 IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)(IndexRecord + IndexRoot->Header.TotalSizeOfEntries);
562 ReleaseAttributeContext(IndexRootCtx);
563
564 DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb->NtfsInfo.BytesPerIndexRecord, IndexRoot->SizeOfEntry);
565
566 while (IndexEntry < IndexEntryEnd &&
567 !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
568 {
569 UNICODE_STRING EntryName;
570 EntryName.Buffer = IndexEntry->FileName.Name;
571 EntryName.Length =
572 EntryName.MaximumLength = IndexEntry->FileName.NameLength * sizeof(WCHAR);
573
574 if (IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
575 DPRINT1("Warning: sub-node browsing unimplemented! (%wZ)\n", &EntryName);
576
577 if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) > 0x10 &&
578 CurrentEntry >= *FirstEntry &&
579 CompareFileName(FileName, IndexEntry, DirSearch))
580 {
581 *OutMFTIndex = (IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK);
582 *FirstEntry = CurrentEntry;
583 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
584 ExFreePoolWithTag(MftRecord, TAG_NTFS);
585 return STATUS_SUCCESS;
586 }
587
588 ++CurrentEntry;
589 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
590 }
591
592 if (IndexRoot->Header.Flags & INDEX_ROOT_LARGE)
593 {
594 DPRINT("Large Index!\n");
595
596 IndexBlockSize = IndexRoot->SizeOfEntry;
597
598 Status = FindAttribute(Vcb, MftRecord, AttributeBitmap, L"$I30", 4, &IndexBitmapCtx);
599 if (!NT_SUCCESS(Status))
600 {
601 DPRINT1("Corrupted filesystem!\n");
602 ExFreePoolWithTag(MftRecord, TAG_NTFS);
603 return Status;
604 }
605 BitmapDataSize = AttributeDataLength(&IndexBitmapCtx->Record);
606 DPRINT("BitmapDataSize: %x\n", (ULONG)BitmapDataSize);
607 if(BitmapDataSize <= 0xFFFFFFFF)
608 BitmapData = ExAllocatePoolWithTag(NonPagedPool, (ULONG)BitmapDataSize, TAG_NTFS);
609 else
610 BitmapData = NULL;
611
612 if (BitmapData == NULL)
613 {
614 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
615 ExFreePoolWithTag(MftRecord, TAG_NTFS);
616 return STATUS_INSUFFICIENT_RESOURCES;
617 }
618 ReadAttribute(Vcb, IndexBitmapCtx, 0, BitmapData, (ULONG)BitmapDataSize);
619 ReleaseAttributeContext(IndexBitmapCtx);
620
621 Status = FindAttribute(Vcb, MftRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationCtx);
622 if (!NT_SUCCESS(Status))
623 {
624 DPRINT("Corrupted filesystem!\n");
625 ExFreePoolWithTag(BitmapData, TAG_NTFS);
626 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
627 ExFreePoolWithTag(MftRecord, TAG_NTFS);
628 return Status;
629 }
630 IndexAllocationSize = AttributeDataLength(&IndexAllocationCtx->Record);
631
632 RecordOffset = 0;
633
634 for (;;)
635 {
636 DPRINT("RecordOffset: %x IndexAllocationSize: %x\n", RecordOffset, IndexAllocationSize);
637 for (; RecordOffset < IndexAllocationSize;)
638 {
639 UCHAR Bit = 1 << ((RecordOffset / IndexBlockSize) & 7);
640 ULONG Byte = (RecordOffset / IndexBlockSize) >> 3;
641 if ((BitmapData[Byte] & Bit))
642 break;
643 RecordOffset += IndexBlockSize;
644 }
645
646 if (RecordOffset >= IndexAllocationSize)
647 {
648 break;
649 }
650
651 ReadAttribute(Vcb, IndexAllocationCtx, RecordOffset, IndexRecord, IndexBlockSize);
652
653 if (!NT_SUCCESS(FixupUpdateSequenceArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs)))
654 {
655 break;
656 }
657
658 IndexBuffer = (PINDEX_BUFFER)IndexRecord;
659 ASSERT(IndexBuffer->Ntfs.Type == NRH_INDX_TYPE);
660 ASSERT(IndexBuffer->Header.AllocatedSize + 0x18 == IndexBlockSize);
661 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.FirstEntryOffset);
662 IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.TotalSizeOfEntries);
663 ASSERT(IndexEntryEnd <= (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexBuffer + IndexBlockSize));
664
665 while (IndexEntry < IndexEntryEnd &&
666 !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
667 {
668 UNICODE_STRING EntryName;
669 EntryName.Buffer = IndexEntry->FileName.Name;
670 EntryName.Length =
671 EntryName.MaximumLength = IndexEntry->FileName.NameLength * sizeof(WCHAR);
672
673 if (IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE)
674 DPRINT1("Warning: sub-node browsing unimplemented! (%wZ)\n", &EntryName);
675
676 if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) > 0x10 &&
677 CurrentEntry >= *FirstEntry &&
678 CompareFileName(FileName, IndexEntry, DirSearch))
679 {
680 DPRINT("File found\n");
681 *OutMFTIndex = (IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK);
682 *FirstEntry = CurrentEntry;
683 ExFreePoolWithTag(BitmapData, TAG_NTFS);
684 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
685 ExFreePoolWithTag(MftRecord, TAG_NTFS);
686 ReleaseAttributeContext(IndexAllocationCtx);
687 return STATUS_SUCCESS;
688 }
689
690 ++CurrentEntry;
691 ASSERT(IndexEntry->Length >= sizeof(INDEX_ENTRY_ATTRIBUTE));
692 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
693 }
694
695 RecordOffset += IndexBlockSize;
696 }
697
698 ReleaseAttributeContext(IndexAllocationCtx);
699 ExFreePoolWithTag(BitmapData, TAG_NTFS);
700 }
701
702 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
703 }
704 else
705 {
706 DPRINT("Can't read MFT record\n");
707 }
708 ExFreePoolWithTag(MftRecord, TAG_NTFS);
709
710 return STATUS_OBJECT_PATH_NOT_FOUND;
711 }
712
713 NTSTATUS
714 NtfsLookupFileAt(PDEVICE_EXTENSION Vcb,
715 PUNICODE_STRING PathName,
716 PFILE_RECORD_HEADER *FileRecord,
717 PNTFS_ATTR_CONTEXT *DataContext,
718 PULONGLONG MFTIndex,
719 ULONGLONG CurrentMFTIndex)
720 {
721 UNICODE_STRING Current, Remaining;
722 NTSTATUS Status;
723 ULONG FirstEntry = 0;
724
725 DPRINT("NtfsLookupFileAt(%p, %wZ, %p, %p, %I64x)\n", Vcb, PathName, FileRecord, DataContext, CurrentMFTIndex);
726
727 FsRtlDissectName(*PathName, &Current, &Remaining);
728
729 while (Current.Length != 0)
730 {
731 DPRINT("Current: %wZ\n", &Current);
732
733 Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, &Current, &FirstEntry, FALSE, &CurrentMFTIndex);
734 if (!NT_SUCCESS(Status))
735 {
736 return Status;
737 }
738
739 if (Remaining.Length == 0)
740 break;
741
742 FsRtlDissectName(Current, &Current, &Remaining);
743 }
744
745 *FileRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
746 if (*FileRecord == NULL)
747 {
748 DPRINT("NtfsLookupFileAt: Can't allocate MFT record\n");
749 return STATUS_INSUFFICIENT_RESOURCES;
750 }
751
752 Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord);
753 if (!NT_SUCCESS(Status))
754 {
755 DPRINT("NtfsLookupFileAt: Can't read MFT record\n");
756 ExFreePoolWithTag(*FileRecord, TAG_NTFS);
757 return Status;
758 }
759
760 if (!((*FileRecord)->Flags & FRH_DIRECTORY))
761 {
762 Status = FindAttribute(Vcb, *FileRecord, AttributeData, L"", 0, DataContext);
763 if (!NT_SUCCESS(Status))
764 {
765 DPRINT("NtfsLookupFileAt: Can't find data attribute\n");
766 ExFreePoolWithTag(*FileRecord, TAG_NTFS);
767 return Status;
768 }
769 }
770 else
771 {
772 *DataContext = NULL;
773 }
774
775 *MFTIndex = CurrentMFTIndex;
776
777 return STATUS_SUCCESS;
778 }
779
780 NTSTATUS
781 NtfsLookupFile(PDEVICE_EXTENSION Vcb,
782 PUNICODE_STRING PathName,
783 PFILE_RECORD_HEADER *FileRecord,
784 PNTFS_ATTR_CONTEXT *DataContext,
785 PULONGLONG MFTIndex)
786 {
787 return NtfsLookupFileAt(Vcb, PathName, FileRecord, DataContext, MFTIndex, NTFS_FILE_ROOT);
788 }
789
790 NTSTATUS
791 NtfsFindFileAt(PDEVICE_EXTENSION Vcb,
792 PUNICODE_STRING SearchPattern,
793 PULONG FirstEntry,
794 PFILE_RECORD_HEADER *FileRecord,
795 PNTFS_ATTR_CONTEXT *DataContext,
796 PULONGLONG MFTIndex,
797 ULONGLONG CurrentMFTIndex)
798 {
799 NTSTATUS Status;
800
801 DPRINT("NtfsFindFileAt(%p, %wZ, %p, %p, %p, %p, %I64x)\n", Vcb, SearchPattern, FirstEntry, FileRecord, DataContext, MFTIndex, CurrentMFTIndex);
802
803 Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, SearchPattern, FirstEntry, TRUE, &CurrentMFTIndex);
804 if (!NT_SUCCESS(Status))
805 {
806 DPRINT("NtfsFindFileAt: NtfsFindMftRecord() failed with status 0x%08lx\n", Status);
807 return Status;
808 }
809
810 *FileRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
811 if (*FileRecord == NULL)
812 {
813 DPRINT("NtfsFindFileAt: Can't allocate MFT record\n");
814 return STATUS_INSUFFICIENT_RESOURCES;
815 }
816
817 Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord);
818 if (!NT_SUCCESS(Status))
819 {
820 DPRINT("NtfsFindFileAt: Can't read MFT record\n");
821 ExFreePoolWithTag(*FileRecord, TAG_NTFS);
822 return Status;
823 }
824
825 if (!((*FileRecord)->Flags & FRH_DIRECTORY))
826 {
827 Status = FindAttribute(Vcb, *FileRecord, AttributeData, L"", 0, DataContext);
828 if (!NT_SUCCESS(Status))
829 {
830 DPRINT("NtfsFindFileAt: Can't find data attribute\n");
831 ExFreePoolWithTag(*FileRecord, TAG_NTFS);
832 return Status;
833 }
834 }
835 else
836 {
837 *DataContext = NULL;
838 }
839
840 *MFTIndex = CurrentMFTIndex;
841
842 return STATUS_SUCCESS;
843 }
844
845 /* EOF */