1294264e696be1d94ec6d1ac3e423008856b5b5d
[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 * Trevor Thompson
28 */
29
30 /* INCLUDES *****************************************************************/
31
32 #include "ntfs.h"
33
34 #define NDEBUG
35 #undef NDEBUG
36 #include <debug.h>
37
38 /* FUNCTIONS ****************************************************************/
39
40 PNTFS_ATTR_CONTEXT
41 PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord)
42 {
43 PNTFS_ATTR_CONTEXT Context;
44
45 Context = ExAllocatePoolWithTag(NonPagedPool,
46 FIELD_OFFSET(NTFS_ATTR_CONTEXT, Record) + AttrRecord->Length,
47 TAG_NTFS);
48 RtlCopyMemory(&Context->Record, AttrRecord, AttrRecord->Length);
49 if (AttrRecord->IsNonResident)
50 {
51 LONGLONG DataRunOffset;
52 ULONGLONG DataRunLength;
53
54 Context->CacheRun = (PUCHAR)&Context->Record + Context->Record.NonResident.MappingPairsOffset;
55 Context->CacheRunOffset = 0;
56 Context->CacheRun = DecodeRun(Context->CacheRun, &DataRunOffset, &DataRunLength);
57 Context->CacheRunLength = DataRunLength;
58 if (DataRunOffset != -1)
59 {
60 /* Normal run. */
61 Context->CacheRunStartLCN =
62 Context->CacheRunLastLCN = DataRunOffset;
63 }
64 else
65 {
66 /* Sparse run. */
67 Context->CacheRunStartLCN = -1;
68 Context->CacheRunLastLCN = 0;
69 }
70 Context->CacheRunCurrentOffset = 0;
71 }
72
73 return Context;
74 }
75
76
77 VOID
78 ReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context)
79 {
80 ExFreePoolWithTag(Context, TAG_NTFS);
81 }
82
83
84 NTSTATUS
85 FindAttribute(PDEVICE_EXTENSION Vcb,
86 PFILE_RECORD_HEADER MftRecord,
87 ULONG Type,
88 PCWSTR Name,
89 ULONG NameLength,
90 PNTFS_ATTR_CONTEXT * AttrCtx)
91 {
92 BOOLEAN Found;
93 NTSTATUS Status;
94 FIND_ATTR_CONTXT Context;
95 PNTFS_ATTR_RECORD Attribute;
96
97 DPRINT("FindAttribute(%p, %p, 0x%x, %S, %u, %p)\n", Vcb, MftRecord, Type, Name, NameLength, AttrCtx);
98
99 Found = FALSE;
100 Status = FindFirstAttribute(&Context, Vcb, MftRecord, FALSE, &Attribute);
101 while (NT_SUCCESS(Status))
102 {
103 if (Attribute->Type == Type && Attribute->NameLength == NameLength)
104 {
105 if (NameLength != 0)
106 {
107 PWCHAR AttrName;
108
109 AttrName = (PWCHAR)((PCHAR)Attribute + Attribute->NameOffset);
110 DPRINT("%.*S, %.*S\n", Attribute->NameLength, AttrName, NameLength, Name);
111 if (RtlCompareMemory(AttrName, Name, NameLength << 1) == (NameLength << 1))
112 {
113 Found = TRUE;
114 }
115 }
116 else
117 {
118 Found = TRUE;
119 }
120
121 if (Found)
122 {
123 /* Found it, fill up the context and return. */
124 DPRINT("Found context\n");
125 *AttrCtx = PrepareAttributeContext(Attribute);
126 FindCloseAttribute(&Context);
127 return STATUS_SUCCESS;
128 }
129 }
130
131 Status = FindNextAttribute(&Context, &Attribute);
132 }
133
134 FindCloseAttribute(&Context);
135 return STATUS_OBJECT_NAME_NOT_FOUND;
136 }
137
138
139 ULONG
140 AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord)
141 {
142 if (AttrRecord->IsNonResident)
143 return AttrRecord->NonResident.AllocatedSize;
144 else
145 return AttrRecord->Resident.ValueLength;
146 }
147
148
149 ULONGLONG
150 AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord)
151 {
152 if (AttrRecord->IsNonResident)
153 return AttrRecord->NonResident.DataSize;
154 else
155 return AttrRecord->Resident.ValueLength;
156 }
157
158
159 ULONG
160 ReadAttribute(PDEVICE_EXTENSION Vcb,
161 PNTFS_ATTR_CONTEXT Context,
162 ULONGLONG Offset,
163 PCHAR Buffer,
164 ULONG Length)
165 {
166 ULONGLONG LastLCN;
167 PUCHAR DataRun;
168 LONGLONG DataRunOffset;
169 ULONGLONG DataRunLength;
170 LONGLONG DataRunStartLCN;
171 ULONGLONG CurrentOffset;
172 ULONG ReadLength;
173 ULONG AlreadyRead;
174 NTSTATUS Status;
175
176 if (!Context->Record.IsNonResident)
177 {
178 if (Offset > Context->Record.Resident.ValueLength)
179 return 0;
180 if (Offset + Length > Context->Record.Resident.ValueLength)
181 Length = (ULONG)(Context->Record.Resident.ValueLength - Offset);
182 RtlCopyMemory(Buffer, (PCHAR)&Context->Record + Context->Record.Resident.ValueOffset + Offset, Length);
183 return Length;
184 }
185
186 /*
187 * Non-resident attribute
188 */
189
190 /*
191 * I. Find the corresponding start data run.
192 */
193
194 AlreadyRead = 0;
195
196 // FIXME: Cache seems to be non-working. Disable it for now
197 //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
198 if (0)
199 {
200 DataRun = Context->CacheRun;
201 LastLCN = Context->CacheRunLastLCN;
202 DataRunStartLCN = Context->CacheRunStartLCN;
203 DataRunLength = Context->CacheRunLength;
204 CurrentOffset = Context->CacheRunCurrentOffset;
205 }
206 else
207 {
208 LastLCN = 0;
209 DataRun = (PUCHAR)&Context->Record + Context->Record.NonResident.MappingPairsOffset;
210 CurrentOffset = 0;
211
212 while (1)
213 {
214 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
215 if (DataRunOffset != -1)
216 {
217 /* Normal data run. */
218 DataRunStartLCN = LastLCN + DataRunOffset;
219 LastLCN = DataRunStartLCN;
220 }
221 else
222 {
223 /* Sparse data run. */
224 DataRunStartLCN = -1;
225 }
226
227 if (Offset >= CurrentOffset &&
228 Offset < CurrentOffset + (DataRunLength * Vcb->NtfsInfo.BytesPerCluster))
229 {
230 break;
231 }
232
233 if (*DataRun == 0)
234 {
235 return AlreadyRead;
236 }
237
238 CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
239 }
240 }
241
242 /*
243 * II. Go through the run list and read the data
244 */
245
246 ReadLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset), Length);
247 if (DataRunStartLCN == -1)
248 {
249 RtlZeroMemory(Buffer, ReadLength);
250 Status = STATUS_SUCCESS;
251 }
252 else
253 {
254 Status = NtfsReadDisk(Vcb->StorageDevice,
255 DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster + Offset - CurrentOffset,
256 ReadLength,
257 Vcb->NtfsInfo.BytesPerSector,
258 (PVOID)Buffer,
259 FALSE);
260 }
261 if (NT_SUCCESS(Status))
262 {
263 Length -= ReadLength;
264 Buffer += ReadLength;
265 AlreadyRead += ReadLength;
266
267 if (ReadLength == DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset))
268 {
269 CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
270 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
271 if (DataRunOffset != (ULONGLONG)-1)
272 {
273 DataRunStartLCN = LastLCN + DataRunOffset;
274 LastLCN = DataRunStartLCN;
275 }
276 else
277 DataRunStartLCN = -1;
278 }
279
280 while (Length > 0)
281 {
282 ReadLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster, Length);
283 if (DataRunStartLCN == -1)
284 RtlZeroMemory(Buffer, ReadLength);
285 else
286 {
287 Status = NtfsReadDisk(Vcb->StorageDevice,
288 DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster,
289 ReadLength,
290 Vcb->NtfsInfo.BytesPerSector,
291 (PVOID)Buffer,
292 FALSE);
293 if (!NT_SUCCESS(Status))
294 break;
295 }
296
297 Length -= ReadLength;
298 Buffer += ReadLength;
299 AlreadyRead += ReadLength;
300
301 /* We finished this request, but there still data in this data run. */
302 if (Length == 0 && ReadLength != DataRunLength * Vcb->NtfsInfo.BytesPerCluster)
303 break;
304
305 /*
306 * Go to next run in the list.
307 */
308
309 if (*DataRun == 0)
310 break;
311 CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
312 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
313 if (DataRunOffset != -1)
314 {
315 /* Normal data run. */
316 DataRunStartLCN = LastLCN + DataRunOffset;
317 LastLCN = DataRunStartLCN;
318 }
319 else
320 {
321 /* Sparse data run. */
322 DataRunStartLCN = -1;
323 }
324 } /* while */
325
326 } /* if Disk */
327
328 Context->CacheRun = DataRun;
329 Context->CacheRunOffset = Offset + AlreadyRead;
330 Context->CacheRunStartLCN = DataRunStartLCN;
331 Context->CacheRunLength = DataRunLength;
332 Context->CacheRunLastLCN = LastLCN;
333 Context->CacheRunCurrentOffset = CurrentOffset;
334
335 return AlreadyRead;
336 }
337
338
339 /**
340 * @name WriteAttribute
341 * @implemented
342 *
343 * Writes an NTFS attribute to the disk. It presently borrows a lot of code from ReadAttribute(),
344 * and it still needs more documentation / cleaning up.
345 *
346 * @param Vcb
347 * Volume Control Block indicating which volume to write the attribute to
348 *
349 * @param Context
350 * Pointer to an NTFS_ATTR_CONTEXT that has information about the attribute
351 *
352 * @param Offset
353 * Offset, in bytes, from the beginning of the attribute indicating where to start
354 * writing data
355 *
356 * @param Buffer
357 * The data that's being written to the device
358 *
359 * @param Length
360 * How much data will be written, in bytes
361 *
362 * @param RealLengthWritten
363 * Pointer to a ULONG which will receive how much data was written, in bytes
364 *
365 * @return
366 * STATUS_SUCCESS if successful, an error code otherwise. STATUS_NOT_IMPLEMENTED if
367 * writing to a sparse file.
368 *
369 * @remarks Note that in this context the word "attribute" isn't referring read-only, hidden,
370 * etc. - the file's data is actually stored in an attribute in NTFS parlance.
371 *
372 */
373
374 NTSTATUS
375 WriteAttribute(PDEVICE_EXTENSION Vcb,
376 PNTFS_ATTR_CONTEXT Context,
377 ULONGLONG Offset,
378 const PUCHAR Buffer,
379 ULONG Length,
380 PULONG RealLengthWritten)
381 {
382 ULONGLONG LastLCN;
383 PUCHAR DataRun;
384 LONGLONG DataRunOffset;
385 ULONGLONG DataRunLength;
386 LONGLONG DataRunStartLCN;
387 ULONGLONG CurrentOffset;
388 ULONG WriteLength;
389 NTSTATUS Status;
390 PUCHAR SourceBuffer = Buffer;
391 LONGLONG StartingOffset;
392
393 DPRINT("WriteAttribute(%p, %p, %I64U, %p, %lu)\n", Vcb, Context, Offset, Buffer, Length);
394
395 // is this a resident attribute?
396 if (!Context->Record.IsNonResident)
397 {
398 DPRINT1("FIXME: Writing to resident NTFS records (small files) is not supported at this time.\n");
399 // (TODO: This should be really easy to implement)
400
401 /* LeftOver code from ReadAttribute(), may be helpful:
402 if (Offset > Context->Record.Resident.ValueLength)
403 return 0;
404 if (Offset + Length > Context->Record.Resident.ValueLength)
405 Length = (ULONG)(Context->Record.Resident.ValueLength - Offset);
406 RtlCopyMemory(Buffer, (PCHAR)&Context->Record + Context->Record.Resident.ValueOffset + Offset, Length);
407 return Length;*/
408
409 return STATUS_NOT_IMPLEMENTED; // until we implement it
410 }
411
412 // This is a non-resident attribute.
413
414 // I. Find the corresponding start data run.
415
416 *RealLengthWritten = 0;
417
418 // FIXME: Cache seems to be non-working. Disable it for now
419 //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
420 /*if (0)
421 {
422 DataRun = Context->CacheRun;
423 LastLCN = Context->CacheRunLastLCN;
424 DataRunStartLCN = Context->CacheRunStartLCN;
425 DataRunLength = Context->CacheRunLength;
426 CurrentOffset = Context->CacheRunCurrentOffset;
427 }
428 else*/
429 {
430 LastLCN = 0;
431 DataRun = (PUCHAR)&Context->Record + Context->Record.NonResident.MappingPairsOffset;
432 CurrentOffset = 0;
433
434 while (1)
435 {
436 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
437 if (DataRunOffset != -1)
438 {
439 // Normal data run.
440 // DPRINT1("Writing to normal data run, LastLCN %I64u DataRunOffset %I64d\n", LastLCN, DataRunOffset);
441 DataRunStartLCN = LastLCN + DataRunOffset;
442 LastLCN = DataRunStartLCN;
443 }
444 else
445 {
446 // Sparse data run. We can't support writing to sparse files yet
447 // (it may require increasing the allocation size).
448 DataRunStartLCN = -1;
449 DPRINT1("FIXME: Writing to sparse files is not supported yet!\n");
450 return STATUS_NOT_IMPLEMENTED;
451 }
452
453 // Have we reached the data run we're trying to write to?
454 if (Offset >= CurrentOffset &&
455 Offset < CurrentOffset + (DataRunLength * Vcb->NtfsInfo.BytesPerCluster))
456 {
457 break;
458 }
459
460 if (*DataRun == 0)
461 {
462 // We reached the last assigned cluster
463 // TODO: assign new clusters to the end of the file.
464 // (Presently, this code will never be reached, the write should have already failed by now)
465 return STATUS_END_OF_FILE;
466 }
467
468 CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
469 }
470 }
471
472 // II. Go through the run list and write the data
473
474 /* REVIEWME -- As adapted from NtfsReadAttribute():
475 We seem to be making a special case for the first applicable data run, but I'm not sure why.
476 Does it have something to do with (not) caching? Is this strategy equally applicable to writing? */
477
478 WriteLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset), Length);
479
480 StartingOffset = DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster + Offset - CurrentOffset;
481
482 // Write the data to the disk
483 Status = NtfsWriteDisk(Vcb->StorageDevice,
484 StartingOffset,
485 WriteLength,
486 Vcb->NtfsInfo.BytesPerSector,
487 (PVOID)SourceBuffer);
488
489 // Did the write fail?
490 if (!NT_SUCCESS(Status))
491 {
492 Context->CacheRun = DataRun;
493 Context->CacheRunOffset = Offset;
494 Context->CacheRunStartLCN = DataRunStartLCN;
495 Context->CacheRunLength = DataRunLength;
496 Context->CacheRunLastLCN = LastLCN;
497 Context->CacheRunCurrentOffset = CurrentOffset;
498
499 return Status;
500 }
501
502 Length -= WriteLength;
503 SourceBuffer += WriteLength;
504 *RealLengthWritten += WriteLength;
505
506 // Did we write to the end of the data run?
507 if (WriteLength == DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset))
508 {
509 // Advance to the next data run
510 CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
511 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
512
513 if (DataRunOffset != (ULONGLONG)-1)
514 {
515 DataRunStartLCN = LastLCN + DataRunOffset;
516 LastLCN = DataRunStartLCN;
517 }
518 else
519 DataRunStartLCN = -1;
520 }
521
522 // Do we have more data to write?
523 while (Length > 0)
524 {
525 // Make sure we don't write past the end of the current data run
526 WriteLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster, Length);
527
528 // Are we dealing with a sparse data run?
529 if (DataRunStartLCN == -1)
530 {
531 DPRINT1("FIXME: Don't know how to write to sparse files yet! (DataRunStartLCN == -1)\n");
532 return STATUS_NOT_IMPLEMENTED;
533 }
534 else
535 {
536 // write the data to the disk
537 Status = NtfsWriteDisk(Vcb->StorageDevice,
538 DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster,
539 WriteLength,
540 Vcb->NtfsInfo.BytesPerSector,
541 (PVOID)SourceBuffer);
542 if (!NT_SUCCESS(Status))
543 break;
544 }
545
546 Length -= WriteLength;
547 SourceBuffer += WriteLength;
548 *RealLengthWritten += WriteLength;
549
550 // We finished this request, but there's still data in this data run.
551 if (Length == 0 && WriteLength != DataRunLength * Vcb->NtfsInfo.BytesPerCluster)
552 break;
553
554 // Go to next run in the list.
555
556 if (*DataRun == 0)
557 {
558 // that was the last run
559 if (Length > 0)
560 {
561 // Failed sanity check.
562 DPRINT1("Encountered EOF before expected!\n");
563 return STATUS_END_OF_FILE;
564 }
565
566 break;
567 }
568
569 // Advance to the next data run
570 CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
571 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
572 if (DataRunOffset != -1)
573 {
574 // Normal data run.
575 DataRunStartLCN = LastLCN + DataRunOffset;
576 LastLCN = DataRunStartLCN;
577 }
578 else
579 {
580 // Sparse data run.
581 DataRunStartLCN = -1;
582 }
583 } // end while (Length > 0) [more data to write]
584
585 Context->CacheRun = DataRun;
586 Context->CacheRunOffset = Offset + *RealLengthWritten;
587 Context->CacheRunStartLCN = DataRunStartLCN;
588 Context->CacheRunLength = DataRunLength;
589 Context->CacheRunLastLCN = LastLCN;
590 Context->CacheRunCurrentOffset = CurrentOffset;
591
592 return Status;
593 }
594
595 NTSTATUS
596 ReadFileRecord(PDEVICE_EXTENSION Vcb,
597 ULONGLONG index,
598 PFILE_RECORD_HEADER file)
599 {
600 ULONGLONG BytesRead;
601
602 DPRINT("ReadFileRecord(%p, %I64x, %p)\n", Vcb, index, file);
603
604 BytesRead = ReadAttribute(Vcb, Vcb->MFTContext, index * Vcb->NtfsInfo.BytesPerFileRecord, (PCHAR)file, Vcb->NtfsInfo.BytesPerFileRecord);
605 if (BytesRead != Vcb->NtfsInfo.BytesPerFileRecord)
606 {
607 DPRINT1("ReadFileRecord failed: %I64u read, %u expected\n", BytesRead, Vcb->NtfsInfo.BytesPerFileRecord);
608 return STATUS_PARTIAL_COPY;
609 }
610
611 /* Apply update sequence array fixups. */
612 return FixupUpdateSequenceArray(Vcb, &file->Ntfs);
613 }
614
615
616 NTSTATUS
617 FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb,
618 PNTFS_RECORD_HEADER Record)
619 {
620 USHORT *USA;
621 USHORT USANumber;
622 USHORT USACount;
623 USHORT *Block;
624
625 USA = (USHORT*)((PCHAR)Record + Record->UsaOffset);
626 USANumber = *(USA++);
627 USACount = Record->UsaCount - 1; /* Exclude the USA Number. */
628 Block = (USHORT*)((PCHAR)Record + Vcb->NtfsInfo.BytesPerSector - 2);
629
630 while (USACount)
631 {
632 if (*Block != USANumber)
633 {
634 DPRINT1("Mismatch with USA: %u read, %u expected\n" , *Block, USANumber);
635 return STATUS_UNSUCCESSFUL;
636 }
637 *Block = *(USA++);
638 Block = (USHORT*)((PCHAR)Block + Vcb->NtfsInfo.BytesPerSector);
639 USACount--;
640 }
641
642 return STATUS_SUCCESS;
643 }
644
645
646 NTSTATUS
647 ReadLCN(PDEVICE_EXTENSION Vcb,
648 ULONGLONG lcn,
649 ULONG count,
650 PVOID buffer)
651 {
652 LARGE_INTEGER DiskSector;
653
654 DiskSector.QuadPart = lcn;
655
656 return NtfsReadSectors(Vcb->StorageDevice,
657 DiskSector.u.LowPart * Vcb->NtfsInfo.SectorsPerCluster,
658 count * Vcb->NtfsInfo.SectorsPerCluster,
659 Vcb->NtfsInfo.BytesPerSector,
660 buffer,
661 FALSE);
662 }
663
664
665 BOOLEAN
666 CompareFileName(PUNICODE_STRING FileName,
667 PINDEX_ENTRY_ATTRIBUTE IndexEntry,
668 BOOLEAN DirSearch)
669 {
670 BOOLEAN Ret, Alloc = FALSE;
671 UNICODE_STRING EntryName;
672
673 EntryName.Buffer = IndexEntry->FileName.Name;
674 EntryName.Length =
675 EntryName.MaximumLength = IndexEntry->FileName.NameLength * sizeof(WCHAR);
676
677 if (DirSearch)
678 {
679 UNICODE_STRING IntFileName;
680 if (IndexEntry->FileName.NameType != NTFS_FILE_NAME_POSIX)
681 {
682 NT_VERIFY(NT_SUCCESS(RtlUpcaseUnicodeString(&IntFileName, FileName, TRUE)));
683 Alloc = TRUE;
684 }
685 else
686 {
687 IntFileName = *FileName;
688 }
689
690 Ret = FsRtlIsNameInExpression(&IntFileName, &EntryName, (IndexEntry->FileName.NameType != NTFS_FILE_NAME_POSIX), NULL);
691
692 if (Alloc)
693 {
694 RtlFreeUnicodeString(&IntFileName);
695 }
696
697 return Ret;
698 }
699 else
700 {
701 return (RtlCompareUnicodeString(FileName, &EntryName, (IndexEntry->FileName.NameType != NTFS_FILE_NAME_POSIX)) == 0);
702 }
703 }
704
705 #if 0
706 static
707 VOID
708 DumpIndexEntry(PINDEX_ENTRY_ATTRIBUTE IndexEntry)
709 {
710 DPRINT1("Entry: %p\n", IndexEntry);
711 DPRINT1("\tData.Directory.IndexedFile: %I64x\n", IndexEntry->Data.Directory.IndexedFile);
712 DPRINT1("\tLength: %u\n", IndexEntry->Length);
713 DPRINT1("\tKeyLength: %u\n", IndexEntry->KeyLength);
714 DPRINT1("\tFlags: %x\n", IndexEntry->Flags);
715 DPRINT1("\tReserved: %x\n", IndexEntry->Reserved);
716 DPRINT1("\t\tDirectoryFileReferenceNumber: %I64x\n", IndexEntry->FileName.DirectoryFileReferenceNumber);
717 DPRINT1("\t\tCreationTime: %I64u\n", IndexEntry->FileName.CreationTime);
718 DPRINT1("\t\tChangeTime: %I64u\n", IndexEntry->FileName.ChangeTime);
719 DPRINT1("\t\tLastWriteTime: %I64u\n", IndexEntry->FileName.LastWriteTime);
720 DPRINT1("\t\tLastAccessTime: %I64u\n", IndexEntry->FileName.LastAccessTime);
721 DPRINT1("\t\tAllocatedSize: %I64u\n", IndexEntry->FileName.AllocatedSize);
722 DPRINT1("\t\tDataSize: %I64u\n", IndexEntry->FileName.DataSize);
723 DPRINT1("\t\tFileAttributes: %x\n", IndexEntry->FileName.FileAttributes);
724 DPRINT1("\t\tNameLength: %u\n", IndexEntry->FileName.NameLength);
725 DPRINT1("\t\tNameType: %x\n", IndexEntry->FileName.NameType);
726 DPRINT1("\t\tName: %.*S\n", IndexEntry->FileName.NameLength, IndexEntry->FileName.Name);
727 }
728 #endif
729
730 NTSTATUS
731 BrowseIndexEntries(PDEVICE_EXTENSION Vcb,
732 PFILE_RECORD_HEADER MftRecord,
733 PCHAR IndexRecord,
734 ULONG IndexBlockSize,
735 PINDEX_ENTRY_ATTRIBUTE FirstEntry,
736 PINDEX_ENTRY_ATTRIBUTE LastEntry,
737 PUNICODE_STRING FileName,
738 PULONG StartEntry,
739 PULONG CurrentEntry,
740 BOOLEAN DirSearch,
741 ULONGLONG *OutMFTIndex)
742 {
743 NTSTATUS Status;
744 ULONG RecordOffset;
745 PINDEX_ENTRY_ATTRIBUTE IndexEntry;
746 PNTFS_ATTR_CONTEXT IndexAllocationCtx;
747 ULONGLONG IndexAllocationSize;
748 PINDEX_BUFFER IndexBuffer;
749
750 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);
751
752 IndexEntry = FirstEntry;
753 while (IndexEntry < LastEntry &&
754 !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
755 {
756 if ((IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK) > 0x10 &&
757 *CurrentEntry >= *StartEntry &&
758 IndexEntry->FileName.NameType != NTFS_FILE_NAME_DOS &&
759 CompareFileName(FileName, IndexEntry, DirSearch))
760 {
761 *StartEntry = *CurrentEntry;
762 *OutMFTIndex = (IndexEntry->Data.Directory.IndexedFile & NTFS_MFT_MASK);
763 return STATUS_SUCCESS;
764 }
765
766 (*CurrentEntry) += 1;
767 ASSERT(IndexEntry->Length >= sizeof(INDEX_ENTRY_ATTRIBUTE));
768 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
769 }
770
771 /* If we're already browsing a subnode */
772 if (IndexRecord == NULL)
773 {
774 return STATUS_OBJECT_PATH_NOT_FOUND;
775 }
776
777 /* If there's no subnode */
778 if (!(IndexEntry->Flags & NTFS_INDEX_ENTRY_NODE))
779 {
780 return STATUS_OBJECT_PATH_NOT_FOUND;
781 }
782
783 Status = FindAttribute(Vcb, MftRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationCtx);
784 if (!NT_SUCCESS(Status))
785 {
786 DPRINT("Corrupted filesystem!\n");
787 return Status;
788 }
789
790 IndexAllocationSize = AttributeDataLength(&IndexAllocationCtx->Record);
791 Status = STATUS_OBJECT_PATH_NOT_FOUND;
792 for (RecordOffset = 0; RecordOffset < IndexAllocationSize; RecordOffset += IndexBlockSize)
793 {
794 ReadAttribute(Vcb, IndexAllocationCtx, RecordOffset, IndexRecord, IndexBlockSize);
795 Status = FixupUpdateSequenceArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs);
796 if (!NT_SUCCESS(Status))
797 {
798 break;
799 }
800
801 IndexBuffer = (PINDEX_BUFFER)IndexRecord;
802 ASSERT(IndexBuffer->Ntfs.Type == NRH_INDX_TYPE);
803 ASSERT(IndexBuffer->Header.AllocatedSize + FIELD_OFFSET(INDEX_BUFFER, Header) == IndexBlockSize);
804 FirstEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.FirstEntryOffset);
805 LastEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.TotalSizeOfEntries);
806 ASSERT(LastEntry <= (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexBuffer + IndexBlockSize));
807
808 Status = BrowseIndexEntries(NULL, NULL, NULL, 0, FirstEntry, LastEntry, FileName, StartEntry, CurrentEntry, DirSearch, OutMFTIndex);
809 if (NT_SUCCESS(Status))
810 {
811 break;
812 }
813 }
814
815 ReleaseAttributeContext(IndexAllocationCtx);
816 return Status;
817 }
818
819 NTSTATUS
820 NtfsFindMftRecord(PDEVICE_EXTENSION Vcb,
821 ULONGLONG MFTIndex,
822 PUNICODE_STRING FileName,
823 PULONG FirstEntry,
824 BOOLEAN DirSearch,
825 ULONGLONG *OutMFTIndex)
826 {
827 PFILE_RECORD_HEADER MftRecord;
828 PNTFS_ATTR_CONTEXT IndexRootCtx;
829 PINDEX_ROOT_ATTRIBUTE IndexRoot;
830 PCHAR IndexRecord;
831 PINDEX_ENTRY_ATTRIBUTE IndexEntry, IndexEntryEnd;
832 NTSTATUS Status;
833 ULONG CurrentEntry = 0;
834
835 DPRINT("NtfsFindMftRecord(%p, %I64d, %wZ, %u, %u, %p)\n", Vcb, MFTIndex, FileName, *FirstEntry, DirSearch, OutMFTIndex);
836
837 MftRecord = ExAllocatePoolWithTag(NonPagedPool,
838 Vcb->NtfsInfo.BytesPerFileRecord,
839 TAG_NTFS);
840 if (MftRecord == NULL)
841 {
842 return STATUS_INSUFFICIENT_RESOURCES;
843 }
844
845 Status = ReadFileRecord(Vcb, MFTIndex, MftRecord);
846 if (!NT_SUCCESS(Status))
847 {
848 ExFreePoolWithTag(MftRecord, TAG_NTFS);
849 return Status;
850 }
851
852 ASSERT(MftRecord->Ntfs.Type == NRH_FILE_TYPE);
853 Status = FindAttribute(Vcb, MftRecord, AttributeIndexRoot, L"$I30", 4, &IndexRootCtx);
854 if (!NT_SUCCESS(Status))
855 {
856 ExFreePoolWithTag(MftRecord, TAG_NTFS);
857 return Status;
858 }
859
860 IndexRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerIndexRecord, TAG_NTFS);
861 if (IndexRecord == NULL)
862 {
863 ReleaseAttributeContext(IndexRootCtx);
864 ExFreePoolWithTag(MftRecord, TAG_NTFS);
865 return STATUS_INSUFFICIENT_RESOURCES;
866 }
867
868 ReadAttribute(Vcb, IndexRootCtx, 0, IndexRecord, Vcb->NtfsInfo.BytesPerIndexRecord);
869 IndexRoot = (PINDEX_ROOT_ATTRIBUTE)IndexRecord;
870 IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)&IndexRoot->Header + IndexRoot->Header.FirstEntryOffset);
871 /* Index root is always resident. */
872 IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)(IndexRecord + IndexRoot->Header.TotalSizeOfEntries);
873 ReleaseAttributeContext(IndexRootCtx);
874
875 DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb->NtfsInfo.BytesPerIndexRecord, IndexRoot->SizeOfEntry);
876
877 Status = BrowseIndexEntries(Vcb, MftRecord, IndexRecord, IndexRoot->SizeOfEntry, IndexEntry, IndexEntryEnd, FileName, FirstEntry, &CurrentEntry, DirSearch, OutMFTIndex);
878
879 ExFreePoolWithTag(IndexRecord, TAG_NTFS);
880 ExFreePoolWithTag(MftRecord, TAG_NTFS);
881
882 return Status;
883 }
884
885 NTSTATUS
886 NtfsLookupFileAt(PDEVICE_EXTENSION Vcb,
887 PUNICODE_STRING PathName,
888 PFILE_RECORD_HEADER *FileRecord,
889 PULONGLONG MFTIndex,
890 ULONGLONG CurrentMFTIndex)
891 {
892 UNICODE_STRING Current, Remaining;
893 NTSTATUS Status;
894 ULONG FirstEntry = 0;
895
896 DPRINT("NtfsLookupFileAt(%p, %wZ, %p, %I64x)\n", Vcb, PathName, FileRecord, CurrentMFTIndex);
897
898 FsRtlDissectName(*PathName, &Current, &Remaining);
899
900 while (Current.Length != 0)
901 {
902 DPRINT("Current: %wZ\n", &Current);
903
904 Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, &Current, &FirstEntry, FALSE, &CurrentMFTIndex);
905 if (!NT_SUCCESS(Status))
906 {
907 return Status;
908 }
909
910 if (Remaining.Length == 0)
911 break;
912
913 FsRtlDissectName(Current, &Current, &Remaining);
914 }
915
916 *FileRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
917 if (*FileRecord == NULL)
918 {
919 DPRINT("NtfsLookupFileAt: Can't allocate MFT record\n");
920 return STATUS_INSUFFICIENT_RESOURCES;
921 }
922
923 Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord);
924 if (!NT_SUCCESS(Status))
925 {
926 DPRINT("NtfsLookupFileAt: Can't read MFT record\n");
927 ExFreePoolWithTag(*FileRecord, TAG_NTFS);
928 return Status;
929 }
930
931 *MFTIndex = CurrentMFTIndex;
932
933 return STATUS_SUCCESS;
934 }
935
936 NTSTATUS
937 NtfsLookupFile(PDEVICE_EXTENSION Vcb,
938 PUNICODE_STRING PathName,
939 PFILE_RECORD_HEADER *FileRecord,
940 PULONGLONG MFTIndex)
941 {
942 return NtfsLookupFileAt(Vcb, PathName, FileRecord, MFTIndex, NTFS_FILE_ROOT);
943 }
944
945 NTSTATUS
946 NtfsFindFileAt(PDEVICE_EXTENSION Vcb,
947 PUNICODE_STRING SearchPattern,
948 PULONG FirstEntry,
949 PFILE_RECORD_HEADER *FileRecord,
950 PULONGLONG MFTIndex,
951 ULONGLONG CurrentMFTIndex)
952 {
953 NTSTATUS Status;
954
955 DPRINT("NtfsFindFileAt(%p, %wZ, %u, %p, %p, %I64x)\n", Vcb, SearchPattern, *FirstEntry, FileRecord, MFTIndex, CurrentMFTIndex);
956
957 Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, SearchPattern, FirstEntry, TRUE, &CurrentMFTIndex);
958 if (!NT_SUCCESS(Status))
959 {
960 DPRINT("NtfsFindFileAt: NtfsFindMftRecord() failed with status 0x%08lx\n", Status);
961 return Status;
962 }
963
964 *FileRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
965 if (*FileRecord == NULL)
966 {
967 DPRINT("NtfsFindFileAt: Can't allocate MFT record\n");
968 return STATUS_INSUFFICIENT_RESOURCES;
969 }
970
971 Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord);
972 if (!NT_SUCCESS(Status))
973 {
974 DPRINT("NtfsFindFileAt: Can't read MFT record\n");
975 ExFreePoolWithTag(*FileRecord, TAG_NTFS);
976 return Status;
977 }
978
979 *MFTIndex = CurrentMFTIndex;
980
981 return STATUS_SUCCESS;
982 }
983
984 /* EOF */