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