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