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