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