[NTFS]
[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 DPRINT1("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", AttrName, 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 DPRINT1("NtfsFindAttribute(%p, %p, %u, %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 BytesRead = ReadAttribute(Vcb, Vcb->MFTContext, index * Vcb->NtfsInfo.BytesPerFileRecord, (PCHAR)file, Vcb->NtfsInfo.BytesPerFileRecord);
401 if (BytesRead != Vcb->NtfsInfo.BytesPerFileRecord)
402 {
403 DPRINT1("ReadFileRecord failed: %u read, %u expected\n", BytesRead, Vcb->NtfsInfo.BytesPerFileRecord);
404 return STATUS_PARTIAL_COPY;
405 }
406
407 /* Apply update sequence array fixups. */
408 return FixupUpdateSequenceArray(Vcb, &file->Ntfs);
409 }
410
411
412 NTSTATUS
413 FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb,
414 PNTFS_RECORD_HEADER Record)
415 {
416 USHORT *USA;
417 USHORT USANumber;
418 USHORT USACount;
419 USHORT *Block;
420
421 USA = (USHORT*)((PCHAR)Record + Record->UsaOffset);
422 USANumber = *(USA++);
423 USACount = Record->UsaCount - 1; /* Exclude the USA Number. */
424 Block = (USHORT*)((PCHAR)Record + Vcb->NtfsInfo.BytesPerSector - 2);
425
426 while (USACount)
427 {
428 if (*Block != USANumber)
429 {
430 DPRINT1("Mismatch with USA: %u read, %u expected\n" , *Block, USANumber);
431 return STATUS_UNSUCCESSFUL;
432 }
433 *Block = *(USA++);
434 Block = (USHORT*)((PCHAR)Block + Vcb->NtfsInfo.BytesPerSector);
435 USACount--;
436 }
437
438 return STATUS_SUCCESS;
439 }
440
441
442 NTSTATUS
443 ReadLCN(PDEVICE_EXTENSION Vcb,
444 ULONGLONG lcn,
445 ULONG count,
446 PVOID buffer)
447 {
448 LARGE_INTEGER DiskSector;
449
450 DiskSector.QuadPart = lcn;
451
452 return NtfsReadSectors(Vcb->StorageDevice,
453 DiskSector.u.LowPart * Vcb->NtfsInfo.SectorsPerCluster,
454 count * Vcb->NtfsInfo.SectorsPerCluster,
455 Vcb->NtfsInfo.BytesPerSector,
456 buffer,
457 FALSE);
458 }
459
460
461 BOOLEAN
462 CompareFileName(PUNICODE_STRING FileName,
463 PINDEX_ENTRY_ATTRIBUTE IndexEntry,
464 BOOLEAN DirSearch)
465 {
466 UNICODE_STRING EntryName;
467
468 EntryName.Buffer = IndexEntry->FileName.Name;
469 EntryName.Length =
470 EntryName.MaximumLength = IndexEntry->FileName.NameLength;
471
472 if (DirSearch)
473 {
474 return FsRtlIsNameInExpression(FileName, &EntryName, (IndexEntry->FileName.NameType != NTFS_FILE_NAME_POSIX), NULL);
475 }
476 else
477 {
478 return (RtlCompareUnicodeString(FileName, &EntryName, (IndexEntry->FileName.NameType != NTFS_FILE_NAME_POSIX)) == TRUE);
479 }
480 }
481
482
483 NTSTATUS
484 NtfsFindMftRecord(PDEVICE_EXTENSION Vcb,
485 ULONGLONG MFTIndex,
486 PUNICODE_STRING FileName,
487 PULONG FirstEntry,
488 BOOLEAN DirSearch,
489 ULONGLONG *OutMFTIndex,
490 PWSTR OutName)
491 {
492 PFILE_RECORD_HEADER MftRecord;
493 //ULONG Magic;
494 PNTFS_ATTR_CONTEXT IndexRootCtx;
495 PNTFS_ATTR_CONTEXT IndexBitmapCtx;
496 PNTFS_ATTR_CONTEXT IndexAllocationCtx;
497 PINDEX_ROOT_ATTRIBUTE IndexRoot;
498 ULONGLONG BitmapDataSize;
499 ULONGLONG IndexAllocationSize;
500 PCHAR BitmapData;
501 PCHAR IndexRecord;
502 PINDEX_ENTRY_ATTRIBUTE IndexEntry, IndexEntryEnd;
503 ULONG RecordOffset;
504 ULONG IndexBlockSize;
505 NTSTATUS Status;
506 ULONG CurrentEntry = 0;
507
508 DPRINT1("NtfsFindMftRecord(%p, %I64d, %wZ, %p, %u, %p, %p)\n", Vcb, MFTIndex, FileName, FirstEntry, DirSearch, OutMFTIndex, OutName);
509
510 MftRecord = ExAllocatePoolWithTag(NonPagedPool,
511 Vcb->NtfsInfo.BytesPerFileRecord,
512 TAG_NTFS);
513 if (MftRecord == NULL)
514 {
515 return STATUS_INSUFFICIENT_RESOURCES;
516 }
517
518 if (NT_SUCCESS(ReadFileRecord(Vcb, MFTIndex, MftRecord)))
519 {
520 //Magic = MftRecord->Magic;
521
522 Status = FindAttribute(Vcb, MftRecord, AttributeIndexRoot, L"$I30", 4, &IndexRootCtx);
523 if (!NT_SUCCESS(Status))
524 {
525 ExFreePoolWithTag(MftRecord, TAG_NTFS);
526 return Status;
527 }
528
529 IndexRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerIndexRecord, TAG_NTFS);
530 if (IndexRecord == NULL)
531 {
532 ExFreePoolWithTag(MftRecord, TAG_NTFS);
533 return STATUS_INSUFFICIENT_RESOURCES;
534 }
535
536 ReadAttribute(Vcb, IndexRootCtx, 0, IndexRecord, Vcb->NtfsInfo.BytesPerIndexRecord);
537 IndexRoot = (PINDEX_ROOT_ATTRIBUTE)IndexRecord;
538 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)&IndexRoot->Header + IndexRoot->Header.FirstEntryOffset);
539 /* Index root is always resident. */
540 IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)(IndexRecord + IndexRootCtx->Record.Resident.ValueLength);
541 ReleaseAttributeContext(IndexRootCtx);
542
543 DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb->NtfsInfo.BytesPerIndexRecord, IndexRoot->SizeOfEntry);
544
545 while (IndexEntry < IndexEntryEnd &&
546 !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
547 {
548 if (CurrentEntry >= *FirstEntry && CompareFileName(FileName, IndexEntry, DirSearch))
549 {
550 *OutMFTIndex = IndexEntry->Data.Directory.IndexedFile;
551 *FirstEntry = CurrentEntry;
552 RtlCopyMemory(OutName, IndexEntry->FileName.Name, IndexEntry->FileName.NameLength);
553 OutName[IndexEntry->FileName.NameLength / sizeof(WCHAR)] = UNICODE_NULL;
554 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
555 ExFreePoolWithTag(MftRecord, TAG_NTFS);
556 return STATUS_SUCCESS;
557 }
558
559 ++CurrentEntry;
560 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
561 }
562
563 if (IndexRoot->Header.Flags & INDEX_ROOT_LARGE)
564 {
565 DPRINT("Large Index!\n");
566
567 IndexBlockSize = IndexRoot->SizeOfEntry;
568
569 Status = FindAttribute(Vcb, MftRecord, AttributeBitmap, L"$I30", 4, &IndexBitmapCtx);
570 if (!NT_SUCCESS(Status))
571 {
572 DPRINT1("Corrupted filesystem!\n");
573 ExFreePoolWithTag(MftRecord, TAG_NTFS);
574 return Status;
575 }
576 BitmapDataSize = AttributeDataLength(&IndexBitmapCtx->Record);
577 DPRINT("BitmapDataSize: %x\n", (ULONG)BitmapDataSize);
578 if(BitmapDataSize <= 0xFFFFFFFF)
579 BitmapData = ExAllocatePoolWithTag(NonPagedPool, (ULONG)BitmapDataSize, TAG_NTFS);
580 else
581 BitmapData = NULL;
582
583 if (BitmapData == NULL)
584 {
585 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
586 ExFreePoolWithTag(MftRecord, TAG_NTFS);
587 return STATUS_INSUFFICIENT_RESOURCES;
588 }
589 ReadAttribute(Vcb, IndexBitmapCtx, 0, BitmapData, (ULONG)BitmapDataSize);
590 ReleaseAttributeContext(IndexBitmapCtx);
591
592 Status = FindAttribute(Vcb, MftRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationCtx);
593 if (!NT_SUCCESS(Status))
594 {
595 DPRINT("Corrupted filesystem!\n");
596 ExFreePoolWithTag(BitmapData, TAG_NTFS);
597 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
598 ExFreePoolWithTag(MftRecord, TAG_NTFS);
599 return Status;
600 }
601 IndexAllocationSize = AttributeDataLength(&IndexAllocationCtx->Record);
602
603 RecordOffset = 0;
604
605 for (;;)
606 {
607 DPRINT("RecordOffset: %x IndexAllocationSize: %x\n", RecordOffset, IndexAllocationSize);
608 for (; RecordOffset < IndexAllocationSize;)
609 {
610 UCHAR Bit = 1 << ((RecordOffset / IndexBlockSize) & 7);
611 ULONG Byte = (RecordOffset / IndexBlockSize) >> 3;
612 if ((BitmapData[Byte] & Bit))
613 break;
614 RecordOffset += IndexBlockSize;
615 }
616
617 if (RecordOffset >= IndexAllocationSize)
618 {
619 break;
620 }
621
622 ReadAttribute(Vcb, IndexAllocationCtx, RecordOffset, IndexRecord, IndexBlockSize);
623
624 if (!NT_SUCCESS(FixupUpdateSequenceArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs)))
625 {
626 break;
627 }
628
629 /* FIXME */
630 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)(IndexRecord + 0x18 + *(USHORT *)(IndexRecord + 0x18));
631 IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)(IndexRecord + IndexBlockSize);
632
633 while (IndexEntry < IndexEntryEnd &&
634 !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
635 {
636 if (CurrentEntry >= *FirstEntry && CompareFileName(FileName, IndexEntry, DirSearch))
637 {
638 DPRINT("File found\n");
639 *OutMFTIndex = IndexEntry->Data.Directory.IndexedFile;
640 *FirstEntry = CurrentEntry;
641 RtlCopyMemory(OutName, IndexEntry->FileName.Name, IndexEntry->FileName.NameLength);
642 OutName[IndexEntry->FileName.NameLength / sizeof(WCHAR)] = UNICODE_NULL;
643 ExFreePoolWithTag(BitmapData, TAG_NTFS);
644 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
645 ExFreePoolWithTag(MftRecord, TAG_NTFS);
646 ReleaseAttributeContext(IndexAllocationCtx);
647 return STATUS_SUCCESS;
648 }
649
650 ++CurrentEntry;
651 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
652 }
653
654 RecordOffset += IndexBlockSize;
655 }
656
657 ReleaseAttributeContext(IndexAllocationCtx);
658 ExFreePoolWithTag(BitmapData, TAG_NTFS);
659 }
660
661 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
662 }
663 else
664 {
665 DPRINT("Can't read MFT record\n");
666 }
667 ExFreePoolWithTag(MftRecord, TAG_NTFS);
668
669 return STATUS_OBJECT_PATH_NOT_FOUND;
670 }
671
672 NTSTATUS
673 NtfsLookupFileAt(PDEVICE_EXTENSION Vcb,
674 PUNICODE_STRING PathName,
675 PFILE_RECORD_HEADER *FileRecord,
676 PNTFS_ATTR_CONTEXT *DataContext,
677 PULONGLONG MFTIndex,
678 ULONGLONG CurrentMFTIndex)
679 {
680 UNICODE_STRING Current, Remaining;
681 NTSTATUS Status;
682 WCHAR FoundName[MAX_PATH + 1];
683 ULONG FirstEntry = 0, Length;
684
685 DPRINT1("NtfsLookupFileAt(%p, %wZ, %p, %p, %I64x)\n", Vcb, PathName, FileRecord, DataContext, CurrentMFTIndex);
686
687 FsRtlDissectName(*PathName, &Current, &Remaining);
688
689 while (Current.Length != 0)
690 {
691 DPRINT1("Lookup: %wZ\n", &Current);
692
693 Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, &Current, &FirstEntry, FALSE, &CurrentMFTIndex, FoundName);
694 if (!NT_SUCCESS(Status))
695 {
696 return Status;
697 }
698
699 FsRtlDissectName(*PathName, &Current, &Remaining);
700 }
701
702 *FileRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
703 if (*FileRecord == NULL)
704 {
705 DPRINT("NtfsLookupFileAt: Can't allocate MFT record\n");
706 return STATUS_INSUFFICIENT_RESOURCES;
707 }
708
709 Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord);
710 if (!NT_SUCCESS(Status))
711 {
712 DPRINT("NtfsLookupFileAt: Can't read MFT record\n");
713 ExFreePoolWithTag(FileRecord, TAG_NTFS);
714 return Status;
715 }
716
717 Length = wcslen(FoundName) * sizeof(WCHAR);
718
719 Status = FindAttribute(Vcb, *FileRecord, AttributeData, FoundName, Length, DataContext);
720 if (!NT_SUCCESS(Status))
721 {
722 DPRINT("NtfsLookupFileAt: Can't find data attribute\n");
723 ExFreePoolWithTag(FileRecord, TAG_NTFS);
724 return Status;
725 }
726
727 *MFTIndex = CurrentMFTIndex;
728
729 return STATUS_SUCCESS;
730 }
731
732 NTSTATUS
733 NtfsLookupFile(PDEVICE_EXTENSION Vcb,
734 PUNICODE_STRING PathName,
735 PFILE_RECORD_HEADER *FileRecord,
736 PNTFS_ATTR_CONTEXT *DataContext,
737 PULONGLONG MFTIndex)
738 {
739 return NtfsLookupFileAt(Vcb, PathName, FileRecord, DataContext, MFTIndex, NTFS_FILE_ROOT);
740 }
741
742 NTSTATUS
743 NtfsFindFileAt(PDEVICE_EXTENSION Vcb,
744 PUNICODE_STRING SearchPattern,
745 PULONG FirstEntry,
746 PFILE_RECORD_HEADER *FileRecord,
747 PNTFS_ATTR_CONTEXT *DataContext,
748 PULONGLONG MFTIndex,
749 ULONGLONG CurrentMFTIndex)
750 {
751 NTSTATUS Status;
752 WCHAR FoundName[MAX_PATH + 1];
753 ULONG Length;
754
755 DPRINT1("NtfsFindFileAt(%p, %wZ, %p, %p, %p, %p, %I64x)\n", Vcb, SearchPattern, FirstEntry, FileRecord, DataContext, MFTIndex, CurrentMFTIndex);
756
757 Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, SearchPattern, FirstEntry, TRUE, &CurrentMFTIndex, FoundName);
758 if (!NT_SUCCESS(Status))
759 {
760 return Status;
761 }
762
763 *FileRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
764 if (*FileRecord == NULL)
765 {
766 DPRINT("NtfsFindFileAt: Can't allocate MFT record\n");
767 return STATUS_INSUFFICIENT_RESOURCES;
768 }
769
770 Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord);
771 if (!NT_SUCCESS(Status))
772 {
773 DPRINT("NtfsFindFileAt: Can't read MFT record\n");
774 ExFreePoolWithTag(FileRecord, TAG_NTFS);
775 return Status;
776 }
777
778 Length = wcslen(FoundName) * sizeof(WCHAR);
779
780 Status = FindAttribute(Vcb, *FileRecord, AttributeData, FoundName, Length, DataContext);
781 if (!NT_SUCCESS(Status))
782 {
783 DPRINT("NtfsFindFileAt: Can't find data attribute\n");
784 ExFreePoolWithTag(FileRecord, TAG_NTFS);
785 return Status;
786 }
787
788 *MFTIndex = CurrentMFTIndex;
789
790 return STATUS_SUCCESS;
791 }
792
793 /* EOF */