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