[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 DPRINT1("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: %u 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 PWSTR OutName)
493 {
494 PFILE_RECORD_HEADER MftRecord;
495 //ULONG Magic;
496 PNTFS_ATTR_CONTEXT IndexRootCtx;
497 PNTFS_ATTR_CONTEXT IndexBitmapCtx;
498 PNTFS_ATTR_CONTEXT IndexAllocationCtx;
499 PINDEX_ROOT_ATTRIBUTE IndexRoot;
500 PINDEX_BUFFER IndexBuffer;
501 ULONGLONG BitmapDataSize;
502 ULONGLONG IndexAllocationSize;
503 PCHAR BitmapData;
504 PCHAR IndexRecord;
505 PINDEX_ENTRY_ATTRIBUTE IndexEntry, IndexEntryEnd;
506 ULONG RecordOffset;
507 ULONG IndexBlockSize;
508 NTSTATUS Status;
509 ULONG CurrentEntry = 0;
510
511 DPRINT1("NtfsFindMftRecord(%p, %I64d, %wZ, %p, %u, %p, %p)\n", Vcb, MFTIndex, FileName, FirstEntry, DirSearch, OutMFTIndex, OutName);
512
513 MftRecord = ExAllocatePoolWithTag(NonPagedPool,
514 Vcb->NtfsInfo.BytesPerFileRecord,
515 TAG_NTFS);
516 if (MftRecord == NULL)
517 {
518 return STATUS_INSUFFICIENT_RESOURCES;
519 }
520
521 if (NT_SUCCESS(ReadFileRecord(Vcb, MFTIndex, MftRecord)))
522 {
523 //Magic = MftRecord->Magic;
524
525 Status = FindAttribute(Vcb, MftRecord, AttributeIndexRoot, L"$I30", 4, &IndexRootCtx);
526 if (!NT_SUCCESS(Status))
527 {
528 ExFreePoolWithTag(MftRecord, TAG_NTFS);
529 return Status;
530 }
531
532 IndexRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerIndexRecord, TAG_NTFS);
533 if (IndexRecord == NULL)
534 {
535 ExFreePoolWithTag(MftRecord, TAG_NTFS);
536 return STATUS_INSUFFICIENT_RESOURCES;
537 }
538
539 ReadAttribute(Vcb, IndexRootCtx, 0, IndexRecord, Vcb->NtfsInfo.BytesPerIndexRecord);
540 IndexRoot = (PINDEX_ROOT_ATTRIBUTE)IndexRecord;
541 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)&IndexRoot->Header + IndexRoot->Header.FirstEntryOffset);
542 /* Index root is always resident. */
543 IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)(IndexRecord + IndexRootCtx->Record.Resident.ValueLength);
544 ReleaseAttributeContext(IndexRootCtx);
545
546 DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb->NtfsInfo.BytesPerIndexRecord, IndexRoot->SizeOfEntry);
547
548 while (IndexEntry < IndexEntryEnd &&
549 !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
550 {
551 if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) > 0x10 &&
552 CurrentEntry >= *FirstEntry &&
553 CompareFileName(FileName, IndexEntry, DirSearch))
554 {
555 *OutMFTIndex = (IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK);
556 *FirstEntry = CurrentEntry;
557 RtlCopyMemory(OutName, IndexEntry->FileName.Name, IndexEntry->FileName.NameLength);
558 OutName[IndexEntry->FileName.NameLength / sizeof(WCHAR)] = UNICODE_NULL;
559 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
560 ExFreePoolWithTag(MftRecord, TAG_NTFS);
561 return STATUS_SUCCESS;
562 }
563
564 ++CurrentEntry;
565 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
566 }
567
568 if (IndexRoot->Header.Flags & INDEX_ROOT_LARGE)
569 {
570 DPRINT("Large Index!\n");
571
572 IndexBlockSize = IndexRoot->SizeOfEntry;
573
574 Status = FindAttribute(Vcb, MftRecord, AttributeBitmap, L"$I30", 4, &IndexBitmapCtx);
575 if (!NT_SUCCESS(Status))
576 {
577 DPRINT1("Corrupted filesystem!\n");
578 ExFreePoolWithTag(MftRecord, TAG_NTFS);
579 return Status;
580 }
581 BitmapDataSize = AttributeDataLength(&IndexBitmapCtx->Record);
582 DPRINT("BitmapDataSize: %x\n", (ULONG)BitmapDataSize);
583 if(BitmapDataSize <= 0xFFFFFFFF)
584 BitmapData = ExAllocatePoolWithTag(NonPagedPool, (ULONG)BitmapDataSize, TAG_NTFS);
585 else
586 BitmapData = NULL;
587
588 if (BitmapData == NULL)
589 {
590 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
591 ExFreePoolWithTag(MftRecord, TAG_NTFS);
592 return STATUS_INSUFFICIENT_RESOURCES;
593 }
594 ReadAttribute(Vcb, IndexBitmapCtx, 0, BitmapData, (ULONG)BitmapDataSize);
595 ReleaseAttributeContext(IndexBitmapCtx);
596
597 Status = FindAttribute(Vcb, MftRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationCtx);
598 if (!NT_SUCCESS(Status))
599 {
600 DPRINT("Corrupted filesystem!\n");
601 ExFreePoolWithTag(BitmapData, TAG_NTFS);
602 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
603 ExFreePoolWithTag(MftRecord, TAG_NTFS);
604 return Status;
605 }
606 IndexAllocationSize = AttributeDataLength(&IndexAllocationCtx->Record);
607
608 RecordOffset = 0;
609
610 for (;;)
611 {
612 DPRINT("RecordOffset: %x IndexAllocationSize: %x\n", RecordOffset, IndexAllocationSize);
613 for (; RecordOffset < IndexAllocationSize;)
614 {
615 UCHAR Bit = 1 << ((RecordOffset / IndexBlockSize) & 7);
616 ULONG Byte = (RecordOffset / IndexBlockSize) >> 3;
617 if ((BitmapData[Byte] & Bit))
618 break;
619 RecordOffset += IndexBlockSize;
620 }
621
622 if (RecordOffset >= IndexAllocationSize)
623 {
624 break;
625 }
626
627 ReadAttribute(Vcb, IndexAllocationCtx, RecordOffset, IndexRecord, IndexBlockSize);
628
629 if (!NT_SUCCESS(FixupUpdateSequenceArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs)))
630 {
631 break;
632 }
633
634 IndexBuffer = (PINDEX_BUFFER)IndexRecord;
635 ASSERT(IndexBuffer->Ntfs.Type == 'XDNI');
636 ASSERT(IndexBuffer->Header.AllocatedSize + 0x18 == IndexBlockSize);
637 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)(&IndexBuffer->Header + IndexBuffer->Header.FirstEntryOffset);
638 IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)(&IndexBuffer->Header + IndexBuffer->Header.TotalSizeOfEntries);
639 //ASSERT(IndexEntryEnd <= (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexBuffer + IndexBlockSize)); FIXME: Why doesn't it work?
640
641 while (IndexEntry < IndexEntryEnd &&
642 !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
643 {
644 if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) > 0x10 &&
645 CurrentEntry >= *FirstEntry &&
646 CompareFileName(FileName, IndexEntry, DirSearch))
647 {
648 DPRINT("File found\n");
649 *OutMFTIndex = (IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK);
650 *FirstEntry = CurrentEntry;
651 RtlCopyMemory(OutName, IndexEntry->FileName.Name, IndexEntry->FileName.NameLength);
652 OutName[IndexEntry->FileName.NameLength / sizeof(WCHAR)] = UNICODE_NULL;
653 ExFreePoolWithTag(BitmapData, TAG_NTFS);
654 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
655 ExFreePoolWithTag(MftRecord, TAG_NTFS);
656 ReleaseAttributeContext(IndexAllocationCtx);
657 return STATUS_SUCCESS;
658 }
659
660 ++CurrentEntry;
661 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
662 }
663
664 RecordOffset += IndexBlockSize;
665 }
666
667 ReleaseAttributeContext(IndexAllocationCtx);
668 ExFreePoolWithTag(BitmapData, TAG_NTFS);
669 }
670
671 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
672 }
673 else
674 {
675 DPRINT("Can't read MFT record\n");
676 }
677 ExFreePoolWithTag(MftRecord, TAG_NTFS);
678
679 return STATUS_OBJECT_PATH_NOT_FOUND;
680 }
681
682 NTSTATUS
683 NtfsLookupFileAt(PDEVICE_EXTENSION Vcb,
684 PUNICODE_STRING PathName,
685 PFILE_RECORD_HEADER *FileRecord,
686 PNTFS_ATTR_CONTEXT *DataContext,
687 PULONGLONG MFTIndex,
688 ULONGLONG CurrentMFTIndex)
689 {
690 UNICODE_STRING Current, Remaining;
691 NTSTATUS Status;
692 WCHAR FoundName[MAX_PATH + 1];
693 ULONG FirstEntry = 0, Length;
694
695 DPRINT1("NtfsLookupFileAt(%p, %wZ, %p, %p, %I64x)\n", Vcb, PathName, FileRecord, DataContext, CurrentMFTIndex);
696
697 FsRtlDissectName(*PathName, &Current, &Remaining);
698
699 while (Current.Length != 0)
700 {
701 DPRINT1("Current: %wZ\n", &Current);
702
703 Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, &Current, &FirstEntry, FALSE, &CurrentMFTIndex, FoundName);
704 if (!NT_SUCCESS(Status))
705 {
706 return Status;
707 }
708
709 if (Remaining.Length == 0)
710 return STATUS_OBJECT_PATH_NOT_FOUND;
711
712 FsRtlDissectName(Current, &Current, &Remaining);
713 }
714
715 *FileRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
716 if (*FileRecord == NULL)
717 {
718 DPRINT("NtfsLookupFileAt: Can't allocate MFT record\n");
719 return STATUS_INSUFFICIENT_RESOURCES;
720 }
721
722 Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord);
723 if (!NT_SUCCESS(Status))
724 {
725 DPRINT("NtfsLookupFileAt: Can't read MFT record\n");
726 ExFreePoolWithTag(*FileRecord, TAG_NTFS);
727 return Status;
728 }
729
730 Length = wcslen(FoundName) * sizeof(WCHAR);
731
732 Status = FindAttribute(Vcb, *FileRecord, AttributeData, FoundName, Length, DataContext);
733 if (!NT_SUCCESS(Status))
734 {
735 DPRINT("NtfsLookupFileAt: Can't find data attribute\n");
736 ExFreePoolWithTag(*FileRecord, TAG_NTFS);
737 return Status;
738 }
739
740 *MFTIndex = CurrentMFTIndex;
741
742 return STATUS_SUCCESS;
743 }
744
745 NTSTATUS
746 NtfsLookupFile(PDEVICE_EXTENSION Vcb,
747 PUNICODE_STRING PathName,
748 PFILE_RECORD_HEADER *FileRecord,
749 PNTFS_ATTR_CONTEXT *DataContext,
750 PULONGLONG MFTIndex)
751 {
752 return NtfsLookupFileAt(Vcb, PathName, FileRecord, DataContext, MFTIndex, NTFS_FILE_ROOT);
753 }
754
755 NTSTATUS
756 NtfsFindFileAt(PDEVICE_EXTENSION Vcb,
757 PUNICODE_STRING SearchPattern,
758 PULONG FirstEntry,
759 PFILE_RECORD_HEADER *FileRecord,
760 PNTFS_ATTR_CONTEXT *DataContext,
761 PULONGLONG MFTIndex,
762 ULONGLONG CurrentMFTIndex)
763 {
764 NTSTATUS Status;
765 WCHAR FoundName[MAX_PATH + 1];
766 ULONG Length;
767
768 DPRINT1("NtfsFindFileAt(%p, %wZ, %p, %p, %p, %p, %I64x)\n", Vcb, SearchPattern, FirstEntry, FileRecord, DataContext, MFTIndex, CurrentMFTIndex);
769
770 Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, SearchPattern, FirstEntry, TRUE, &CurrentMFTIndex, FoundName);
771 if (!NT_SUCCESS(Status))
772 {
773 return Status;
774 }
775
776 *FileRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
777 if (*FileRecord == NULL)
778 {
779 DPRINT("NtfsFindFileAt: Can't allocate MFT record\n");
780 return STATUS_INSUFFICIENT_RESOURCES;
781 }
782
783 Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord);
784 if (!NT_SUCCESS(Status))
785 {
786 DPRINT("NtfsFindFileAt: Can't read MFT record\n");
787 ExFreePoolWithTag(*FileRecord, TAG_NTFS);
788 return Status;
789 }
790
791 Length = wcslen(FoundName) * sizeof(WCHAR);
792
793 Status = FindAttribute(Vcb, *FileRecord, AttributeData, FoundName, Length, DataContext);
794 if (!NT_SUCCESS(Status))
795 {
796 DPRINT("NtfsFindFileAt: Can't find data attribute\n");
797 ExFreePoolWithTag(*FileRecord, TAG_NTFS);
798 return Status;
799 }
800
801 *MFTIndex = CurrentMFTIndex;
802
803 return STATUS_SUCCESS;
804 }
805
806 /* EOF */